Jump to content

Paintings and Packets


Recommended Posts

I'm making a new mod that subclasses paintings. Instead of just one texture file full of paintings, I will have 16 files, the file number being set to the color of carpet (in place of wool) used to craft my new painting item. I think I got the item-to-entity stuff figured out, and I even wrote a renderer. However, the client-server communication has just thrown me for a loop.


Because my alt painting entity has an extra parameter (the texture number), the vanilla packet & handler doesn't work (I wish it had wrapped and sent the NBT tag, 'cause I overrode the read and write of that, but no such luck). It looks like I'll need to define my own packet to replace S10PacketSpawnPainting. That doesn't look to hard, but...


Where's the hook to tell MC to actually use my packet when it has my subclass of EntityPainting? Right now, it detects an instance of EntityPainting and flows into the vanilla handler for paintings (where it blows up). Is it even possible for me to intercept that flow and insert my own code? I don't think I need a whole channel, just a custom packet on whatever channel is used for paintings.


Granted, I've only done an hour's reading on packets, but all I've done is confuse myself more. The one "tutorial" I found online told me all about what to put into a packet, but it said nothing about how to get MC to use it. Is there an explanation somewhere that you can point me to?




The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

I think you are searching for IEntityAdditionalSpawnData.

I think that's it!  :)


If I understand it correctly, then it's only for the class members beyond standard entity stuff (x, y, z are already handled). I'll pick thru EntitySpawnMessage in more detail before deciding what needs to be in my read and write, but it looks as if EntityHanging is not one of the subclasses served, so I'll start from there down my inheritance line.


I also found an entity registration method. With just one entity class in my mod, I'm calling thusly in my main's init method:


    EntityRegistry.registerModEntity (classAltPainting.class, MODID, 0, MODID, 128, 20, false);


I'll check the other info posted here to see if I should pass different values.

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

  • 2 weeks later...

If anyone searching similar problems comes upon this thread and wonders what code solved mine, here it is. What I ended up doing was a bit more involved than the above suggestions, but that's because:


a) I'm stubborn, and

b) Modding is a Java learning exercise for me, and I need to learn reflection.


Implementing the IEntityAdditionalSpawnData interface let me add to and read from the existing painting packet, but FML's EntitySpawnMessage doesn't call my read method until after constructing my entity with the simplest of all constructors. My parent class, EntityPainting, handled its packet by reading the data and then calling an elaborate constructor. If I waited until my read was called, then I would not be able to use the constructors  I wanted (mine and parent and grandparent). That would create problems.


Fortunately, I noticed another interesting action in EntitySpawnMessage: It could be told to call a "custom spawn method". This looked like what I wanted. I resolved to write my own entity spawn method that would read my extra data and then call the constructor I wanted. But there was a hitch: My extra data was locked away in the message's private datastream, and the message, not datastream, was what got passed into the custom spawn method.


The data was close enough that I could taste it. Looking at the control flow, I could see that the datastream had already disgorged its other data and had no purpose except to be passed to me (into my read method a few lines further down). It was ready for me if I could just find a way to crack it. [Of course, if the coding of EntitySpawnMessage ever changes this, then my mod will blow sky high!]


This is where I decided to study reflection, especially how to look into private fields I'm not supposed to read. An online Java tutorial showed me how to pick apart the message class, look at a field, change the reflected field's privacy setting, and then access my data via the reflected field (the compiler still protected against normal access).


Finally, the message handling occurs only on the client side. That meant that I ran into some SideOnly issues that forced me to learn about proxies. I'm still sorting out the meaning of SideOnly, especially when applied to things other than whole classes, so don't trust my use of the attribute in the snips below.


Putting these things together, here are the relevant code snippets...


Set custom spawning:

public class ClientProxy extends CommonProxy {

  public void init(FMLInitializationEvent e) {          // Register client-specific stuff (renderer & callback)
    super.init (e);
    ModContainer mc = Loader.instance ().getIndexedModList ().get (classAltPaintingsMod.MODID);
    EntityRegistration registration = EntityRegistry.instance ().lookupModSpawn (mc, 0); // my mod's only entity is #0
    registration.setCustomSpawning (new classAltPaintingSpawnCallback (), false);


The callback class that cracks datastream's privacy using reflection:

public class classAltPaintingSpawnCallback implements Function<EntitySpawnMessage, Entity> {

  protected Field fieldStream;

  public classAltPaintingSpawnCallback() {      // This will be constructed in the init phase
    System.out.println ("Constructing classAltPaintingSpawnCallback");
    try {   // Crack open the message and enable access to dataStream via Field variable (reflection)
      this.fieldStream = EntitySpawnMessage.class.getDeclaredField ("dataStream");
      this.fieldStream.setAccessible (true);
    } catch (NoSuchFieldException e) {
      System.out.println ("ERROR: In classAltPaintingSpawnCallback constructor, " + e.getMessage ());

   * Helper class with one function to parse message and call the correct entity constructor that returns an instance of my entity
   * class. Got all that? I wrote it, and it still makes my head hurt.
   * Why does the message protocol delay reading my extra data until after entity construction? I may never know, but I do know that
   * it's there waiting for me, so I'm taking it by force. 
  public Entity apply(EntitySpawnMessage m) {
    WorldClient wc = FMLClientHandler.instance ().getWorldClient ();
    try {
      ByteBuf b = (ByteBuf) fieldStream.get (m);    // Steal the dataStream from message m
      int x = b.readInt ();                         // world tile x (+ is East)
      int y = b.readInt ();                         // world tile y (+ is Up)
      int z = b.readInt ();                         // world tile z (+ is South)
      int SWNE = b.readByte ();                     // Compass dir 0-3 is S, W, N, E
      int lot = b.readByte ();                      // Texture number 0-15 taken from carpet color used to craft item
      String title = ByteBufUtils.readUTF8String (b);
      return new classAltPainting (wc, x, y, z, SWNE, lot, title);
    } catch (ReflectiveOperationException e) {
      System.out.println ("ERROR:  In classAltPaintingSpawnCallback.apply(), " + e.getMessage ());
      return null;


Amazingly, this now works in client-server multiplayer Minecraft Forge 1.7.10. All I need to do now is make 15 more texture files filled with 31 paintings each  :o

The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.

Link to comment
Share on other sites

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.

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.


  • Create New...

Important Information

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