Add validation and safety checks across protocols

Harden NBT/packet handling and add defensive checks across multiple protocols and utilities.

- ServerBot: Improved create-state NBT parsing, fill legacy fallbacks, validate skin list entries, and guard loading of saved actions/configs with try/catch and logged warnings.
- LeavesProtocolManager: Treat malformed payloads as rejected (INVALID_PAYLOAD), avoid throwing on decode failures, add safe bytebuf invocation with logging, and a selector description helper.
- REIServerProtocol: Enforce tag types and presence, validate indices and slot bounds, and improve error messages for malformed REI data.
- LitematicaSchematic: Validate schematic metadata (non-empty regions, palette), check region sizes and limit volume to a maximum, and avoid silent failures.
- ServuxLitematicsProtocol: Wrap Litematica paste handling in try/catch, notify player on invalid data, and log warnings.
- LitematicaBitArray: Validate array size/backing storage, prevent oversized backing arrays, and add index bounds checks.
- LitematicaBlockStatePalette: Improve error messaging for invalid palette entries.
- SchematicPlacement: Validate required tags (Schematics/Origin/SubRegion positions), use safe enum-by-ordinal lookup, and guard against null vanilla boxes when streaming chunk positions.
- CommunicationManager: Validate ordinals for rotation/mirror, limit sub-region modification counts, and catch/reject malformed Syncmatica packets with logging.

