Jump to content

Recommended Posts

Posted (edited)

I have a fairly simple laser entity based on Arrow abstract classes which I have been trying to troubleshoot to no avail.

The entity is spawned, but the renderer does not fire on the client.

No errors are thrown, it just doesn't appear.

 

Laser cannon (Item) fires Laser (Entity) on a right-click (similar to an arrow, but no pre-load)

ItemLaserCannon is extended from Item

EntityLaser is extended from AbstractArrowEntity

RenderLaser is extended from ArrowRenderer

 

So far I have determined the following

1. The entity is being created and runs in Server Thread (caught breakpoint in Tick method, confirmed interaction with player)

2. The renderer appears to be successfully registered (caught breakpoint registry event injection)

3. No errors or anomalies are being registered in the logs.

4. Renderer does not appear to be running in Client Thread (breakpoint in "doRender" method is not caught)

 

I think I have registered everything correctly, another entity I created in the same mod is functioning as expected.

I used this Item/Entity in 1.12 and it worked fine, but in that case I copied the rendering code instead of extending an existing class 

 

My Code 

 

TIA

/P

 

Edited by PhilipChonacky
Posted
19 hours ago, diesieben07 said:

By extending AbstractArrowEntity the game uses SSpawnObjectPacket as the spawn packet. This packet can only handle a set few vanilla entities.

You need to override Entity#createSpawnPacket and return a custom packet which will spawn your entity on the client.

Stop me if I'm going down a rabbit hole, but using SSpawnObjectPacket as a reference, I need to implement IPacket<IClientPlayNetHandler> which has a limited set of handlers for vanilla SpawnPackets (Object, Mob, etc.)

Can I extend something simple like SSpawnGlobalEntityPacket (which has a method in IClientPlayNetHandler) or would it be better to implement a Client Side spawning method [in place of implementing IClientPlayNetHandler?]

 

I may be getting in over my head as IClientPlayNetHandler seems to implement several Thread safeguards that I'm not familiar with.

 

Bonus question: SSpawnObjectPacket seems to use a deprecated methods for identifying/retrieving the EntityEntry  so it can be serialized and sent to the Client - is there a preferred method? The only thing I could come up with is retrieving the entire map from the [Forge] Registry and transmitting the key for lookup on the Client side (which could be just reinventing the wheel)

 

thanks again.

/P

Posted (edited)

OK, I read through the SimpleChannel documentation [several times], and have some idea about how it operates.  I also read some of the code example here, with the understanding that since IMessage no longer exists, it is not required to implement it in the custom packet classes. (just create the required methods and register them through the registerMessage method).

 

The question I still have is whether I need to create a handler (or not) to spawn the Entity on the client.   The docs say that it is handled in Forge, but don’t specify whether some method needs to be invoked (or event caught).  A further question if Forge handles the client-side spawn is  which data do I need to send.

 

TIA

/P

Edited by PhilipChonacky
embellishment
Posted

Do I need to spawn the custom entity using my custom packet handler, or is there already a process within Forge that will handle that

(and if so, which process is it and what data do I need to send).

 

Apologies if this sounds like a stupid question, but much of the networking process is a bit over my head, and I haven't as of yet found any other code examples for spawning custom entities in 1.13/1.14].  I tend to learn much better by example

 

Thanks again.

/P 

Posted (edited)

The more I'm thinking about this, the more I'm thinking that my custom packet 'handle' method needs a spawn [LaserEntity] in it.  The only remaining question would be what I data would I need (UUID?) in order to keep it associated with the copy on the server so that it properly receives tracking updates.

 

Comments?

 

I promise when this is all over I will write a brief tutorial for others (assuming I'm not the only one going down this road)

 

/P

Edited by PhilipChonacky
spelling
Posted (edited)

For the past couple days, I have been attempting to make this work creating what I believe to be the correct class files.

The original reason that object entities (extended from AbstractArrowEntity and probably Snowball as well) won't spawn on the client is that the Class ClientPlayNetHandler which handles SSPawnObjectSpawnPackets is testing for specific Vanilla EntityTypes and doesn't recognize my custom entity.

 

I have created the following classes to implement a SimpleChannel spawn packet

1. LaserSpawnPacket.java which is the spawn packet class - includes encode, decode, and handle methods per the requirements.  I put the code for spawning the entity [Client Side], but I'm not clear if it should go here, or I should create an additional class (Client only) to perform the spawn.

2. ChickenModPacketHandler.java which holds the static SimpleChannel instance, and registers the spawn packet methods

 

EntityLaser.java is the [Object] Entity I am trying to spawn, it overrides the createSpawnPacket method from AbstractArrowEntity.

EntityLaser is spawned from an onRightClick method from ItemLaserCannon.java.

 

Where I'm currently stuck:

1. The Java compiler is not accepting my methods as meeting the requirements (int, Class<MSG>, BiConsumer<MSG,PacketBuffer>, Function<PacketBuffer,MSG>, BiConsumer<MSG,Supplier<NetworkEvent.Context>>) and I'm not sure why - to my knowledge (which with regards to  Java/Minecraft is somewhat limited) I have done it correctly.

[fixed, I was loading the wrong Supplier library]

 

2. As mentioned above, I'm not sure if I should spawn the client-side entity from my own method, or hand it off somehow to a Forge method

[I determined that the SpawnPacket class is responsible for this, that's why we use NetworkEvent.Context#get.enqueueWork to execute on the Main (Client) thread]

 

3. As I'm not really sure how Minecraft tracks the plurality of entities between Server and Client, I don't know if I'm successful, whether tracking from Server to Client would continue (or not).  Assigning ID?

 

Any help would be appreciated, as I have looked around, I haven't found any mention of others doing this in 1.14, and the only advise I have gotten so far is "override Entity#createSpawnPacket and return a custom packet which will spawn your entity on the client.".  Maybe I'm overlooking something obvious.  in 1.12 it was as simple as extending AbstractArrow or Snowball and it would work with relatively little coding, since 1.13, all this has changed.

Edited by PhilipChonacky
update
Posted (edited)

Hey, so I've managed to spawn my custom entity by letting my custom spawn packet implement

IPacket<ClientPlayNetHandler>

and processing the packet in the packet class as well by using some of the code from

ClientPlayNetHandler - handleSpawnObject(SSpawnObjectPacket packetIn)

 

I also return a new packet instance in my entity class in the createSpawnPacket() method.

 

i.e.

process method in my packet class

@Override
public void processPacket(ClientPlayNetHandler handler)
{
        Entity entity = entityType.create(handler.getWorld());

        entity.func_213312_b(this.x, this.y, this.z);
        entity.rotationPitch = (float)(this.pitch * 360) / 256.0F;
        entity.rotationYaw = (float)(this.yaw * 360) / 256.0F;
        entity.setEntityId(this.entityId);
        entity.setUniqueId(this.uuid);
        handler.getWorld().addEntity(this.entityId, entity);
}

 

createSpawnPacket() in my entity class

@Override
public IPacket<?> createSpawnPacket()
{
        return new MagicModEntitySpawnPacket(MagicModRegistry.TestEntityType, this.getEntityId(), this.entityUniqueID, this.posX, this.posY, this.posZ, this.rotationYaw, this.rotationPitch);
}

 

Edited by FilUnderscore
code examples
Posted (edited)
7 hours ago, FilUnderscore said:

Hey, so I've managed to spawn my custom entity by letting my custom spawn packet implement


IPacket<ClientPlayNetHandler>

and processing the packet in the packet class as well by using some of the code from


ClientPlayNetHandler - handleSpawnObject(SSpawnObjectPacket packetIn)

 

I also return a new packet instance in my entity class in the createSpawnPacket() method.

How did you handle the networking? ClientPlayNetHandler  usually receives the spawn packets.  Is there an event that can be subscribed to?

Do you have a GitRepo I can look at?

 

I've seen other posts advising to stay away from IPacket, so I didn't and sent the packet using SimpleChannel 

(returning super.createSpawnPacket to keep MC from crashing)

 

...not working yet, but at least it doesn't crash

 

I'll post my code when I get home

Edited by PhilipChonacky
Posted

Update: I got the SimpleChannel system to work (mostly)

entityLaser#createSpawnPacket sends packet with PacketDistributor.TRACKING_CHUNK  which is passed and handled by LaserSpawnPacket.Handler#handle

I confirmed the new EntityLaser instance is accurately created, but for whatever reason, it doesn't get added to chunk (if I'm understanding that field entity correctly)

...need to double-check my math, I may be spawning in the wrong location

Posted (edited)
4 hours ago, PhilipChonacky said:

Update: I got the SimpleChannel system to work (mostly)

entityLaser#createSpawnPacket sends packet with PacketDistributor.TRACKING_CHUNK  which is passed and handled by LaserSpawnPacket.Handler#handle

I confirmed the new EntityLaser instance is accurately created, but for whatever reason, it doesn't get added to chunk (if I'm understanding that field entity correctly)

