diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/FullMappings.java b/api/src/main/java/com/viaversion/viaversion/api/data/FullMappings.java index d1b579d72..e9081ef20 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/FullMappings.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/FullMappings.java @@ -49,17 +49,17 @@ public interface FullMappings extends BiMappings { * Returns the unmapped string identifier for the given mapped id. * * @param id unmapped id - * @return unmapped string identifier + * @return unmapped string identifier, or null if out of bounds */ - String identifier(int id); + @Nullable String identifier(int id); /** * Returns the mapped string identifier for the given mapped id. * * @param mappedId mapped id - * @return mapped string identifier + * @return mapped string identifier, or null if out of bounds */ - String mappedIdentifier(int mappedId); + @Nullable String mappedIdentifier(int mappedId); /** * Returns the mapped string identifier for the given unmapped identifier. diff --git a/api/src/main/java/com/viaversion/viaversion/api/data/FullMappingsBase.java b/api/src/main/java/com/viaversion/viaversion/api/data/FullMappingsBase.java index 77742763d..3a204ed06 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/data/FullMappingsBase.java +++ b/api/src/main/java/com/viaversion/viaversion/api/data/FullMappingsBase.java @@ -66,12 +66,20 @@ public class FullMappingsBase implements FullMappings { @Override public String identifier(final int id) { + if (id < 0 || id >= idToString.length) { + return null; + } + final String identifier = idToString[id]; return Key.namespaced(identifier); } @Override public String mappedIdentifier(final int mappedId) { + if (mappedId < 0 || mappedId >= mappedIdToString.length) { + return null; + } + final String identifier = mappedIdToString[mappedId]; return Key.namespaced(identifier); } diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java index 9a9562679..0fe359720 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/CommandRewriter.java @@ -18,12 +18,14 @@ package com.viaversion.viaversion.rewriter; import com.google.common.base.Preconditions; +import com.viaversion.viaversion.api.data.FullMappings; import com.viaversion.viaversion.api.protocol.Protocol; import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType; import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.type.Type; import java.util.HashMap; import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Abstract rewriter for the declare commands packet to handle argument type name and content changes. @@ -118,16 +120,23 @@ public class CommandRewriter { if (nodeType == 2) { // Argument node int argumentTypeId = wrapper.read(Type.VAR_INT); String argumentType = argumentType(argumentTypeId); + if (argumentType == null) { + // Modded servers may send unknown argument types that are ignored by the client + // Adjust the id to the hopefully still assumed out-of-bounds pos... + wrapper.write(Type.VAR_INT, mapInvalidArgumentType(argumentTypeId)); + continue; + } + String newArgumentType = handleArgumentType(argumentType); Preconditions.checkNotNull(newArgumentType, "No mapping for argument type %s", argumentType); wrapper.write(Type.VAR_INT, mappedArgumentTypeId(newArgumentType)); // Always call the handler using the previous name handleArgument(wrapper, argumentType); - } - if ((flags & 0x10) != 0) { - wrapper.passthrough(Type.STRING); // Suggestion type + if ((flags & 0x10) != 0) { + wrapper.passthrough(Type.STRING); // Suggestion type + } } } @@ -148,21 +157,35 @@ public class CommandRewriter { * @param argumentType argument type * @return mapped argument type */ - public String handleArgumentType(String argumentType) { + public String handleArgumentType(final String argumentType) { if (protocol.getMappingData() != null && protocol.getMappingData().getArgumentTypeMappings() != null) { return protocol.getMappingData().getArgumentTypeMappings().mappedIdentifier(argumentType); } return argumentType; } - protected String argumentType(int argumentTypeId) { - return protocol.getMappingData().getArgumentTypeMappings().identifier(argumentTypeId); + protected @Nullable String argumentType(final int argumentTypeId) { + final FullMappings mappings = protocol.getMappingData().getArgumentTypeMappings(); + final String identifier = mappings.identifier(argumentTypeId); + // Allow unknown argument types to be passed through as long as they are actually out of bounds + Preconditions.checkArgument(identifier != null || argumentTypeId >= mappings.size(), "Unknown argument type id %s", argumentTypeId); + return identifier; } - protected int mappedArgumentTypeId(String mappedArgumentType) { + protected int mappedArgumentTypeId(final String mappedArgumentType) { return protocol.getMappingData().getArgumentTypeMappings().mappedId(mappedArgumentType); } + private int mapInvalidArgumentType(final int id) { + if (id < 0) { + return id; + } + + final FullMappings mappings = protocol.getMappingData().getArgumentTypeMappings(); + final int idx = id - mappings.size(); + return mappings.mappedSize() + idx; + } + @FunctionalInterface public interface CommandArgumentConsumer {