Overall this commit improves robustness by validating inputs, avoiding unhandled runtime exceptions from malformed or malicious data, and adding informative logs for rejected payloads.
This commit is contained in:
Bacteriawa
2026-06-09 02:24:45 +08:00
parent 13c5bd7331
commit 11e9760eb3
9 changed files with 212 additions and 57 deletions
@@ -417,23 +417,31 @@ public class ServerBot extends ServerPlayer {
super.readAdditionalSaveData(nbt);
this.setShiftKeyDown(nbt.getBooleanOr("isShiftKeyDown", false));
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC).orElseThrow();
CompoundTag createNbt = nbt.read("createStatus", CompoundTag.CODEC)
.orElseThrow(() -> new IllegalArgumentException("Missing bot createStatus"));
String rawName = createNbt.getString("rawName")
.orElseGet(() -> createNbt.getString("realName")
.orElseThrow(() -> new IllegalArgumentException("Missing bot rawName")));
String name = createNbt.getString("name")
.orElseThrow(() -> new IllegalArgumentException("Missing bot name"));
String skinName = createNbt.getStringOr("skinName", rawName);
BotCreateState.Builder createBuilder = BotCreateState
.builder(createNbt.getString("rawName")
.orElseGet(() -> createNbt.getString("realName")
.orElseThrow()), null) // Convert from legacy version, consider to use ca.spottedleaf.dataconverter.minecraft.MCDataConverter instead for release version
.name(createNbt.getString("name").orElseThrow());
.builder(rawName, null) // Convert from legacy version, consider to use ca.spottedleaf.dataconverter.minecraft.MCDataConverter instead for release version
.name(name);
String[] skin = null;
if (createNbt.contains("skin")) {
ListTag skinTag = createNbt.getList("skin").orElseThrow();
ListTag skinTag = createNbt.getList("skin")
.orElseThrow(() -> new IllegalArgumentException("Invalid bot skin list"));
skin = new String[skinTag.size()];
for (int i = 0; i < skinTag.size(); i++) {
skin[i] = skinTag.getString(i).orElseThrow();
final int skinIndex = i;
skin[i] = skinTag.getString(i)
.orElseThrow(() -> new IllegalArgumentException("Invalid bot skin entry at index " + skinIndex));
}
}
createBuilder.skinName(createNbt.getString("skinName").orElseThrow()).skin(skin);
createBuilder.skinName(skinName).skin(skin);
createBuilder.createReason(BotCreateEvent.CreateReason.INTERNAL).creator(null);
this.createState = createBuilder.build();
@@ -443,11 +451,17 @@ public class ServerBot extends ServerPlayer {
if (FakePlayerCompatConfig.fakePlayerReloadAction && nbt.list("actions", CompoundTag.CODEC).isPresent()) {
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
actionNbt.forEach(actionTag -> {
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
if (action != null) {
AbstractBotAction<?> newAction = action.create();
newAction.load(actionTag);
this.actions.add(newAction);
try {
String actionName = actionTag.getString("actionName")
.orElseThrow(() -> new IllegalArgumentException("Missing actionName"));
AbstractBotAction<?> action = Actions.getForName(actionName);
if (action != null) {
AbstractBotAction<?> newAction = action.create();
newAction.load(actionTag);
this.actions.add(newAction);
}
} catch (RuntimeException exception) {
LophineLogger.LOGGER.warn("Skipped invalid saved action for bot {}", this.getScoreboardName(), exception);
}
});
}
@@ -455,10 +469,16 @@ public class ServerBot extends ServerPlayer {
if (nbt.list("configs", CompoundTag.CODEC).isPresent()) {
ValueInput.TypedInputList<CompoundTag> configNbt = nbt.list("configs", CompoundTag.CODEC).orElseThrow();
for (CompoundTag configTag : configNbt) {
AbstractBotConfig<?, ?> config = Configs.getConfig(configTag.getString("configName").orElseThrow());
if (config != null) {
config.setBot(this);
config.load(configTag);
try {
String configName = configTag.getString("configName")
.orElseThrow(() -> new IllegalArgumentException("Missing configName"));
AbstractBotConfig<?, ?> config = Configs.getConfig(configName);
if (config != null) {
config.setBot(this);
config.load(configTag);
}
} catch (RuntimeException exception) {
LophineLogger.LOGGER.warn("Skipped invalid saved config for bot {}", this.getScoreboardName(), exception);
}
}
}
@@ -45,6 +45,7 @@ import java.util.jar.JarFile;
public class LeavesProtocolManager {
private static final Logger LOGGER = LogUtils.getClassLogger();
private static final LeavesCustomPayload INVALID_PAYLOAD = new InvalidPayload();
private static final Map<Class<? extends LeavesCustomPayload>, PayloadReceiverInvokerHolder> PAYLOAD_RECEIVERS = new HashMap<>();
private static final Map<Class<? extends LeavesCustomPayload>, Identifier> IDS = new HashMap<>();
@@ -217,8 +218,8 @@ public class LeavesProtocolManager {
try {
return codec.decode(ProtocolUtils.decorate(buf));
} catch (Exception e) {
LOGGER.error("Failed to decode payload {}", location, e);
throw e;
LOGGER.warn("Rejected malformed Leaves payload {}", location, e);
return INVALID_PAYLOAD;
}
}
@@ -238,9 +239,16 @@ public class LeavesProtocolManager {
}
public static void handlePayload(IdentifierSelector selector, LeavesCustomPayload payload) {
if (payload == INVALID_PAYLOAD) {
return;
}
PayloadReceiverInvokerHolder holder;
if ((holder = PAYLOAD_RECEIVERS.get(payload.getClass())) != null) {
holder.invoke(selector, payload);
try {
holder.invoke(selector, payload);
} catch (RuntimeException exception) {
LOGGER.warn("Rejected malformed Leaves payload {} from {}", payload.getClass().getName(), describeSelector(selector), exception);
}
}
}
@@ -248,22 +256,44 @@ public class LeavesProtocolManager {
RegistryFriendlyByteBuf buf1 = ProtocolUtils.decorate(buf);
BytebufReceiverInvokerHolder holder;
if ((holder = STRICT_BYTEBUF_RECEIVERS.get(location.toString())) != null) {
holder.invoke(selector, buf1);
safeInvokeBytebuf(holder, selector, location, buf1);
return true;
}
if ((holder = NAMESPACED_BYTEBUF_RECEIVERS.get(location.getNamespace())) != null) {
if (holder.invoke(selector, buf1)) {
if (safeInvokeBytebuf(holder, selector, location, buf1)) {
return true;
}
}
for (var holder1 : GENERIC_BYTEBUF_RECEIVERS) {
if (holder1.invoke(selector, buf1)) {
if (safeInvokeBytebuf(holder1, selector, location, buf1)) {
return true;
}
}
return false;
}
private static boolean safeInvokeBytebuf(BytebufReceiverInvokerHolder holder, IdentifierSelector selector, Identifier location, RegistryFriendlyByteBuf buf) {
try {
return holder.invoke(selector, buf);
} catch (RuntimeException exception) {
LOGGER.warn("Rejected malformed bytebuf payload {} from {}", location, describeSelector(selector), exception);
return true;
}
}
private static String describeSelector(IdentifierSelector selector) {
if (selector.player() != null) {
return selector.player().getScoreboardName();
}
if (selector.context() != null) {
return selector.context().profile().name();
}
return "unknown";
}
private record InvalidPayload() implements LeavesCustomPayload {
}
public static void handleTick() {
long currentTime = System.currentTimeMillis() / MinecraftServer.getServer().tickRateManager().nanosecondsPerTick();
if (currentTime == lastAcceptTime) return;
@@ -414,4 +444,4 @@ public class LeavesProtocolManager {
}
}
}
}
}
@@ -400,15 +400,25 @@ public class REIServerProtocol implements LeavesProtocol {
private static List<List<ItemStack>> readInputs(RegistryAccess registryAccess, ListTag tag) {
List<List<ItemStack>> items = new ArrayList<>();
for (Tag t : tag) {
CompoundTag compoundTag = (CompoundTag) t;
compoundTag.getInt("Index").orElseThrow();
if (!(t instanceof CompoundTag compoundTag)) {
throw new IllegalStateException("Invalid REI input entry");
}
if (compoundTag.getInt("Index").isEmpty()) {
throw new IllegalStateException("Missing REI input index");
}
ListTag ingredientList = compoundTag.getListOrEmpty("Ingredient");
List<ItemStack> slotItems = new ArrayList<>();
for (Tag ingredient : ingredientList) {
CompoundTag ingredientTag = (CompoundTag) ingredient;
if (!(ingredient instanceof CompoundTag ingredientTag)) {
throw new IllegalStateException("Invalid REI ingredient entry");
}
Tag value = ingredientTag.get("value");
if (value == null) {
throw new IllegalStateException("Missing REI ingredient value");
}
ItemStack stack = ItemStack.OPTIONAL_CODEC.parse(
registryAccess.createSerializationContext(NbtOps.INSTANCE),
ingredientTag.get("value")
value
).getOrThrow();
slotItems.add(stack);
}
@@ -420,16 +430,28 @@ public class REIServerProtocol implements LeavesProtocol {
private static List<SlotAccessor> readSlots(AbstractContainerMenu menu, ServerPlayer player, ListTag tag) {
List<SlotAccessor> slots = new ArrayList<>();
for (Tag t : tag) {
CompoundTag compoundTag = (CompoundTag) t;
String id = compoundTag.getString("id").orElseThrow();
if (!(t instanceof CompoundTag compoundTag)) {
throw new IllegalStateException("Invalid REI slot entry");
}
String id = compoundTag.getString("id").orElseThrow(() -> new IllegalStateException("Missing REI slot id"));
if (!id.startsWith(PROTOCOL_ID + ":")) {
throw new IllegalStateException("Invalid slot id: " + id + ", expected to start with '" + PROTOCOL_ID + ":'");
}
id = id.substring((PROTOCOL_ID + ":").length());
int slot = compoundTag.getInt("Slot").orElseThrow();
int slot = compoundTag.getInt("Slot").orElseThrow(() -> new IllegalStateException("Missing REI slot index"));
SlotAccessor accessor = switch (id) {
case "vanilla" -> new VanillaSlotAccessor(menu.slots.get(slot));
case "player" -> new PlayerInventorySlotAccessor(player, slot);
case "vanilla" -> {
if (slot < 0 || slot >= menu.slots.size()) {
throw new IllegalStateException("Invalid vanilla slot index: " + slot);
}
yield new VanillaSlotAccessor(menu.slots.get(slot));
}
case "player" -> {
if (slot < 0 || slot >= player.getInventory().getContainerSize()) {
throw new IllegalStateException("Invalid player slot index: " + slot);
}
yield new PlayerInventorySlotAccessor(player, slot);
}
default -> throw new IllegalStateException("Unknown container id: " + id);
};
slots.add(accessor);
@@ -51,17 +51,21 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
public static final int MINECRAFT_DATA_VERSION = SharedConstants.getProtocolVersion();
public static final int SCHEMATIC_VERSION = 7;
private static final long MAX_REGION_VOLUME = 16_777_216L;
@NotNull
@Contract("_ -> new")
public static LitematicaSchematic readFromNBT(@NotNull CompoundTag nbt) {
if (nbt.contains("Version")) {
final int version = nbt.getIntOr("Version", -1);
final int minecraftDataVersion = nbt.contains("MinecraftDataVersion") ? nbt.getInt("MinecraftDataVersion").orElseThrow() : SharedConstants.getProtocolVersion();
final int minecraftDataVersion = nbt.getIntOr("MinecraftDataVersion", SharedConstants.getProtocolVersion());
if (version >= 1 && version <= SCHEMATIC_VERSION) {
SchematicMetadata metadata = SchematicMetadata.readFromNBT(nbt.getCompoundOrEmpty("Metadata"), version, minecraftDataVersion, FileType.LITEMATICA_SCHEMATIC);
Map<String, SubRegion> subRegions = readSubRegionsFromNBT(nbt.getCompoundOrEmpty("Regions"), version, minecraftDataVersion);
if (subRegions.isEmpty()) {
throw new IllegalArgumentException("Schematic has no regions");
}
return new LitematicaSchematic(subRegions, metadata);
} else {
throw new RuntimeException("Unsupported or future schematic version");
@@ -116,6 +120,7 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
if (position == null || size == null) {
throw new IllegalArgumentException("Invalid region");
}
validateRegionSize(size);
Map<BlockPos, CompoundTag> tileEntities;
List<EntityInfo> entities;
@@ -141,6 +146,9 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
Tag blockState = regionTag.get("BlockStates");
if (blockState != null && blockState.getId() == Tag.TAG_LONG_ARRAY) {
ListTag palette = regionTag.getListOrEmpty("BlockStatePalette");
if (palette.isEmpty()) {
throw new IllegalArgumentException("Missing block state palette");
}
long[] blockStateArr = ((LongArrayTag) blockState).getAsLongArray();
BlockPos posEndRel = PositionUtils.getRelativeEndPositionFromAreaSize(size).offset(position);
BlockPos posMin = PositionUtils.getMinCorner(position, posEndRel);
@@ -155,6 +163,19 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
return new SubRegion(blockContainers, tileEntities, pendingBlockTicks, pendingFluidTicks, entities, position, size);
}
private static void validateRegionSize(BlockPos size) {
long x = Math.abs((long) size.getX());
long y = Math.abs((long) size.getY());
long z = Math.abs((long) size.getZ());
if (x == 0 || y == 0 || z == 0) {
throw new IllegalArgumentException("Region has zero size");
}
long volume = Math.multiplyExact(Math.multiplyExact(x, y), z);
if (volume > MAX_REGION_VOLUME) {
throw new IllegalArgumentException("Region volume too large: " + volume);
}
}
private static List<EntityInfo> readEntitiesFromNBT(ListTag tagList) {
List<EntityInfo> entityList = new ArrayList<>();
final int size = tagList.size();
@@ -278,4 +299,4 @@ public record LitematicaSchematic(Map<String, SubRegion> subRegions, SchematicMe
this.nbt = nbt;
}
}
}
}
@@ -264,12 +264,17 @@ public class ServuxLitematicsProtocol implements LeavesProtocol {
}
if (tags.getStringOr("Task", "").equals("LitematicaPaste")) {
ServuxProtocol.LOGGER.debug("litematic_data: Servux Paste request from player {}", player.getName().getString());
ServerLevel serverLevel = player.level();
long timeStart = System.currentTimeMillis();
SchematicPlacement placement = SchematicPlacement.createFromNbt(tags);
ReplaceBehavior replaceMode = ReplaceBehavior.fromStringStatic(tags.getStringOr("ReplaceMode", ReplaceBehavior.NONE.name()));
placement.pasteTo(serverLevel, replaceMode, player, timeStart);
try {
ServuxProtocol.LOGGER.debug("litematic_data: Servux Paste request from player {}", player.getName().getString());
ServerLevel serverLevel = player.level();
long timeStart = System.currentTimeMillis();
SchematicPlacement placement = SchematicPlacement.createFromNbt(tags);
ReplaceBehavior replaceMode = ReplaceBehavior.fromStringStatic(tags.getStringOr("ReplaceMode", ReplaceBehavior.NONE.name()));
placement.pasteTo(serverLevel, replaceMode, player, timeStart);
} catch (RuntimeException exception) {
player.getBukkitEntity().sendActionBar(Component.text("Invalid Litematica paste data", NamedTextColor.RED));
ServuxProtocol.LOGGER.warn("Rejected invalid Litematica paste request from {}", player.getScoreboardName(), exception);
}
}
}
@@ -438,4 +443,4 @@ public class ServuxLitematicsProtocol implements LeavesProtocol {
return !this.hasBuffer() && !this.hasNbt();
}
}
}
}
@@ -47,11 +47,19 @@ public class LitematicaBitArray {
public LitematicaBitArray(int bitsPerEntryIn, long arraySizeIn, @Nullable long[] longArrayIn) {
Validate.inclusiveBetween(1L, 32L, bitsPerEntryIn);
Validate.isTrue(arraySizeIn >= 0L, "arraySize must not be negative");
this.arraySize = arraySizeIn;
this.bitsPerEntry = bitsPerEntryIn;
this.maxEntryValue = (1L << bitsPerEntryIn) - 1L;
this.longArray = Objects.requireNonNullElseGet(longArrayIn, () -> new long[(int) (roundUp(arraySizeIn * bitsPerEntryIn, 64L) / 64L)]);
long backingLength = roundUp(Math.multiplyExact(arraySizeIn, bitsPerEntryIn), 64L) / 64L;
Validate.isTrue(backingLength <= Integer.MAX_VALUE, "BitArray backing storage is too large");
if (longArrayIn != null) {
Validate.isTrue(longArrayIn.length >= backingLength, "BitArray backing storage is too small");
this.longArray = longArrayIn;
} else {
this.longArray = new long[(int) backingLength];
}
}
public static long roundUp(long value, long interval) {
@@ -71,6 +79,7 @@ public class LitematicaBitArray {
}
public void setAt(long index, int value) {
this.checkIndex(index);
long startOffset = index * (long) this.bitsPerEntry;
int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
@@ -85,6 +94,7 @@ public class LitematicaBitArray {
}
public int getAt(long index) {
this.checkIndex(index);
long startOffset = index * (long) this.bitsPerEntry;
int startArrIndex = (int) (startOffset >> 6); // startOffset / 64
int endArrIndex = (int) (((index + 1L) * (long) this.bitsPerEntry - 1L) >> 6);
@@ -101,4 +111,10 @@ public class LitematicaBitArray {
public long size() {
return this.arraySize;
}
}
private void checkIndex(long index) {
if (index < 0L || index >= this.arraySize) {
throw new IndexOutOfBoundsException("BitArray index " + index + " out of bounds for length " + this.arraySize);
}
}
}
@@ -49,7 +49,9 @@ public interface LitematicaBlockStatePalette {
final int size = tagList.size();
for (int i = 0; i < size; ++i) {
CompoundTag tag = tagList.getCompound(i).orElseThrow();
final int paletteIndex = i;
CompoundTag tag = tagList.getCompound(i)
.orElseThrow(() -> new IllegalArgumentException("Invalid block state palette entry at index " + paletteIndex));
BlockState state = NbtUtils.readBlockState(lookup, tag);
if (i > 0 || state != LitematicaBlockStateContainer.AIR_BLOCK_STATE) {
@@ -59,4 +61,4 @@ public interface LitematicaBlockStatePalette {
}
ListTag writeToNBT();
}
}
@@ -62,20 +62,31 @@ public class SchematicPlacement {
}
public static SchematicPlacement createFromNbt(CompoundTag tags) {
if (!tags.contains("Schematics")) {
throw new IllegalArgumentException("Missing Schematics tag");
}
BlockPos origin = NbtUtils.readBlockPosFromArrayTag(tags, "Origin");
if (origin == null) {
throw new IllegalArgumentException("Missing or invalid Origin tag");
}
SchematicPlacement placement = new SchematicPlacement(
LitematicaSchematic.readFromNBT(tags.getCompoundOrEmpty("Schematics")),
NbtUtils.readBlockPosFromArrayTag(tags, "Origin"),
origin,
tags.getStringOr("Name", "")
);
placement.mirror = Mirror.values()[tags.getIntOr("Mirror", 0)];
placement.rotation = Rotation.values()[tags.getIntOr("Rotation", 0)];
placement.mirror = enumByOrdinal(Mirror.values(), tags.getIntOr("Mirror", 0), "Mirror");
placement.rotation = enumByOrdinal(Rotation.values(), tags.getIntOr("Rotation", 0), "Rotation");
for (String name : tags.getCompoundOrEmpty("SubRegions").keySet()) {
CompoundTag compound = tags.getCompoundOrEmpty("SubRegions").getCompoundOrEmpty(name);
BlockPos pos = NbtUtils.readBlockPosFromArrayTag(compound, "Pos");
if (pos == null) {
throw new IllegalArgumentException("Missing or invalid Pos tag for sub-region " + name);
}
var sub = new SubRegionPlacement(
compound.getStringOr("Name", "?"),
NbtUtils.readBlockPosFromArrayTag(compound, "Pos"),
Rotation.values()[compound.getIntOr("Rotation", 0)],
Mirror.values()[compound.getIntOr("Mirror", 0)],
pos,
enumByOrdinal(Rotation.values(), compound.getIntOr("Rotation", 0), "SubRegions." + name + ".Rotation"),
enumByOrdinal(Mirror.values(), compound.getIntOr("Mirror", 0), "SubRegions." + name + ".Mirror"),
compound.getBooleanOr("Enabled", true),
compound.getBooleanOr("IgnoreEntities", false)
);
@@ -84,6 +95,13 @@ public class SchematicPlacement {
return placement;
}
private static <T> T enumByOrdinal(T[] values, int ordinal, String tagName) {
if (ordinal < 0 || ordinal >= values.length) {
throw new IllegalArgumentException("Invalid " + tagName + " ordinal: " + ordinal);
}
return values[ordinal];
}
public static IntBoundingBox getBoundsWithinChunkForBox(Box box, int chunkX, int chunkZ) {
final int chunkXMin = chunkX << 4;
final int chunkZMin = chunkZ << 4;
@@ -302,7 +320,13 @@ public class SchematicPlacement {
AtomicInteger count_full = new AtomicInteger();
AtomicInteger count = new AtomicInteger();
streamChunkPos(Objects.requireNonNull(enclosingBox.toVanilla())).forEach(chunkPos -> {
BlockBox vanillaBox = enclosingBox.toVanilla();
if (vanillaBox == null) {
ServuxProtocol.LOGGER.error("receiver a null vanilla enclosing box");
return;
}
streamChunkPos(vanillaBox).forEach(chunkPos -> {
RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
serverWorld,
chunkPos.x(),
@@ -51,6 +51,7 @@ public class CommunicationManager implements LeavesProtocol {
protected static final Map<UUID, Exchange> modifyState = new ConcurrentHashMap<>();
protected static final Rotation[] rotOrdinals = Rotation.values();
protected static final Mirror[] mirOrdinals = Mirror.values();
private static final int MAX_SUB_REGION_MODIFICATIONS = 4096;
private static final Map<UUID, List<ServerPlacement>> downloadingFile = new ConcurrentHashMap<>();
private static final Map<ExchangeTarget, ServerPlayer> playerMap = new ConcurrentHashMap<>();
@@ -87,7 +88,11 @@ public class CommunicationManager implements LeavesProtocol {
@ProtocolHandler.PayloadReceiver(payload = SyncmaticaPayload.class)
public static void onPacketGet(ServerPlayer player, SyncmaticaPayload payload) {
onPacket(player.connection.exchangeTarget, payload.packetType(), payload.data());
try {
onPacket(player.connection.exchangeTarget, payload.packetType(), payload.data());
} catch (IllegalArgumentException | IndexOutOfBoundsException exception) {
LOGGER.warn("Rejected malformed Syncmatica packet {} from {}", payload.packetType(), player.getScoreboardName(), exception);
}
}
public static void onPacket(final @NotNull ExchangeTarget source, final Identifier id, final FriendlyByteBuf packetBuf) {
@@ -326,20 +331,30 @@ public class CommunicationManager implements LeavesProtocol {
public static void receivePositionData(final @NotNull ServerPlacement placement, final @NotNull FriendlyByteBuf buf, final @NotNull ExchangeTarget exchangeTarget) {
final BlockPos pos = buf.readBlockPos();
final String dimensionId = buf.readUtf(32767);
final Rotation rot = rotOrdinals[buf.readInt()];
final Mirror mir = mirOrdinals[buf.readInt()];
final Rotation rot = readOrdinal(rotOrdinals, buf.readInt(), "rotation");
final Mirror mir = readOrdinal(mirOrdinals, buf.readInt(), "mirror");
placement.move(dimensionId, pos, rot, mir);
if (exchangeTarget.getFeatureSet().hasFeature(Feature.CORE_EX)) {
final SubRegionData subRegionData = placement.getSubRegionData();
subRegionData.reset();
final int limit = buf.readInt();
if (limit < 0 || limit > MAX_SUB_REGION_MODIFICATIONS) {
throw new IllegalArgumentException("Invalid sub-region modification count: " + limit);
}
for (int i = 0; i < limit; i++) {
subRegionData.modify(buf.readUtf(32767), buf.readBlockPos(), rotOrdinals[buf.readInt()], mirOrdinals[buf.readInt()]);
subRegionData.modify(buf.readUtf(32767), buf.readBlockPos(), readOrdinal(rotOrdinals, buf.readInt(), "sub-region rotation"), readOrdinal(mirOrdinals, buf.readInt(), "sub-region mirror"));
}
}
}
private static <T> T readOrdinal(T[] values, int ordinal, String name) {
if (ordinal < 0 || ordinal >= values.length) {
throw new IllegalArgumentException("Invalid " + name + " ordinal: " + ordinal);
}
return values[ordinal];
}
public static void download(final ServerPlacement syncmatic, final ExchangeTarget source) throws NoSuchAlgorithmException, IOException {
if (!SyncmaticaProtocol.getFileStorage().getLocalState(syncmatic).isReadyForDownload()) {
throw new IllegalArgumentException(syncmatic.toString() + " is not ready for download local state is: " + SyncmaticaProtocol.getFileStorage().getLocalState(syncmatic).toString());