...need to double-check my math, I may be spawning in the wrong location

After reading your previous responses, I've managed to do the same thing and got my entity to spawn too - following the SimpleChannel implementation, the main thing is that apparently you need to call

world.addEntity(entID, entityInstance)

instead of world.addEntity(entityInstance)

Edited by FilUnderscore
Posted (edited)
19 hours ago, FilUnderscore said:

you need to call


world.addEntity(entID, entityInstance)

instead of world.addEntity(entityInstance)

Which version of MC are you using?  I'm using 1.14.3 [Forge 27.0.25] and that overload isn't available

Edited by PhilipChonacky
Posted (edited)
3 hours ago, PhilipChonacky said:

Which version of MC are you using?  I'm using 1.14.3 [Forge 27.0.25] and that overload isn't available

1.14.3 - Forge 27.0.51

It's a method in the ClientWorld class in this version of Forge.

Edited by FilUnderscore
Posted (edited)

Using that [ClientWorld], and I spawned the entity - now I have a thread violation (reaching across server/client).

I think the server thread is crashing or getting caught in a loop

 

Repo Updated

 

...I think I need to make sure the handler code runs only on the Client

 

 

Edited by PhilipChonacky
Posted (edited)
5 hours ago, PhilipChonacky said:

Using that [ClientWorld], and I spawned the entity - now I have a thread violation (reaching across server/client).

I think the server thread is crashing or getting caught in a loop

 

Repo Updated

 

...I think I need to make sure the handler code runs only on the Client

I believe that this line:

world.addEntity(msg.shooterId,entity);

should be:

world.addEntity(entity.getEntityID(),entity);

 

I don't know whether this has anything to do with the threading?

Edited by FilUnderscore
Posted
9 minutes ago, FilUnderscore said:

I believe that this line:


world.addEntity(msg.shooterId,entity);

should be:


world.addEntity(entity.getEntityID(),entity);

 

I don't know whether this has anything to do with the threading?

That doesn't make sense.  Entity ID is an index number given to keep track of the entity in the world.

Normally when you create a new entity, this number is incremented (so all entries are unique)

I think this is the filed that MC uses to synchronize objects between server and client side.

Using ClientWorld#addEntity(id, entity) adds the entity to the [client] world using the provided ID instead of the  ID from the entity you just created

public void addEntity(int p_217411_1_, Entity p_217411_2_) {
      this.addEntityImpl(p_217411_1_, p_217411_2_);
   }

   private void addEntityImpl(int p_217424_1_, Entity p_217424_2_) {
      if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_217424_2_, this))) return;
      this.removeEntityFromWorld(p_217424_1_);
      this.entitiesById.put(p_217424_1_, p_217424_2_);
      this.getChunkProvider().getChunk(MathHelper.floor(p_217424_2_.posX / 16.0D), MathHelper.floor(p_217424_2_.posZ / 16.0D), ChunkStatus.FULL, true).addEntity(p_217424_2_);
      p_217424_2_.onAddedToWorld();
   }

 

