Merge branch 'distant-horizons-core-serverSupport'
This commit is contained in:
+15
@@ -0,0 +1,15 @@
|
||||
package com.seibel.distanthorizons.api.methods.events.interfaces;
|
||||
|
||||
/**
|
||||
* @author Cailin
|
||||
*/
|
||||
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 serverMessageReceived(String channel, byte[] message);
|
||||
|
||||
}
|
||||
@@ -21,6 +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.IServerKeyedClientLevel;
|
||||
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.world.*;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
@@ -36,64 +39,64 @@ import com.seibel.distanthorizons.coreapi.util.math.Mat4f;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.render.renderer.TestRenderer;
|
||||
import com.seibel.distanthorizons.core.util.RenderUtil;
|
||||
import com.seibel.distanthorizons.core.world.DhClientWorld;
|
||||
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||
import com.seibel.distanthorizons.core.world.IDhClientWorld;
|
||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
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.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
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
public static final boolean ENABLE_EVENT_LOGGING = true;
|
||||
public static boolean prefLoggerEnabled = false;
|
||||
|
||||
|
||||
public static final ClientApi INSTANCE = new 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 IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||
|
||||
public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
|
||||
|
||||
|
||||
private boolean configOverrideReminderPrinted = false;
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
|
||||
private long lastFlushNanoTime = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean isServerCommunicationEnabled = true;
|
||||
|
||||
private boolean serverIsMalformed = false;
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
|
||||
private ClientApi()
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// events //
|
||||
//========//
|
||||
|
||||
|
||||
public void onClientOnlyConnected()
|
||||
{
|
||||
// only continue if the client is connected to a different server
|
||||
@@ -103,11 +106,11 @@ public class ClientApi
|
||||
{
|
||||
LOGGER.info("Client on ClientOnly mode connecting.");
|
||||
}
|
||||
|
||||
|
||||
SharedApi.setDhWorld(new DhClientWorld());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void onClientOnlyDisconnected()
|
||||
{
|
||||
if (MC.clientConnectedToDedicatedServer())
|
||||
@@ -119,13 +122,17 @@ public class ClientApi
|
||||
{
|
||||
LOGGER.info("Client on ClientOnly mode disconnecting.");
|
||||
}
|
||||
|
||||
|
||||
world.close();
|
||||
SharedApi.setDhWorld(null);
|
||||
}
|
||||
this.isServerCommunicationEnabled = false;
|
||||
this.serverIsMalformed = false;
|
||||
KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(false);
|
||||
KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void clientChunkLoadEvent(IChunkWrapper chunk, IClientLevelWrapper level)
|
||||
{
|
||||
if (SharedApi.getEnvironment() == EWorldEnvironment.Client_Only)
|
||||
@@ -137,7 +144,7 @@ public class ClientApi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void clientChunkSaveEvent(IChunkWrapper chunk, IClientLevelWrapper level)
|
||||
{
|
||||
if (SharedApi.getEnvironment() == EWorldEnvironment.Client_Only)
|
||||
@@ -149,14 +156,14 @@ public class ClientApi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void clientLevelUnloadEvent(IClientLevelWrapper level)
|
||||
{
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Client level "+level+" unloading.");
|
||||
}
|
||||
|
||||
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
if (world != null)
|
||||
{
|
||||
@@ -164,12 +171,22 @@ public class ClientApi
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void clientLevelLoadEvent(IClientLevelWrapper level)
|
||||
{
|
||||
if (this.isServerCommunicationEnabled)
|
||||
{
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Server supports communication, deferring loading.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Client level "+level+" loading.");
|
||||
LOGGER.info("Client level " + level + " loading.");
|
||||
}
|
||||
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
@@ -179,40 +196,55 @@ public class ClientApi
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(level));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void serverLevelLoadEvent(IServerKeyedClientLevel level)
|
||||
{
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Server level " + level + " (" + level.getServerLevelKey() + ") loading.");
|
||||
}
|
||||
|
||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||
if (world != null)
|
||||
{
|
||||
world.getOrLoadLevel(level);
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(level));
|
||||
}
|
||||
}
|
||||
|
||||
public void rendererShutdownEvent()
|
||||
{
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Renderer shutting down.");
|
||||
}
|
||||
|
||||
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.push("DH-RendererShutdown");
|
||||
|
||||
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
public void rendererStartupEvent()
|
||||
{
|
||||
if (ENABLE_EVENT_LOGGING)
|
||||
{
|
||||
LOGGER.info("Renderer starting up.");
|
||||
}
|
||||
|
||||
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.push("DH-RendererStartup");
|
||||
|
||||
|
||||
// make sure the GLProxy is created before the LodBufferBuilder needs it
|
||||
GLProxy.getInstance();
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
public void clientTickEvent()
|
||||
{
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.push("DH-ClientTick");
|
||||
|
||||
|
||||
boolean doFlush = System.nanoTime() - this.lastFlushNanoTime >= SPAM_LOGGER_FLUSH_NS;
|
||||
if (doFlush)
|
||||
{
|
||||
@@ -221,7 +253,7 @@ public class ClientApi
|
||||
}
|
||||
ConfigBasedLogger.updateAll();
|
||||
ConfigBasedSpamLogger.updateAll(doFlush);
|
||||
|
||||
|
||||
IDhClientWorld clientWorld = SharedApi.getIDhClientWorld();
|
||||
if (clientWorld != null)
|
||||
{
|
||||
@@ -230,12 +262,75 @@ public class ClientApi
|
||||
profiler.pop();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
short commandLength = byteBuf.readShort();
|
||||
if (commandLength > 32)
|
||||
{
|
||||
LOGGER.error("Server sent command > 32");
|
||||
ClientApi.INSTANCE.serverIsMalformed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
String eventType = byteBuf.readCharSequence(commandLength, StandardCharsets.UTF_8).toString();
|
||||
switch (eventType)
|
||||
{
|
||||
case "ServerCommsEnabled":
|
||||
LOGGER.info("Server supports DH protocol.");
|
||||
ClientApi.INSTANCE.isServerCommunicationEnabled = true;
|
||||
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.
|
||||
this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
|
||||
});
|
||||
break;
|
||||
|
||||
case "WorldChanged":
|
||||
short worldKeyLength = byteBuf.readShort();
|
||||
if (worldKeyLength > 128)
|
||||
{
|
||||
LOGGER.error("Server sent worldKey > 128");
|
||||
this.serverIsMalformed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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.executeOnRenderThread(() -> {
|
||||
if (MC.getWrappedClientWorld() != null)
|
||||
{
|
||||
this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld());
|
||||
}
|
||||
IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientWorld(), worldKey);
|
||||
KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel);
|
||||
this.serverLevelLoadEvent(clientLevel);
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// rendering //
|
||||
//===========//
|
||||
|
||||
|
||||
public void renderLods(IClientLevelWrapper levelWrapper, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks)
|
||||
{
|
||||
if (ModInfo.IS_DEV_BUILD && !this.configOverrideReminderPrinted && MC.playerExists())
|
||||
@@ -246,8 +341,8 @@ public class ClientApi
|
||||
MC.sendChatMessage("Here be dragons!");
|
||||
this.configOverrideReminderPrinted = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
IProfilerWrapper profiler = MC.getProfiler();
|
||||
profiler.pop(); // get out of "terrain"
|
||||
profiler.push("DH-RenderLevel");
|
||||
@@ -257,20 +352,20 @@ public class ClientApi
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//FIXME: Improve class hierarchy of DhWorld, IClientWorld, IServerWorld to fix all this hard casting
|
||||
// (also in RenderUtil)
|
||||
IDhClientWorld dhClientWorld = SharedApi.getIDhClientWorld();
|
||||
IDhClientLevel level = dhClientWorld.getOrLoadClientLevel(levelWrapper);
|
||||
|
||||
|
||||
if (prefLoggerEnabled)
|
||||
{
|
||||
level.dumpRamUsage();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
profiler.push("Render" + (Config.Client.Advanced.Debugging.rendererMode.get() == ERendererMode.DEFAULT ? "-lods" : "-debug"));
|
||||
try
|
||||
{
|
||||
@@ -280,7 +375,7 @@ public class ClientApi
|
||||
new DhApiRenderParam(mcProjectionMatrix, mcModelViewMatrix,
|
||||
RenderUtil.createLodProjectionMatrix(mcProjectionMatrix, partialTicks),
|
||||
RenderUtil.createLodModelViewMatrix(mcModelViewMatrix), partialTicks);
|
||||
|
||||
|
||||
boolean renderingCanceled = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderEvent.class, new DhApiBeforeRenderEvent.EventParam(renderEventParam));
|
||||
if (!this.rendererDisabledBecauseOfExceptions && !renderingCanceled)
|
||||
{
|
||||
@@ -298,7 +393,7 @@ public class ClientApi
|
||||
{
|
||||
this.rendererDisabledBecauseOfExceptions = true;
|
||||
LOGGER.error("Renderer thrown an uncaught exception: ", e);
|
||||
|
||||
|
||||
MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons"
|
||||
+ " renderer has encountered an exception!");
|
||||
MC.sendChatMessage("\u00A74Renderer is now disabled to prevent further issues.");
|
||||
@@ -316,13 +411,13 @@ public class ClientApi
|
||||
profiler.push("terrain"); // go back into "terrain"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// DEBUG USE //
|
||||
//=================//
|
||||
|
||||
|
||||
/** Trigger once on key press, with CLIENT PLAYER. */
|
||||
public void keyPressedEvent(int glfwKey)
|
||||
{
|
||||
@@ -331,8 +426,8 @@ public class ClientApi
|
||||
// keybindings are disabled
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (glfwKey == GLFW.GLFW_KEY_F8)
|
||||
{
|
||||
Config.Client.Advanced.Debugging.debugRendering.set(EDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
|
||||
@@ -349,6 +444,6 @@ public class ClientApi
|
||||
MC.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnR
|
||||
import com.seibel.distanthorizons.core.dataObjects.transformers.DataRenderTransformer;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.FullDataFileHandler;
|
||||
import com.seibel.distanthorizons.core.generation.WorldGenerationQueue;
|
||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||
import com.seibel.distanthorizons.core.world.*;
|
||||
import com.seibel.distanthorizons.core.world.*;
|
||||
|
||||
@@ -12,6 +13,7 @@ import com.seibel.distanthorizons.core.world.*;
|
||||
public class SharedApi
|
||||
{
|
||||
private static AbstractDhWorld currentWorld;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+59
-31
@@ -5,9 +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.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;
|
||||
@@ -25,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())
|
||||
{
|
||||
@@ -54,34 +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 (Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get() == 0)
|
||||
// Use the server provided key if one was provided
|
||||
if (newLevelWrapper instanceof IServerKeyedClientLevel)
|
||||
{
|
||||
if (this.fileMatcher != null)
|
||||
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.
|
||||
return new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey());
|
||||
}
|
||||
|
||||
|
||||
// use multiverse matching if enabled
|
||||
if (Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get() != 0)
|
||||
{
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -129,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;
|
||||
@@ -141,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;
|
||||
@@ -173,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()
|
||||
{
|
||||
@@ -213,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() + "]"; }
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler;
|
||||
import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure;
|
||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
|
||||
+24
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -45,6 +45,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||
return this.levels.computeIfAbsent((IClientLevelWrapper) wrapper, (clientLevelWrapper) ->
|
||||
{
|
||||
File file = this.saveStructure.getLevelFolder(wrapper);
|
||||
|
||||
if (file == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
+3
@@ -138,5 +138,8 @@ public interface IMinecraftClientWrapper extends IBindable
|
||||
void crashMinecraft(String errorMessage, Throwable exception); //FIXME: Move to IMinecraftSharedWrapper
|
||||
|
||||
Object getOptionsObject();
|
||||
|
||||
/** Executes the given task on Minecraft's render thread. */
|
||||
void executeOnRenderThread(Runnable runnable);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user