diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index a516a8138..d5b781fd8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -52,11 +52,13 @@ public class ClientPluginChannelApi Objects.requireNonNull(session); this.session = session; session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - session.registerHandler(CloseEvent.class, false, this::onClose); + session.registerHandler(CloseEvent.class, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { + // prefix@namespace:path + // 1-50 characters in total, all parts except namespace can be omitted if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$")) { throw new IllegalArgumentException("Server sent invalid level key."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 091475f97..130a5ca73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -64,7 +64,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.session.registerHandler(CloseEvent.class, false, msg -> + this.session.registerHandler(CloseEvent.class, msg -> { this.configReceived = false; }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index d3a15c1e4..68c90ddde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -53,8 +53,8 @@ public class ServerPlayerState this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(CloseEvent.class, false, event -> { - // Noop + this.session.registerHandler(CloseEvent.class, event -> { + // No-op. removes "Unhandled message" log entries }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 73059d19a..8d63dfa38 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -108,16 +108,11 @@ public abstract class NetworkEventSource } } - public abstract void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation); - - public final void registerHandler(Class handlerClass, Consumer handlerImplementation) + public abstract void registerHandler(Class handlerClass, Consumer handlerImplementation); + + protected final void registerHandler(NetworkEventSource instance, Class handlerClass, Consumer handlerImplementation) { - this.registerHandler(handlerClass, true, handlerImplementation); - } - - protected final void registerHandler(NetworkEventSource instance, Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) - { - if (throwIfMessageNotRegistered) + if (!InternalEvent.class.isAssignableFrom(handlerClass)) { MessageRegistry.INSTANCE.getMessageId(handlerClass); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java index be6ff4272..e3e167174 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java @@ -8,14 +8,16 @@ import org.jetbrains.annotations.Nullable; */ public class ProtocolErrorEvent extends InternalEvent { - public final Throwable throwable; + public final Throwable reason; @Nullable public final NetworkMessage message; + public final boolean replyWithCloseReason; - public ProtocolErrorEvent(Throwable throwable, @Nullable NetworkMessage message) + public ProtocolErrorEvent(Throwable reason, @Nullable NetworkMessage message, boolean replyWithCloseReason) { - this.throwable = throwable; + this.reason = reason; this.message = message; + this.replyWithCloseReason = replyWithCloseReason; } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index ddd7da67f..3d9ff50a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -39,7 +39,7 @@ public final class ScopedNetworkEventSource extends NetworkEventSource @Override - public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.isClosed) { @@ -47,9 +47,9 @@ public final class ScopedNetworkEventSource extends NetworkEventSource } //noinspection unchecked - this.parent.registerHandler(this, handlerClass, throwIfMessageNotRegistered, (Consumer) this.actualHandleMessageStable); + this.parent.registerHandler(this, handlerClass, (Consumer) this.actualHandleMessageStable); - super.registerHandler(this, handlerClass, throwIfMessageNotRegistered, handlerImplementation); + super.registerHandler(this, handlerClass, handlerImplementation); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index 1487c30da..e0b8a7a85 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; @@ -30,6 +31,7 @@ import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessag 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.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.coreapi.ModInfo; import java.util.HashMap; import java.util.Map; @@ -37,6 +39,7 @@ import java.util.function.Supplier; public class MessageRegistry { + public static final boolean DEBUG_ENABLE_CODEC_CRASH_MESSAGE = ModInfo.IS_DEV_BUILD; public static final MessageRegistry INSTANCE = new MessageRegistry(); private final Map> idToSupplier = new HashMap<>(); @@ -49,6 +52,7 @@ public class MessageRegistry // Note: Messages must have parameterless constructors // When the communication is about to be stopped, either side can send this message + // There may be messages after this, but they should be ignored if it's possible this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); // Level keys @@ -66,6 +70,12 @@ public class MessageRegistry this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); this.registerMessage(FullDataChunkMessage.class, FullDataChunkMessage::new); + + // Debug messages are always last, and not included into release builds. + if (DEBUG_ENABLE_CODEC_CRASH_MESSAGE) + { + this.registerMessage(CodecCrashMessage.class, CodecCrashMessage::new); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index c85e70f66..6bd0215ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.event.NetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; +import com.seibel.distanthorizons.core.network.event.ProtocolErrorEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; @@ -48,6 +49,16 @@ public class Session extends NetworkEventSource { this.close(new SessionClosedException(msg.reason)); }); + + this.registerHandler(ProtocolErrorEvent.class, event -> + { + if (event.replyWithCloseReason) + { + this.sendMessage(new CloseReasonMessage("Internal error on other side")); + } + + this.close(event.reason); + }); } @@ -68,20 +79,20 @@ public class Session extends NetworkEventSource catch (Throwable e) { LOGGER.error("Failed to handle the message. New messages will be ignored.", e); - LOGGER.error("Message: " + message); + LOGGER.error("Message: {}", message); this.close(); } } @Override - public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.closeReason.get() != null) { return; } - this.registerHandler(this, handlerClass, throwIfMessageNotRegistered, handlerImplementation); + this.registerHandler(this, handlerClass, handlerImplementation); } public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass)