refactor and rename

This commit is contained in:
James Seibel
2023-07-04 10:29:32 -05:00
parent 55f39996cb
commit 2b0ee29a8c
9 changed files with 150 additions and 139 deletions
@@ -1,15 +1,15 @@
package com.seibel.distanthorizons.api.methods.events.interfaces;
/**
*
* @author Cailin
*/
public interface IDhServerMessageRecieved<T> extends IDhApiEvent<T>
public interface IDhServerMessageReceived<T> extends IDhApiEvent<T>
{
/**
* Triggered when a plugin message is received from the server.
* @param channel The name of the channel this was received on.
* @param message The message sent from the server.
*/
void serverMessageRecieved(String channel, byte[] message);
void serverMessageReceived(String channel, byte[] message);
}
@@ -21,10 +21,9 @@ package com.seibel.distanthorizons.core.api.internal;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
import com.seibel.distanthorizons.core.level.IServerEnhancedClientLevel;
import com.seibel.distanthorizons.core.level.IEnhancedServerManager;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.world.*;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IFriendlyByteBuf;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.level.IDhClientLevel;
import com.seibel.distanthorizons.core.config.Config;
@@ -45,20 +44,19 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import io.netty.buffer.ByteBuf;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.glfw.GLFW;
import java.nio.charset.Charset;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* This holds the methods that should be called
* by the host mod loader (Fabric, Forge, etc.).
* Specifically for the client.
*
* @author James Seibel
* @version 2022-9-16
*/
public class ClientApi
{
@@ -70,8 +68,7 @@ public class ClientApi
public static TestRenderer testRenderer = new TestRenderer();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IEnhancedServerManager ENHANCED_SERVER_MANAGER
= SingletonInjector.INSTANCE.get(IEnhancedServerManager.class);
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
@@ -131,8 +128,8 @@ public class ClientApi
}
this.isServerCommunicationEnabled = false;
this.serverIsMalformed = false;
ENHANCED_SERVER_MANAGER.setUseOverrideWrapper(false);
ENHANCED_SERVER_MANAGER.registerServerEnhancedLevel(null);
KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(false);
KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(null);
}
}
@@ -177,15 +174,21 @@ public class ClientApi
public void clientLevelLoadEvent(IClientLevelWrapper level)
{
if (ENABLE_EVENT_LOGGING) {
if (this.isServerCommunicationEnabled) {
if (this.isServerCommunicationEnabled)
{
if (ENABLE_EVENT_LOGGING)
{
LOGGER.info("Server supports communication, deferring loading.");
return;
}
return;
}
if (ENABLE_EVENT_LOGGING)
{
LOGGER.info("Client level " + level + " loading.");
}
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world != null)
{
@@ -194,11 +197,11 @@ public class ClientApi
}
}
public void serverLevelLoadEvent(IServerEnhancedClientLevel level)
public void serverLevelLoadEvent(IServerKeyedClientLevel level)
{
if (ENABLE_EVENT_LOGGING)
{
LOGGER.info("Server level " + level + " (" + level.getServerWorldKey() + ") loading.");
LOGGER.info("Server level " + level + " (" + level.getServerLevelKey() + ") loading.");
}
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
@@ -258,54 +261,65 @@ public class ClientApi
}
profiler.pop();
}
public void serverMessageReceived(IFriendlyByteBuf buf)
/** @param byteBuf is Netty's {@link ByteBuffer} wrapper. */
public void serverMessageReceived(ByteBuf byteBuf)
{
// It is important to ensure malicious server input is ignored.
if(this.serverIsMalformed) {
if (this.serverIsMalformed)
{
return;
}
short commandLength = buf.readShort();
if(commandLength > 32) {
short commandLength = byteBuf.readShort();
if (commandLength > 32)
{
LOGGER.error("Server sent command > 32");
ClientApi.INSTANCE.serverIsMalformed = true;
return;
}
String eventType = buf.readCharSequence(commandLength, Charset.forName("UTF-8")).toString();
switch(eventType) {
String eventType = byteBuf.readCharSequence(commandLength, StandardCharsets.UTF_8).toString();
switch (eventType)
{
case "ServerCommsEnabled":
LOGGER.info("Server supports DH protocol.");
ClientApi.INSTANCE.isServerCommunicationEnabled = true;
ENHANCED_SERVER_MANAGER.setUseOverrideWrapper(true);
MC.execute(() -> {
KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(true);
MC.executeOnRenderThread(() -> {
// Go ahead and unload the current world, because it may be wrong. We expect
// a followup WorldChanged event from the server soon anyways.
clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
});
break;
case "WorldChanged":
short worldKeyLength = buf.readShort();
if(worldKeyLength > 128) {
short worldKeyLength = byteBuf.readShort();
if (worldKeyLength > 128)
{
LOGGER.error("Server sent worldKey > 128");
this.serverIsMalformed = true;
return;
}
String worldKey = buf.readCharSequence(worldKeyLength, Charset.forName("UTF-8")).toString();
if(!worldKey.matches("[a-zA-Z0-9_]+")) {
String worldKey = byteBuf.readCharSequence(worldKeyLength, StandardCharsets.UTF_8).toString();
if (!worldKey.matches("[a-zA-Z0-9_]+"))
{
LOGGER.error("Server sent invalid world key name, and is being ignored.");
this.isServerCommunicationEnabled = false;
this.serverIsMalformed = true;
return;
}
LOGGER.info("Server sent world change event: " + worldKey);
MC.execute(() -> {
if(MC.getWrappedClientWorld() != null) {
clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
MC.executeOnRenderThread(() -> {
if (MC.getWrappedClientWorld() != null)
{
this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
}
IServerEnhancedClientLevel clientLevel
= ENHANCED_SERVER_MANAGER.getServerEnhancedLevel(MC.getWrappedClientWorld(), worldKey);
ENHANCED_SERVER_MANAGER.registerServerEnhancedLevel(clientLevel);
serverLevelLoadEvent(clientLevel);
IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientWorld(), worldKey);
KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel);
this.serverLevelLoadEvent(clientLevel);
});
break;
}
@@ -5,10 +5,11 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.file.subDimMatching.SubDimensionLevelMatcher;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.api.enums.config.EServerFolderNameMode;
import com.seibel.distanthorizons.core.level.IServerEnhancedClientLevel;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.util.objects.ParsedIp;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import javax.annotation.Nullable;
@@ -26,17 +27,17 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
{
final File folder;
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]";
SubDimensionLevelMatcher fileMatcher = null;
final HashMap<ILevelWrapper, File> levelToFileMap = new HashMap<>();
SubDimensionLevelMatcher subDimMatcher = null;
final HashMap<ILevelWrapper, File> levelWrapperToFileMap = new HashMap<>();
public ClientOnlySaveStructure()
{
this.folder = new File(MC_CLIENT.getGameDirectory().getPath() +
File.separatorChar + "Distant_Horizons_server_data" + File.separatorChar + getServerFolderName());
this.folder = new File(getSaveStructureFolderPath());
if (!this.folder.exists())
{
@@ -55,42 +56,51 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
//================//
@Override
public File getLevelFolder(ILevelWrapper level)
public File getLevelFolder(ILevelWrapper levelWrapper)
{
return this.levelToFileMap.computeIfAbsent(level, (newLevel) ->
return this.levelWrapperToFileMap.computeIfAbsent(levelWrapper, (newLevelWrapper) ->
{
if (newLevel instanceof IServerEnhancedClientLevel) {
IServerEnhancedClientLevel secl = (IServerEnhancedClientLevel) newLevel;
// Use the server provided key if one was provided
if (newLevelWrapper instanceof IServerKeyedClientLevel)
{
IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper;
LOGGER.info("Loading level "+newLevelWrapper.getDimensionType().getDimensionName()+" with key: "+keyedClientLevel.getServerLevelKey());
// This world was identified by the server directly, so we can know for sure which folder to use.
File seclFolder = new File(this.folder.getParent(), MC_CLIENT.getCurrentServerIp().toString());
seclFolder = new File(seclFolder, secl.getServerWorldKey());
return seclFolder;
return new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey());
}
if (Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get() == 0)
// use multiverse matching if enabled
if (Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get() != 0)
{
if (this.fileMatcher != null)
// create the matcher if one doesn't exist
if (this.subDimMatcher == null || !this.subDimMatcher.isFindingLevel(newLevelWrapper))
{
this.fileMatcher.close();
this.fileMatcher = null;
LOGGER.info("Loading level " + newLevelWrapper.getDimensionType().getDimensionName());
this.subDimMatcher = new SubDimensionLevelMatcher(newLevelWrapper, this.folder,
this.getMatchingLevelFolders(newLevelWrapper).toArray(new File[0] /* surprisingly we don't need to create an array of any specific size for this to work */));
}
return this.getLevelFolderWithoutSimilarityMatching(newLevel);
File levelFile = this.subDimMatcher.tryGetLevel();
if (levelFile != null)
{
this.subDimMatcher.close();
this.subDimMatcher = null;
}
return levelFile;
}
if (this.fileMatcher == null || !this.fileMatcher.isFindingLevel(newLevel))
// we aren't using multiverse matching, shut down the matcher
// TODO this additional call may not be needed
if (this.subDimMatcher != null)
{
LOGGER.info("Loading level for world " + newLevel.getDimensionType().getDimensionName());
this.fileMatcher = new SubDimensionLevelMatcher(newLevel, this.folder,
this.getMatchingLevelFolders(newLevel).toArray(new File[0] /* surprisingly we don't need to create an array of any specific size for this to work */));
this.subDimMatcher.close();
this.subDimMatcher = null;
}
File levelFile = this.fileMatcher.tryGetLevel();
if (levelFile != null)
{
this.fileMatcher.close();
this.fileMatcher = null;
}
return levelFile;
// get the default folder
return this.getLevelFolderWithoutSimilarityMatching(newLevelWrapper);
});
}
@@ -138,7 +148,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
@Override
public File getRenderCacheFolder(ILevelWrapper level)
{
File levelFolder = this.levelToFileMap.get(level);
File levelFolder = this.levelWrapperToFileMap.get(level);
if (levelFolder == null)
{
return null;
@@ -150,7 +160,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
@Override
public File getFullDataFolder(ILevelWrapper level)
{
File levelFolder = this.levelToFileMap.get(level);
File levelFolder = this.levelWrapperToFileMap.get(level);
if (levelFolder == null)
{
return null;
@@ -182,7 +192,16 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
// a valid level folder needs to have DH specific folders in it
return files != null && files.length != 0;
}
private static String getSaveStructureFolderPath()
{
String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar
+ "Distant_Horizons_server_data" + File.separatorChar
+ getServerFolderName();
return path;
}
/** Generated from the server the client is currently connected to. */
private static String getServerFolderName()
{
@@ -222,15 +241,15 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure
// This fixes some issues when the server is named something in other languages
return new PercentEscaper("", true).escape(folderName);
}
//==================//
// override methods //
//==================//
@Override
public void close() { this.fileMatcher.close(); }
public void close() { this.subDimMatcher.close(); }
@Override
public String toString() { return "[" + this.getClass().getSimpleName() + "@" + this.folder.getName() + "]"; }
@@ -1,26 +0,0 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
public interface IEnhancedServerManager extends IBindable {
/**
* Called when a client level is wrapped by a ServerEnhancedClientLevel, for integration into mod internals.
* @param clientLevel
*/
void registerServerEnhancedLevel(IServerEnhancedClientLevel clientLevel);
/**
* Returns a new instance of a ServerEnhancedClientLevel.
* @param level
* @param worldKey
* @return
*/
IServerEnhancedClientLevel getServerEnhancedLevel(ILevelWrapper level, String worldKey);
/**
* Sets the LOD engine to use the override wrapper, if the server has communication enabled.
* @param useOverrideWrapper
*/
void setUseOverrideWrapper(boolean useOverrideWrapper);
}
@@ -0,0 +1,24 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
/**
* Handles level overrides initiated by servers that
* support differentiating between different levels.
*/
public interface IKeyedClientLevelManager extends IBindable
{
/** Called when a client level is wrapped by a ServerEnhancedClientLevel, for integration into mod internals. */
void setServerKeyedLevel(IServerKeyedClientLevel clientLevel);
IServerKeyedClientLevel getOverrideWrapper();
/** Returns a new instance of a ServerEnhancedClientLevel. */
IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey);
/** Sets the LOD engine to use the override wrapper, if the server has communication enabled. */
void setUseOverrideWrapper(boolean useOverrideWrapper);
boolean getUseOverrideWrapper();
}
@@ -1,15 +0,0 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
/**
* Enhances an IClientLevelWrapper with server provided world information.
*/
public interface IServerEnhancedClientLevel extends IClientLevelWrapper
{
/**
* Returns the world key, which is used to select the correct folder on the client.
* @return
*/
String getServerWorldKey();
}
@@ -0,0 +1,11 @@
package com.seibel.distanthorizons.core.level;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
/** Enhances a {@link IClientLevelWrapper} with server provided level information. */
public interface IServerKeyedClientLevel extends IClientLevelWrapper
{
/** Returns the level key, which is used to select the correct folder on the client. */
String getServerLevelKey();
}
@@ -1,13 +0,0 @@
package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft;
import java.nio.charset.Charset;
/**
* Interface that wraps the net.minecraft.network.FriendlyByteBuffer.
*/
public interface IFriendlyByteBuf {
short readShort();
CharSequence readCharSequence(int length, Charset charset);
}
@@ -139,10 +139,7 @@ public interface IMinecraftClientWrapper extends IBindable
Object getOptionsObject();
/**
* Executes a task on the Minecraft render thread.
* @param runnable
*/
void execute(Runnable runnable);
/** Executes the given task on Minecraft's render thread. */
void executeOnRenderThread(Runnable runnable);
}