From 371925b28dca09df7c7ca6e6ed5575aaaf592fef Mon Sep 17 00:00:00 2001 From: canitzp <12819060+canitzp@users.noreply.github.com> Date: Thu, 27 May 2021 19:34:50 +0200 Subject: [PATCH] Add NetworkManager hook for Entity spawn packets (#88) * New utility hook for creating a entity spawn packet. before every mod had to implement this in itself. * Apply suggestions from code review Co-authored-by: shedaniel * Update common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java * Update common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java * Properly implement SpawnEntityPacket & Format style Signed-off-by: shedaniel * Format Signed-off-by: shedaniel * createEntitySpawnPacket -> createAddEntityPacket for mojmap consistency Signed-off-by: shedaniel * Bump version to 1.17 Co-authored-by: Max Co-authored-by: shedaniel --- .../networking/NetworkManager.java | 15 +++ .../init/fabric/ArchitecturyClient.java | 3 + .../networking/fabric/NetworkManagerImpl.java | 5 + .../networking/fabric/SpawnEntityPacket.java | 106 ++++++++++++++++++ .../networking/forge/NetworkManagerImpl.java | 6 + gradle.properties | 2 +- .../shedaniel/architectury/test/TestMod.java | 7 +- .../architectury/test/entity/TestEntity.java | 45 ++++++++ .../test/registry/TestRegistries.java | 6 + 9 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 fabric/src/main/java/me/shedaniel/architectury/networking/fabric/SpawnEntityPacket.java create mode 100644 testmod-common/src/main/java/me/shedaniel/architectury/test/entity/TestEntity.java diff --git a/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java b/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java index d643596a..3c976ddb 100644 --- a/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java +++ b/common/src/main/java/me/shedaniel/architectury/networking/NetworkManager.java @@ -28,6 +28,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import java.util.Objects; @@ -74,6 +75,20 @@ public final class NetworkManager { throw new AssertionError(); } + /** + * Easy to use utility method to create an entity spawn packet. + * This packet is needed everytime any mod adds a non-living entity. + * The entity should override {@link Entity#getAddEntityPacket()} to point to this method! + * + * @param entity The entity which should be spawned. + * @return The ready to use packet to spawn the entity on the client. + * @see Entity#getAddEntityPacket() + */ + @ExpectPlatform + public static Packet createAddEntityPacket(Entity entity) { + throw new AssertionError(); + } + @FunctionalInterface public interface NetworkReceiver { void receive(FriendlyByteBuf buf, PacketContext context); diff --git a/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java index 3d1f2046..57d9008f 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java +++ b/fabric/src/main/java/me/shedaniel/architectury/init/fabric/ArchitecturyClient.java @@ -20,10 +20,13 @@ package me.shedaniel.architectury.init.fabric; import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; +import me.shedaniel.architectury.networking.fabric.SpawnEntityPacket; import net.minecraft.client.Minecraft; public class ArchitecturyClient { public static void init() { ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(Minecraft.getInstance()); + + SpawnEntityPacket.register(); } } diff --git a/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java index 66f421a4..ab731349 100644 --- a/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java +++ b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/NetworkManagerImpl.java @@ -31,6 +31,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; public class NetworkManagerImpl { @@ -88,6 +89,10 @@ public class NetworkManagerImpl { return ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, id); } + public static Packet createAddEntityPacket(Entity entity) { + return SpawnEntityPacket.create(entity); + } + @Environment(EnvType.CLIENT) private static Packet toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) { return ClientSidePacketRegistry.INSTANCE.toPacket(id, buf); diff --git a/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/SpawnEntityPacket.java b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/SpawnEntityPacket.java new file mode 100644 index 00000000..a37d4377 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/architectury/networking/fabric/SpawnEntityPacket.java @@ -0,0 +1,106 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program 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; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.networking.fabric; + +import me.shedaniel.architectury.networking.NetworkManager; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.minecraft.client.Minecraft; +import net.minecraft.core.Registry; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.protocol.Packet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.phys.Vec3; + +import java.util.UUID; + +/** + * @see net.minecraft.network.protocol.game.ClientboundAddEntityPacket + */ +public class SpawnEntityPacket { + private static final ResourceLocation PACKET_ID = new ResourceLocation("architectury", "spawn_entity_packet"); + + @Environment(EnvType.CLIENT) + public static void register() { + NetworkManager.registerReceiver(NetworkManager.s2c(), PACKET_ID, SpawnEntityPacket::receive); + } + + public static Packet create(Entity entity) { + if (entity.level.isClientSide()) { + throw new IllegalStateException("SpawnPacketUtil.create called on the logical client!"); + } + FriendlyByteBuf buffer = PacketByteBufs.create(); + buffer.writeVarInt(Registry.ENTITY_TYPE.getId(entity.getType())); + buffer.writeUUID(entity.getUUID()); + buffer.writeVarInt(entity.getId()); + Vec3 position = entity.position(); + buffer.writeDouble(position.x); + buffer.writeDouble(position.y); + buffer.writeDouble(position.z); + buffer.writeFloat(entity.xRot); + buffer.writeFloat(entity.yRot); + buffer.writeFloat(entity.getYHeadRot()); + Vec3 deltaMovement = entity.getDeltaMovement(); + buffer.writeDouble(deltaMovement.x); + buffer.writeDouble(deltaMovement.y); + buffer.writeDouble(deltaMovement.z); + return NetworkManager.toPacket(NetworkManager.s2c(), PACKET_ID, buffer); + } + + @Environment(EnvType.CLIENT) + public static void receive(FriendlyByteBuf buf, NetworkManager.PacketContext context) { + int entityTypeId = buf.readVarInt(); + UUID uuid = buf.readUUID(); + int id = buf.readVarInt(); + double x = buf.readDouble(); + double y = buf.readDouble(); + double z = buf.readDouble(); + float xRot = buf.readFloat(); + float yRot = buf.readFloat(); + float yHeadRot = buf.readFloat(); + double deltaX = buf.readDouble(); + double deltaY = buf.readDouble(); + double deltaZ = buf.readDouble(); + context.queue(() -> { + EntityType entityType = Registry.ENTITY_TYPE.byId(entityTypeId); + if (entityType == null) { + throw new IllegalStateException("Entity type (" + entityTypeId + ") is unknown, spawning at (" + x + ", " + y + ", " + z + ")"); + } + if (Minecraft.getInstance().level == null) { + throw new IllegalStateException("Client world is null!"); + } + Entity entity = entityType.create(Minecraft.getInstance().level); + if (entity == null) { + throw new IllegalStateException("Created entity is null!"); + } + entity.setUUID(uuid); + entity.setId(id); + entity.setPacketCoordinates(x, y, z); + entity.absMoveTo(x, y, z, xRot, yRot); + entity.setYHeadRot(yHeadRot); + entity.setYBodyRot(yHeadRot); + Minecraft.getInstance().level.putNonPlayerEntity(id, entity); + entity.lerpMotion(deltaX, deltaY, deltaZ); + }); + } +} diff --git a/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java index b7975911..ef6ab95e 100644 --- a/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java +++ b/forge/src/main/java/me/shedaniel/architectury/networking/forge/NetworkManagerImpl.java @@ -30,6 +30,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.protocol.Packet; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -40,6 +41,7 @@ import net.minecraftforge.fml.LogicalSide; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fml.network.NetworkEvent; +import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.event.EventNetworkChannel; import org.apache.commons.lang3.tuple.Pair; @@ -148,6 +150,10 @@ public class NetworkManagerImpl { return clientReceivables.get(player).contains(id); } + public static Packet createAddEntityPacket(Entity entity){ + return NetworkHooks.getEntitySpawningPacket(entity); + } + static FriendlyByteBuf sendSyncPacket(Map map) { List availableIds = Lists.newArrayList(map.keySet()); FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); diff --git a/gradle.properties b/gradle.properties index be9e6c22..b047092e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ supported_version=1.16.4/5 archives_base_name=architectury archives_base_name_snapshot=architectury-snapshot -base_version=1.16 +base_version=1.17 maven_group=me.shedaniel fabric_loader_version=0.11.1 diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java index 1193c342..9498cfcd 100644 --- a/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/TestMod.java @@ -20,9 +20,11 @@ package me.shedaniel.architectury.test; import me.shedaniel.architectury.platform.Platform; +import me.shedaniel.architectury.registry.entity.EntityRenderers; import me.shedaniel.architectury.test.debug.ConsoleMessageSink; import me.shedaniel.architectury.test.debug.MessageSink; import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; +import me.shedaniel.architectury.test.entity.TestEntity; import me.shedaniel.architectury.test.events.DebugEvents; import me.shedaniel.architectury.test.gamerule.TestGameRules; import me.shedaniel.architectury.test.registry.TestRegistries; @@ -31,6 +33,7 @@ import me.shedaniel.architectury.test.tags.TestTags; import me.shedaniel.architectury.test.trade.TestTrades; import me.shedaniel.architectury.utils.Env; import me.shedaniel.architectury.utils.EnvExecutor; +import net.minecraft.client.renderer.entity.MinecartRenderer; public class TestMod { public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new); @@ -42,7 +45,9 @@ public class TestMod { TestGameRules.init(); TestTags.initialize(); TestTrades.init(); - if (Platform.getEnvironment() == Env.CLIENT) + if (Platform.getEnvironment() == Env.CLIENT) { TestKeybinds.initialize(); + EntityRenderers.register(TestEntity.TYPE, MinecartRenderer::new); + } } } diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/entity/TestEntity.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/entity/TestEntity.java new file mode 100644 index 00000000..81b3fd9b --- /dev/null +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/entity/TestEntity.java @@ -0,0 +1,45 @@ +/* + * This file is part of architectury. + * Copyright (C) 2020, 2021 architectury + * + * This program 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; either + * version 3 of the License, or (at your option) any later version. + * + * This program 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 program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package me.shedaniel.architectury.test.entity; + +import me.shedaniel.architectury.networking.NetworkManager; +import net.minecraft.network.protocol.Packet; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.entity.vehicle.AbstractMinecart; +import net.minecraft.world.level.Level; + +public class TestEntity extends AbstractMinecart { + public static final EntityType TYPE = EntityType.Builder.of(TestEntity::new, MobCategory.MISC).sized(0.98F, 0.7F).clientTrackingRange(8).build("test_entity"); + + public TestEntity(EntityType entityType, Level level) { + super(entityType, level); + } + + @Override + public Type getMinecartType() { + return Type.RIDEABLE; + } + + @Override + public Packet getAddEntityPacket() { + return NetworkManager.createAddEntityPacket(this); + } +} diff --git a/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java index ff485c8a..65c7483d 100644 --- a/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java +++ b/testmod-common/src/main/java/me/shedaniel/architectury/test/registry/TestRegistries.java @@ -24,10 +24,12 @@ import me.shedaniel.architectury.registry.BlockProperties; import me.shedaniel.architectury.registry.DeferredRegister; import me.shedaniel.architectury.registry.RegistrySupplier; import me.shedaniel.architectury.test.TestMod; +import me.shedaniel.architectury.test.entity.TestEntity; import me.shedaniel.architectury.test.registry.objects.EquippableTickingItem; import me.shedaniel.architectury.test.tab.TestCreativeTabs; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.level.BlockGetter; @@ -42,6 +44,7 @@ import static me.shedaniel.architectury.test.TestMod.SINK; public class TestRegistries { public static final DeferredRegister ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY); public static final DeferredRegister BLOCKS = DeferredRegister.create(TestMod.MOD_ID, Registry.BLOCK_REGISTRY); + public static final DeferredRegister> ENTITY_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.ENTITY_TYPE_REGISTRY); public static final RegistrySupplier TEST_ITEM = ITEMS.register("test_item", () -> new Item(new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); @@ -64,8 +67,11 @@ public class TestRegistries { public static final RegistrySupplier COLLISION_BLOCK_ITEM = ITEMS.register("collision_block", () -> new BlockItem(COLLISION_BLOCK.get(), new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); + public static final RegistrySupplier> TEST_ENTITY = ENTITY_TYPES.register("test_entity", () -> TestEntity.TYPE); + public static void initialize() { BLOCKS.register(); ITEMS.register(); + ENTITY_TYPES.register(); } }