From d96c96fc6e45fde48a300b85f3a9f9edc6f995c1 Mon Sep 17 00:00:00 2001 From: Cailin Smith Date: Sun, 2 Jul 2023 19:44:55 +0200 Subject: [PATCH] Add ability for servers to communicate with the client to set the world. This prevents the client from accidentally selected the wrong world folder to load LODs from, since levels of the same dimension can't naturally be distinguished from each other. With level similarity detection, this can sometimes work, but in general is not reliable. This mechanism instead allows servers to send a packet to the client on load, enabling the override system, and then a second packet on world change, which specifically sets the world key, based on knowledge that only the server has, leading to a reliable way of detecting the correct world. --- .../common/wrappers/DependencySetup.java | 3 + .../level/ServerEnhancedClientLevel.java | 20 ++++++ .../wrappers/level/ServerEnhancedManager.java | 34 ++++++++++ .../minecraft/FriendlyByteBufWrapper.java | 26 ++++++++ .../minecraft/MinecraftClientWrapper.java | 7 ++- .../wrappers/world/ClientLevelWrapper.java | 36 ++++++++++- coreSubProjects | 2 +- fabric/build.gradle | 1 + .../fabric/FabricClientProxy.java | 9 +-- .../fabric/FabricServerProxy.java | 63 ++++++++++++------- 10 files changed, 169 insertions(+), 32 deletions(-) create mode 100644 common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedClientLevel.java create mode 100644 common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedManager.java create mode 100644 common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/FriendlyByteBufWrapper.java diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java index ec01f988b..c6bd30a8c 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java @@ -20,7 +20,9 @@ package com.seibel.distanthorizons.common.wrappers; import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper; +import com.seibel.distanthorizons.common.wrappers.level.ServerEnhancedManager; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper; +import com.seibel.distanthorizons.core.level.IServerEnhancedManager; import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; @@ -49,6 +51,7 @@ public class DependencySetup { SingletonInjector.INSTANCE.bind(ILangWrapper.class, LangWrapper.INSTANCE); SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE); SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE); + SingletonInjector.INSTANCE.bind(IServerEnhancedManager.class, ServerEnhancedManager.INSTANCE); DependencySetupDoneCheck.isDone = true; } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedClientLevel.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedClientLevel.java new file mode 100644 index 000000000..024e0603d --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedClientLevel.java @@ -0,0 +1,20 @@ +package com.seibel.distanthorizons.common.wrappers.level; + +import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; +import com.seibel.distanthorizons.core.level.IServerEnhancedClientLevel; +import net.minecraft.client.multiplayer.ClientLevel; + +public class ServerEnhancedClientLevel extends ClientLevelWrapper implements IServerEnhancedClientLevel { + + private final String serverKey; + + public ServerEnhancedClientLevel(ClientLevel level, String serverKey) { + super(level); + this.serverKey = serverKey; + } + + @Override + public String getServerWorldKey() { + return this.serverKey; + } +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedManager.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedManager.java new file mode 100644 index 000000000..62f96d9ae --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/level/ServerEnhancedManager.java @@ -0,0 +1,34 @@ +package com.seibel.distanthorizons.common.wrappers.level; + +import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; +import com.seibel.distanthorizons.core.level.IServerEnhancedClientLevel; +import com.seibel.distanthorizons.core.level.IServerEnhancedManager; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import net.minecraft.client.multiplayer.ClientLevel; +import org.apache.logging.log4j.core.jmx.Server; + +import java.util.Objects; + +public class ServerEnhancedManager implements IServerEnhancedManager { + + public static ServerEnhancedManager INSTANCE = new ServerEnhancedManager(); + + @Override + public void registerServerEnhancedLevel(IServerEnhancedClientLevel clientLevel) { + ClientLevelWrapper.setWrappedLevel(clientLevel); + } + + + + @Override + public IServerEnhancedClientLevel getServerEnhancedLevel(ILevelWrapper level, String worldKey) { + Objects.requireNonNull(level); + Objects.requireNonNull(worldKey); + return new ServerEnhancedClientLevel((ClientLevel) level.getWrappedMcObject(), worldKey); + } + + @Override + public void setUseOverrideWrapper(boolean useOverrideWrapper) { + ClientLevelWrapper.setUseOverrideWrapper(useOverrideWrapper); + } +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/FriendlyByteBufWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/FriendlyByteBufWrapper.java new file mode 100644 index 000000000..e9a9a7623 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/FriendlyByteBufWrapper.java @@ -0,0 +1,26 @@ +package com.seibel.distanthorizons.common.wrappers.minecraft; + +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IFriendlyByteBuf; +import net.minecraft.network.FriendlyByteBuf; + +import java.nio.charset.Charset; + +public class FriendlyByteBufWrapper implements IFriendlyByteBuf +{ + private final FriendlyByteBuf buf; + + public FriendlyByteBufWrapper(FriendlyByteBuf buf) { + this.buf = buf; + } + + @Override + public short readShort() + { + return buf.readShort(); + } + + public CharSequence readCharSequence(int length, Charset charset) + { + return buf.readCharSequence(length, charset); + } +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java index a892f1be1..881ebdeb7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java @@ -180,7 +180,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra return null; } - return ClientLevelWrapper.getWrapper(mc.level); + return ClientLevelWrapper.getOriginalWrapper(mc.level); } /** Please move over to getInstallationDirectory() */ @@ -261,4 +261,9 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra public File getInstallationDirectory() { return mc.gameDirectory; } + + @Override + public void execute(Runnable runnable) { + mc.execute(runnable); + } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java index 89d05d57c..4eca3012f 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java @@ -38,7 +38,34 @@ public class ClientLevelWrapper implements IClientLevelWrapper private static final ConcurrentHashMap levelWrapperMap = new ConcurrentHashMap<>(); - public static ClientLevelWrapper getWrapper(ClientLevel level) { + /** + * This is set and managed by the ClientApi for servers with support for DH. + */ + private static IClientLevelWrapper overrideWrapper = null; + private static boolean useOverrideWrapper = false; + + public static void setWrappedLevel(IClientLevelWrapper wrapper) { + overrideWrapper = wrapper; + } + + public static void setUseOverrideWrapper(boolean useOverrideWrapper) { + ClientLevelWrapper.useOverrideWrapper = useOverrideWrapper; + } + + public static IClientLevelWrapper getWrapper(ClientLevel level) { + if(useOverrideWrapper) { + return overrideWrapper; + } + return getOriginalWrapper(level); + } + + /** + * Gets the original level wrapper, regardless of whether or not the server is overriding the + * level wrapper. + * @param level + * @return + */ + public static IClientLevelWrapper getOriginalWrapper(ClientLevel level) { return levelWrapperMap.computeIfAbsent(level, ClientLevelWrapper::new); } public static void closeWrapper(ClientLevel level) @@ -46,9 +73,11 @@ public class ClientLevelWrapper implements IClientLevelWrapper levelWrapperMap.remove(level); } - private ClientLevelWrapper(ClientLevel level) { + protected ClientLevelWrapper(ClientLevel level) { this.level = level; } + + final ClientLevel level; ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this); @Nullable @@ -184,6 +213,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper @Override public String toString() { + if(level == null) { + return "Wrapped{null}"; + } return "Wrapped{" + level.toString() + "@" + getDimensionType().getDimensionName() + "}"; } diff --git a/coreSubProjects b/coreSubProjects index d04b4c0d5..368541b09 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit d04b4c0d55b56e015cd150b2050f468aff9e1048 +Subproject commit 368541b09c72c8e9d6c95fbe0f4d471b12ced5cd diff --git a/fabric/build.gradle b/fabric/build.gradle index ae5cb6a04..c3fc3acef 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -60,6 +60,7 @@ dependencies { addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy) addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) + addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version)) // Mod Menu modImplementation("com.terraformersmc:modmenu:${rootProject.modmenu_version}") diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java index 17818f2c3..89fcabe5c 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java @@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.fabric.wrappers.modAccessor.ImmersivePortalsAccessor; import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor; import net.fabricmc.api.EnvType; @@ -100,7 +101,7 @@ public class FabricClientProxy //#if PRE_MC_1_18_1 // in 1.18+, we use mixin hook in setClientLightReady(true) ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> { - ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); ClientApi.INSTANCE.clientChunkLoadEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel @@ -119,7 +120,7 @@ public class FabricClientProxy { // LOGGER.info("attack block at blockpos: " + blockPos); - ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); ClientApi.INSTANCE.clientChunkLoadEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel @@ -146,7 +147,7 @@ public class FabricClientProxy { // LOGGER.info("use block at blockpos: " + hitResult.getBlockPos()); - ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level); ClientApi.INSTANCE.clientChunkLoadEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel @@ -163,7 +164,7 @@ public class FabricClientProxy // ClientChunkSaveEvent ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) -> { - ClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); + IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level); ClientApi.INSTANCE.clientChunkSaveEvent( new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java index 71e006f31..819dc2db2 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java @@ -2,20 +2,29 @@ package com.seibel.distanthorizons.fabric; import com.seibel.distanthorizons.common.networking.Networking; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; +import com.seibel.distanthorizons.common.wrappers.minecraft.FriendlyByteBufWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ServerApi; +import com.seibel.distanthorizons.core.level.IServerEnhancedManager; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.common.wrappers.level.ServerEnhancedClientLevel; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; +import net.fabricmc.fabric.api.networking.v1.PacketSender; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.TitleScreen; import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ClientPacketListener; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; @@ -35,50 +44,50 @@ public class FabricServerProxy { private static final ServerApi SERVER_API = ServerApi.INSTANCE; private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - + private final boolean isDedicated; public static Supplier isGenerationThreadChecker = null; - - - + + + public FabricServerProxy(boolean isDedicated) { this.isDedicated = isDedicated; } - - - + + + private boolean isValidTime() { if (isDedicated) { return true; } - + //FIXME: This may cause init issue... return !(Minecraft.getInstance().screen instanceof TitleScreen); } - - private ClientLevelWrapper getClientLevelWrapper(ClientLevel level) { return ClientLevelWrapper.getWrapper(level); } + + private IClientLevelWrapper getClientLevelWrapper(ClientLevel level) { return ClientLevelWrapper.getWrapper(level); } private ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); } - + /** Registers Fabric Events */ public void registerEvents() { LOGGER.info("Registering Fabric Server Events"); isGenerationThreadChecker = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread; - + /* Register the mod needed event callbacks */ - + // TEST EVENT //ServerTickEvents.END_SERVER_TICK.register(this::tester); - + // ServerTickEvent ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent()); - + // ServerWorldLoadEvent //TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?) - ServerLifecycleEvents.SERVER_STARTING.register((server) -> + ServerLifecycleEvents.SERVER_STARTING.register((server) -> { if (isValidTime()) { @@ -86,16 +95,16 @@ public class FabricServerProxy } }); // ServerWorldUnloadEvent - ServerLifecycleEvents.SERVER_STOPPED.register((server) -> + ServerLifecycleEvents.SERVER_STOPPED.register((server) -> { if (isValidTime()) { ServerApi.INSTANCE.serverUnloadEvent(); } }); - + // ServerLevelLoadEvent - ServerWorldEvents.LOAD.register((server, level) -> + ServerWorldEvents.LOAD.register((server, level) -> { if (isValidTime()) { @@ -103,16 +112,16 @@ public class FabricServerProxy } }); // ServerLevelUnloadEvent - ServerWorldEvents.UNLOAD.register((server, level) -> + ServerWorldEvents.UNLOAD.register((server, level) -> { if (isValidTime()) { ServerApi.INSTANCE.serverLevelUnloadEvent(getServerLevelWrapper(level)); } }); - + // ServerChunkLoadEvent - ServerChunkEvents.CHUNK_LOAD.register((server, chunk) -> + ServerChunkEvents.CHUNK_LOAD.register((server, chunk) -> { ILevelWrapper level = getServerLevelWrapper((ServerLevel) chunk.getLevel()); if (isValidTime()) @@ -123,8 +132,14 @@ public class FabricServerProxy } }); // ServerChunkSaveEvent - Done in MixinChunkMap + + ClientPlayNetworking.registerGlobalReceiver(new ResourceLocation("distant_horizons", "world_control"), + (Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender) -> + { + ClientApi.INSTANCE.serverMessageReceived(new FriendlyByteBufWrapper(buf)); + }); } - + // This just exists here for testing purposes, it'll be removed in the future public void tester(MinecraftServer server) { // I disabled the Networking functions for now so this will not work atm - coolGi @@ -136,5 +151,5 @@ public class FabricServerProxy Networking.send(player, payload); } } - + }