diff --git a/patches/net/minecraft/world/entity/Entity.java.patch b/patches/net/minecraft/world/entity/Entity.java.patch index 97c9c388935..c1d1318c4e7 100644 --- a/patches/net/minecraft/world/entity/Entity.java.patch +++ b/patches/net/minecraft/world/entity/Entity.java.patch @@ -1,6 +1,10 @@ --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -123,7 +123,7 @@ +@@ -120,10 +120,11 @@ + import net.minecraft.world.phys.shapes.VoxelShape; + import net.minecraft.world.scores.PlayerTeam; + import net.minecraft.world.scores.Team; ++import net.neoforged.neoforge.entity.IEntityWithAdditionalSpawnData; import org.joml.Vector3f; import org.slf4j.Logger; @@ -9,7 +13,7 @@ private static final Logger LOGGER = LogUtils.getLogger(); public static final String ID_TAG = "id"; public static final String PASSENGERS_TAG = "Passengers"; -@@ -144,6 +144,7 @@ +@@ -144,6 +145,7 @@ private static final double LAVA_SLOW_FLOW_SCALE = 0.0023333333333333335; public static final String UUID_TAG = "UUID"; private static double viewScale = 1.0; @@ -17,7 +21,7 @@ private final EntityType type; private int id = ENTITY_COUNTER.incrementAndGet(); public boolean blocksBuilding; -@@ -190,8 +191,10 @@ +@@ -190,8 +192,10 @@ public int tickCount; private int remainingFireTicks = -this.getFireImmuneTicks(); protected boolean wasTouchingWater; @@ -28,7 +32,7 @@ private final Set> fluidOnEyes = new HashSet<>(); public int invulnerableTime; protected boolean firstTick = true; -@@ -242,6 +245,7 @@ +@@ -242,6 +246,7 @@ private BlockState feetBlockState = null; public Entity(EntityType p_19870_, Level p_19871_) { @@ -36,7 +40,7 @@ this.type = p_19870_; this.level = p_19871_; this.dimensions = p_19870_.getDimensions(); -@@ -259,7 +263,11 @@ +@@ -259,7 +264,11 @@ this.entityData.define(DATA_TICKS_FROZEN, 0); this.defineSynchedData(); this.setPos(0.0, 0.0, 0.0); @@ -49,7 +53,7 @@ } public boolean isColliding(BlockPos p_20040_, BlockState p_20041_) { -@@ -351,6 +359,7 @@ +@@ -351,6 +360,7 @@ public void remove(Entity.RemovalReason p_146834_) { this.setRemoved(p_146834_); @@ -57,7 +61,7 @@ } public void onClientRemoval() { -@@ -467,7 +476,7 @@ +@@ -467,7 +477,7 @@ if (this.isInLava()) { this.lavaHurt(); @@ -66,7 +70,7 @@ } this.checkBelowWorld(); -@@ -672,7 +681,7 @@ +@@ -672,7 +682,7 @@ double d1 = vec3.x; double d2 = vec3.y; double d3 = vec3.z; @@ -75,7 +79,7 @@ BlockPos blockpos1 = this.getOnPos(); BlockState blockstate1 = this.level().getBlockState(blockpos1); boolean flag1 = this.isStateClimbable(blockstate1); -@@ -716,16 +725,16 @@ +@@ -716,16 +726,16 @@ this.setRemainingFireTicks(-this.getFireImmuneTicks()); } @@ -95,7 +99,7 @@ } } } -@@ -811,9 +820,7 @@ +@@ -811,9 +821,7 @@ return blockpos; } else { BlockState blockstate = this.level().getBlockState(blockpos); @@ -106,7 +110,7 @@ ? blockpos.atY(Mth.floor(this.position.y - (double)p_216987_)) : blockpos; } -@@ -886,12 +893,11 @@ +@@ -886,12 +894,11 @@ boolean flag1 = p_20273_.y != vec3.y; boolean flag2 = p_20273_.z != vec3.z; boolean flag3 = this.onGround() || flag1 && p_20273_.y < 0.0; @@ -124,7 +128,7 @@ Vec3 vec33 = collideBoundingBox(this, new Vec3(p_20273_.x, 0.0, p_20273_.z), aabb.move(vec32), this.level(), list).add(vec32); if (vec33.horizontalDistanceSqr() > vec31.horizontalDistanceSqr()) { vec31 = vec33; -@@ -1039,19 +1045,20 @@ +@@ -1039,19 +1046,20 @@ return !blockstate.is(BlockTags.INSIDE_STEP_SOUND_BLOCKS) && !blockstate.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS) ? p_278049_ : blockpos; } @@ -151,7 +155,7 @@ this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch()); } -@@ -1187,20 +1194,21 @@ +@@ -1187,20 +1195,21 @@ public void updateSwimming() { if (this.isSwimming()) { @@ -180,7 +184,7 @@ } void updateInWaterStateAndDoWaterCurrentPushing() { -@@ -1226,6 +1234,7 @@ +@@ -1226,6 +1235,7 @@ private void updateFluidOnEyes() { this.wasEyeInWater = this.isEyeInFluid(FluidTags.WATER); this.fluidOnEyes.clear(); @@ -188,7 +192,7 @@ double d0 = this.getEyeY() - 0.11111111F; Entity entity = this.getVehicle(); if (entity instanceof Boat boat && !boat.isUnderWater() && boat.getBoundingBox().maxY >= d0 && boat.getBoundingBox().minY <= d0) { -@@ -1236,7 +1245,7 @@ +@@ -1236,7 +1246,7 @@ FluidState fluidstate = this.level().getFluidState(blockpos); double d1 = (double)((float)blockpos.getY() + fluidstate.getHeight(this.level(), blockpos)); if (d1 > d0) { @@ -197,7 +201,7 @@ } } -@@ -1281,12 +1290,13 @@ +@@ -1281,12 +1291,13 @@ } public boolean canSpawnSprintParticle() { @@ -212,7 +216,7 @@ if (blockstate.getRenderShape() != RenderShape.INVISIBLE) { Vec3 vec3 = this.getDeltaMovement(); BlockPos blockpos1 = this.blockPosition(); -@@ -1299,17 +1309,23 @@ +@@ -1299,17 +1310,23 @@ if (blockpos1.getZ() != blockpos.getZ()) { d1 = Mth.clamp(d1, (double)blockpos.getZ(), (double)blockpos.getZ() + 1.0); } @@ -239,7 +243,7 @@ } public void moveRelative(float p_19921_, Vec3 p_19922_) { -@@ -1626,6 +1642,8 @@ +@@ -1626,6 +1643,8 @@ p_20241_.putBoolean("HasVisualFire", this.hasVisualFire); } @@ -248,7 +252,7 @@ if (!this.tags.isEmpty()) { ListTag listtag = new ListTag(); -@@ -1636,6 +1654,10 @@ +@@ -1636,6 +1655,10 @@ p_20241_.put("Tags", listtag); } @@ -259,7 +263,7 @@ this.addAdditionalSaveData(p_20241_); if (this.isVehicle()) { ListTag listtag1 = new ListTag(); -@@ -1716,6 +1738,9 @@ +@@ -1716,6 +1739,9 @@ this.setGlowingTag(p_20259_.getBoolean("Glowing")); this.setTicksFrozen(p_20259_.getInt("TicksFrozen")); this.hasVisualFire = p_20259_.getBoolean("HasVisualFire"); @@ -269,7 +273,7 @@ if (p_20259_.contains("Tags", 9)) { this.tags.clear(); ListTag listtag3 = p_20259_.getList("Tags", 8); -@@ -1800,6 +1825,8 @@ +@@ -1800,6 +1826,8 @@ } else { ItemEntity itementity = new ItemEntity(this.level(), this.getX(), this.getY() + (double)p_19986_, this.getZ(), p_19985_); itementity.setDefaultPickUpDelay(); @@ -278,7 +282,7 @@ this.level().addFreshEntity(itementity); return itementity; } -@@ -1846,6 +1873,7 @@ +@@ -1846,6 +1874,7 @@ public void rideTick() { this.setDeltaMovement(Vec3.ZERO); @@ -286,7 +290,7 @@ this.tick(); if (this.isPassenger()) { this.getVehicle().positionRider(this); -@@ -1902,6 +1930,7 @@ +@@ -1902,6 +1931,7 @@ } } @@ -294,7 +298,7 @@ if (p_19967_ || this.canRide(p_19966_) && p_19966_.canAddPassenger(this)) { if (this.isPassenger()) { this.stopRiding(); -@@ -1933,6 +1962,7 @@ +@@ -1933,6 +1963,7 @@ public void removeVehicle() { if (this.vehicle != null) { Entity entity = this.vehicle; @@ -302,7 +306,7 @@ this.vehicle = null; entity.removePassenger(this); } -@@ -1982,6 +2012,8 @@ +@@ -1982,6 +2013,8 @@ return this.passengers.isEmpty(); } @@ -311,7 +315,7 @@ protected boolean couldAcceptPassenger() { return true; } -@@ -2187,7 +2219,7 @@ +@@ -2187,7 +2220,7 @@ } public boolean isVisuallyCrawling() { @@ -320,7 +324,7 @@ } public void setSwimming(boolean p_20283_) { -@@ -2296,7 +2328,7 @@ +@@ -2296,7 +2329,7 @@ this.setSecondsOnFire(8); } @@ -329,7 +333,7 @@ } public void onAboveBubbleCol(boolean p_20313_) { -@@ -2391,7 +2423,7 @@ +@@ -2391,7 +2424,7 @@ } protected Component getTypeName() { @@ -338,7 +342,7 @@ } public boolean is(Entity p_20356_) { -@@ -2474,14 +2506,20 @@ +@@ -2474,14 +2507,20 @@ @Nullable public Entity changeDimension(ServerLevel p_20118_) { @@ -360,7 +364,7 @@ this.level().getProfiler().popPush("reloading"); Entity entity = this.getType().create(p_20118_); if (entity != null) { -@@ -2489,17 +2527,19 @@ +@@ -2489,17 +2528,19 @@ entity.moveTo(portalinfo.pos.x, portalinfo.pos.y, portalinfo.pos.z, portalinfo.yRot, entity.getXRot()); entity.setDeltaMovement(portalinfo.speed); p_20118_.addDuringTeleport(entity); @@ -382,7 +386,7 @@ } } else { return null; -@@ -2629,6 +2669,7 @@ +@@ -2629,6 +2670,7 @@ return this.stringUUID; } @@ -390,7 +394,7 @@ public boolean isPushedByFluid() { return true; } -@@ -2752,8 +2793,10 @@ +@@ -2752,8 +2794,10 @@ EntityDimensions entitydimensions = this.dimensions; Pose pose = this.getPose(); EntityDimensions entitydimensions1 = this.getDimensions(pose); @@ -402,7 +406,7 @@ this.reapplyPosition(); boolean flag = (double)entitydimensions1.width <= 4.0 && (double)entitydimensions1.height <= 4.0; if (!this.level().isClientSide -@@ -2766,9 +2809,10 @@ +@@ -2766,9 +2810,10 @@ double d0 = (double)Math.max(0.0F, entitydimensions1.width - entitydimensions.width) + 1.0E-6; double d1 = (double)Math.max(0.0F, entitydimensions1.height - entitydimensions.height) + 1.0E-6; VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3, d0, d1, d0)); @@ -414,7 +418,7 @@ } } -@@ -3060,9 +3104,17 @@ +@@ -3060,9 +3105,17 @@ this.yRotO = this.getYRot(); } @@ -433,7 +437,7 @@ } else { AABB aabb = this.getBoundingBox().deflate(0.001); int i = Mth.floor(aabb.minX); -@@ -3077,25 +3129,28 @@ +@@ -3077,25 +3130,28 @@ Vec3 vec3 = Vec3.ZERO; int k1 = 0; BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); @@ -469,7 +473,7 @@ } } } -@@ -3103,27 +3158,28 @@ +@@ -3103,27 +3159,28 @@ } } @@ -508,7 +512,7 @@ } } -@@ -3136,7 +3192,10 @@ +@@ -3136,7 +3193,10 @@ return !this.level().hasChunksAt(i, k, j, l); } @@ -519,7 +523,7 @@ return this.fluidHeight.getDouble(p_204037_); } -@@ -3273,6 +3332,7 @@ +@@ -3273,6 +3333,7 @@ this.levelCallback.onMove(); } @@ -527,7 +531,17 @@ } public void checkDespawn() { -@@ -3344,10 +3404,27 @@ +@@ -3293,6 +3354,9 @@ + this.setYRot(p_146866_.getYRot()); + this.setId(i); + this.setUUID(p_146866_.getUUID()); ++ if (this instanceof IEntityWithAdditionalSpawnData customSpawner) { ++ customSpawner.readSpawnData(p_146866_.getExtendedData()); ++ } + } + + @Nullable +@@ -3344,10 +3408,27 @@ return false; } @@ -555,7 +569,7 @@ public void setMaxUpStep(float p_275672_) { this.maxUpStep = p_275672_; } -@@ -3403,6 +3480,103 @@ +@@ -3403,6 +3484,103 @@ public boolean mayInteract(Level p_146843_, BlockPos p_146844_) { return true; } diff --git a/patches/net/minecraft/world/entity/EntityType.java.patch b/patches/net/minecraft/world/entity/EntityType.java.patch index b12c3cf7895..ef33b3b36c5 100644 --- a/patches/net/minecraft/world/entity/EntityType.java.patch +++ b/patches/net/minecraft/world/entity/EntityType.java.patch @@ -1,18 +1,26 @@ --- a/net/minecraft/world/entity/EntityType.java +++ b/net/minecraft/world/entity/EntityType.java -@@ -599,6 +599,11 @@ +@@ -158,6 +158,7 @@ + import net.minecraft.world.phys.AABB; + import net.minecraft.world.phys.shapes.Shapes; + import net.minecraft.world.phys.shapes.VoxelShape; ++import net.neoforged.neoforge.network.payload.AdvancedAddEntityPayload; + import org.slf4j.Logger; + + public class EntityType implements FeatureElement, EntityTypeTest { +@@ -599,6 +600,11 @@ private final EntityDimensions dimensions; private final FeatureFlagSet requiredFeatures; + private final java.util.function.Predicate> velocityUpdateSupplier; + private final java.util.function.ToIntFunction> trackingRangeSupplier; + private final java.util.function.ToIntFunction> updateIntervalSupplier; -+ private final java.util.function.BiFunction customClientFactory; ++ private final java.util.function.BiFunction customClientFactory; + private static EntityType register(String p_20635_, EntityType.Builder p_20636_) { return Registry.register(BuiltInRegistries.ENTITY_TYPE, p_20635_, p_20636_.build(p_20635_)); } -@@ -624,17 +629,24 @@ +@@ -624,17 +630,24 @@ int p_273451_, FeatureFlagSet p_273518_ ) { @@ -29,7 +37,7 @@ - this.requiredFeatures = p_273518_; + this(p_273268_, p_272918_, p_273417_, p_273389_, p_273556_, p_272654_, p_273631_, p_272946_, p_272895_, p_273451_, p_273518_, EntityType::defaultVelocitySupplier, EntityType::defaultTrackingRangeSupplier, EntityType::defaultUpdateIntervalSupplier, null); + } -+ public EntityType(EntityType.EntityFactory p_251402_, MobCategory p_251431_, boolean p_251439_, boolean p_251973_, boolean p_252007_, boolean p_250908_, ImmutableSet p_250201_, EntityDimensions p_251742_, int p_250479_, int p_249249_, FeatureFlagSet p_250427_, final java.util.function.Predicate> velocityUpdateSupplier, final java.util.function.ToIntFunction> trackingRangeSupplier, final java.util.function.ToIntFunction> updateIntervalSupplier, final java.util.function.BiFunction customClientFactory) { ++ public EntityType(EntityType.EntityFactory p_251402_, MobCategory p_251431_, boolean p_251439_, boolean p_251973_, boolean p_252007_, boolean p_250908_, ImmutableSet p_250201_, EntityDimensions p_251742_, int p_250479_, int p_249249_, FeatureFlagSet p_250427_, final java.util.function.Predicate> velocityUpdateSupplier, final java.util.function.ToIntFunction> trackingRangeSupplier, final java.util.function.ToIntFunction> updateIntervalSupplier, final java.util.function.BiFunction customClientFactory) { + this.factory = p_251402_; + this.category = p_251431_; + this.canSpawnFarFromPlayer = p_250908_; @@ -48,7 +56,7 @@ } @Nullable -@@ -788,6 +800,9 @@ +@@ -788,6 +801,9 @@ } public boolean canSpawnFarFromPlayer() { @@ -58,7 +66,7 @@ return this.canSpawnFarFromPlayer; } -@@ -935,10 +950,16 @@ +@@ -935,10 +951,16 @@ } public int clientTrackingRange() { @@ -75,11 +83,11 @@ return this.updateInterval; } -@@ -978,6 +999,12 @@ +@@ -978,6 +1000,12 @@ return this.builtInRegistryHolder; } -+ public T customClientSpawn(net.neoforged.neoforge.network.PlayMessages.SpawnEntity packet, Level world) { ++ public T customClientSpawn(AdvancedAddEntityPayload packet, Level world) { + if (customClientFactory == null) return this.create(world); + return customClientFactory.apply(packet, world); + } @@ -88,19 +96,19 @@ public static class Builder { private final EntityType.EntityFactory factory; private final MobCategory category; -@@ -991,6 +1018,11 @@ +@@ -991,6 +1019,11 @@ private EntityDimensions dimensions = EntityDimensions.scalable(0.6F, 1.8F); private FeatureFlagSet requiredFeatures = FeatureFlags.VANILLA_SET; + private java.util.function.Predicate> velocityUpdateSupplier = EntityType::defaultVelocitySupplier; + private java.util.function.ToIntFunction> trackingRangeSupplier = EntityType::defaultTrackingRangeSupplier; + private java.util.function.ToIntFunction> updateIntervalSupplier = EntityType::defaultUpdateIntervalSupplier; -+ private java.util.function.BiFunction customClientFactory; ++ private java.util.function.BiFunction customClientFactory; + private Builder(EntityType.EntityFactory p_20696_, MobCategory p_20697_) { this.factory = p_20696_; this.category = p_20697_; -@@ -1050,6 +1082,30 @@ +@@ -1050,6 +1083,30 @@ return this; } @@ -123,7 +131,7 @@ + * By default, entities are spawned clientside via {@link EntityType#create(Level)}}. + * If you need finer control over the spawning process, use this to get read access to the spawn packet. + */ -+ public EntityType.Builder setCustomClientFactory(java.util.function.BiFunction customClientFactory) { ++ public EntityType.Builder setCustomClientFactory(java.util.function.BiFunction customClientFactory) { + this.customClientFactory = customClientFactory; + return this; + } @@ -131,7 +139,7 @@ public EntityType build(String p_20713_) { if (this.serialize) { Util.fetchChoiceType(References.ENTITY_TREE, p_20713_); -@@ -1066,7 +1122,11 @@ +@@ -1066,7 +1123,11 @@ this.dimensions, this.clientTrackingRange, this.updateInterval, diff --git a/src/main/java/net/neoforged/neoforge/network/PlayMessages.java b/src/main/java/net/neoforged/neoforge/network/PlayMessages.java index 97b6a916823..1b27e217fea 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 6abadc6a5b9..d92d63cc9f9 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 00000000000..dea6f999bda --- /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; + } +}