diff --git a/src/main/java/com/mythlane/gravityflip/wand/WandSelectionStore.java b/src/main/java/com/mythlane/gravityflip/wand/WandSelectionStore.java new file mode 100644 index 0000000..0b99f17 --- /dev/null +++ b/src/main/java/com/mythlane/gravityflip/wand/WandSelectionStore.java @@ -0,0 +1,82 @@ +package com.mythlane.gravityflip.wand; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Thread-safe per-player wand selection store. Tracks the two corner positions + * ({@code pos1} = Primary click, {@code pos2} = Secondary click) of each + * builder's current selection, keyed by player {@link UUID}. + * + *
Pure-data : no Hytale runtime dependency — same philosophy as + * {@code FallDamageGuard}. Testable with JUnit alone, reusable by future + * {@code /gravityflip define} command (Phase 04-02+) without runtime coupling. + * + *
Thread-safety : backed by a {@link ConcurrentHashMap} whose + * {@code compute(...)} mutations are atomic. Safe for concurrent + * {@link #setPos1}/{@link #setPos2} calls on the same UUID (STRIDE T-04-01-04). + * + *
Lifecycle : in-memory only — selections are discarded on plugin
+ * shutdown. Conscious design : a builder will not quit mid-{@code define}.
+ */
+public final class WandSelectionStore {
+
+ /** Immutable holder for a (possibly partial) selection. */
+ public static final class Selection {
+ /** {@code {x,y,z}} of the Primary click, or {@code null} if unset. */
+ public final int[] pos1;
+ /** {@code {x,y,z}} of the Secondary click, or {@code null} if unset. */
+ public final int[] pos2;
+
+ Selection(int[] pos1, int[] pos2) {
+ this.pos1 = pos1;
+ this.pos2 = pos2;
+ }
+
+ /** True iff both corners have been picked. */
+ public boolean isComplete() {
+ return pos1 != null && pos2 != null;
+ }
+ }
+
+ private static final Selection EMPTY = new Selection(null, null);
+
+ private final ConcurrentHashMap