Jump to content

Recommended Posts

Posted

Hey again. I've managed to succesfully implement a few different capabilities. However when I open a world the Server gets the correct values while my client doesn't. To solve this I thought I'd implement a packet that sends NBT data to the client and uses my Capabilities wrapper to set the correct values.

 

The error I get is the following: 

java.lang.ClassCastException: net.minecraft.client.network.NetHandlerPlayClient cannot be cast to net.minecraft.network.NetHandlerPlayServer
	at net.minecraftforge.fml.common.network.simpleimpl.MessageContext.getServerHandler(MessageContext.java:55) ~[MessageContext.class:?]
	at com.cheese.rpvp.capabilities.synchers.BarMessage$MessageHandler.onMessage(BarMessage.java:46) ~[BarMessage$MessageHandler.class:?]
	at com.cheese.rpvp.capabilities.synchers.BarMessage$MessageHandler.onMessage(BarMessage.java:38) ~[BarMessage$MessageHandler.class:?]
	at net.minecraftforge.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper.channelRead0(SimpleChannelHandlerWrapper.java:56) ~[SimpleChannelHandlerWrapper.class:?]
	at net.minecraftforge.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper.channelRead0(SimpleChannelHandlerWrapper.java:36) ~[SimpleChannelHandlerWrapper.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) ~[SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[MessageToMessageDecoder.class:4.0.23.Final]
	at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111) ~[MessageToMessageCodec.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) ~[DefaultChannelPipeline.class:4.0.23.Final]
	at io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:169) ~[EmbeddedChannel.class:4.0.23.Final]
	at net.minecraftforge.fml.common.network.internal.FMLProxyPacket.processPacket(FMLProxyPacket.java:109) [FMLProxyPacket.class:?]
	at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:157) [NetworkManager.class:?]
	at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:51) [NetworkManager.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.handleClientSideCustomPacket(NetworkDispatcher.java:410) [NetworkDispatcher.class:?]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:276) [NetworkDispatcher.class:?]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:73) [NetworkDispatcher.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) [DefaultChannelPipeline.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel.finishPeerRead(LocalChannel.java:326) [LocalChannel.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel.access$400(LocalChannel.java:45) [LocalChannel.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel$5.run(LocalChannel.java:312) [LocalChannel$5.class:4.0.23.Final]
	at io.netty.channel.local.LocalEventLoop.run(LocalEventLoop.java:33) [LocalEventLoop.class:4.0.23.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) [SingleThreadEventExecutor$2.class:4.0.23.Final]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]

 

My classes are as follows: 

package com.cheese.rpvp.capabilities.synchers;

import net.minecraftforge.fml.common.network.NetworkRegistry;
import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;

public class BarPacketHandler {

    public static final SimpleNetworkWrapper barWrapper = NetworkRegistry.INSTANCE.newSimpleChannel("rpvp");

}

This package defines my simpleChannel named rpvp after my client info. I create an instance of this class in my Main class with 

   public static BarPacketHandler packetHandler;

and then register it in my commonProxy like so:

BarPacketHandler.barWrapper.registerMessage(BarMessage.MessageHandler.class, BarMessage.class, 1, Side.CLIENT);

 

My goal here is to send a packet from the server (Since the server has the correct values) and pass them on to the client. Since the client needs to be updated. 

 

I subscribed an Event for PlayerLoggedInEvent that will get my Capability and save all relevant info to be updated to an NBTTagCompound and then calls the sendTo method from my Message class. 

    @SubscribeEvent
    public void loggedIn(PlayerLoggedInEvent event){
        if(!event.player.worldObj.isRemote) {
            if (event.player.hasCapability(CAPABILITY_BAR, EnumFacing.DOWN)) {
                final IBarHandler instance = getHandler(event.player);
                final NBTTagCompound tag = new NBTTagCompound();
                tag.setInteger("mana", instance.getMana());
                tag.setInteger("health", instance.getHealth());
                tag.setInteger("fatigue", instance.getFatigue());
                tag.setInteger("maxhealth", instance.getMaxHealth());
                tag.setInteger("maxmana", instance.getMaxMana());
                Main.packetHandler.barWrapper.sendTo(new BarMessage(tag), (EntityPlayerMP)event.player);
                System.out.println("Message sent");
            }
        }
    }

 

Then finally we have the actual BarMessage class with an inner class "MessageHandler". Both have the default method, so that can't be the error. 

 

public class BarMessage implements IMessage {
    public BarMessage(){}
    private NBTTagCompound toSend;
    public BarMessage(NBTTagCompound tag){
        this.toSend = tag;
    }

    @Override
    public void toBytes(ByteBuf buf) {
        System.out.println("toBytes called");
        ByteBufUtils.writeTag(buf, this.toSend);
    }

    @Override
    public void fromBytes(ByteBuf buf) {
        System.out.println("frombytes called");
        this.toSend = ByteBufUtils.readTag(buf);
    }
    public NBTTagCompound getTag(){
        return toSend;
    }

    public static class MessageHandler implements IMessageHandler<BarMessage, IMessage> {

        public MessageHandler(){}

        @Override
        public IMessage onMessage(BarMessage content, MessageContext ctx) {
            System.out.println("messagehandler called");

            EntityPlayerMP serverPlayer = ctx.getServerHandler().playerEntity;
            final IBarHandler handler = getHandler(serverPlayer);
            NBTTagCompound tag = content.toSend;
            handler.setMana(tag.getInteger("mana"));
            handler.setMaxMana(tag.getInteger("maxmana"));
            handler.setHealth(tag.getInteger("health"));
            handler.setMaxHealth(tag.getInteger("maxhealth"));
            handler.setFatigue(tag.getInteger("fatigue"));

            return null;
        }
    }
}

 

I have also tried a Variant of the onMessage where I've set content and ctx to final and tried to make an inner class that uses a scheduled task to set these things, but I assumed that since I'm not influencing the world directly it shouldn't be a problem. 

 

Now the main Issue 

I examined the MessageContext class, which returns the nethandler. Somewhere in my code I've somehow set my nethandler to NetHandlerPlayClient, and when I try to cast it using the getServerHandler it crashes since I only have the NetHandlerPlayClient.

 

I know I must be making some simple mistake that I overlooked, I've googled for a few hours now and It's driving me crazy.

 

I've tried to find a way to access playerEntity outside of the ctx, but that didn't work. I need playerEntity to pass to my getHandler(playerEntity) so I can call the methods for setting my Capabilities, the server sends the Target player with the message. so the Packet gets executed on the Client. I assumed that since I was running the handler on the client I could somehow use

 final IBarHandler handler = Minecraft.getMinecraft().thePlayer().GetCapability(CAPABILITY_BAR);

But that returned a nullpointer exception.

 

I've followed the documentation at https://mcforge.readthedocs.io/en/latest/networking/simpleimpl/ to the T, I don't quite understand what I'm doing wrong here, or what I could do to improve. Some hints would be appreciated.

Posted (edited)

The Nullpointer exception only occurs when I try to register an entity from Minecraft.GetMinecraft.theplayer.getCapability instead of 

PlayerEntityMP player = ctx.getServer etc. etc.

 

java.lang.NullPointerException
	at com.cheese.rpvp.capabilities.synchers.BarMessage$MessageHandler.onMessage(BarMessage.java:47) ~[BarMessage$MessageHandler.class:?]
	at com.cheese.rpvp.capabilities.synchers.BarMessage$MessageHandler.onMessage(BarMessage.java:40) ~[BarMessage$MessageHandler.class:?]
	at net.minecraftforge.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper.channelRead0(SimpleChannelHandlerWrapper.java:56) ~[SimpleChannelHandlerWrapper.class:?]
	at net.minecraftforge.fml.common.network.simpleimpl.SimpleChannelHandlerWrapper.channelRead0(SimpleChannelHandlerWrapper.java:36) ~[SimpleChannelHandlerWrapper.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) ~[SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[MessageToMessageDecoder.class:4.0.23.Final]
	at io.netty.handler.codec.MessageToMessageCodec.channelRead(MessageToMessageCodec.java:111) ~[MessageToMessageCodec.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) ~[AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) ~[DefaultChannelPipeline.class:4.0.23.Final]
	at io.netty.channel.embedded.EmbeddedChannel.writeInbound(EmbeddedChannel.java:169) ~[EmbeddedChannel.class:4.0.23.Final]
	at net.minecraftforge.fml.common.network.internal.FMLProxyPacket.processPacket(FMLProxyPacket.java:109) [FMLProxyPacket.class:?]
	at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:157) [NetworkManager.class:?]
	at net.minecraft.network.NetworkManager.channelRead0(NetworkManager.java:51) [NetworkManager.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.handleClientSideCustomPacket(NetworkDispatcher.java:410) [NetworkDispatcher.class:?]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:276) [NetworkDispatcher.class:?]
	at net.minecraftforge.fml.common.network.handshake.NetworkDispatcher.channelRead0(NetworkDispatcher.java:73) [NetworkDispatcher.class:?]
	at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:105) [SimpleChannelInboundHandler.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319) [AbstractChannelHandlerContext.class:4.0.23.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787) [DefaultChannelPipeline.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel.finishPeerRead(LocalChannel.java:326) [LocalChannel.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel.access$400(LocalChannel.java:45) [LocalChannel.class:4.0.23.Final]
	at io.netty.channel.local.LocalChannel$5.run(LocalChannel.java:312) [LocalChannel$5.class:4.0.23.Final]
	at io.netty.channel.local.LocalEventLoop.run(LocalEventLoop.java:33) [LocalEventLoop.class:4.0.23.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116) [SingleThreadEventExecutor$2.class:4.0.23.Final]
	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]

 

My Capability is registered in my main class in the preInit as such:


        CustomDataHandlerBar.register();

which obviously tells you nothing. the Barhandler is 

public class CustomDataHandlerBar {

    @CapabilityInject(IBarHandler.class)
    public static final Capability<IBarHandler> CAPABILITY_BAR = null;

    public static void register(){
        CapabilityManager.INSTANCE.register(IBarHandler.class, new Storage(), DefaultBarHandler.class);
        MinecraftForge.EVENT_BUS.register(new CustomDataHandlerBar());
    }

    @SubscribeEvent
    public void attachCapabilities(AttachCapabilitiesEvent<Entity> event){
        if(event.getObject() instanceof EntityPlayer)
            event.addCapability(new ResourceLocation(Main.MODID, "BARS"), new BarProvider());


    }
    @SubscribeEvent
    public void clonePlayer(PlayerEvent.Clone event){

        final IBarHandler original = getHandler(event.getOriginal());
        final IBarHandler clone = getHandler(event.getEntity());
        clone.setMana(original.getMana());
        clone.setMaxMana(original.getMaxMana());
        clone.setHealth(original.getHealth());
        clone.setMaxHealth(original.getMaxHealth());
        clone.setFatigue(original.getFatigue());
    }

    @SubscribeEvent
    public void loggedIn(PlayerLoggedInEvent event){
        if(!event.player.worldObj.isRemote) {
            if (event.player.hasCapability(CAPABILITY_BAR, EnumFacing.DOWN)) {
                final IBarHandler instance = getHandler(event.player);
                final NBTTagCompound tag = new NBTTagCompound();
                tag.setInteger("mana", instance.getMana());
                tag.setInteger("health", instance.getHealth());
                tag.setInteger("fatigue", instance.getFatigue());
                tag.setInteger("maxhealth", instance.getMaxHealth());
                tag.setInteger("maxmana", instance.getMaxMana());
                Main.packetHandler.barWrapper.sendTo(new BarMessage(tag), (EntityPlayerMP)event.player);
                System.out.println("Message sent");
            }
        }
    }

    public static IBarHandler getHandler(Entity entity){
            if(entity.hasCapability(CAPABILITY_BAR, EnumFacing.DOWN)){
                System.out.println("Player has capability");
                return entity.getCapability(CAPABILITY_BAR, EnumFacing.DOWN);
            }
        return null;
    }


}

 

I'm able to interact with the Capability and I'm able to set different values using items and equipment and such. I'm not sure if the problem lies with the Capability system.  Though I'm not entirely sure why the server end saves my information properly yet the Client doesn't synchronise with it.  The Forge classes are pretty complex for someone with only a year of Java experience and there's not a lot of online knowledge to find on the current version and what to use, I'm mostly just going by feel and the official docs, which also don't cover much.

 

That's why I'm creating the packet, to keep the client synchronized and to learn how to create packets for the future.

 

Also, when I comment out the Capabilities stuff in the original entityplayerMP = method I'm still getting the same Can't cast error. 

Edited by oldcheese
clarification.
Posted (edited)

 

Quote

 

Warning

As of Minecraft 1.8 packets are by default handled on the network thread.

That means that your IMessageHandler can not interact with most game objects directly. Minecraft provides a convenient way to make your code execute on the main thread instead using IThreadListener.addScheduledTask.

The way to obtain an IThreadListener is using either the Minecraft instance (client side) or a WorldServer instance (server side). The code above shows an example of this by getting a WorldServer instance from an EntityPlayerMP.

 

http://mcforge.readthedocs.io/en/latest/networking/simpleimpl/

Edited by Draco18s
  • Like 1

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Posted
2 minutes ago, Draco18s said:

 

Welp. That's it. 

 

I've tried to change it to a similar code. But I was using ctx instead of Minecraft.GetMinecraft to initiate my IthreadListener.

 

It seems like every time I get stuck on something for multiple hours it turns out to be a big oversight I should've noticed.

 

 

For future readers who run into my topic. The final code for my onMessage ended up being 

 

        @Override
        public IMessage onMessage(final BarMessage content,final MessageContext ctx) {

            IThreadListener myThread = Minecraft.getMinecraft();
            myThread.addScheduledTask(new Runnable(){
                @Override
                public void run(){
                    System.out.println("messagehandler called");
                    final IBarHandler handler = Minecraft.getMinecraft().thePlayer.getCapability(CAPABILITY_BAR, EnumFacing.DOWN);
                    NBTTagCompound tag = content.toSend;
                    handler.setMana(tag.getInteger("mana"));
                    handler.setMaxMana(tag.getInteger("maxmana"));
                    handler.setHealth(tag.getInteger("health"));
                    handler.setMaxHealth(tag.getInteger("maxhealth"));
                    handler.setFatigue(tag.getInteger("fatigue"));

                }
            });
                return null;
        }

 

 

Thanks for the help everyone.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Hi,  I'm using Forge 47.3.0 for Minecraft 1.20.1 I apologise if this is obvious I am very new to modding for Minecraft. I sucessfully made a mod that launched without errors or crashes (without it doing anything) but in order to add the features I need, I need to add "Custom Portal API [Forge]" as a dependency. However no matter the way I've tried to acheive this, it crashes. I am pretty sure it's not the way I'm putting it in the repositories, the dependencies or the way I'm refrencing it, as I've a hundred diffrent combinations and multiple Maven methods. And on all those diffrent variations I still get this crash: pastebin.com/UhumzZCZ Any tips would be invaluable as I've been loosing my mind over this!
    • Hi, i'm really having problems trying to set the texture to my custom item. I thought i'm doing everything correctly, but all i see is the missing texture block for my item. I am trying this for over a week now and getting really frustrated. The only time i could make the texture work, was when i used an older Forge version (52.0.1) for Minecraft (1.21.4). Was there a fundamental change for textures and models somewhere between versions that i'm missing? I started with Forge 54.1.0 and had this problem, so in my frustration i tried many things: Upgrading to Forge 54.1.1, created multiple new projects, workspaces, redownloaded everything and setting things up multiple times, as it was suggested in an older thread. Therea are no errors in the console logs, but maybe i'm blind, so i pasted the console logs to pastebin anyway: https://pastebin.com/zAM8RiUN The only time i see an error is when i change the models JSON file to an incorrect JSON which makes sense and that suggests to me it is actually reading the JSON file.   I set the github repository to public, i would be so thankful if anyone could take a look and tell me what i did wrong: https://github.com/xLorkin/teleport_pug_forge   As a note: i'm pretty new to modding, this is my first mod ever. But i'm used to programming. I had some up and downs, but through reading the documentation, using google and experimenting, i could solve all other problems. I only started modding for Minecraft because my son is such a big fan and wanted this mod.
    • Please read the FAQ (link in orange bar at top of page), and post logs as described there.
    • Hello fellow Minecrafters! I recently returned to Minecraft and realized I needed a wiki that displays basic information easily and had great user navigation. That’s why I decided to build: MinecraftSearch — a site by a Minecraft fan, for Minecraft fans. Key Features So Far Straight-to-the-Point Info: No extra fluff; just the essentials on items, mobs, recipes, loot and more. Clean & Intuitive Layout: Easy navigation so you spend less time scrolling and more time playing. Optimized Search: Search for anything—items, mobs, blocks—and get results instantly. What I’m Thinking of Adding More data/information: Catch chances for fishing rod, traveling villager trades, biomes info and a lot more. The website is still under development and need a lot more data added. Community Contributions: Potential for user-uploaded tips for items/mobs/blocks in the future. Feature Requests Welcome: Your ideas could shape how the wiki evolves! You can see my roadmap at the About page https://minecraftsearch.com/about I’d love for you to check out MinecraftSearch and see if it helps you find the info you need faster. Feedback is crucial—I want to develop this further based on what the community needs most, so please let me know what you think. Thanks, and happy crafting!
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.