using addEntity(entity.getEntityID(),entity) copies the ID from  the entity you just created, which is the next sequential ID, that's why I pull the ID from the spawn packet (which has the original ID).

I resolved the cross-side [thread] issue by creating a separate client-only Class [ClientWork] and putting the spawn code there as a static method.  It works now, but the client rendered entity disappears almost immediately.  If I increment the ID, it spawns an unsynchronized client entity that just drops to the ground (can't even kill it with '/kill @e' .

Updated Code

...so close

Posted (edited)
12 minutes ago, PhilipChonacky said:

That doesn't make sense.  Entity ID is an index number given to keep track of the entity in the world.

Normally when you create a new entity, this number is incremented (so all entries are unique)

I think this is the filed that MC uses to synchronize objects between server and client side.

Using ClientWorld#addEntity(id, entity) adds the entity to the [client] world using the provided ID instead of the  ID from the entity you just created


public void addEntity(int p_217411_1_, Entity p_217411_2_) {
      this.addEntityImpl(p_217411_1_, p_217411_2_);
   }

   private void addEntityImpl(int p_217424_1_, Entity p_217424_2_) {
      if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.entity.EntityJoinWorldEvent(p_217424_2_, this))) return;
      this.removeEntityFromWorld(p_217424_1_);
      this.entitiesById.put(p_217424_1_, p_217424_2_);
      this.getChunkProvider().getChunk(MathHelper.floor(p_217424_2_.posX / 16.0D), MathHelper.floor(p_217424_2_.posZ / 16.0D), ChunkStatus.FULL, true).addEntity(p_217424_2_);
      p_217424_2_.onAddedToWorld();
   }

 

using addEntity(entity.getEntityID(),entity) copies the ID from  the entity you just created, which is the next sequential ID, that's why I pull the ID from the spawn packet (which has the original ID).

I resolved the cross-side [thread] issue by creating a separate client-only Class [ClientWork] and putting the spawn code there as a static method.  It works now, but the client rendered entity disappears almost immediately.  If I increment the ID, it spawns an unsynchronized client entity that just drops to the ground (can't even kill it with '/kill @e' .

Updated Code

...so close

I do set my client-side entity's ID and UUID through entity.setEntityID() and entity.setUniqueID() from the packet before adding it, which I notice you aren't doing?

Edited by FilUnderscore
Posted (edited)

 

 I put in logging code to track the entity on both Server and Client, which clearly shows the Entity gets spawned on both Server & Client with matching IDs, but the rendering stops almost immediately.  The Client-side entity is

being removed after one or two ticks.   Not sure why this is happening.  

 

Edited by PhilipChonacky
Update
Posted (edited)

This solved the problem for me

First register your entity with custom client factory like this 

 

.setCustomClientFactory((spawnEntity, world) -> new ExempleEntity(world))

 

And then use NetworkHooks#getEntitySpawningPacket to get Entity Spawning Packet

 

    @Override
    public IPacket<?> createSpawnPacket() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }

 

that's it ?

Edited by xieao
  • Thanks 6
Posted (edited)
2 hours ago, xieao said:

This solved the problem for me

First register your entity with custom client factory like this 

 


.setCustomClientFactory((spawnEntity, world) -> new ExempleEntity(world))

 

And then use forge hooks to get Entity Spawning Packet

 


    @Override
    public IPacket<?> createSpawnPacket() {
        return NetworkHooks.getEntitySpawningPacket(this);
    }

 

that's it ?

That was it! Thanks! [so simple]

 

If only that had been mentioned before, I would have had to try and reinvent the wheel.

 

Edited by PhilipChonacky

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



×
×
  • Create New...

Important Information

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