Fix server loading.

This commit is contained in:
Acuadragon100
2026-04-25 20:16:20 +02:00
parent ef3e7763dc
commit 7149baf0f6
12 changed files with 162 additions and 14 deletions
@@ -68,6 +68,7 @@ import org.lwjgl.glfw.GLFW;
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;
@@ -128,7 +129,7 @@ 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 loaded before the {@link ClientApi#clientLevelLoadEvent(IClientLevelWrapper)} was fired. */
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
@@ -235,6 +236,34 @@ public class ClientApi
//endregion //endregion
public boolean canLoadAlready(IClientLevelWrapper wrapper) {
// 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 (this.firstLevelLoadTimer == null)
{
this.firstLevelLoadTimer = TimerUtil.CreateTimer("FirstLevelLoadTimer");
this.firstLevelLoadTimer.schedule(new TimerTask()
{
@Override
public void run() { canLoadAlready(wrapper); }
}, FIRST_LEVEL_LOAD_DELAY_IN_MS);
return false;
}
this.firstLevelLoadTimer.cancel();
}
if (!this.pluginChannelApi.allowLevelLoading(wrapper))
{
LOGGER.debug("Levels in this connection are managed by the server, skipping auto-load of " + wrapper);
AbstractDhWorld world = SharedApi.getAbstractDhWorld();
if (world == null) return false;
// 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;
}
return true;
}
//==============// //==============//
// level events // // level events //
@@ -77,7 +77,30 @@ public class ServerApi
} }
//==============//
// level events //
//==============//
public void serverLevelLoadEvent(IServerLevelWrapper levelWrapper)
{
LOGGER.debug("Server Level " + levelWrapper + " loading");
AbstractDhWorld serverWorld = SharedApi.getAbstractDhWorld();
if (serverWorld != null)
{
serverWorld.getOrLoadLevel(levelWrapper);
}
}
public void serverLevelUnloadEvent(IServerLevelWrapper level)
{
LOGGER.debug("Server Level " + level + " unloading");
AbstractDhWorld serverWorld = SharedApi.getAbstractDhWorld();
if (serverWorld != null)
{
serverWorld.unloadLevel(level);
}
}
//=======================// //=======================//
@@ -202,7 +202,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
// Check if the player is in this dimension, // Check if the player is in this dimension,
// since handling multiple dimensions isn't allowed // since handling multiple dimensions isn't allowed
if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper()) /*if (message.getSession().serverPlayer.getLevel() != this.getLevelWrapper())
{ {
// If the message can be replied to - reply with an error, otherwise just ignore // If the message can be replied to - reply with an error, otherwise just ignore
if (message instanceof AbstractTrackableMessage) if (message instanceof AbstractTrackableMessage)
@@ -218,7 +218,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
} }
return false; return false;
} }*/
return true; return true;
} }
@@ -237,16 +237,14 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel
{ {
ClientNetworkState networkState = this.networkState; ClientNetworkState networkState = this.networkState;
boolean isClientUsable = false, isAllowedDimension = false; boolean isClientUsable = false;
if (networkState != null) if (networkState != null)
{ {
isClientUsable = networkState.isReady(); isClientUsable = networkState.isReady();
isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper;
} }
return isClientUsable return isClientUsable
&& networkState.sessionConfig.isDistantGenerationEnabled() && networkState.sessionConfig.isDistantGenerationEnabled()
&& isAllowedDimension
&& this.clientside.isRendering(); && this.clientside.isRendering();
} }
@@ -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,9 @@ 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)
@@ -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,17 +10,20 @@ 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.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 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
{ {
@@ -66,6 +70,12 @@ public class ServerPlayerState implements Closeable
this.sendConfigMessage(); this.sendConfigMessage();
}); });
this.networkSession.registerHandler(RequestLevelInitMessage.class, (requestLevelKeyMessage) ->
{
sendLevelKey(requestLevelKeyMessage.clientLevelKey);
});
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 +95,13 @@ public class ServerPlayerState implements Closeable
//=================// //=================//
private void onLevelKeyPrefixConfigChanged(String newLevelKey) { this.sendLevelKey(); } private void onLevelKeyPrefixConfigChanged(String newLevelKey) { this.sendLevelKey(); }
private void sendLevelKey()
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;
@@ -99,6 +110,17 @@ public class ServerPlayerState implements Closeable
} }
} }
private void sendLevelKey(String clientLevelKey)
{
sendLevelKey(() -> SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class)
.getWrappedServerLevel(clientLevelKey).getKeyedLevelDimensionName());
}
private void sendLevelKey()
{
sendLevelKey(() -> this.getServerPlayer().getLevel().getKeyedLevelDimensionName());
}
private void sendConfigMessage() private void sendConfigMessage()
{ {
double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale(); double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale();
@@ -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);
@@ -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 clientLevelKey;
//=============//
// constructor //
//=============//
public RequestLevelInitMessage() { }
public RequestLevelInitMessage(String clientLevelKey) { this.clientLevelKey = clientLevelKey; }
//===============//
// serialization //
//===============//
@Override
public void encode(ByteBuf out) { this.writeString(this.clientLevelKey, out); }
@Override
public void decode(ByteBuf in) { this.clientLevelKey = this.readString(in); }
//================//
// base overrides //
//================//
@Override
public MoreObjects.ToStringHelper toStringHelper()
{
return super.toStringHelper()
.add("levelKey", this.clientLevelKey);
}
}
@@ -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));
} }
@@ -167,6 +167,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
if (serverLevelWrapper != null) if (serverLevelWrapper != null)
{ {
serverLevelWrapper.onUnload(); serverLevelWrapper.onUnload();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(serverLevelWrapper));
} }
// close levels asynchronously to speed up // close levels asynchronously to speed up
@@ -92,8 +92,12 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
{ {
try try
{ {
if (!ClientApi.INSTANCE.canLoadAlready(clientLevelWrapper)) {
return null;
}
DhClientLevel level = new DhClientLevel(this.saveStructure, clientLevelWrapper, this.networkState); DhClientLevel level = new DhClientLevel(this.saveStructure, clientLevelWrapper, this.networkState);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(wrapper)); ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(wrapper));
ClientApi.INSTANCE.loadWaitingChunksForLevel(clientLevelWrapper);
return level; return level;
} }
catch (Exception e) catch (Exception e)
@@ -162,6 +166,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
if (clientLevelWrapper != null) if (clientLevelWrapper != null)
{ {
clientLevelWrapper.onUnload(); clientLevelWrapper.onUnload();
ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelUnloadEvent.class, new DhApiLevelUnloadEvent.EventParam(clientLevelWrapper));
} }
@@ -19,6 +19,7 @@
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 java.io.File; import java.io.File;
@@ -31,6 +32,6 @@ public interface IMinecraftSharedWrapper extends IBindable
int getPlayerCount(); int getPlayerCount();
IServerLevelWrapper getWrappedServerLevel(String levelKey);
} }