diff --git a/src/main/java/net/neoforged/neoforge/network/PlayMessages.java b/src/main/java/net/neoforged/neoforge/network/PlayMessages.java index 97b6a91682..1b27e217fe 100644 --- a/src/main/java/net/neoforged/neoforge/network/PlayMessages.java +++ b/src/main/java/net/neoforged/neoforge/network/PlayMessages.java @@ -46,86 +46,6 @@ public static class SpawnEntity { private final int velX, velY, velZ; private final FriendlyByteBuf buf; - SpawnEntity(Entity e) { - this.entity = e; - this.typeId = BuiltInRegistries.ENTITY_TYPE.getId(e.getType()); //TODO: Codecs - this.entityId = e.getId(); - this.uuid = e.getUUID(); - this.posX = e.getX(); - this.posY = e.getY(); - this.posZ = e.getZ(); - this.pitch = (byte) Mth.floor(e.getXRot() * 256.0F / 360.0F); - this.yaw = (byte) Mth.floor(e.getYRot() * 256.0F / 360.0F); - this.headYaw = (byte) (e.getYHeadRot() * 256.0F / 360.0F); - Vec3 vec3d = e.getDeltaMovement(); - double d1 = Mth.clamp(vec3d.x, -3.9D, 3.9D); - double d2 = Mth.clamp(vec3d.y, -3.9D, 3.9D); - double d3 = Mth.clamp(vec3d.z, -3.9D, 3.9D); - this.velX = (int) (d1 * 8000.0D); - this.velY = (int) (d2 * 8000.0D); - this.velZ = (int) (d3 * 8000.0D); - this.buf = null; - } - - private SpawnEntity(int typeId, int entityId, UUID uuid, double posX, double posY, double posZ, byte pitch, byte yaw, byte headYaw, int velX, int velY, int velZ, FriendlyByteBuf buf) { - this.entity = null; - this.typeId = typeId; - this.entityId = entityId; - this.uuid = uuid; - this.posX = posX; - this.posY = posY; - this.posZ = posZ; - this.pitch = pitch; - this.yaw = yaw; - this.headYaw = headYaw; - this.velX = velX; - this.velY = velY; - this.velZ = velZ; - this.buf = buf; - } - - public static void encode(SpawnEntity msg, FriendlyByteBuf buf) { - buf.writeVarInt(msg.typeId); - buf.writeInt(msg.entityId); - buf.writeLong(msg.uuid.getMostSignificantBits()); - buf.writeLong(msg.uuid.getLeastSignificantBits()); - buf.writeDouble(msg.posX); - buf.writeDouble(msg.posY); - buf.writeDouble(msg.posZ); - buf.writeByte(msg.pitch); - buf.writeByte(msg.yaw); - buf.writeByte(msg.headYaw); - buf.writeShort(msg.velX); - buf.writeShort(msg.velY); - buf.writeShort(msg.velZ); - if (msg.entity instanceof IEntityAdditionalSpawnData entityAdditionalSpawnData) { - final FriendlyByteBuf spawnDataBuffer = new FriendlyByteBuf(Unpooled.buffer()); - - entityAdditionalSpawnData.writeSpawnData(spawnDataBuffer); - - buf.writeVarInt(spawnDataBuffer.readableBytes()); - buf.writeBytes(spawnDataBuffer); - - spawnDataBuffer.release(); - } else { - buf.writeVarInt(0); - } - } - - public static SpawnEntity decode(FriendlyByteBuf buf) { - return new SpawnEntity(buf.readVarInt(), buf.readInt(), new UUID(buf.readLong(), buf.readLong()), buf.readDouble(), buf.readDouble(), buf.readDouble(), buf.readByte(), buf.readByte(), buf.readByte(), buf.readShort(), buf.readShort(), buf.readShort(), readSpawnDataPacket(buf)); - } - - private static FriendlyByteBuf readSpawnDataPacket(FriendlyByteBuf buf) { - final int count = buf.readVarInt(); - if (count > 0) { - final FriendlyByteBuf spawnDataBuffer = new FriendlyByteBuf(Unpooled.buffer()); - spawnDataBuffer.writeBytes(buf, count); - return spawnDataBuffer; - } - - return new FriendlyByteBuf(Unpooled.buffer()); - } public static boolean handle(SpawnEntity msg, NetworkEvent.Context ctx) { try { diff --git a/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java b/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java index 6abadc6a5b..d92d63cc9f 100644 --- a/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java +++ b/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java @@ -2,18 +2,26 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import io.netty.buffer.Unpooled; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.Level; +import net.neoforged.neoforge.common.util.LogicalSidedProvider; +import net.neoforged.neoforge.entity.IEntityAdditionalSpawnData; import net.neoforged.neoforge.network.ConfigSync; import net.neoforged.neoforge.network.handling.ConfigurationPayloadContext; -import net.neoforged.neoforge.network.payload.ConfigFilePayload; -import net.neoforged.neoforge.network.payload.FrozenRegistryPayload; -import net.neoforged.neoforge.network.payload.FrozenRegistrySyncCompletePayload; -import net.neoforged.neoforge.network.payload.FrozenRegistrySyncStartPayload; +import net.neoforged.neoforge.network.handling.PlayPayloadContext; +import net.neoforged.neoforge.network.payload.*; import net.neoforged.neoforge.registries.ForgeRegistry; import net.neoforged.neoforge.registries.GameData; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -59,4 +67,32 @@ public void handle(ConfigurationPayloadContext context, FrozenRegistrySyncComple public void handle(ConfigurationPayloadContext context, ConfigFilePayload payload) { ConfigSync.INSTANCE.receiveSyncedConfig(payload.contents(), payload.fileName()); } + + public void handle(PlayPayloadContext context, AdvancedAddEntityPayload msg) { + EntityType type = BuiltInRegistries.ENTITY_TYPE.byId(msg.typeId()); + Optional world = LogicalSidedProvider.CLIENTWORLD.get(ctx.getDirection().getReceptionSide()); + Entity e = world.map(w -> type.customClientSpawn(msg, w)).orElse(null); + if (e == null) { + return; + } + + /* + * Sets the postiion on the client, Mirrors what + * Entity#recreateFromPacket and LivingEntity#recreateFromPacket does. + */ + e.syncPacketPositionCodec(msg.posX(), msg.posY(), msg.posZ()); + e.absMoveTo(msg.posX(), msg.posY(), msg.posZ(), (msg.yaw() * 360) / 256.0F, (msg.pitch() * 360) / 256.0F); + e.setYHeadRot((msg.headYaw() * 360) / 256.0F); + e.setYBodyRot((msg.headYaw() * 360) / 256.0F); + + e.setId(msg.entityId()); + e.setUUID(msg.uuid()); + world.filter(ClientLevel.class::isInstance).ifPresent(w -> ((ClientLevel) w).addEntity(e)); + e.lerpMotion(msg.velX() / 8000.0, msg.velY() / 8000.0, msg.velZ() / 8000.0); + if (e instanceof IEntityAdditionalSpawnData entityAdditionalSpawnData) { + final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(msg.customPayload())); + entityAdditionalSpawnData.readSpawnData(buf); + buf.release(); + } + } } diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java new file mode 100644 index 0000000000..dea6f999bd --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java @@ -0,0 +1,102 @@ +package net.neoforged.neoforge.network.payload; + +import io.netty.buffer.Unpooled; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.dimension.BuiltinDimensionTypes; +import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.entity.IEntityAdditionalSpawnData; +import org.jetbrains.annotations.NotNull; + +import java.util.UUID; + +public record AdvancedAddEntityPayload(int typeId, + int entityId, + UUID uuid, + double posX, + double posY, + double posZ, + byte pitch, + byte yaw, + byte headYaw, + int velX, + int velY, + int velZ, + byte[] customPayload) implements CustomPacketPayload { + + public static final ResourceLocation ID = new ResourceLocation("neoforge", "advanced_add_entity"); + + public AdvancedAddEntityPayload(FriendlyByteBuf buf) { + this( + buf.readVarInt(), + buf.readVarInt(), + buf.readUUID(), + buf.readDouble(), + buf.readDouble(), + buf.readDouble(), + buf.readByte(), + buf.readByte(), + buf.readByte(), + buf.readVarInt(), + buf.readVarInt(), + buf.readVarInt(), + buf.readByteArray() + ); + } + + public AdvancedAddEntityPayload(Entity e) { + this( + BuiltInRegistries.ENTITY_TYPE.getId(e.getType()), + e.getId(), + e.getUUID(), + e.getX(), + e.getY(), + e.getZ(), + (byte) Mth.floor(e.getXRot() * 256.0F / 360.0F), + (byte) Mth.floor(e.getYRot() * 256.0F / 360.0F), + (byte) (e.getYHeadRot() * 256.0F / 360.0F), + (int) (Mth.clamp(e.getDeltaMovement().x, -3.9D, 3.9D) * 8000.0D), + (int) (Mth.clamp(e.getDeltaMovement().y, -3.9D, 3.9D) * 8000.0D), + (int) (Mth.clamp(e.getDeltaMovement().z, -3.9D, 3.9D) * 8000.0D), + writeCustomData(e) + ); + } + + private static byte[] writeCustomData(final Entity entity) { + final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + + if (entity instanceof IEntityAdditionalSpawnData additionalSpawnData) { + additionalSpawnData.writeSpawnData(buf); + } + + final byte[] payload = buf.array(); + buf.release(); + return payload; + } + + @Override + public void write(@NotNull FriendlyByteBuf buffer) { + buffer.writeVarInt(typeId); + buffer.writeVarInt(entityId); + buffer.writeUUID(uuid); + buffer.writeDouble(posX); + buffer.writeDouble(posY); + buffer.writeDouble(posZ); + buffer.writeByte(pitch); + buffer.writeByte(yaw); + buffer.writeByte(headYaw); + buffer.writeVarInt(velX); + buffer.writeVarInt(velY); + buffer.writeVarInt(velZ); + buffer.writeBytes(customPayload); + } + + @Override + public @NotNull ResourceLocation id() { + return ID; + } +}