diff --git a/patches/minecraft/net/minecraft/entity/EntityTracker.java.patch b/patches/minecraft/net/minecraft/entity/EntityTracker.java.patch index c43b21a26..863316af5 100644 --- a/patches/minecraft/net/minecraft/entity/EntityTracker.java.patch +++ b/patches/minecraft/net/minecraft/entity/EntityTracker.java.patch @@ -1,6 +1,17 @@ --- a/net/minecraft/entity/EntityTracker.java +++ b/net/minecraft/entity/EntityTracker.java -@@ -296,4 +296,18 @@ +@@ -71,6 +71,10 @@ + } + + public void func_72786_a(Entity p_72786_1_) { ++ if (p_72786_1_.func_200600_R().hasCustomTracking()) { ++ this.func_72785_a(p_72786_1_, p_72786_1_.func_200600_R().getTrackingRange(), p_72786_1_.func_200600_R().getUpdateFrequency(), p_72786_1_.func_200600_R().shouldSendVelocityUpdates()); ++ return; ++ } + if (p_72786_1_ instanceof EntityPlayerMP) { + this.func_72791_a(p_72786_1_, 512, 2); + EntityPlayerMP entityplayermp = (EntityPlayerMP)p_72786_1_; +@@ -296,4 +300,18 @@ } } diff --git a/patches/minecraft/net/minecraft/entity/EntityType.java.patch b/patches/minecraft/net/minecraft/entity/EntityType.java.patch index 2f519d4e7..ccb015895 100644 --- a/patches/minecraft/net/minecraft/entity/EntityType.java.patch +++ b/patches/minecraft/net/minecraft/entity/EntityType.java.patch @@ -20,7 +20,90 @@ public static final EntityType field_200788_b = func_200712_a("area_effect_cloud", EntityType.Builder.func_201757_a(EntityAreaEffectCloud.class, EntityAreaEffectCloud::new)); public static final EntityType field_200789_c = func_200712_a("armor_stand", EntityType.Builder.func_201757_a(EntityArmorStand.class, EntityArmorStand::new)); public static final EntityType field_200790_d = func_200712_a("arrow", EntityType.Builder.func_201757_a(EntityTippedArrow.class, EntityTippedArrow::new)); -@@ -411,7 +412,7 @@ +@@ -227,6 +228,11 @@ + private String field_210762_aX; + @Nullable + private final Type field_206832_aX; ++ private boolean useVanillaSpawning; ++ private Function customSpawnCallback; ++ private boolean hasCustomTracking; ++ private int customTrackingRange, customUpdateFrequency; ++ private boolean customSendVelocityUpdates; + + public static EntityType func_200712_a(String p_200712_0_, EntityType.Builder p_200712_1_) { + EntityType entitytype = p_200712_1_.func_206830_a(p_200712_0_); +@@ -244,6 +250,18 @@ + return field_200787_a.func_82594_a(ResourceLocation.func_208304_a(p_200713_0_)); + } + ++ public EntityType(Class clazz, Function factory, boolean serializable, boolean summonable, @Nullable Type dataFixerType, ++ boolean useVanillaSpawning, Function customSpawnCallback, ++ boolean hasCustomTracking, int range, int updateFreq, boolean sendVelocityUpdates) { ++ this(clazz, factory, serializable, summonable, dataFixerType); ++ this.useVanillaSpawning = useVanillaSpawning; ++ this.customSpawnCallback = customSpawnCallback; ++ this.hasCustomTracking = hasCustomTracking; ++ this.customTrackingRange = range; ++ this.customUpdateFrequency = updateFreq; ++ this.customSendVelocityUpdates = sendVelocityUpdates; ++ } ++ + public EntityType(Class p_i49579_1_, Function p_i49579_2_, boolean p_i49579_3_, boolean p_i49579_4_, @Nullable Type p_i49579_5_) { + this.field_201761_aK = p_i49579_1_; + this.field_200732_aK = p_i49579_2_; +@@ -375,15 +393,32 @@ + return p_200719_1_ == null ? null : p_200719_1_.func_200721_a(p_200719_0_); + } + ++ public boolean hasCustomTracking() { return hasCustomTracking; } ++ public int getTrackingRange() { return customTrackingRange; } ++ public int getUpdateFrequency() { return customUpdateFrequency; } ++ public boolean shouldSendVelocityUpdates() { return customSendVelocityUpdates; } ++ public boolean usesVanillaSpawning() { return useVanillaSpawning; } ++ @Nullable public Entity handleSpawnMessage(World world, net.minecraftforge.fml.network.FMLPlayMessages.SpawnEntity msg) ++ { ++ return customSpawnCallback == null ? func_200721_a(world) : customSpawnCallback.apply(msg); ++ } ++ + public static class Builder { + private final Class field_201759_a; + private final Function field_200709_a; + private boolean field_200710_b = true; + private boolean field_200711_c = true; ++ private boolean useVanillaSpawning; ++ private Function customSpawnCallback = null; ++ private boolean hasCustomTracking = false; ++ private int trackingRange; ++ private int updateFrequency; ++ private boolean sendVelocityUpdates; + + private Builder(Class p_i48756_1_, Function p_i48756_2_) { + this.field_201759_a = p_i48756_1_; + this.field_200709_a = p_i48756_2_; ++ this.useVanillaSpawning = field_201759_a.getName().startsWith("net.minecraft"); + } + + public static EntityType.Builder func_201757_a(Class p_201757_0_, Function p_201757_1_) { +@@ -406,12 +441,26 @@ + return this; + } + ++ public EntityType.Builder customSpawning(Function cb, boolean useVanillaSpawning) { ++ this.customSpawnCallback = cb; ++ this.useVanillaSpawning = useVanillaSpawning; ++ return this; ++ } ++ ++ public final EntityType.Builder tracker(int range, int updateFrequency, boolean sendVelocityUpdates) { ++ this.hasCustomTracking = true; ++ this.trackingRange = range; ++ this.updateFrequency = updateFrequency; ++ this.sendVelocityUpdates = sendVelocityUpdates; ++ return this; ++ } ++ + public EntityType func_206830_a(String p_206830_1_) { + Type type = null; if (this.field_200710_b) { try { type = DataFixesManager.func_210901_a().getSchema(DataFixUtils.makeKey(1519)).getChoiceType(TypeReferences.field_211298_n, p_206830_1_); @@ -29,3 +112,12 @@ if (SharedConstants.field_206244_b) { throw illegalstateexception; } +@@ -420,7 +469,7 @@ + } + } + +- return new EntityType(this.field_201759_a, this.field_200709_a, this.field_200710_b, this.field_200711_c, type); ++ return new EntityType(this.field_201759_a, this.field_200709_a, this.field_200710_b, this.field_200711_c, type, useVanillaSpawning, customSpawnCallback, hasCustomTracking, trackingRange, updateFrequency, sendVelocityUpdates); + } + } + } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/IEntityAdditionalSpawnData.java b/src/main/java/net/minecraftforge/fml/common/registry/IEntityAdditionalSpawnData.java index 7524fbdf7..dbbc8c60e 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/IEntityAdditionalSpawnData.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/IEntityAdditionalSpawnData.java @@ -19,7 +19,7 @@ package net.minecraftforge.fml.common.registry; -import io.netty.buffer.ByteBuf; +import net.minecraft.network.PacketBuffer; /** * A interface for Entities that need extra information to be communicated @@ -33,7 +33,7 @@ public interface IEntityAdditionalSpawnData * * @param buffer The packet data stream */ - void writeSpawnData(ByteBuf buffer); + void writeSpawnData(PacketBuffer buffer); /** * Called by the client when it receives a Entity spawn packet. @@ -41,5 +41,5 @@ public interface IEntityAdditionalSpawnData * * @param additionalData The packet data stream */ - void readSpawnData(ByteBuf additionalData); + void readSpawnData(PacketBuffer additionalData); } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/IThrowableEntity.java b/src/main/java/net/minecraftforge/fml/common/registry/IThrowableEntity.java deleted file mode 100644 index b3f285703..000000000 --- a/src/main/java/net/minecraftforge/fml/common/registry/IThrowableEntity.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Minecraft Forge - * Copyright (c) 2016-2018. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation version 2.1 - * of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -package net.minecraftforge.fml.common.registry; - -import net.minecraft.entity.Entity; - -/** - * This interface should be implemented by an Entity that can be 'thrown', like snowballs. - * This was created to mimic ModLoaderMP's 'owner' functionality. - */ -public interface IThrowableEntity -{ - /** - * Gets the entity that threw/created this entity. - * @return The owner instance, Null if none. - */ - Entity getThrower(); - - /** - * Sets the entity that threw/created this entity. - * @param entity The new thrower/creator. - */ - void setThrower(Entity entity); -} diff --git a/src/main/java/net/minecraftforge/fml/network/FMLPlayHandler.java b/src/main/java/net/minecraftforge/fml/network/FMLPlayHandler.java new file mode 100644 index 000000000..02b04e578 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/network/FMLPlayHandler.java @@ -0,0 +1,18 @@ +package net.minecraftforge.fml.network; + +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.network.simple.SimpleChannel; + +public class FMLPlayHandler +{ + public static final SimpleChannel channel = NetworkRegistry.ChannelBuilder + .named(new ResourceLocation("fml", "play")) + .clientAcceptedVersions(a -> true) + .serverAcceptedVersions(a -> true) + .networkProtocolVersion(() -> NetworkHooks.NETVERSION) + .simpleChannel(); + static + { + channel.registerMessage(0, FMLPlayMessages.SpawnEntity.class, FMLPlayMessages.SpawnEntity::encode, FMLPlayMessages.SpawnEntity::decode, FMLPlayMessages.SpawnEntity::handle); + } +} diff --git a/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java b/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java new file mode 100644 index 000000000..7d4fd2669 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/network/FMLPlayMessages.java @@ -0,0 +1,136 @@ +package net.minecraftforge.fml.network; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityTracker; +import net.minecraft.entity.EntityType; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; + +import java.util.UUID; +import java.util.function.Supplier; + +public class FMLPlayMessages +{ + public static class SpawnEntity + { + private final Entity entity; + private final int typeId; + private final int entityId; + private final UUID uuid; + private final double posX, posY, posZ; + private final byte pitch, yaw; + private final short velX, velY, velZ; + private final PacketBuffer buf; + + SpawnEntity(Entity e) + { + this.entity = e; + this.typeId = EntityType.REGISTRY.getId(e.getType()); + this.entityId = e.getEntityId(); + this.uuid = e.getUniqueID(); + this.posX = e.posX; + this.posY = e.posY; + this.posZ = e.posZ; + this.pitch = (byte) MathHelper.floor(e.rotationPitch * 256.0F / 360.0F); + this.yaw = (byte) MathHelper.floor(e.rotationYaw * 256.0F / 360.0F); + this.velX = (short)(MathHelper.clamp(e.motionX, -3.9D, 3.9D) * 8000.0D); + this.velY = (short)(MathHelper.clamp(e.motionY, -3.9D, 3.9D) * 8000.0D); + this.velZ = (short)(MathHelper.clamp(e.motionZ, -3.9D, 3.9D) * 8000.0D); + this.buf = null; + } + + private SpawnEntity(int typeId, int entityId, UUID uuid, double posX, double posY, double posZ, + byte pitch, byte yaw, short velX, short velY, short velZ, PacketBuffer 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.velX = velX; + this.velY = velY; + this.velZ = velZ; + this.buf = buf; + } + + public static void encode(SpawnEntity msg, PacketBuffer 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.writeShort(msg.velX); + buf.writeShort(msg.velY); + buf.writeShort(msg.velZ); + if (msg.entity instanceof IEntityAdditionalSpawnData) + { + ((IEntityAdditionalSpawnData) msg.entity).writeSpawnData(buf); + } + } + + public static SpawnEntity decode(PacketBuffer 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.readShort(), buf.readShort(), buf.readShort(), + buf + ); + } + + public static void handle(SpawnEntity msg, Supplier ctx) + { + ctx.get().enqueueWork(() -> { + EntityType type = EntityType.REGISTRY.get(msg.typeId); + if (type == null) + { + throw new RuntimeException(String.format("Could not spawn entity (id %d) with unknown type at (%f, %f, %f)", msg.entityId, msg.posX, msg.posY, msg.posZ)); + } + + Entity e = type.handleSpawnMessage(Minecraft.getInstance().world, msg); + if (e == null) + { + return; + } + + EntityTracker.updateServerPosition(e, msg.posX, msg.posY, msg.posZ); + e.rotationPitch = (float)(msg.pitch * 360) / 256.0F; + e.rotationYaw = (float)(msg.yaw * 360) / 256.0F; + + Entity[] parts = e.getParts(); + if (parts != null) + { + int offset = msg.entityId - e.getEntityId(); + for (Entity part : parts) + { + part.setEntityId(part.getEntityId() + offset); + } + } + + e.setEntityId(msg.entityId); + e.setUniqueId(msg.uuid); + Minecraft.getInstance().world.addEntityToWorld(msg.entityId, e); + e.setVelocity(msg.velX / 8000.0, msg.velY / 8000.0, msg.velZ / 8000.0); + if (e instanceof IEntityAdditionalSpawnData) + { + ((IEntityAdditionalSpawnData) e).readSpawnData(msg.buf); + } + }); + ctx.get().setPacketHandled(true); + } + } +} diff --git a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java index ea4e03d6d..76948c245 100644 --- a/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java +++ b/src/main/java/net/minecraftforge/fml/network/NetworkHooks.java @@ -52,6 +52,10 @@ public class NetworkHooks public static Packet getEntitySpawningPacket(Entity entity) { + if (!entity.getType().usesVanillaSpawning()) + { + return FMLPlayHandler.channel.toVanillaPacket(new FMLPlayMessages.SpawnEntity(entity), NetworkDirection.PLAY_TO_CLIENT); + } return null; } diff --git a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java index ab0dfa1a4..01697b546 100644 --- a/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java +++ b/src/main/java/net/minecraftforge/fml/network/simple/SimpleChannel.java @@ -83,8 +83,12 @@ public class SimpleChannel public void sendTo(MSG message, NetworkManager manager, NetworkDirection direction) { - ICustomPacket> payload = direction.buildPacket(toBuffer(message), instance.getChannelName()); - manager.sendPacket(payload.getThis()); + manager.sendPacket(toVanillaPacket(message, direction)); + } + + public Packet toVanillaPacket(MSG message, NetworkDirection direction) + { + return direction.buildPacket(toBuffer(message), instance.getChannelName()).getThis(); } public void reply(MSG msgToReply, NetworkEvent.Context context)