diff --git a/patches/net/minecraft/server/level/ServerPlayer.java.patch b/patches/net/minecraft/server/level/ServerPlayer.java.patch index a8a70b3b778..be52cd03203 100644 --- a/patches/net/minecraft/server/level/ServerPlayer.java.patch +++ b/patches/net/minecraft/server/level/ServerPlayer.java.patch @@ -91,8 +91,31 @@ return this.isReachableBedBlock(p_9117_) || this.isReachableBedBlock(p_9117_.relative(p_9118_.getOpposite())); } -@@ -1003,6 +_,7 @@ - .send(new ClientboundOpenScreenPacket(abstractcontainermenu.containerId, abstractcontainermenu.getType(), p_9033_.getDisplayName())); +@@ -983,6 +_,11 @@ + + @Override + public OptionalInt openMenu(@Nullable MenuProvider p_9033_) { ++ return openMenu(p_9033_, (java.util.function.Consumer) null); ++ } ++ ++ @Override ++ public OptionalInt openMenu(@Nullable MenuProvider p_9033_, @Nullable java.util.function.Consumer extraDataWriter) { + if (p_9033_ == null) { + return OptionalInt.empty(); + } else { +@@ -999,10 +_,16 @@ + + return OptionalInt.empty(); + } else { +- this.connection +- .send(new ClientboundOpenScreenPacket(abstractcontainermenu.containerId, abstractcontainermenu.getType(), p_9033_.getDisplayName())); ++ if (extraDataWriter == null) { ++ this.connection ++ .send(new ClientboundOpenScreenPacket(abstractcontainermenu.containerId, abstractcontainermenu.getType(), p_9033_.getDisplayName())); ++ } else { ++ this.connection ++ .send(new net.neoforged.neoforge.network.payload.AdvancedOpenScreenPayload(abstractcontainermenu.containerId, abstractcontainermenu.getType(), p_9033_.getDisplayName(), extraDataWriter)); ++ } this.initMenu(abstractcontainermenu); this.containerMenu = abstractcontainermenu; + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.post(new net.neoforged.neoforge.event.entity.player.PlayerContainerEvent.Open(this, this.containerMenu)); diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IPlayerExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IPlayerExtension.java index 4dee1e3cb84..dac364e825f 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IPlayerExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IPlayerExtension.java @@ -5,13 +5,19 @@ package net.neoforged.neoforge.common.extensions; +import java.util.OptionalInt; +import java.util.function.Consumer; import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.world.MenuProvider; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.Vec3; +import net.neoforged.neoforge.client.ConfigScreenHandler; import net.neoforged.neoforge.common.NeoForgeMod; +import org.jetbrains.annotations.Nullable; public interface IPlayerExtension { @@ -92,4 +98,31 @@ default boolean isCloseEnough(Entity entity, double dist) { return aabb.distanceToSqr(eye) < dist * dist; } + /** + * Request to open a GUI on the client, from the server + *

+ * Refer to {@link ConfigScreenHandler.ConfigScreenFactory} for how to provide a function to consume + * these GUI requests on the client. + * + * @param menuProvider A supplier of container properties including the registry name of the container + * @param pos A block pos, which will be encoded into the additional data for this request + */ + default OptionalInt openMenu(@Nullable MenuProvider menuProvider, BlockPos pos) { + return openMenu(menuProvider, buf -> buf.writeBlockPos(pos)); + } + + /** + * Request to open a GUI on the client, from the server + *

+ * Refer to {@link ConfigScreenHandler.ConfigScreenFactory} for how to provide a function to consume + * these GUI requests on the client. + *

+ * The maximum size for #extraDataWriter is 32600 bytes. + * + * @param menuProvider A supplier of container properties including the registry name of the container + * @param extraDataWriter Consumer to write any additional data the GUI needs + */ + default OptionalInt openMenu(@Nullable MenuProvider menuProvider, @Nullable Consumer extraDataWriter) { + return OptionalInt.empty(); + } } diff --git a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java index 35b4e09f99f..1742200a4ff 100644 --- a/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java +++ b/src/main/java/net/neoforged/neoforge/network/NetworkInitialization.java @@ -60,6 +60,11 @@ public static void register(final RegisterPacketHandlerEvent event) { AdvancedAddEntityPayload.ID, AdvancedAddEntityPayload::new, handlers -> handlers.client(ClientPayloadHandler.getInstance()::handle) + ) + .play( + AdvancedOpenScreenPayload.ID, + AdvancedOpenScreenPayload::new, + handlers -> handlers.client(ClientPayloadHandler.getInstance()::handle) ); } 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 ba0ede4fb82..172d92f6ded 100644 --- a/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java +++ b/src/main/java/net/neoforged/neoforge/network/handlers/ClientPayloadHandler.java @@ -7,13 +7,17 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.screens.MenuScreens; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.client.gui.screens.inventory.MenuAccess; 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.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; import net.minecraft.world.level.Level; import net.neoforged.neoforge.common.TierSortingRegistry; import net.neoforged.neoforge.common.util.LogicalSidedProvider; @@ -22,6 +26,7 @@ import net.neoforged.neoforge.network.handling.ConfigurationPayloadContext; import net.neoforged.neoforge.network.handling.PlayPayloadContext; import net.neoforged.neoforge.network.payload.AdvancedAddEntityPayload; +import net.neoforged.neoforge.network.payload.AdvancedOpenScreenPayload; import net.neoforged.neoforge.network.payload.ConfigFilePayload; import net.neoforged.neoforge.network.payload.FrozenRegistryPayload; import net.neoforged.neoforge.network.payload.FrozenRegistrySyncCompletedPayload; @@ -77,9 +82,8 @@ public void handle(ConfigurationPayloadContext context, TierSortingRegistryPaylo } public void handle(PlayPayloadContext context, AdvancedAddEntityPayload msg) { - EntityType type = BuiltInRegistries.ENTITY_TYPE.byId(msg.typeId()); Optional world = LogicalSidedProvider.CLIENTWORLD.get(context.flow().getReceptionSide()); - Entity e = world.map(w -> type.customClientSpawn(msg, w)).orElse(null); + Entity e = world.map(w -> msg.typeId().customClientSpawn(msg, w)).orElse(null); if (e == null) { return; } @@ -99,8 +103,29 @@ public void handle(PlayPayloadContext context, AdvancedAddEntityPayload msg) { 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); + try { + entityAdditionalSpawnData.readSpawnData(buf); + } finally { + buf.release(); + } + } + } + + public void handle(PlayPayloadContext context, AdvancedOpenScreenPayload msg) { + final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(msg.additionalData())); + try { + createMenuScreen(msg.name(), msg.menuType(), msg.windowId(), buf); + } finally { buf.release(); } } + + private static void createMenuScreen(Component name, MenuType menuType, int windowId, FriendlyByteBuf buf) { + Minecraft mc = Minecraft.getInstance(); + MenuScreens.getScreenFactory(menuType, mc, windowId, name).ifPresent(f -> { + Screen s = f.create(menuType.create(windowId, mc.player.getInventory(), buf), mc.player.getInventory(), name); + mc.player.containerMenu = ((MenuAccess) s).getMenu(); + mc.setScreen(s); + }); + } } diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java index 4be15e05d66..c3598f27f33 100644 --- a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedAddEntityPayload.java @@ -7,16 +7,14 @@ 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.minecraft.world.entity.EntityType; import net.neoforged.neoforge.entity.IEntityAdditionalSpawnData; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; -import org.jetbrains.annotations.NotNull; import java.util.UUID; public record AdvancedAddEntityPayload( - int typeId, + EntityType typeId, int entityId, UUID uuid, double posX, @@ -34,7 +32,7 @@ public record AdvancedAddEntityPayload( public AdvancedAddEntityPayload(FriendlyByteBuf buf) { this( - buf.readVarInt(), + buf.readById(BuiltInRegistries.ENTITY_TYPE), buf.readVarInt(), buf.readUUID(), buf.readDouble(), @@ -52,7 +50,7 @@ public AdvancedAddEntityPayload(FriendlyByteBuf buf) { public AdvancedAddEntityPayload(Entity e) { this( - BuiltInRegistries.ENTITY_TYPE.getId(e.getType()), + e.getType(), e.getId(), e.getUUID(), e.getX(), @@ -70,31 +68,32 @@ public AdvancedAddEntityPayload(Entity e) { private static byte[] writeCustomData(final Entity entity) { final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + try { + if (entity instanceof IEntityAdditionalSpawnData additionalSpawnData) { + additionalSpawnData.writeSpawnData(buf); + } - if (entity instanceof IEntityAdditionalSpawnData additionalSpawnData) { - additionalSpawnData.writeSpawnData(buf); + return buf.array(); + } finally { + buf.release(); } - - final byte[] payload = buf.array(); - buf.release(); - return payload; } @Override public void write(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.writeByteArray(customPayload); + buffer.writeId(BuiltInRegistries.ENTITY_TYPE, 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.writeByteArray(customPayload()); } @Override diff --git a/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java new file mode 100644 index 00000000000..ab8ba2e4a9b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/network/payload/AdvancedOpenScreenPayload.java @@ -0,0 +1,50 @@ +package net.neoforged.neoforge.network.payload; + +import io.netty.buffer.Unpooled; +import java.util.function.Consumer; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; + +public record AdvancedOpenScreenPayload( + int windowId, + MenuType menuType, + Component name, + byte[] additionalData +) implements CustomPacketPayload { + public static final ResourceLocation ID = new ResourceLocation(NeoForgeVersion.MOD_ID, "advanced_open_screen"); + + public AdvancedOpenScreenPayload(int windowId, MenuType menuType, Component name, Consumer dataWriter) { + this(windowId, menuType, name, writeCustomData(dataWriter)); + } + + private static byte[] writeCustomData(Consumer dataWriter) { + final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer()); + try { + dataWriter.accept(buf); + return buf.array(); + } finally { + buf.release(); + } + } + + public AdvancedOpenScreenPayload(FriendlyByteBuf buffer) { + this(buffer.readVarInt(), buffer.readById(BuiltInRegistries.MENU), buffer.readComponentTrusted(), buffer.readByteArray()); + } + @Override + public void write(FriendlyByteBuf buffer) { + buffer.writeVarInt(windowId()); + buffer.writeId(BuiltInRegistries.MENU, menuType()); + buffer.writeComponent(name()); + buffer.writeByteArray(additionalData()); + } + + @Override + public ResourceLocation id() { + return ID; + } +}