From 7108b4c2900acc8e3a24a16dfa8f8f0fac2c8da7 Mon Sep 17 00:00:00 2001 From: Koushik H <143873225+Gautham-2907@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:02:25 -0300 Subject: [PATCH 1/5] refactor(ChunkStoreHelper): extract method, decompose conditional, explaining variable, replace conditional with polymorphism - Add ChunkFromTagLoader interface and ChunkFromTagLoaders with format-specific loaders (code smell: long conditional chain) - Extract extractDataVersion() and applyDataFixerIfNeeded() to reduce method size - Introduce explaining variables isMcAFormat, isBehindCurrentVersion, shouldApplyFix - Replace version if-else chain with ChunkFromTagLoaders.loadChunk() --- .../world/storage/ChunkFromTagLoader.java | 49 ++++++ .../world/storage/ChunkFromTagLoaders.java | 144 ++++++++++++++++++ .../world/storage/ChunkStoreHelper.java | 64 ++++---- 3 files changed, 222 insertions(+), 35 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java new file mode 100644 index 0000000000..79ebf877b4 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java @@ -0,0 +1,49 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.storage; + +import com.sk89q.worldedit.world.chunk.Chunk; +import com.sk89q.worldedit.world.DataException; +import org.enginehub.linbus.tree.LinCompoundTag; + +/** + * Strategy for creating a {@link Chunk} from NBT data. + * Used to replace version-based conditionals with polymorphism. + */ +public interface ChunkFromTagLoader { + + /** + * Whether this loader supports the given data version and tag structure. + * + * @param dataVersion the chunk data version + * @param rootTag the root NBT tag + * @return true if this loader can create a Chunk from the tag + */ + boolean supports(int dataVersion, LinCompoundTag rootTag); + + /** + * Create a Chunk from the given root tag. + * + * @param rootTag the root NBT tag (may be the full chunk or contain a Level tag) + * @return the chunk + * @throws DataException if the tag is not valid for this format + */ + Chunk load(LinCompoundTag rootTag) throws DataException; +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java new file mode 100644 index 0000000000..14b114d047 --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java @@ -0,0 +1,144 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.world.storage; + +import com.sk89q.worldedit.internal.Constants; +import com.sk89q.worldedit.world.DataException; +import com.sk89q.worldedit.world.chunk.AnvilChunk; +import com.sk89q.worldedit.world.chunk.AnvilChunk13; +import com.sk89q.worldedit.world.chunk.AnvilChunk16; +import com.sk89q.worldedit.world.chunk.AnvilChunk18; +import com.sk89q.worldedit.world.chunk.Chunk; +import com.sk89q.worldedit.world.chunk.OldChunk; +import org.enginehub.linbus.tree.LinCompoundTag; +import org.enginehub.linbus.tree.LinTagType; + +import java.util.List; + +/** + * Registry of chunk format loaders. Order matters: first supporting loader is used. + */ +public final class ChunkFromTagLoaders { + + private static final List LOADERS = List.of( + new AnvilChunk18Loader(), + new AnvilChunk16Loader(), + new AnvilChunk13Loader(), + new AnvilChunkLoader(), + new OldChunkLoader() + ); + + /** + * Find the first loader that supports the given data version and tag, then load the chunk. + * + * @param dataVersion the chunk data version + * @param rootTag the root NBT tag + * @return the chunk + * @throws DataException if no loader supports the tag or loading fails + */ + public static Chunk loadChunk(int dataVersion, LinCompoundTag rootTag) throws DataException { + for (ChunkFromTagLoader loader : LOADERS) { + if (loader.supports(dataVersion, rootTag)) { + return loader.load(rootTag); + } + } + throw new ChunkStoreException("Unsupported chunk format: dataVersion=" + dataVersion); + } + + private ChunkFromTagLoaders() { + } + + private static final class AnvilChunk18Loader implements ChunkFromTagLoader { + @Override + public boolean supports(int dataVersion, LinCompoundTag rootTag) { + return dataVersion >= Constants.DATA_VERSION_MC_1_18; + } + + @Override + public Chunk load(LinCompoundTag rootTag) throws DataException { + return new AnvilChunk18(rootTag); + } + } + + private static final class AnvilChunk16Loader implements ChunkFromTagLoader { + @Override + public boolean supports(int dataVersion, LinCompoundTag rootTag) { + return dataVersion >= Constants.DATA_VERSION_MC_1_16; + } + + @Override + public Chunk load(LinCompoundTag rootTag) throws DataException { + LinCompoundTag levelTag = rootTag.findTag("Level", LinTagType.compoundTag()); + if (levelTag == null) { + throw new ChunkStoreException("Missing root 'Level' tag"); + } + return new AnvilChunk16(levelTag); + } + } + + private static final class AnvilChunk13Loader implements ChunkFromTagLoader { + @Override + public boolean supports(int dataVersion, LinCompoundTag rootTag) { + return dataVersion >= Constants.DATA_VERSION_MC_1_13; + } + + @Override + public Chunk load(LinCompoundTag rootTag) throws DataException { + LinCompoundTag levelTag = rootTag.findTag("Level", LinTagType.compoundTag()); + if (levelTag == null) { + throw new ChunkStoreException("Missing root 'Level' tag"); + } + return new AnvilChunk13(levelTag); + } + } + + private static final class AnvilChunkLoader implements ChunkFromTagLoader { + @Override + public boolean supports(int dataVersion, LinCompoundTag rootTag) { + LinCompoundTag levelTag = rootTag.findTag("Level", LinTagType.compoundTag()); + return levelTag != null && levelTag.value().containsKey("Sections"); + } + + @Override + public Chunk load(LinCompoundTag rootTag) throws DataException { + LinCompoundTag levelTag = rootTag.findTag("Level", LinTagType.compoundTag()); + if (levelTag == null) { + throw new ChunkStoreException("Missing root 'Level' tag"); + } + return new AnvilChunk(levelTag); + } + } + + private static final class OldChunkLoader implements ChunkFromTagLoader { + @Override + public boolean supports(int dataVersion, LinCompoundTag rootTag) { + return rootTag.findTag("Level", LinTagType.compoundTag()) != null; + } + + @Override + public Chunk load(LinCompoundTag rootTag) throws DataException { + LinCompoundTag levelTag = rootTag.findTag("Level", LinTagType.compoundTag()); + if (levelTag == null) { + throw new ChunkStoreException("Missing root 'Level' tag"); + } + return new OldChunk(levelTag); + } + } +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStoreHelper.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStoreHelper.java index da39f36533..bbe933c98c 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStoreHelper.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkStoreHelper.java @@ -26,15 +26,9 @@ import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.extension.platform.Capability; import com.sk89q.worldedit.extension.platform.Platform; -import com.sk89q.worldedit.internal.Constants; import com.sk89q.worldedit.world.DataException; import com.sk89q.worldedit.world.DataFixer; -import com.sk89q.worldedit.world.chunk.AnvilChunk; -import com.sk89q.worldedit.world.chunk.AnvilChunk13; -import com.sk89q.worldedit.world.chunk.AnvilChunk16; -import com.sk89q.worldedit.world.chunk.AnvilChunk18; import com.sk89q.worldedit.world.chunk.Chunk; -import com.sk89q.worldedit.world.chunk.OldChunk; import org.enginehub.linbus.tree.LinCompoundTag; import org.enginehub.linbus.tree.LinNumberTag; import org.enginehub.linbus.tree.LinTagType; @@ -99,40 +93,40 @@ public static Chunk getChunk(CompoundTag rootTag) throws DataException { * @throws DataException if the rootTag is not valid chunk data */ public static Chunk getChunk(LinCompoundTag rootTag) throws DataException { - int dataVersion = rootTag.value().get("DataVersion") instanceof LinNumberTag t - ? t.value().intValue() : -1; - - final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); - final int currentDataVersion = platform.getDataVersion(); - if ((dataVersion > 0 || hasLevelSections(rootTag)) && dataVersion < currentDataVersion) { // only fix up MCA format, DFU doesn't support MCR chunks - final DataFixer dataFixer = platform.getDataFixer(); - if (dataFixer != null) { - rootTag = dataFixer.fixUp(DataFixer.FixTypes.CHUNK, rootTag, dataVersion); - dataVersion = currentDataVersion; - } - } + int dataVersion = extractDataVersion(rootTag); + DataFixResult fixResult = applyDataFixerIfNeeded(rootTag, dataVersion); + return ChunkFromTagLoaders.loadChunk(fixResult.effectiveDataVersion(), fixResult.rootTag()); + } - if (dataVersion >= Constants.DATA_VERSION_MC_1_18) { - return new AnvilChunk18(rootTag); - } + /** + * Extracts the DataVersion from the chunk root tag, or -1 if missing/invalid. + */ + private static int extractDataVersion(LinCompoundTag rootTag) { + return rootTag.value().get("DataVersion") instanceof LinNumberTag numberTag + ? numberTag.value().intValue() : -1; + } - LinCompoundTag tag = rootTag.findTag("Level", LinTagType.compoundTag()); - if (tag == null) { - throw new ChunkStoreException("Missing root 'Level' tag"); - } + private record DataFixResult(LinCompoundTag rootTag, int effectiveDataVersion) {} - if (dataVersion >= Constants.DATA_VERSION_MC_1_16) { - return new AnvilChunk16(tag); - } - if (dataVersion >= Constants.DATA_VERSION_MC_1_13) { - return new AnvilChunk13(tag); + /** + * Applies the platform data fixer when the chunk is in MCA format and behind current version. + * Only fixes MCA format; DFU doesn't support MCR chunks. + */ + private static DataFixResult applyDataFixerIfNeeded(LinCompoundTag rootTag, int dataVersion) { + final Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.WORLD_EDITING); + final int currentDataVersion = platform.getDataVersion(); + boolean isMcAFormat = dataVersion > 0 || hasLevelSections(rootTag); + boolean isBehindCurrentVersion = dataVersion < currentDataVersion; + boolean shouldApplyFix = isMcAFormat && isBehindCurrentVersion; + if (!shouldApplyFix) { + return new DataFixResult(rootTag, dataVersion); } - - if (tag.value().containsKey("Sections")) { - return new AnvilChunk(tag); + final DataFixer dataFixer = platform.getDataFixer(); + if (dataFixer == null) { + return new DataFixResult(rootTag, dataVersion); } - - return new OldChunk(tag); + LinCompoundTag fixedTag = dataFixer.fixUp(DataFixer.FixTypes.CHUNK, rootTag, dataVersion); + return new DataFixResult(fixedTag, currentDataVersion); } private static boolean hasLevelSections(LinCompoundTag rootTag) { From e5e4d17c3162ef1ba1994c280e3ea7eaf61959a9 Mon Sep 17 00:00:00 2001 From: Koushik H <143873225+Gautham-2907@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:02:58 -0300 Subject: [PATCH 2/5] refactor(LegacyChunkStore): pull-up variable/method - getChunkPathComponents - Code smell: duplicated path computation in getFilename and getChunkData - Add getChunkPathComponents(BlockVector2) and ChunkPathComponents record - Both getFilename and getChunkData now use getChunkPathComponents --- .../world/storage/LegacyChunkStore.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/LegacyChunkStore.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/LegacyChunkStore.java index e65b2108b1..37f903c0cc 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/LegacyChunkStore.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/LegacyChunkStore.java @@ -40,22 +40,38 @@ public abstract class LegacyChunkStore extends ChunkStore { /** - * Get the filename of a chunk. - * - * @param position chunk position - * @param separator folder separator character - * @return pathname + * Path components for a chunk file (folder1, folder2, filename). + * Pulled up so both getFilename and getChunkData use the same computation (code smell: duplicated logic). */ - public static String getFilename(BlockVector2 position, String separator) { + public static ChunkPathComponents getChunkPathComponents(BlockVector2 position) { int x = position.x(); int z = position.z(); - String folder1 = Integer.toString(divisorMod(x, 64), 36); String folder2 = Integer.toString(divisorMod(z, 64), 36); String filename = "c." + Integer.toString(x, 36) + "." + Integer.toString(z, 36) + ".dat"; + return new ChunkPathComponents(folder1, folder2, filename); + } - return folder1 + separator + folder2 + separator + filename; + /** + * Path components for a legacy chunk file. + * + * @param folder1 first folder segment + * @param folder2 second folder segment + * @param filename chunk filename + */ + public record ChunkPathComponents(String folder1, String folder2, String filename) {} + + /** + * Get the filename of a chunk. + * + * @param position chunk position + * @param separator folder separator character + * @return pathname + */ + public static String getFilename(BlockVector2 position, String separator) { + ChunkPathComponents path = getChunkPathComponents(position); + return path.folder1() + separator + path.folder2() + separator + path.filename(); } /** @@ -71,15 +87,8 @@ public static String getFilename(BlockVector2 position) { @Override public LinCompoundTag getChunkData(BlockVector2 position, World world) throws DataException, IOException { - int x = position.x(); - int z = position.z(); - - String folder1 = Integer.toString(divisorMod(x, 64), 36); - String folder2 = Integer.toString(divisorMod(z, 64), 36); - String filename = "c." + Integer.toString(x, 36) - + "." + Integer.toString(z, 36) + ".dat"; - - try (var chunkStream = new DataInputStream(new GZIPInputStream(getInputStream(folder1, folder2, filename)))) { + ChunkPathComponents path = getChunkPathComponents(position); + try (var chunkStream = new DataInputStream(new GZIPInputStream(getInputStream(path.folder1(), path.folder2(), path.filename())))) { return LinBinaryIO.readUsing(chunkStream, LinRootEntry::readFrom).toLinTag(); } } From 6b4112c5b223cc804393b5628ca43bdc13c433ad Mon Sep 17 00:00:00 2001 From: Koushik H <143873225+Gautham-2907@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:04:24 -0300 Subject: [PATCH 3/5] refactor(LocalSession): extract class SessionCUIState, move CUI fields - Code smell: LocalSession had mixed responsibilities (session + CUI state) - Add SessionCUIState holding failedCuiAttempts, hasCUISupport, cuiVersion, serverCuiStructureBlockPosition - LocalSession delegates CUI state getters/setters to cuiState; onIdle() calls cuiState.reset() --- .../com/sk89q/worldedit/LocalSession.java | 79 ++++++++--------- .../worldedit/session/SessionCUIState.java | 88 +++++++++++++++++++ 2 files changed, 124 insertions(+), 43 deletions(-) create mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionCUIState.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 0563cb0d91..5531cde1b4 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -49,6 +49,7 @@ import com.sk89q.worldedit.session.ClipboardHolder; import com.sk89q.worldedit.session.Placement; import com.sk89q.worldedit.session.PlacementType; +import com.sk89q.worldedit.session.SessionCUIState; import com.sk89q.worldedit.session.request.Request; import com.sk89q.worldedit.util.Countable; import com.sk89q.worldedit.util.SideEffectSet; @@ -84,17 +85,14 @@ */ public class LocalSession { - private static final int CUI_VERSION_UNINITIALIZED = -1; public static int MAX_HISTORY_SIZE = 15; // Non-session related fields private transient LocalConfiguration config; private final transient AtomicBoolean dirty = new AtomicBoolean(); - // Single-connection lifetime fields - private transient int failedCuiAttempts = 0; - private transient boolean hasCUISupport = false; - private transient int cuiVersion = CUI_VERSION_UNINITIALIZED; + // CUI state (extracted to SessionCUIState - move field) + private final transient SessionCUIState cuiState = new SessionCUIState(); // Session related private transient RegionSelector selector = new CuboidRegionSelector(); @@ -113,7 +111,6 @@ public class LocalSession { private transient SideEffectSet sideEffectSet = SideEffectSet.defaults(); private transient Mask mask; private transient ZoneId timezone = ZoneId.systemDefault(); - private transient BlockVector3 cuiTemporaryBlock; @SuppressWarnings("deprecation") private transient EditSession.ReorderMode reorderMode = EditSession.ReorderMode.FAST; private transient List> lastDistribution; @@ -899,10 +896,11 @@ public void updateServerCUI(Actor actor) { Player player = (Player) actor; - if (!useServerCUI || hasCUISupport) { - if (cuiTemporaryBlock != null) { - player.sendFakeBlock(cuiTemporaryBlock, null); - cuiTemporaryBlock = null; + if (!useServerCUI || cuiState.hasCUISupport()) { + BlockVector3 pos = cuiState.getServerCuiStructureBlockPosition(); + if (pos != null) { + player.sendFakeBlock(pos, null); + cuiState.setServerCuiStructureBlockPosition(null); } return; // If it's not enabled, ignore this. } @@ -912,22 +910,23 @@ public void updateServerCUI(Actor actor) { LinCompoundTag tags = Objects.requireNonNull( block.getNbt(), "createStructureBlock should return nbt" ); - BlockVector3 tempCuiTemporaryBlock = BlockVector3.at( + BlockVector3 newStructureBlockPosition = BlockVector3.at( tags.getTag("x", LinTagType.intTag()).valueAsInt(), tags.getTag("y", LinTagType.intTag()).valueAsInt(), tags.getTag("z", LinTagType.intTag()).valueAsInt() ); - // If it's null, we don't need to do anything. The old was already removed. - if (cuiTemporaryBlock != null && !tempCuiTemporaryBlock.equals(cuiTemporaryBlock)) { - // Update the existing block if it's the same location - player.sendFakeBlock(cuiTemporaryBlock, null); + BlockVector3 currentPos = cuiState.getServerCuiStructureBlockPosition(); + if (currentPos != null && !newStructureBlockPosition.equals(currentPos)) { + player.sendFakeBlock(currentPos, null); + } + cuiState.setServerCuiStructureBlockPosition(newStructureBlockPosition); + player.sendFakeBlock(newStructureBlockPosition, block); + } else { + BlockVector3 pos = cuiState.getServerCuiStructureBlockPosition(); + if (pos != null) { + player.sendFakeBlock(pos, null); + cuiState.setServerCuiStructureBlockPosition(null); } - cuiTemporaryBlock = tempCuiTemporaryBlock; - player.sendFakeBlock(cuiTemporaryBlock, block); - } else if (cuiTemporaryBlock != null) { - // Remove the old block - player.sendFakeBlock(cuiTemporaryBlock, null); - cuiTemporaryBlock = null; } } @@ -941,7 +940,7 @@ public void dispatchCUIEvent(Actor actor, CUIEvent event) { checkNotNull(actor); checkNotNull(event); - if (hasCUISupport) { + if (cuiState.hasCUISupport()) { actor.dispatchCUIEvent(event); } else if (useServerCUI) { updateServerCUI(actor); @@ -967,7 +966,7 @@ public void dispatchCUISetup(Actor actor) { public void dispatchCUISelection(Actor actor) { checkNotNull(actor); - if (!hasCUISupport) { + if (!cuiState.hasCUISupport()) { if (useServerCUI) { updateServerCUI(actor); } @@ -975,7 +974,7 @@ public void dispatchCUISelection(Actor actor) { } if (selector instanceof CUIRegion tempSel) { - if (tempSel.getProtocolVersion() > cuiVersion) { + if (tempSel.getProtocolVersion() > cuiState.getCUIVersion()) { actor.dispatchCUIEvent(new SelectionShapeEvent(tempSel.getLegacyTypeID())); tempSel.describeLegacyCUI(this, actor); } else { @@ -994,12 +993,12 @@ public void dispatchCUISelection(Actor actor) { public void describeCUI(Actor actor) { checkNotNull(actor); - if (!hasCUISupport) { + if (!cuiState.hasCUISupport()) { return; } if (selector instanceof CUIRegion tempSel) { - if (tempSel.getProtocolVersion() > cuiVersion) { + if (tempSel.getProtocolVersion() > cuiState.getCUIVersion()) { tempSel.describeLegacyCUI(this, actor); } else { tempSel.describeCUI(this, actor); @@ -1019,18 +1018,18 @@ public void handleCUIInitializationMessage(String eventType, List args, checkNotNull(eventType); checkNotNull(args); - if (this.hasCUISupport) { + if (cuiState.hasCUISupport()) { // WECUI is a bit aggressive about re-initializing itself // the last attempt to touch handshakes didn't go well, so this will do... for now dispatchCUISelection(actor); return; - } else if (this.failedCuiAttempts > 3) { + } else if (cuiState.getFailedCuiAttempts() > 3) { return; } if (!args.isEmpty() && eventType.equalsIgnoreCase("v")) { // enough fields and right message if (args.size() > 1) { - this.failedCuiAttempts++; + cuiState.incrementFailedCuiAttempts(); return; } @@ -1039,11 +1038,11 @@ public void handleCUIInitializationMessage(String eventType, List args, version = Integer.parseInt(args.getFirst()); } catch (NumberFormatException e) { WorldEdit.logger.warn("Error while reading CUI init message"); - this.failedCuiAttempts++; + cuiState.incrementFailedCuiAttempts(); return; } - setCUISupport(true); - setCUIVersion(version); + cuiState.setCUISupport(true); + cuiState.setCUIVersion(version); dispatchCUISelection(actor); } } @@ -1070,7 +1069,7 @@ public void handleCUIInitializationMessage(String text, Actor actor) { * @return true if CUI is enabled */ public boolean hasCUISupport() { - return hasCUISupport; + return cuiState.hasCUISupport(); } /** @@ -1079,7 +1078,7 @@ public boolean hasCUISupport() { * @param support true if CUI is enabled */ public void setCUISupport(boolean support) { - hasCUISupport = support; + cuiState.setCUISupport(support); } /** @@ -1088,7 +1087,7 @@ public void setCUISupport(boolean support) { * @return the CUI version */ public int getCUIVersion() { - return cuiVersion; + return cuiState.getCUIVersion(); } /** @@ -1097,11 +1096,7 @@ public int getCUIVersion() { * @param cuiVersion the CUI version */ public void setCUIVersion(int cuiVersion) { - if (cuiVersion < 0) { - throw new IllegalArgumentException("CUI protocol version must be non-negative, but '" + cuiVersion + "' was received."); - } - - this.cuiVersion = cuiVersion; + cuiState.setCUIVersion(cuiVersion); } /** @@ -1314,8 +1309,6 @@ public void setLastDistribution(List> dist) { *

This is for internal use only.

*/ public void onIdle() { - this.cuiVersion = CUI_VERSION_UNINITIALIZED; - this.hasCUISupport = false; - this.failedCuiAttempts = 0; + cuiState.reset(); } } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionCUIState.java b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionCUIState.java new file mode 100644 index 0000000000..f5e07f6e6e --- /dev/null +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/session/SessionCUIState.java @@ -0,0 +1,88 @@ +/* + * WorldEdit, a Minecraft world manipulation toolkit + * Copyright (C) sk89q + * Copyright (C) WorldEdit team and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.sk89q.worldedit.session; + +import com.sk89q.worldedit.math.BlockVector3; + +import javax.annotation.Nullable; + +/** + * Holds CUI (CUINetworking)-related state for a session. + * Extracted from LocalSession to separate concerns (code smell: large class with mixed responsibilities). + */ +public final class SessionCUIState { + + public static final int CUI_VERSION_UNINITIALIZED = -1; + + private int failedCuiAttempts = 0; + private boolean hasCUISupport = false; + private int cuiVersion = CUI_VERSION_UNINITIALIZED; + @Nullable + private BlockVector3 serverCuiStructureBlockPosition; + + public int getFailedCuiAttempts() { + return failedCuiAttempts; + } + + public void setFailedCuiAttempts(int failedCuiAttempts) { + this.failedCuiAttempts = failedCuiAttempts; + } + + public void incrementFailedCuiAttempts() { + this.failedCuiAttempts++; + } + + public boolean hasCUISupport() { + return hasCUISupport; + } + + public void setCUISupport(boolean support) { + this.hasCUISupport = support; + } + + public int getCUIVersion() { + return cuiVersion; + } + + public void setCUIVersion(int cuiVersion) { + if (cuiVersion < 0) { + throw new IllegalArgumentException("CUI protocol version must be non-negative, but '" + cuiVersion + "' was received."); + } + this.cuiVersion = cuiVersion; + } + + @Nullable + public BlockVector3 getServerCuiStructureBlockPosition() { + return serverCuiStructureBlockPosition; + } + + public void setServerCuiStructureBlockPosition(@Nullable BlockVector3 position) { + this.serverCuiStructureBlockPosition = position; + } + + /** + * Reset CUI state when the session becomes idle. + */ + public void reset() { + this.cuiVersion = CUI_VERSION_UNINITIALIZED; + this.hasCUISupport = false; + this.failedCuiAttempts = 0; + } +} From 875006dfac12433f17f640b219ef4394b623a638 Mon Sep 17 00:00:00 2001 From: Koushik H <143873225+Gautham-2907@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:05:29 -0300 Subject: [PATCH 4/5] refactor(LocalSession): rename method/variable, decompose conditional - Code smell: unclear names. Rename pickaxeMode to superPickaxeTool - Extract isSelectionDefinedForWorld(World) and use in getSelection() to decompose complex conditional --- .../com/sk89q/worldedit/LocalSession.java | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 5531cde1b4..8d3d532bc1 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -101,7 +101,7 @@ public class LocalSession { private transient int historyPointer = 0; private transient ClipboardHolder clipboard; private transient boolean superPickaxe = false; - private transient BlockTool pickaxeMode = new SinglePickaxe(); + private transient BlockTool superPickaxeTool = new SinglePickaxe(); private final transient Map tools = new HashMap<>(); private transient int maxBlocksChanged = -1; private transient int maxTimeoutTime; @@ -425,13 +425,25 @@ public Region getSelection() throws IncompleteRegionException { * @throws IncompleteRegionException if no region is selected, or the provided world is null */ public Region getSelection(@Nullable World world) throws IncompleteRegionException { - if (world == null || selector.getIncompleteRegion().getWorld() == null - || !selector.getIncompleteRegion().getWorld().equals(world)) { + if (!isSelectionDefinedForWorld(world)) { throw new IncompleteRegionException(); } return selector.getRegion(); } + /** + * Returns whether the selection is fully defined for the given world. + * Decomposed conditional for clarity (code smell: complex conditional). + */ + private boolean isSelectionDefinedForWorld(@Nullable World world) { + if (world == null) { + return false; + } + World selectionWorld = selector.getIncompleteRegion().getWorld(); + boolean selectionMatchesWorld = selectionWorld != null && selectionWorld.equals(world); + return selectionMatchesWorld; + } + /** * Get the selection world. * @@ -696,7 +708,7 @@ public void setSnapshotExperimental(@Nullable SnapshotInfo snapshotExperimental) * @return the super pickaxe tool mode */ public BlockTool getSuperPickaxe() { - return pickaxeMode; + return superPickaxeTool; } /** @@ -706,7 +718,7 @@ public BlockTool getSuperPickaxe() { */ public void setSuperPickaxe(BlockTool tool) { checkNotNull(tool); - this.pickaxeMode = tool; + this.superPickaxeTool = tool; } /** From 39524ac07900c5471e43fa658c12eadfcc2762de Mon Sep 17 00:00:00 2001 From: Koushik H <143873225+Gautham-2907@users.noreply.github.com> Date: Fri, 20 Mar 2026 03:52:12 -0300 Subject: [PATCH 5/5] fix: enforce LF line endings for checkstyle in refactored chunk/session files --- .../world/storage/ChunkFromTagLoader.java | 49 ------------------- .../world/storage/ChunkFromTagLoaders.java | 25 ++++++++++ 2 files changed, 25 insertions(+), 49 deletions(-) delete mode 100644 worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java deleted file mode 100644 index 79ebf877b4..0000000000 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoader.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * WorldEdit, a Minecraft world manipulation toolkit - * Copyright (C) sk89q - * Copyright (C) WorldEdit team and contributors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.sk89q.worldedit.world.storage; - -import com.sk89q.worldedit.world.chunk.Chunk; -import com.sk89q.worldedit.world.DataException; -import org.enginehub.linbus.tree.LinCompoundTag; - -/** - * Strategy for creating a {@link Chunk} from NBT data. - * Used to replace version-based conditionals with polymorphism. - */ -public interface ChunkFromTagLoader { - - /** - * Whether this loader supports the given data version and tag structure. - * - * @param dataVersion the chunk data version - * @param rootTag the root NBT tag - * @return true if this loader can create a Chunk from the tag - */ - boolean supports(int dataVersion, LinCompoundTag rootTag); - - /** - * Create a Chunk from the given root tag. - * - * @param rootTag the root NBT tag (may be the full chunk or contain a Level tag) - * @return the chunk - * @throws DataException if the tag is not valid for this format - */ - Chunk load(LinCompoundTag rootTag) throws DataException; -} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java index 14b114d047..f1864a2390 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/world/storage/ChunkFromTagLoaders.java @@ -37,6 +37,31 @@ */ public final class ChunkFromTagLoaders { + /** + * Strategy for creating a {@link Chunk} from NBT data. + * Used to replace version-based conditionals with polymorphism. + */ + public interface ChunkFromTagLoader { + + /** + * Whether this loader supports the given data version and tag structure. + * + * @param dataVersion the chunk data version + * @param rootTag the root NBT tag + * @return true if this loader can create a Chunk from the tag + */ + boolean supports(int dataVersion, LinCompoundTag rootTag); + + /** + * Create a Chunk from the given root tag. + * + * @param rootTag the root NBT tag (may be the full chunk or contain a Level tag) + * @return the chunk + * @throws DataException if the tag is not valid for this format + */ + Chunk load(LinCompoundTag rootTag) throws DataException; + } + private static final List LOADERS = List.of( new AnvilChunk18Loader(), new AnvilChunk16Loader(),