merge immersive portals compat
Thanks Acuadragon100!
This commit is contained in:
@@ -31,7 +31,7 @@ public final class ModInfo
|
|||||||
public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial";
|
public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial";
|
||||||
|
|
||||||
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
|
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
|
||||||
public static final int PROTOCOL_VERSION = 13;
|
public static final int PROTOCOL_VERSION = 14;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The full plugin channel name (RESOURCE_NAMESPACE:WRAPPER_PACKET_PATH)
|
* The full plugin channel name (RESOURCE_NAMESPACE:WRAPPER_PACKET_PATH)
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import com.seibel.distanthorizons.core.util.objects.Pair;
|
|||||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IImmersivePortalsAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhVanillaFadeRenderer;
|
||||||
@@ -53,7 +54,6 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
|
|||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
|
||||||
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
|
||||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
|
||||||
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.world.DhClientWorld;
|
import com.seibel.distanthorizons.core.world.DhClientWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
@@ -65,9 +65,9 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.lwjgl.glfw.GLFW;
|
import org.lwjgl.glfw.GLFW;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.RejectedExecutionException;
|
import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
@@ -87,6 +87,12 @@ public class ClientApi
|
|||||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
|
||||||
|
|
||||||
|
/** Delayed accessing is necessary since this object will be created before the mod accessors are bound. */
|
||||||
|
private static class DelayedAccessors
|
||||||
|
{
|
||||||
|
public static final IImmersivePortalsAccessor IMMERSIVE_PORTALS = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
|
||||||
|
}
|
||||||
|
|
||||||
/** this includes the is dev build message and low allocated memory warning */
|
/** this includes the is dev build message and low allocated memory warning */
|
||||||
private static final int MS_BETWEEN_STATIC_STARTUP_MESSAGES = 4_000;
|
private static final int MS_BETWEEN_STATIC_STARTUP_MESSAGES = 4_000;
|
||||||
|
|
||||||
@@ -124,7 +130,7 @@ public class ClientApi
|
|||||||
|
|
||||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||||
|
|
||||||
private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent);
|
private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi();
|
||||||
|
|
||||||
/** Delay loading the first level to give the server some time to respond with level to actually load */
|
/** Delay loading the first level to give the server some time to respond with level to actually load */
|
||||||
private Timer firstLevelLoadTimer;
|
private Timer firstLevelLoadTimer;
|
||||||
@@ -132,8 +138,8 @@ public class ClientApi
|
|||||||
|
|
||||||
/** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */
|
/** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */
|
||||||
public final HashSet<IClientLevelWrapper> waitingClientLevels = new HashSet<>();
|
public final HashSet<IClientLevelWrapper> waitingClientLevels = new HashSet<>();
|
||||||
/** Holds any chunks that were loaded before the {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} was fired. */
|
/** Holds any chunks that were found before the client levels are loaded. */
|
||||||
public final HashMap<Pair<IClientLevelWrapper, DhChunkPos>, IChunkWrapper> waitingChunkByClientLevelAndPos = new HashMap<>();
|
public final Map<Pair<IClientLevelWrapper, DhChunkPos>, IChunkWrapper> waitingChunkByClientLevelAndPos = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/** publicly available so {@link F3Screen} can display the error */
|
/** publicly available so {@link F3Screen} can display the error */
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -149,9 +155,10 @@ public class ClientApi
|
|||||||
* tracked should also be to keep the ratio roughly the same.
|
* tracked should also be to keep the ratio roughly the same.
|
||||||
* @see ClientApi#MIN_MS_BETWEEN_SPEED_CHECKS
|
* @see ClientApi#MIN_MS_BETWEEN_SPEED_CHECKS
|
||||||
*/
|
*/
|
||||||
public RollingAverage cameraSpeedRollingAverage = new RollingAverage(40);
|
private final RollingAverage cameraSpeedRollingAverage = new RollingAverage(40);
|
||||||
private Vec3d lastCameraPosForSpeedCheck = new Vec3d();
|
private Vec3d lastCameraPosForSpeedCheck = new Vec3d();
|
||||||
private long msSinceLastSpeedCheck = 0L;
|
private long msSinceLastSpeedCheck = 0L;
|
||||||
|
public double getAvgCameraSpeed() { return cameraSpeedRollingAverage.getAverage(); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* keeping track of this is necessary to fix
|
* keeping track of this is necessary to fix
|
||||||
@@ -179,7 +186,7 @@ public class ClientApi
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* May be fired slightly before or after the associated
|
* May be fired slightly before or after the associated
|
||||||
* {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} event
|
* level is loaded
|
||||||
* depending on how the host mod loader functions. <br><br>
|
* depending on how the host mod loader functions. <br><br>
|
||||||
*
|
*
|
||||||
* Synchronized shouldn't be necessary, but is present to match {@see onClientOnlyDisconnected} and prevent any unforeseen issues.
|
* Synchronized shouldn't be necessary, but is present to match {@see onClientOnlyDisconnected} and prevent any unforeseen issues.
|
||||||
@@ -216,14 +223,6 @@ public class ClientApi
|
|||||||
|
|
||||||
this.pluginChannelApi.onJoinServer(world.networkState.getSession());
|
this.pluginChannelApi.onJoinServer(world.networkState.getSession());
|
||||||
world.networkState.sendConfigMessage();
|
world.networkState.sendConfigMessage();
|
||||||
|
|
||||||
LOGGER.info("Loading [" + this.waitingClientLevels.size() + "] waiting client level wrappers.");
|
|
||||||
for (IClientLevelWrapper level : this.waitingClientLevels)
|
|
||||||
{
|
|
||||||
this.clientLevelLoadEvent(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.waitingClientLevels.clear();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +249,6 @@ public class ClientApi
|
|||||||
|
|
||||||
// remove any waiting items
|
// remove any waiting items
|
||||||
this.waitingChunkByClientLevelAndPos.clear();
|
this.waitingChunkByClientLevelAndPos.clear();
|
||||||
this.waitingClientLevels.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
@@ -262,44 +260,12 @@ public class ClientApi
|
|||||||
//==============//
|
//==============//
|
||||||
//region level events
|
//region level events
|
||||||
|
|
||||||
public void clientLevelUnloadEvent(IClientLevelWrapper level)
|
/**
|
||||||
|
* used in conjunction with the server networking to
|
||||||
|
* handle level load requests.
|
||||||
|
*/
|
||||||
|
public boolean canLoadClientLevel(IClientLevelWrapper wrapper)
|
||||||
{
|
{
|
||||||
try
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-[" + level.getDhIdentifier() + "].");
|
|
||||||
|
|
||||||
if (level instanceof IServerKeyedClientLevel)
|
|
||||||
{
|
|
||||||
this.pluginChannelApi.onClientLevelUnload();
|
|
||||||
}
|
|
||||||
|
|
||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
|
||||||
if (world != null)
|
|
||||||
{
|
|
||||||
world.unloadLevel(level);
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.waitingClientLevels.remove(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// handle errors here to prevent blowing up a mixin or API up stream
|
|
||||||
LOGGER.error("Unexpected error in ClientApi.clientLevelUnloadEvent(), error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clientLevelLoadEvent(@Nullable IClientLevelWrapper levelWrapper)
|
|
||||||
{
|
|
||||||
// can happen if there was an issue during level load
|
|
||||||
if (levelWrapper == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// wait a moment before loading the level to give the server a chance to handle the client's login request
|
// wait a moment before loading the level to give the server a chance to handle the client's login request
|
||||||
if (MC_CLIENT.clientConnectedToDedicatedServer())
|
if (MC_CLIENT.clientConnectedToDedicatedServer())
|
||||||
{
|
{
|
||||||
@@ -309,48 +275,41 @@ public class ClientApi
|
|||||||
this.firstLevelLoadTimer.schedule(new TimerTask()
|
this.firstLevelLoadTimer.schedule(new TimerTask()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void run() { ClientApi.this.clientLevelLoadEvent(levelWrapper); }
|
public void run() { canLoadClientLevel(wrapper); }
|
||||||
}, FIRST_LEVEL_LOAD_DELAY_IN_MS);
|
}, FIRST_LEVEL_LOAD_DELAY_IN_MS);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.firstLevelLoadTimer.cancel();
|
this.firstLevelLoadTimer.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.pluginChannelApi.allowLevelLoading(wrapper))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
LOGGER.info("Loading client level [" + levelWrapper + "]-[" + levelWrapper.getDhIdentifier() + "].");
|
LOGGER.debug("Client levels in this connection are managed by the server, skipping auto-load of: ["+wrapper+"]");
|
||||||
|
|
||||||
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
if (world != null)
|
if (world == null)
|
||||||
{
|
{
|
||||||
if (!this.pluginChannelApi.allowLevelLoading(levelWrapper))
|
return false;
|
||||||
{
|
|
||||||
LOGGER.info("Levels in this connection are managed by the server, skipping auto-load.");
|
|
||||||
|
|
||||||
// Instead of attempting to load themselves, send the config and wait for a server provided level key.
|
|
||||||
((DhClientWorld) world).networkState.sendConfigMessage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
world.getOrLoadLevel(levelWrapper);
|
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(levelWrapper));
|
|
||||||
|
|
||||||
this.loadWaitingChunksForLevel(levelWrapper);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.waitingClientLevels.add(levelWrapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instead of attempting to load themselves, send the config and wait for a server provided level key.
|
||||||
|
((DhClientWorld) world).networkState.sendLevelInitRequest(wrapper.getDimensionName());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
|
||||||
{
|
return true;
|
||||||
// handle errors here to prevent blowing up a mixin or API up stream
|
|
||||||
LOGGER.error("Unexpected error in ClientApi.clientLevelLoadEvent(), error: "+e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
private void loadWaitingChunksForLevel(IClientLevelWrapper level)
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//==============//
|
||||||
|
// level events //
|
||||||
|
//==============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public void loadWaitingChunksForLevel(IClientLevelWrapper level)
|
||||||
{
|
{
|
||||||
HashSet<Pair<IClientLevelWrapper, DhChunkPos>> keysToRemove = new HashSet<>();
|
HashSet<Pair<IClientLevelWrapper, DhChunkPos>> keysToRemove = new HashSet<>();
|
||||||
for (Pair<IClientLevelWrapper, DhChunkPos> levelChunkPair : this.waitingChunkByClientLevelAndPos.keySet())
|
for (Pair<IClientLevelWrapper, DhChunkPos> levelChunkPair : this.waitingChunkByClientLevelAndPos.keySet())
|
||||||
@@ -501,7 +460,10 @@ public class ClientApi
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
long nowMs = System.currentTimeMillis();
|
long nowMs = System.currentTimeMillis();
|
||||||
if (this.msSinceLastSpeedCheck + MIN_MS_BETWEEN_SPEED_CHECKS < nowMs)
|
if (this.msSinceLastSpeedCheck + MIN_MS_BETWEEN_SPEED_CHECKS < nowMs
|
||||||
|
// don't track camera speed for dimensions the player isn't in
|
||||||
|
&& (DelayedAccessors.IMMERSIVE_PORTALS == null
|
||||||
|
|| !DelayedAccessors.IMMERSIVE_PORTALS.isRenderingPortal()))
|
||||||
{
|
{
|
||||||
// calc time since last check
|
// calc time since last check
|
||||||
double secSinceLastCheck = (nowMs - this.msSinceLastSpeedCheck) / 1_000.0;
|
double secSinceLastCheck = (nowMs - this.msSinceLastSpeedCheck) / 1_000.0;
|
||||||
@@ -725,8 +687,7 @@ public class ClientApi
|
|||||||
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
||||||
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
||||||
)
|
)
|
||||||
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
&& shouldRenderFade())
|
||||||
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
|
||||||
{
|
{
|
||||||
RENDER_PARAMS.update(EDhApiRenderPass.OPAQUE, RENDER_STATE);
|
RENDER_PARAMS.update(EDhApiRenderPass.OPAQUE, RENDER_STATE);
|
||||||
fadeRenderer.render(RENDER_PARAMS);
|
fadeRenderer.render(RENDER_PARAMS);
|
||||||
@@ -755,8 +716,7 @@ public class ClientApi
|
|||||||
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
// or if LOD-only mode is enabled (fading is used to remove the MC render pass)
|
||||||
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
|| Config.Client.Advanced.Debugging.lodOnlyMode.get()
|
||||||
)
|
)
|
||||||
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
&& shouldRenderFade();
|
||||||
&& !DhApiRenderProxy.INSTANCE.getDeferTransparentRendering();
|
|
||||||
if (renderFade)
|
if (renderFade)
|
||||||
{
|
{
|
||||||
RENDER_PARAMS.update(EDhApiRenderPass.TRANSPARENT, RENDER_STATE);
|
RENDER_PARAMS.update(EDhApiRenderPass.TRANSPARENT, RENDER_STATE);
|
||||||
@@ -765,6 +725,25 @@ public class ClientApi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean shouldRenderFade()
|
||||||
|
{
|
||||||
|
// don't fade when Iris shaders are active, otherwise the rendering can get weird
|
||||||
|
if (DhApiRenderProxy.INSTANCE.getDeferTransparentRendering())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't render fade through immersive portals, this causes the fade to apply incorrectly
|
||||||
|
IImmersivePortalsAccessor immersivePortals = ModAccessorInjector.INSTANCE.get(IImmersivePortalsAccessor.class);
|
||||||
|
if (immersivePortals != null
|
||||||
|
&& immersivePortals.isRenderingPortal())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+7
-25
@@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent
|
|||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
|
import com.seibel.distanthorizons.core.world.AbstractDhWorld;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@@ -30,9 +31,6 @@ public class ClientPluginChannelApi
|
|||||||
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||||
|
|
||||||
private final Consumer<IServerKeyedClientLevel> levelLoadHandler;
|
|
||||||
private final Consumer<IClientLevelWrapper> levelUnloadHandler;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public NetworkSession networkSession;
|
public NetworkSession networkSession;
|
||||||
|
|
||||||
@@ -42,10 +40,8 @@ public class ClientPluginChannelApi
|
|||||||
// constructor //
|
// constructor //
|
||||||
//=============//
|
//=============//
|
||||||
|
|
||||||
public ClientPluginChannelApi(Consumer<IServerKeyedClientLevel> levelLoadHandler, Consumer<IClientLevelWrapper> levelUnloadHandler)
|
public ClientPluginChannelApi()
|
||||||
{
|
{
|
||||||
this.levelLoadHandler = levelLoadHandler;
|
|
||||||
this.levelUnloadHandler = levelUnloadHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,24 +90,6 @@ public class ClientPluginChannelApi
|
|||||||
{
|
{
|
||||||
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
|
||||||
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
|
||||||
|
|
||||||
if (existingKeyedClientLevel != null)
|
|
||||||
{
|
|
||||||
if (!existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey))
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading previous level with key: [" + existingKeyedClientLevel.getServerLevelKey() + "].");
|
|
||||||
this.levelUnloadHandler.accept(existingKeyedClientLevel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.info("Level key matches the previous level key, ignoring the message.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDhIdentifier() + "].");
|
|
||||||
this.levelUnloadHandler.accept(clientLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (existingKeyedClientLevel == null
|
if (existingKeyedClientLevel == null
|
||||||
|| !existingKeyedClientLevel.getServerKey().equals(msg.serverKey)
|
|| !existingKeyedClientLevel.getServerKey().equals(msg.serverKey)
|
||||||
@@ -119,7 +97,11 @@ public class ClientPluginChannelApi
|
|||||||
{
|
{
|
||||||
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
|
LOGGER.info("Loading level with key: [" + msg.levelKey + "].");
|
||||||
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.serverKey, msg.levelKey);
|
IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.serverKey, msg.levelKey);
|
||||||
this.levelLoadHandler.accept(keyedLevel);
|
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
|
||||||
|
if (world != null)
|
||||||
|
{
|
||||||
|
world.getOrLoadLevel(keyedLevel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,13 +19,10 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.api.internal;
|
package com.seibel.distanthorizons.core.api.internal;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
|
||||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
|
||||||
import com.seibel.distanthorizons.core.world.*;
|
import com.seibel.distanthorizons.core.world.*;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
|
||||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
@@ -77,7 +74,6 @@ public class ServerApi
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==============//
|
//==============//
|
||||||
// level events //
|
// level events //
|
||||||
//==============//
|
//==============//
|
||||||
@@ -90,7 +86,6 @@ public class ServerApi
|
|||||||
if (serverWorld != null)
|
if (serverWorld != null)
|
||||||
{
|
{
|
||||||
serverWorld.getOrLoadLevel(levelWrapper);
|
serverWorld.getOrLoadLevel(levelWrapper);
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(levelWrapper));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void serverLevelUnloadEvent(IServerLevelWrapper level)
|
public void serverLevelUnloadEvent(IServerLevelWrapper level)
|
||||||
@@ -101,12 +96,10 @@ public class ServerApi
|
|||||||
if (serverWorld != null)
|
if (serverWorld != null)
|
||||||
{
|
{
|
||||||
serverWorld.unloadLevel(level);
|
serverWorld.unloadLevel(level);
|
||||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(level));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//=======================//
|
//=======================//
|
||||||
// chunk modified events //
|
// chunk modified events //
|
||||||
//=======================//
|
//=======================//
|
||||||
|
|||||||
+15
-8
@@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
|||||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
import com.seibel.distanthorizons.core.level.IDhClientLevel;
|
||||||
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListCheckout;
|
import com.seibel.distanthorizons.core.util.objects.pooling.PhantomArrayListCheckout;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
import com.seibel.distanthorizons.coreapi.util.ColorUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
|
||||||
@@ -62,6 +63,12 @@ public class ColumnBox
|
|||||||
// variable setup //
|
// variable setup //
|
||||||
//================//
|
//================//
|
||||||
|
|
||||||
|
IClientLevelWrapper clientLevelWrapper = clientLevel.getClientLevelWrapper();
|
||||||
|
if (clientLevelWrapper == null)
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("addBoxQuadsToBuilder getClientLevelWrapper should always succeed");
|
||||||
|
}
|
||||||
|
|
||||||
short maxX = (short) (minX + blockWidth);
|
short maxX = (short) (minX + blockWidth);
|
||||||
short maxY = (short) (minY + yHeight);
|
short maxY = (short) (minY + yHeight);
|
||||||
short maxZ = (short) (minZ + blockWidth);
|
short maxZ = (short) (minZ + blockWidth);
|
||||||
@@ -105,7 +112,7 @@ public class ColumnBox
|
|||||||
&& !isTopTransparent;
|
&& !isTopTransparent;
|
||||||
if (!skipTop)
|
if (!skipTop)
|
||||||
{
|
{
|
||||||
builder.addQuadUp(minX, maxY, minZ, blockWidth, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
|
builder.addQuadUp(minX, maxY, minZ, blockWidth, ColorUtil.applyShade(color, clientLevelWrapper.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +123,7 @@ public class ColumnBox
|
|||||||
&& !isBottomTransparent;
|
&& !isBottomTransparent;
|
||||||
if (!skipBottom)
|
if (!skipBottom)
|
||||||
{
|
{
|
||||||
builder.addQuadDown(minX, minY, minZ, blockWidth, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
|
builder.addQuadDown(minX, minY, minZ, blockWidth, ColorUtil.applyShade(color, clientLevelWrapper.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +153,7 @@ public class ColumnBox
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.NORTH,
|
||||||
minX, minY, minZ, blockWidth, yHeight,
|
minX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
@@ -171,7 +178,7 @@ public class ColumnBox
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.SOUTH,
|
||||||
minX, minY, maxZ, blockWidth, yHeight,
|
minX, minY, maxZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
@@ -196,7 +203,7 @@ public class ColumnBox
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.WEST,
|
||||||
minX, minY, minZ, blockWidth, yHeight,
|
minX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
@@ -221,7 +228,7 @@ public class ColumnBox
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
makeAdjVerticalQuad(
|
makeAdjVerticalQuad(
|
||||||
builder, phantomArrayCheckout,
|
builder, phantomArrayCheckout, clientLevelWrapper,
|
||||||
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST,
|
adjCol, adjSameDetailLevel, caveCullingMaxY, EDhDirection.EAST,
|
||||||
maxX, minY, minZ, blockWidth, yHeight,
|
maxX, minY, minZ, blockWidth, yHeight,
|
||||||
color, irisBlockMaterialId, blockLight);
|
color, irisBlockMaterialId, blockLight);
|
||||||
@@ -230,7 +237,7 @@ public class ColumnBox
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void makeAdjVerticalQuad(
|
private static void makeAdjVerticalQuad(
|
||||||
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout,
|
LodQuadBuilder builder, PhantomArrayListCheckout phantomArrayCheckout, IClientLevelWrapper clientLevelWrapper,
|
||||||
@NotNull ColumnRenderView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
@NotNull ColumnRenderView adjColumnView, boolean adjacentIsSameDetailLevel, int caveCullingMaxY, EDhDirection direction,
|
||||||
short x, short yMin, short z, short horizontalBlockWidth, short ySize,
|
short x, short yMin, short z, short horizontalBlockWidth, short ySize,
|
||||||
int color, byte irisBlockMaterialId, byte blockLight)
|
int color, byte irisBlockMaterialId, byte blockLight)
|
||||||
@@ -246,7 +253,7 @@ public class ColumnBox
|
|||||||
// no adjacent data //
|
// no adjacent data //
|
||||||
//==================//
|
//==================//
|
||||||
|
|
||||||
color = ColorUtil.applyShade(color, MC_RENDER.getShade(direction));
|
color = ColorUtil.applyShade(color, clientLevelWrapper.getShade(direction));
|
||||||
|
|
||||||
if (adjColumnView.size == 0
|
if (adjColumnView.size == 0
|
||||||
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
|
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
|
||||||
|
|||||||
+1
-1
@@ -420,7 +420,7 @@ public class LodQuadBuilder implements AutoCloseable
|
|||||||
// for horizontal and bottom faces of grass blocks, use the dirt color to
|
// for horizontal and bottom faces of grass blocks, use the dirt color to
|
||||||
// prevent green cliff walls
|
// prevent green cliff walls
|
||||||
color = this.clientLevelWrapper.getDirtBlockColor();
|
color = this.clientLevelWrapper.getDirtBlockColor();
|
||||||
color = ColorUtil.applyShade(color, MC_RENDER.getShade(quad.direction));
|
color = ColorUtil.applyShade(color, this.clientLevelWrapper.getShade(quad.direction));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-11
@@ -123,17 +123,6 @@ public class LodRequestModule implements Closeable
|
|||||||
// if the world is read only don't generate anything
|
// if the world is read only don't generate anything
|
||||||
shouldDoWorldGen &= !DhApiWorldProxy.INSTANCE.tryGetReadOnly();
|
shouldDoWorldGen &= !DhApiWorldProxy.INSTANCE.tryGetReadOnly();
|
||||||
|
|
||||||
// don't generate chunks for client levels that aren't being rendered
|
|
||||||
// (this can happen when moving between dimensions)
|
|
||||||
if (this.level instanceof IDhClientLevel)
|
|
||||||
{
|
|
||||||
boolean isRendering = ((IDhClientLevel) this.level).isRendering();
|
|
||||||
if (!isRendering)
|
|
||||||
{
|
|
||||||
shouldDoWorldGen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
boolean isWorldGenRunning = this.isWorldGenRunning();
|
boolean isWorldGenRunning = this.isWorldGenRunning();
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.multiplayer.server.FullDataSourceRequestH
|
|||||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException;
|
import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException;
|
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.SectionRequiresSplittingException;
|
import com.seibel.distanthorizons.core.network.exceptions.SectionRequiresSplittingException;
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
|
||||||
@@ -200,26 +199,6 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
|
|||||||
|
|
||||||
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
LodUtil.assertTrue(message.getSession().serverPlayer != null);
|
||||||
|
|
||||||
// Check if the player is in this dimension,
|
|
||||||
// since handling multiple dimensions isn't allowed
|
|
||||||
if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper())
|
|
||||||
{
|
|
||||||
// If the message can be replied to - reply with an error, otherwise just ignore
|
|
||||||
if (message instanceof AbstractTrackableMessage)
|
|
||||||
{
|
|
||||||
((AbstractTrackableMessage) message).sendResponse(
|
|
||||||
new RequestRejectedException(
|
|
||||||
"Generation not allowed. " +
|
|
||||||
"Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " +
|
|
||||||
"player dimension: [" + message.getSession().serverPlayer.getLevel().getDhIdentifier() + "], " +
|
|
||||||
"handler dimension: [" + this.getLevelWrapper().getDhIdentifier() + "]"
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ public class ClientLevelModule implements Closeable, IDataSourceUpdateListenerFu
|
|||||||
ClientRenderState clientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
|
ClientRenderState clientRenderState = new ClientRenderState(this.clientLevel, this.clientLevel.getFullDataProvider());
|
||||||
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState))
|
||||||
{
|
{
|
||||||
LOGGER.warn("Renderer already started for ["+this+"].");
|
LOGGER.warn("Renderer already started for ["+this.clientLevel.getClientLevelWrapper()+"].");
|
||||||
clientRenderState.close();
|
clientRenderState.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,15 +168,17 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check this before decoding data to prevent errors if multiple client levels
|
||||||
|
// are receiving data at once (Immersive Portals compatibility).
|
||||||
|
boolean isSameLevel = message.isSameLevelAs(this.levelWrapper);
|
||||||
|
//NETWORK_LOGGER.debug("Buffer ["+message.payload.dtoBufferId+"] isSameLevel: ["+isSameLevel+"]");
|
||||||
|
if (!isSameLevel)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try (FullDataSourceV2DTO dataSourceDto = this.networkState.fullDataPayloadReceiver.decodeDataSource(message.payload))
|
try (FullDataSourceV2DTO dataSourceDto = this.networkState.fullDataPayloadReceiver.decodeDataSource(message.payload))
|
||||||
{
|
{
|
||||||
boolean isSameLevel = message.isSameLevelAs(this.levelWrapper);
|
|
||||||
NETWORK_LOGGER.debug("Buffer ["+message.payload.dtoBufferId+"] isSameLevel: ["+isSameLevel+"]");
|
|
||||||
if (!isSameLevel)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Executor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
Executor executor = ThreadPoolUtil.getFileHandlerExecutor();
|
||||||
if (executor != null)
|
if (executor != null)
|
||||||
@@ -220,6 +222,15 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// only tick the level the player is currently in
|
||||||
|
// (done to prevent ticking LodQuadTree's for levels that aren't rendering)
|
||||||
|
IClientLevelWrapper clientLevelWrapper = MC_CLIENT.getWrappedClientLevel();
|
||||||
|
if (clientLevelWrapper == null
|
||||||
|
|| clientLevelWrapper.getDhLevel() != this)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.clientside.clientTick();
|
this.clientside.clientTick();
|
||||||
|
|
||||||
if (this.syncOnLoadRequestQueue != null)
|
if (this.syncOnLoadRequestQueue != null)
|
||||||
|
|||||||
@@ -72,7 +72,19 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli
|
|||||||
//region
|
//region
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clientTick() { this.clientside.clientTick(); }
|
public void clientTick()
|
||||||
|
{
|
||||||
|
// only tick the level the player is currently in
|
||||||
|
// (done to prevent ticking LodQuadTree's for levels that aren't rendering)
|
||||||
|
IClientLevelWrapper clientLevelWrapper = MC_CLIENT.getWrappedClientLevel();
|
||||||
|
if (clientLevelWrapper == null
|
||||||
|
|| clientLevelWrapper.getDhLevel() != this)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clientside.clientTick();
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -13,6 +13,7 @@ import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource;
|
|||||||
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.base.RequestLevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
||||||
@@ -164,7 +165,8 @@ public class ClientNetworkState implements Closeable
|
|||||||
// send message //
|
// send message //
|
||||||
//==============//
|
//==============//
|
||||||
|
|
||||||
|
public void sendLevelInitRequest(String clientLevelKey)
|
||||||
|
{ this.getSession().sendMessage(new RequestLevelInitMessage(clientLevelKey)); }
|
||||||
|
|
||||||
public void sendConfigMessage() { this.sendConfigMessage(true); }
|
public void sendConfigMessage() { this.sendConfigMessage(true); }
|
||||||
public void sendConfigMessage(boolean blocking)
|
public void sendConfigMessage(boolean blocking)
|
||||||
|
|||||||
+36
-1
@@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.multiplayer.server;
|
|||||||
|
|
||||||
import com.seibel.distanthorizons.core.config.Config;
|
import com.seibel.distanthorizons.core.config.Config;
|
||||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||||
|
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||||
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
|
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
|
||||||
import com.seibel.distanthorizons.core.multiplayer.fullData.FullDataPayloadSender;
|
import com.seibel.distanthorizons.core.multiplayer.fullData.FullDataPayloadSender;
|
||||||
@@ -9,20 +10,27 @@ import com.seibel.distanthorizons.core.multiplayer.fullData.SharedBandwidthLimit
|
|||||||
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.base.RequestLevelInitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
||||||
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
|
||||||
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
|
import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||||
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
import com.seibel.distanthorizons.core.network.session.NetworkSession;
|
||||||
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter;
|
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public class ServerPlayerState implements Closeable
|
public class ServerPlayerState implements Closeable
|
||||||
{
|
{
|
||||||
|
private final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
|
||||||
|
|
||||||
private final ConfigChangeListener<String> levelKeyPrefixChangeListener
|
private final ConfigChangeListener<String> levelKeyPrefixChangeListener
|
||||||
= new ConfigChangeListener<>(Config.Server.levelKeyPrefix, this::onLevelKeyPrefixConfigChanged);
|
= new ConfigChangeListener<>(Config.Server.levelKeyPrefix, this::onLevelKeyPrefixConfigChanged);
|
||||||
private final SessionConfig.AnyChangeListener configAnyChangeListener = new SessionConfig.AnyChangeListener(this::sendConfigMessage);
|
private final SessionConfig.AnyChangeListener configAnyChangeListener = new SessionConfig.AnyChangeListener(this::sendConfigMessage);
|
||||||
@@ -66,6 +74,12 @@ public class ServerPlayerState implements Closeable
|
|||||||
this.sendConfigMessage();
|
this.sendConfigMessage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.networkSession.registerHandler(RequestLevelInitMessage.class, (requestLevelInitMessage) ->
|
||||||
|
{
|
||||||
|
sendLevelKey(requestLevelInitMessage.dimensionResourceLocation);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
this.networkSession.registerHandler(CloseInternalEvent.class, event -> {
|
this.networkSession.registerHandler(CloseInternalEvent.class, event -> {
|
||||||
// No-op. prevents "Unhandled message" log entries
|
// No-op. prevents "Unhandled message" log entries
|
||||||
});
|
});
|
||||||
@@ -85,12 +99,33 @@ public class ServerPlayerState implements Closeable
|
|||||||
//=================//
|
//=================//
|
||||||
|
|
||||||
private void onLevelKeyPrefixConfigChanged(String newLevelKey) { this.sendLevelKey(); }
|
private void onLevelKeyPrefixConfigChanged(String newLevelKey) { this.sendLevelKey(); }
|
||||||
|
|
||||||
|
private void sendLevelKey(String dimensionResourceLocation)
|
||||||
|
{
|
||||||
|
sendLevelKey(() ->
|
||||||
|
{
|
||||||
|
IServerLevelWrapper serverLevelWrapper = MC_SHARED.getWrappedServerLevelWithDimensionResourceLocation(dimensionResourceLocation);
|
||||||
|
if (serverLevelWrapper == null)
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("Unable to get server level from");
|
||||||
|
}
|
||||||
|
|
||||||
|
return serverLevelWrapper.getKeyedLevelDimensionName();
|
||||||
|
});
|
||||||
|
}
|
||||||
private void sendLevelKey()
|
private void sendLevelKey()
|
||||||
|
{
|
||||||
|
sendLevelKey(() ->
|
||||||
|
this.getServerPlayer()
|
||||||
|
.getLevel()
|
||||||
|
.getKeyedLevelDimensionName());
|
||||||
|
}
|
||||||
|
private void sendLevelKey(Supplier<String> levelKeySupplier)
|
||||||
{
|
{
|
||||||
if (Config.Server.sendLevelKeys.get())
|
if (Config.Server.sendLevelKeys.get())
|
||||||
{
|
{
|
||||||
|
String levelKey = levelKeySupplier.get();
|
||||||
// let the client's know about the change
|
// let the client's know about the change
|
||||||
String levelKey = this.getServerPlayer().getLevel().getKeyedLevelDimensionName();
|
|
||||||
if (!levelKey.equals(this.lastLevelKey))
|
if (!levelKey.equals(this.lastLevelKey))
|
||||||
{
|
{
|
||||||
this.lastLevelKey = levelKey;
|
this.lastLevelKey = levelKey;
|
||||||
|
|||||||
+2
-4
@@ -21,12 +21,9 @@ package com.seibel.distanthorizons.core.network.messages;
|
|||||||
|
|
||||||
import com.google.common.collect.BiMap;
|
import com.google.common.collect.BiMap;
|
||||||
import com.google.common.collect.HashBiMap;
|
import com.google.common.collect.HashBiMap;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
|
import com.seibel.distanthorizons.core.network.messages.base.*;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSplitMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
|
|
||||||
import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage;
|
import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage;
|
||||||
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage;
|
||||||
@@ -60,6 +57,7 @@ public class MessageRegistry
|
|||||||
|
|
||||||
// Level keys
|
// Level keys
|
||||||
this.registerMessage(LevelInitMessage.class, LevelInitMessage::new);
|
this.registerMessage(LevelInitMessage.class, LevelInitMessage::new);
|
||||||
|
this.registerMessage(RequestLevelInitMessage.class, RequestLevelInitMessage::new);
|
||||||
|
|
||||||
// Config (for full DH support)
|
// Config (for full DH support)
|
||||||
this.registerMessage(SessionConfigMessage.class, SessionConfigMessage::new);
|
this.registerMessage(SessionConfigMessage.class, SessionConfigMessage::new);
|
||||||
|
|||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.network.messages.base;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
|
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
|
/** used for full DH support */
|
||||||
|
public class RequestLevelInitMessage extends AbstractNetworkMessage
|
||||||
|
{
|
||||||
|
public String dimensionResourceLocation;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public RequestLevelInitMessage() { }
|
||||||
|
public RequestLevelInitMessage(String dimensionResourceLocation) { this.dimensionResourceLocation = dimensionResourceLocation; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===============//
|
||||||
|
// serialization //
|
||||||
|
//===============//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(ByteBuf out) { this.writeString(this.dimensionResourceLocation, out); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decode(ByteBuf in) { this.dimensionResourceLocation = this.readString(in); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MoreObjects.ToStringHelper toStringHelper()
|
||||||
|
{
|
||||||
|
return super.toStringHelper()
|
||||||
|
.add("dimensionResourceLocation", this.dimensionResourceLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -79,7 +79,7 @@ public class RenderParams extends DhApiRenderParam
|
|||||||
this.dhClientWorld = SharedApi.tryGetDhClientWorld();
|
this.dhClientWorld = SharedApi.tryGetDhClientWorld();
|
||||||
if (this.dhClientWorld != null)
|
if (this.dhClientWorld != null)
|
||||||
{
|
{
|
||||||
this.dhClientLevel = (IDhClientLevel) this.dhClientWorld.getLevel(this.clientLevelWrapper);
|
this.dhClientLevel = this.dhClientWorld.getOrLoadClientLevel(clientLevelWrapper);
|
||||||
if (this.dhClientLevel != null)
|
if (this.dhClientLevel != null)
|
||||||
{
|
{
|
||||||
this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
|
this.renderBufferHandler = this.dhClientLevel.getRenderBufferHandler();
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ public class RenderUtil
|
|||||||
|
|
||||||
if (Config.Client.Advanced.Graphics.Culling.reduceOverdrawWithFastMovement.get())
|
if (Config.Client.Advanced.Graphics.Culling.reduceOverdrawWithFastMovement.get())
|
||||||
{
|
{
|
||||||
double avgSpeed = ClientApi.INSTANCE.cameraSpeedRollingAverage.getAverage();
|
double avgSpeed = ClientApi.INSTANCE.getAvgCameraSpeed();
|
||||||
if (avgSpeed >= DynamicOverdraw.MIN_SPEED)
|
if (avgSpeed >= DynamicOverdraw.MIN_SPEED)
|
||||||
{
|
{
|
||||||
// if the player is moving fast enough,
|
// if the player is moving fast enough,
|
||||||
|
|||||||
+1
-1
@@ -170,7 +170,7 @@ public class ThreadPoolUtil
|
|||||||
*/
|
*/
|
||||||
public static boolean worldGenThreadsCanRun()
|
public static boolean worldGenThreadsCanRun()
|
||||||
{
|
{
|
||||||
double cameraSpeed = ClientApi.INSTANCE.cameraSpeedRollingAverage.getAverage();
|
double cameraSpeed = ClientApi.INSTANCE.getAvgCameraSpeed();
|
||||||
// stop these threads if moving a little bit slower than max elytra speed
|
// stop these threads if moving a little bit slower than max elytra speed
|
||||||
double maxAllowedSpeed = (LodUtil.ROCKET_ELYTRA_SPEED_IN_BLOCKS_PER_SEC - 10.0);
|
double maxAllowedSpeed = (LodUtil.ROCKET_ELYTRA_SPEED_IN_BLOCKS_PER_SEC - 10.0);
|
||||||
if (cameraSpeed > maxAllowedSpeed)
|
if (cameraSpeed > maxAllowedSpeed)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.seibel.distanthorizons.core.world;
|
package com.seibel.distanthorizons.core.world;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
||||||
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure;
|
||||||
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
import com.seibel.distanthorizons.core.level.AbstractDhServerLevel;
|
||||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
@@ -8,6 +9,7 @@ import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManag
|
|||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -138,6 +140,7 @@ public abstract class AbstractDhServerWorld<TDhServerLevel extends AbstractDhSer
|
|||||||
if (serverLevelWrapper != null)
|
if (serverLevelWrapper != null)
|
||||||
{
|
{
|
||||||
serverLevelWrapper.onUnload();
|
serverLevelWrapper.onUnload();
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(serverLevelWrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,14 +19,18 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.world;
|
package com.seibel.distanthorizons.core.world;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.level.DhClientServerLevel;
|
import com.seibel.distanthorizons.core.level.DhClientServerLevel;
|
||||||
|
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -34,7 +38,18 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
|
|
||||||
public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLevel> implements IDhClientWorld
|
public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLevel> implements IDhClientWorld
|
||||||
{
|
{
|
||||||
private final Set<DhClientServerLevel> dhLevels = Collections.synchronizedSet(new HashSet<>());
|
/**
|
||||||
|
* Having a set of level wrappers is done to handle an issue where the client
|
||||||
|
* level would get unloaded when jumping back and forth between dimensions. <br><br>
|
||||||
|
*
|
||||||
|
* We might have more than one {@link ILevelWrapper} pointing to the same {@link IDhLevel}
|
||||||
|
* since they're not immediately unloaded, and we don't want to unload the {@link IDhLevel}
|
||||||
|
* until all the {@link ILevelWrapper} for that {@link IDhLevel} have been unloaded.
|
||||||
|
* Any stale {@link IDhLevel} references should disappear on their own after about
|
||||||
|
* 30 seconds or so thanks to the automatic cleanup.
|
||||||
|
*/
|
||||||
|
private final Map<DhClientServerLevel, Set<ILevelWrapper>> clientLevelWrapperSetByDhLevel
|
||||||
|
= Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
|
private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
|
||||||
|
|
||||||
@@ -47,14 +62,14 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
public DhClientServerWorld()
|
public DhClientServerWorld()
|
||||||
{
|
{
|
||||||
super(EWorldEnvironment.CLIENT_SERVER);
|
super(EWorldEnvironment.CLIENT_SERVER);
|
||||||
LOGGER.info("Started DhWorld of type " + this.environment);
|
LOGGER.info("Started DhWorld of type [" + this.environment + "].");
|
||||||
|
|
||||||
this.clientTickTimer.scheduleAtFixedRate(new TimerTask()
|
this.clientTickTimer.scheduleAtFixedRate(new TimerTask()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
DhClientServerWorld.this.dhLevels.forEach(DhClientServerLevel::clientTick);
|
DhClientServerWorld.this.clientLevelWrapperSetByDhLevel.keySet().forEach(DhClientServerLevel::clientTick);
|
||||||
}
|
}
|
||||||
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
|
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
|
||||||
}
|
}
|
||||||
@@ -75,7 +90,8 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
DhClientServerLevel level = new DhClientServerLevel(this.saveStructure, (IServerLevelWrapper) levelWrapper, this.getServerPlayerStateManager());
|
DhClientServerLevel level = new DhClientServerLevel(this.saveStructure, (IServerLevelWrapper) levelWrapper, this.getServerPlayerStateManager());
|
||||||
this.dhLevels.add(level);
|
this.clientLevelWrapperSetByDhLevel.computeIfAbsent(level, (clientServerLevel) -> Collections.synchronizedSet(new HashSet<>()));
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(wrapper));
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@@ -92,11 +108,22 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (wrapper instanceof IClientLevelWrapper)
|
||||||
|
{
|
||||||
|
((IClientLevelWrapper) wrapper).markAccessed();
|
||||||
|
}
|
||||||
|
|
||||||
return this.dhLevelByLevelWrapper.computeIfAbsent(wrapper, (levelWrapper) ->
|
return this.dhLevelByLevelWrapper.computeIfAbsent(wrapper, (levelWrapper) ->
|
||||||
{
|
{
|
||||||
|
if (!(levelWrapper instanceof IClientLevelWrapper))
|
||||||
|
{
|
||||||
|
LodUtil.assertNotReach("tryGetServerSideWrapper given a non-IClientLevelWrapper.");
|
||||||
|
}
|
||||||
|
|
||||||
IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) levelWrapper;
|
IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) levelWrapper;
|
||||||
IServerLevelWrapper serverLevelWrapper = clientLevelWrapper.tryGetServerSideWrapper();
|
IServerLevelWrapper serverLevelWrapper = clientLevelWrapper.tryGetServerSideWrapper();
|
||||||
LodUtil.assertTrue(serverLevelWrapper != null);
|
LodUtil.assertTrue(serverLevelWrapper != null);
|
||||||
|
|
||||||
if (!clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType()))
|
if (!clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType()))
|
||||||
{
|
{
|
||||||
LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: [" + clientLevelWrapper.getDhIdentifier() + "] ServerLevelWrapper dim: [" + serverLevelWrapper.getDhIdentifier() + "].");
|
LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: [" + clientLevelWrapper.getDhIdentifier() + "] ServerLevelWrapper dim: [" + serverLevelWrapper.getDhIdentifier() + "].");
|
||||||
@@ -111,13 +138,14 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
|
|
||||||
level.startRenderer();
|
level.startRenderer();
|
||||||
clientLevelWrapper.setDhLevel(level);
|
clientLevelWrapper.setDhLevel(level);
|
||||||
|
clientLevelWrapperSetByDhLevel.get(level).add(wrapper);
|
||||||
return level;
|
return level;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unloadLevel(@NotNull ILevelWrapper wrapper)
|
public boolean unloadLevel(@NotNull ILevelWrapper wrapper)
|
||||||
{
|
{
|
||||||
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
||||||
{
|
{
|
||||||
@@ -128,16 +156,33 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
|
|
||||||
DhClientServerLevel clientServerLevel = this.dhLevelByLevelWrapper.remove(wrapper);
|
DhClientServerLevel clientServerLevel = this.dhLevelByLevelWrapper.remove(wrapper);
|
||||||
clientServerLevel.close();
|
clientServerLevel.close();
|
||||||
this.dhLevels.remove(clientServerLevel);
|
this.clientLevelWrapperSetByDhLevel.remove(clientServerLevel);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the level wrapper is a Client Level Wrapper, then that means the client side leaves the level,
|
// If the level wrapper is a Client Level Wrapper, then that means the client side leaves the level,
|
||||||
// but note that the server side still has the level loaded. So, we don't want to unload the level,
|
// but note that the server side still has the level loaded. So, we don't want to unload the level,
|
||||||
// we just want to stop rendering it.
|
// we just want to stop rendering it.
|
||||||
this.dhLevelByLevelWrapper.remove(wrapper).stopRenderer(); // Ignore resource warning. The level obj is referenced elsewhere.
|
DhClientServerLevel level = this.dhLevelByLevelWrapper.remove(wrapper); // Ignore resource warning. The level obj is referenced elsewhere.
|
||||||
|
Set<ILevelWrapper> wrappers = clientLevelWrapperSetByDhLevel.get(level);
|
||||||
|
if (wrappers != null)
|
||||||
|
{
|
||||||
|
wrappers.remove(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((wrappers == null || wrappers.isEmpty())
|
||||||
|
&& level.isRendering())
|
||||||
|
{
|
||||||
|
level.stopRenderer();
|
||||||
|
}
|
||||||
|
wrapper.onUnload(); // We still want to unload the wrapper though.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(wrapper));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -152,16 +197,24 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
|
|||||||
{
|
{
|
||||||
ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>();
|
ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>();
|
||||||
|
|
||||||
synchronized (this.dhLevels)
|
synchronized (this.clientLevelWrapperSetByDhLevel)
|
||||||
{
|
{
|
||||||
// close each level
|
// close each level
|
||||||
for (DhClientServerLevel level : this.dhLevels)
|
for (DhClientServerLevel level : this.clientLevelWrapperSetByDhLevel.keySet())
|
||||||
{
|
{
|
||||||
// level wrapper shouldn't be null, but just in case
|
// level wrapper shouldn't be null, but just in case
|
||||||
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper();
|
||||||
if (serverLevelWrapper != null)
|
if (serverLevelWrapper != null)
|
||||||
{
|
{
|
||||||
serverLevelWrapper.onUnload();
|
serverLevelWrapper.onUnload();
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(serverLevelWrapper));
|
||||||
|
}
|
||||||
|
|
||||||
|
IClientLevelWrapper clientLevelWrapper = level.getClientLevelWrapper();
|
||||||
|
if (clientLevelWrapper != null)
|
||||||
|
{
|
||||||
|
clientLevelWrapper.onUnload();
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(clientLevelWrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
// close levels asynchronously to speed up
|
// close levels asynchronously to speed up
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.world;
|
package com.seibel.distanthorizons.core.world;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||||
@@ -28,21 +30,32 @@ import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
|
|||||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
||||||
{
|
{
|
||||||
private final ConcurrentHashMap<IClientLevelWrapper, DhClientLevel> levels;
|
|
||||||
public final ClientOnlySaveStructure saveStructure;
|
public final ClientOnlySaveStructure saveStructure;
|
||||||
public final ClientNetworkState networkState = new ClientNetworkState();
|
public final ClientNetworkState networkState = new ClientNetworkState();
|
||||||
|
|
||||||
|
|
||||||
|
private final ConcurrentHashMap<String, DhClientLevel> clientLevelByDhId;
|
||||||
|
/**
|
||||||
|
* Having a set of level wrappers is done to handle an issue where the client
|
||||||
|
* level would get unloaded when jumping back and forth between dimensions. <br><br>
|
||||||
|
*
|
||||||
|
* We might have more than one {@link ILevelWrapper} pointing to the same {@link IDhLevel}
|
||||||
|
* since they're not immediately unloaded, and we don't want to unload the {@link IDhLevel}
|
||||||
|
* until all the {@link ILevelWrapper} for that {@link IDhLevel} have been unloaded.
|
||||||
|
* Any stale {@link IDhLevel} references should disappear on their own after about
|
||||||
|
* 30 seconds or so thanks to the automatic cleanup.
|
||||||
|
*/
|
||||||
|
private final Map<String, Set<IClientLevelWrapper>> clientLevelWrapperSetByDhId = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
|
private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +69,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
super(EWorldEnvironment.CLIENT_ONLY);
|
super(EWorldEnvironment.CLIENT_ONLY);
|
||||||
|
|
||||||
this.saveStructure = new ClientOnlySaveStructure();
|
this.saveStructure = new ClientOnlySaveStructure();
|
||||||
this.levels = new ConcurrentHashMap<>();
|
this.clientLevelByDhId = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
LOGGER.info("Started DhWorld of type " + this.environment);
|
LOGGER.info("Started DhWorld of type " + this.environment);
|
||||||
|
|
||||||
@@ -65,7 +78,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
DhClientWorld.this.levels.values().forEach(DhClientLevel::clientTick);
|
DhClientWorld.this.clientLevelByDhId.values().forEach(DhClientLevel::clientTick);
|
||||||
}
|
}
|
||||||
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
|
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
|
||||||
}
|
}
|
||||||
@@ -84,24 +97,51 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.levels.computeIfAbsent((IClientLevelWrapper) wrapper,
|
IClientLevelWrapper clientLevelWrapper = (IClientLevelWrapper) wrapper;
|
||||||
(clientLevelWrapper) ->
|
clientLevelWrapper.markAccessed();
|
||||||
|
DhClientLevel storedLevel = this.clientLevelByDhId.computeIfAbsent(wrapper.getDhIdentifier(),
|
||||||
|
(key) -> createClientLevel(clientLevelWrapper)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (storedLevel != null
|
||||||
|
&& storedLevel.getClientLevelWrapper() != wrapper)
|
||||||
|
{
|
||||||
|
unloadLevel(storedLevel.getLevelWrapper());
|
||||||
|
storedLevel = createClientLevel(clientLevelWrapper);
|
||||||
|
if (storedLevel != null)
|
||||||
{
|
{
|
||||||
try
|
this.clientLevelByDhId.put(wrapper.getDhIdentifier(), storedLevel);
|
||||||
{
|
}
|
||||||
return new DhClientLevel(this.saveStructure, clientLevelWrapper, this.networkState);
|
}
|
||||||
}
|
return storedLevel;
|
||||||
catch (Exception e)
|
}
|
||||||
{
|
private DhClientLevel createClientLevel(@NotNull IClientLevelWrapper clientLevelWrapper)
|
||||||
LOGGER.fatal("Failed to load client level, error: ["+e.getMessage()+"].", e);
|
{
|
||||||
|
try
|
||||||
ClientApi.INSTANCE.showChatMessageNextFrame(
|
{
|
||||||
MinecraftTextFormat.RED + "Distant Horizons: Client level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
if (!ClientApi.INSTANCE.canLoadClientLevel(clientLevelWrapper))
|
||||||
"Unable to load level ["+clientLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
|
{
|
||||||
|
return null;
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
});
|
DhClientLevel level = new DhClientLevel(this.saveStructure, clientLevelWrapper, this.networkState);
|
||||||
|
clientLevelWrapperSetByDhId.computeIfAbsent(clientLevelWrapper.getDhIdentifier(), (dhId) -> Collections.synchronizedSet(new HashSet<>())).add(clientLevelWrapper);
|
||||||
|
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(clientLevelWrapper));
|
||||||
|
ClientApi.INSTANCE.loadWaitingChunksForLevel(clientLevelWrapper);
|
||||||
|
|
||||||
|
return level;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LOGGER.fatal("Failed to load client level, error: ["+e.getMessage()+"].", e);
|
||||||
|
|
||||||
|
ClientApi.INSTANCE.showChatMessageNextFrame(
|
||||||
|
MinecraftTextFormat.RED + "Distant Horizons: Client level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
|
||||||
|
"Unable to load level ["+clientLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -112,28 +152,39 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.levels.get(wrapper);
|
return this.clientLevelByDhId.get(wrapper.getDhIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.levels.values(); }
|
public Iterable<? extends IDhLevel> getAllLoadedLevels() { return this.clientLevelByDhId.values(); }
|
||||||
@Override
|
@Override
|
||||||
public int getLoadedLevelCount() { return this.levels.size(); }
|
public int getLoadedLevelCount() { return this.clientLevelByDhId.size(); }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unloadLevel(@NotNull ILevelWrapper wrapper)
|
public boolean unloadLevel(@NotNull ILevelWrapper wrapper)
|
||||||
{
|
{
|
||||||
if (!(wrapper instanceof IClientLevelWrapper))
|
if (!(wrapper instanceof IClientLevelWrapper))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.levels.containsKey(wrapper))
|
if (this.clientLevelByDhId.containsKey(wrapper.getDhIdentifier()))
|
||||||
{
|
{
|
||||||
LOGGER.info("Unloading level " + this.levels.get(wrapper));
|
LOGGER.info("Unloading level [" + this.clientLevelByDhId.get(wrapper.getDhIdentifier()) + "].");
|
||||||
wrapper.onUnload();
|
wrapper.onUnload();
|
||||||
this.levels.remove(wrapper).close();
|
Set<IClientLevelWrapper> wrapperSet = this.clientLevelWrapperSetByDhId.get(wrapper.getDhIdentifier());
|
||||||
|
wrapperSet.remove(wrapper);
|
||||||
|
if (wrapperSet.isEmpty())
|
||||||
|
{
|
||||||
|
this.clientLevelByDhId.remove(wrapper.getDhIdentifier()).close();
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(wrapper));
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -149,13 +200,14 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
this.networkState.close();
|
this.networkState.close();
|
||||||
|
|
||||||
ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>();
|
ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>();
|
||||||
for (DhClientLevel dhClientLevel : this.levels.values())
|
for (DhClientLevel dhClientLevel : this.clientLevelByDhId.values())
|
||||||
{
|
{
|
||||||
// level wrapper shouldn't be null, but just in case
|
// level wrapper shouldn't be null, but just in case
|
||||||
IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper();
|
IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper();
|
||||||
if (clientLevelWrapper != null)
|
if (clientLevelWrapper != null)
|
||||||
{
|
{
|
||||||
clientLevelWrapper.onUnload();
|
clientLevelWrapper.onUnload();
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(clientLevelWrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -177,7 +229,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
|
|||||||
future.join();
|
future.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.levels.clear();
|
this.clientLevelByDhId.clear();
|
||||||
|
this.clientLevelWrapperSetByDhId.clear();
|
||||||
this.clientTickTimer.cancel();
|
this.clientTickTimer.cancel();
|
||||||
LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
|
LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,12 +19,15 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.world;
|
package com.seibel.distanthorizons.core.world;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent;
|
||||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||||
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
|
||||||
import com.seibel.distanthorizons.core.generation.PregenManager;
|
import com.seibel.distanthorizons.core.generation.PregenManager;
|
||||||
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
import com.seibel.distanthorizons.core.level.DhServerLevel;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
|
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -64,7 +67,9 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new DhServerLevel(this.saveStructure, (IServerLevelWrapper) serverLevelWrapper, this.getServerPlayerStateManager());
|
DhServerLevel level = new DhServerLevel(this.saveStructure, (IServerLevelWrapper) serverLevelWrapper, this.getServerPlayerStateManager());
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(wrapper));
|
||||||
|
return level;
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -80,19 +85,22 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unloadLevel(@NotNull ILevelWrapper wrapper)
|
public boolean unloadLevel(@NotNull ILevelWrapper wrapper)
|
||||||
{
|
{
|
||||||
if (!(wrapper instanceof IServerLevelWrapper))
|
if (!(wrapper instanceof IServerLevelWrapper))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
if (this.dhLevelByLevelWrapper.containsKey(wrapper))
|
||||||
{
|
{
|
||||||
DhServerLevel level = this.dhLevelByLevelWrapper.get(wrapper);
|
|
||||||
wrapper.onUnload();
|
wrapper.onUnload();
|
||||||
this.dhLevelByLevelWrapper.remove(wrapper).close();
|
this.dhLevelByLevelWrapper.remove(wrapper).close();
|
||||||
|
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(wrapper));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
*/
|
*/
|
||||||
public interface IDhWorld extends Closeable
|
public interface IDhWorld extends Closeable
|
||||||
{
|
{
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
IDhLevel getOrLoadLevel(@NotNull ILevelWrapper levelWrapper);
|
IDhLevel getOrLoadLevel(@NotNull ILevelWrapper levelWrapper);
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -49,6 +48,14 @@ public interface IDhWorld extends Closeable
|
|||||||
Iterable<? extends IDhLevel> getAllLoadedLevels();
|
Iterable<? extends IDhLevel> getAllLoadedLevels();
|
||||||
int getLoadedLevelCount();
|
int getLoadedLevelCount();
|
||||||
|
|
||||||
void unloadLevel(@NotNull ILevelWrapper levelWrapper);
|
/**
|
||||||
|
* Returns
|
||||||
|
* true if the level was unloaded,
|
||||||
|
* false if the level isn't present in this world
|
||||||
|
* or couldn't be unloaded for some other reason
|
||||||
|
*/
|
||||||
|
boolean unloadLevel(@NotNull ILevelWrapper levelWrapper);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
|||||||
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
public interface IMinecraftClientWrapper extends IBindable
|
public interface IMinecraftClientWrapper extends IBindable
|
||||||
{
|
{
|
||||||
@@ -64,11 +65,13 @@ public interface IMinecraftClientWrapper extends IBindable
|
|||||||
* Returns the level the client is currently in. <br>
|
* Returns the level the client is currently in. <br>
|
||||||
* Returns null if the client isn't in a level.
|
* Returns null if the client isn't in a level.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
IClientLevelWrapper getWrappedClientLevel();
|
IClientLevelWrapper getWrappedClientLevel();
|
||||||
/**
|
/**
|
||||||
* Returns the level the client is currently in. <br>
|
* Returns the level the client is currently in. <br>
|
||||||
* Returns null if the client isn't in a level.
|
* Returns null if the client isn't in a level.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager);
|
IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
-2
@@ -99,8 +99,6 @@ public interface IMinecraftRenderWrapper extends IBindable
|
|||||||
@Nullable
|
@Nullable
|
||||||
ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level);
|
ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level);
|
||||||
|
|
||||||
float getShade(EDhDirection lodDirection);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
@@ -19,7 +19,9 @@
|
|||||||
|
|
||||||
package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft;
|
package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -31,6 +33,9 @@ public interface IMinecraftSharedWrapper extends IBindable
|
|||||||
|
|
||||||
int getPlayerCount();
|
int getPlayerCount();
|
||||||
|
|
||||||
|
/** If used on the client will only return a non-null object if the client is hosting a LAN server */
|
||||||
|
@Nullable
|
||||||
|
IServerLevelWrapper getWrappedServerLevelWithDimensionResourceLocation(String dimensionResourceLocation);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+157
@@ -0,0 +1,157 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.api.DhApi;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderEvent;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam;
|
||||||
|
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||||
|
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import java.lang.invoke.MethodHandle;
|
||||||
|
import java.lang.invoke.MethodHandles;
|
||||||
|
import java.lang.invoke.MethodType;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public abstract class AbstractImmersivePortalsAccessor implements IImmersivePortalsAccessor
|
||||||
|
{
|
||||||
|
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||||
|
|
||||||
|
private static MethodHandle isRenderingMethodHandle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=============//
|
||||||
|
// constructor //
|
||||||
|
//=============//
|
||||||
|
//region
|
||||||
|
|
||||||
|
public AbstractImmersivePortalsAccessor()
|
||||||
|
{
|
||||||
|
LOGGER.info("Immersive Portals detected: some DH features will be disabled or may only partially function.");
|
||||||
|
|
||||||
|
BeforeRenderEvent event = new BeforeRenderEvent(this);
|
||||||
|
DhApi.events.bind(DhApiBeforeRenderEvent.class, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=====================//
|
||||||
|
// reflection handling //
|
||||||
|
//=====================//
|
||||||
|
//region
|
||||||
|
|
||||||
|
private static Class<?> getPortalRenderingClass()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Class.forName("qouteall.imm_ptl.core.render.context_management.PortalRendering");
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException first)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Class.forName("com.qouteall.immersive_portals.render.context_management.PortalRendering"); // 1.16
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException second)
|
||||||
|
{
|
||||||
|
RuntimeException err = new RuntimeException(first);
|
||||||
|
err.addSuppressed(second);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//===========//
|
||||||
|
// overrides //
|
||||||
|
//===========//
|
||||||
|
//region
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModName() { return "Immersive Portals"; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRenderingPortal()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isRenderingMethodHandle == null)
|
||||||
|
{
|
||||||
|
isRenderingMethodHandle = MethodHandles.lookup().findStatic(
|
||||||
|
getPortalRenderingClass(),
|
||||||
|
"isRendering", MethodType.methodType(Boolean.TYPE)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (boolean) isRenderingMethodHandle.invoke();
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=======//
|
||||||
|
// event //
|
||||||
|
//=======//
|
||||||
|
//region
|
||||||
|
|
||||||
|
private static class BeforeRenderEvent extends DhApiBeforeRenderEvent
|
||||||
|
{
|
||||||
|
@NotNull
|
||||||
|
private final IImmersivePortalsAccessor immersivePortals;
|
||||||
|
|
||||||
|
|
||||||
|
public BeforeRenderEvent(@NotNull IImmersivePortalsAccessor portalAccessor) { this.immersivePortals = portalAccessor; }
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeRender(DhApiCancelableEventParam<DhApiRenderParam> event)
|
||||||
|
{
|
||||||
|
// needed because otherwise DH doesn't render to the level anyway
|
||||||
|
// and will probably render the level the player is currently in instead
|
||||||
|
if (this.immersivePortals.isRenderingPortal())
|
||||||
|
{
|
||||||
|
event.cancelEvent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+77
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the Distant Horizons mod
|
||||||
|
* licensed under the GNU LGPL v3 License.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 James Seibel
|
||||||
|
*
|
||||||
|
* 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, version 3.
|
||||||
|
*
|
||||||
|
* 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, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor;
|
||||||
|
|
||||||
|
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||||
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
|
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||||
|
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public interface IImmersivePortalsAccessor extends IModAccessor
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns true if Immersive Portals is currently rendering a portal.
|
||||||
|
* This can be used to determine if the level currently being rendered
|
||||||
|
* is being seen through a portal if called on the render thread.
|
||||||
|
*/
|
||||||
|
boolean isRenderingPortal();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the player's position for the level they're currently in.
|
||||||
|
* <br><br>
|
||||||
|
* Necessary since Immersive Portals messes with vanilla MC's
|
||||||
|
* variables in order to render the camera in multiple dimensions.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
DhBlockPos getActualPlayerBlockPos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the player's position for the level they're currently in.
|
||||||
|
* <br><br>
|
||||||
|
* Necessary since Immersive Portals messes with vanilla MC's
|
||||||
|
* variables in order to render the camera in multiple dimensions.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
DhChunkPos getActualPlayerChunkPos();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the client level the player is currently in.
|
||||||
|
* <br><br>
|
||||||
|
* Necessary since Immersive Portals messes with vanilla MC's
|
||||||
|
* variables in order to render the camera in multiple dimensions.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
IClientLevelWrapper getActualClientLevelWrapper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the camera position for the level the player is currently in.
|
||||||
|
* <br><br>
|
||||||
|
* Necessary since Immersive Portals messes with vanilla MC's
|
||||||
|
* variables in order to render the camera in multiple dimensions.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
Vec3d getActualCameraPos();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+6
@@ -20,6 +20,7 @@
|
|||||||
package com.seibel.distanthorizons.core.wrapperInterfaces.world;
|
package com.seibel.distanthorizons.core.wrapperInterfaces.world;
|
||||||
|
|
||||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||||
|
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -29,6 +30,9 @@ import java.awt.*;
|
|||||||
public interface IClientLevelWrapper extends ILevelWrapper
|
public interface IClientLevelWrapper extends ILevelWrapper
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/** used to track when this level was last used for Immersive portals support */
|
||||||
|
void markAccessed();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
IServerLevelWrapper tryGetServerSideWrapper();
|
IServerLevelWrapper tryGetServerSideWrapper();
|
||||||
|
|
||||||
@@ -41,4 +45,6 @@ public interface IClientLevelWrapper extends ILevelWrapper
|
|||||||
|
|
||||||
Color getCloudColor(float tickDelta);
|
Color getCloudColor(float tickDelta);
|
||||||
|
|
||||||
|
float getShade(EDhDirection lodDirection);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user