Эх сурвалжийг харах

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 <daniel@shedaniel.me>

* 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 <daniel@shedaniel.me>

* Format

Signed-off-by: shedaniel <daniel@shedaniel.me>

* createEntitySpawnPacket -> createAddEntityPacket for mojmap consistency

Signed-off-by: shedaniel <daniel@shedaniel.me>

* Bump version to 1.17

Co-authored-by: Max <maxh2709@gmail.com>
Co-authored-by: shedaniel <daniel@shedaniel.me>
canitzp 4 жил өмнө
parent
commit
371925b28d

+ 15 - 0
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);

+ 3 - 0
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();
     }
 }

+ 5 - 0
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);

+ 106 - 0
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);
+        });
+    }
+}

+ 6 - 0
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<ResourceLocation, NetworkReceiver> map) {
         List<ResourceLocation> availableIds = Lists.newArrayList(map.keySet());
         FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer());

+ 1 - 1
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

+ 6 - 1
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<TestEntity>::new);
+        }
     }
 }

+ 45 - 0
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<TestEntity> TYPE = EntityType.Builder.of(TestEntity::new, MobCategory.MISC).sized(0.98F, 0.7F).clientTrackingRange(8).build("test_entity");
+    
+    public TestEntity(EntityType<? extends AbstractMinecart> entityType, Level level) {
+        super(entityType, level);
+    }
+    
+    @Override
+    public Type getMinecartType() {
+        return Type.RIDEABLE;
+    }
+    
+    @Override
+    public Packet<?> getAddEntityPacket() {
+        return NetworkManager.createAddEntityPacket(this);
+    }
+}

+ 6 - 0
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<Item> ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY);
     public static final DeferredRegister<Block> BLOCKS = DeferredRegister.create(TestMod.MOD_ID, Registry.BLOCK_REGISTRY);
+    public static final DeferredRegister<EntityType<?>> ENTITY_TYPES = DeferredRegister.create(TestMod.MOD_ID, Registry.ENTITY_TYPE_REGISTRY);
     
     public static final RegistrySupplier<Item> 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<Item> COLLISION_BLOCK_ITEM = ITEMS.register("collision_block", () ->
             new BlockItem(COLLISION_BLOCK.get(), new Item.Properties().tab(TestCreativeTabs.TEST_TAB)));
     
+    public static final RegistrySupplier<EntityType<TestEntity>> TEST_ENTITY = ENTITY_TYPES.register("test_entity", () -> TestEntity.TYPE);
+    
     public static void initialize() {
         BLOCKS.register();
         ITEMS.register();
+        ENTITY_TYPES.register();
     }
 }