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>
This commit is contained in:
canitzp
2021-05-27 19:34:50 +02:00
committed by GitHub
parent 30dcaa480b
commit 371925b28d
9 changed files with 193 additions and 2 deletions

View File

@@ -28,6 +28,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import java.util.Objects; import java.util.Objects;
@@ -74,6 +75,20 @@ public final class NetworkManager {
throw new AssertionError(); 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 @FunctionalInterface
public interface NetworkReceiver { public interface NetworkReceiver {
void receive(FriendlyByteBuf buf, PacketContext context); void receive(FriendlyByteBuf buf, PacketContext context);

View File

@@ -20,10 +20,13 @@
package me.shedaniel.architectury.init.fabric; package me.shedaniel.architectury.init.fabric;
import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent; import me.shedaniel.architectury.event.events.client.ClientLifecycleEvent;
import me.shedaniel.architectury.networking.fabric.SpawnEntityPacket;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
public class ArchitecturyClient { public class ArchitecturyClient {
public static void init() { public static void init() {
ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(Minecraft.getInstance()); ClientLifecycleEvent.CLIENT_SETUP.invoker().stateChanged(Minecraft.getInstance());
SpawnEntityPacket.register();
} }
} }

View File

@@ -31,6 +31,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
public class NetworkManagerImpl { public class NetworkManagerImpl {
@@ -88,6 +89,10 @@ public class NetworkManagerImpl {
return ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, id); return ServerSidePacketRegistry.INSTANCE.canPlayerReceive(player, id);
} }
public static Packet<?> createAddEntityPacket(Entity entity) {
return SpawnEntityPacket.create(entity);
}
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
private static Packet<?> toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) { private static Packet<?> toC2SPacket(ResourceLocation id, FriendlyByteBuf buf) {
return ClientSidePacketRegistry.INSTANCE.toPacket(id, buf); return ClientSidePacketRegistry.INSTANCE.toPacket(id, buf);

View File

@@ -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);
});
}
}

View File

@@ -30,6 +30,7 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; 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.common.Mod;
import net.minecraftforge.fml.network.NetworkDirection; import net.minecraftforge.fml.network.NetworkDirection;
import net.minecraftforge.fml.network.NetworkEvent; import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraftforge.fml.network.NetworkHooks;
import net.minecraftforge.fml.network.NetworkRegistry; import net.minecraftforge.fml.network.NetworkRegistry;
import net.minecraftforge.fml.network.event.EventNetworkChannel; import net.minecraftforge.fml.network.event.EventNetworkChannel;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
@@ -148,6 +150,10 @@ public class NetworkManagerImpl {
return clientReceivables.get(player).contains(id); return clientReceivables.get(player).contains(id);
} }
public static Packet<?> createAddEntityPacket(Entity entity){
return NetworkHooks.getEntitySpawningPacket(entity);
}
static FriendlyByteBuf sendSyncPacket(Map<ResourceLocation, NetworkReceiver> map) { static FriendlyByteBuf sendSyncPacket(Map<ResourceLocation, NetworkReceiver> map) {
List<ResourceLocation> availableIds = Lists.newArrayList(map.keySet()); List<ResourceLocation> availableIds = Lists.newArrayList(map.keySet());
FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer()); FriendlyByteBuf packetBuffer = new FriendlyByteBuf(Unpooled.buffer());

View File

@@ -6,7 +6,7 @@ supported_version=1.16.4/5
archives_base_name=architectury archives_base_name=architectury
archives_base_name_snapshot=architectury-snapshot archives_base_name_snapshot=architectury-snapshot
base_version=1.16 base_version=1.17
maven_group=me.shedaniel maven_group=me.shedaniel
fabric_loader_version=0.11.1 fabric_loader_version=0.11.1

View File

@@ -20,9 +20,11 @@
package me.shedaniel.architectury.test; package me.shedaniel.architectury.test;
import me.shedaniel.architectury.platform.Platform; 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.ConsoleMessageSink;
import me.shedaniel.architectury.test.debug.MessageSink; import me.shedaniel.architectury.test.debug.MessageSink;
import me.shedaniel.architectury.test.debug.client.ClientOverlayMessageSink; 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.events.DebugEvents;
import me.shedaniel.architectury.test.gamerule.TestGameRules; import me.shedaniel.architectury.test.gamerule.TestGameRules;
import me.shedaniel.architectury.test.registry.TestRegistries; 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.test.trade.TestTrades;
import me.shedaniel.architectury.utils.Env; import me.shedaniel.architectury.utils.Env;
import me.shedaniel.architectury.utils.EnvExecutor; import me.shedaniel.architectury.utils.EnvExecutor;
import net.minecraft.client.renderer.entity.MinecartRenderer;
public class TestMod { public class TestMod {
public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new); public static final MessageSink SINK = EnvExecutor.getEnvSpecific(() -> ClientOverlayMessageSink::new, () -> ConsoleMessageSink::new);
@@ -42,7 +45,9 @@ public class TestMod {
TestGameRules.init(); TestGameRules.init();
TestTags.initialize(); TestTags.initialize();
TestTrades.init(); TestTrades.init();
if (Platform.getEnvironment() == Env.CLIENT) if (Platform.getEnvironment() == Env.CLIENT) {
TestKeybinds.initialize(); TestKeybinds.initialize();
EntityRenderers.register(TestEntity.TYPE, MinecartRenderer<TestEntity>::new);
}
} }
} }

View File

@@ -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);
}
}

View File

@@ -24,10 +24,12 @@ import me.shedaniel.architectury.registry.BlockProperties;
import me.shedaniel.architectury.registry.DeferredRegister; import me.shedaniel.architectury.registry.DeferredRegister;
import me.shedaniel.architectury.registry.RegistrySupplier; import me.shedaniel.architectury.registry.RegistrySupplier;
import me.shedaniel.architectury.test.TestMod; 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.registry.objects.EquippableTickingItem;
import me.shedaniel.architectury.test.tab.TestCreativeTabs; import me.shedaniel.architectury.test.tab.TestCreativeTabs;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@@ -42,6 +44,7 @@ import static me.shedaniel.architectury.test.TestMod.SINK;
public class TestRegistries { public class TestRegistries {
public static final DeferredRegister<Item> ITEMS = DeferredRegister.create(TestMod.MOD_ID, Registry.ITEM_REGISTRY); 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<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", () -> public static final RegistrySupplier<Item> TEST_ITEM = ITEMS.register("test_item", () ->
new Item(new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); 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", () -> public static final RegistrySupplier<Item> COLLISION_BLOCK_ITEM = ITEMS.register("collision_block", () ->
new BlockItem(COLLISION_BLOCK.get(), new Item.Properties().tab(TestCreativeTabs.TEST_TAB))); 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() { public static void initialize() {
BLOCKS.register(); BLOCKS.register();
ITEMS.register(); ITEMS.register();
ENTITY_TYPES.register();
} }
} }