refactor: clean GSD references + shrink comments in wand/

This commit is contained in:
2026-04-24 17:11:54 +02:00
parent 4a9602fee3
commit b8754617d4
3 changed files with 10 additions and 73 deletions
@@ -15,38 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
import java.util.UUID;
/**
* Gravity Flip wand interaction — reads Primary / Secondary clicks and pushes
* the targeted block position into a {@link WandSelectionStore} keyed by the
* clicker's player UUID.
*
* <p><b>Binding :</b> registered at {@code setup()} time via
* {@code getCodecRegistry(Interaction.CODEC).register("GravityFlipWand", …)} —
* same pattern as {@code ExitInstanceInteraction} (cf. 04-00 SPIKE-RESULT,
* Finding 3). The matching {@code "Type": "GravityFlipWand"} reference in
* {@code Items/gravityflip_wand.json} wires the click packet to this class.
*
* <p><b>One class for both click types :</b> {@link #firstRun} receives the
* {@link InteractionType}. Primary → {@code setPos1}, Secondary → {@code setPos2}.
* Centralising both in one class keeps a single registration entry and a
* single CODEC — any other split would duplicate wiring for no gain.
*
* <p><b>Store injection :</b> the Hytale CODEC instantiates interactions via a
* no-arg constructor, so we cannot inject the store through the constructor.
* Instead, {@link #bindStore(WandSelectionStore)} installs a {@code volatile}
* reference at plugin start. If the store is not bound (mis-wired plugin),
* {@link #firstRun} is a safe no-op — fail-silent.
*
* <p><b>Threat surface :</b>
* <ul>
* <li>Chat feedback uses {@link PlayerRef#sendMessage} — directed to the
* clicker only, no broadcast (T-04-01-03).</li>
* <li>{@code TargetBlock} coordinates are stored raw as {@code int[]} — no
* numeric processing in this plan; {@code /gravityflip define} (04-03)
* is responsible for clamping / validating before region creation
* (T-04-01-01).</li>
* </ul>
*/
/** Wand interaction: Primary click sets pos1, Secondary click sets pos2 in the shared selection store. */
public final class GravityFlipWandInteraction extends SimpleInstantInteraction {
@Nonnull
@@ -60,19 +29,14 @@ public final class GravityFlipWandInteraction extends SimpleInstantInteraction {
+ "Secondary click sets pos2, for the clicker's selection."))
.build();
/**
* Store shared by every instance of this interaction — injected at plugin
* start via {@link #bindStore}. Volatile so the writer thread ({@code setup()})
* publishes a safe reference to the reader threads (interaction dispatch).
*/
// Volatile: writer (setup thread) must publish safely to reader threads (interaction dispatch).
private static volatile WandSelectionStore STORE;
/** Wire the selection store before any click can be processed. Call once at {@code setup()}. */
/** Wire the selection store before any click can be processed. */
public static void bindStore(WandSelectionStore store) {
STORE = store;
}
/** Required no-arg constructor used by the CODEC factory. */
public GravityFlipWandInteraction() {
}
@@ -82,7 +46,6 @@ public final class GravityFlipWandInteraction extends SimpleInstantInteraction {
@Nonnull CooldownHandler cooldownHandler) {
WandSelectionStore store = STORE;
if (store == null) {
// Plugin mis-wired (bindStore never called). Silent no-op — don't crash the click.
return;
}
@@ -94,7 +57,6 @@ public final class GravityFlipWandInteraction extends SimpleInstantInteraction {
PlayerRef playerRef = commandBuffer.getComponent(entityRef, PlayerRef.getComponentType());
if (playerRef == null) {
// Clicker is not a player (mob, arrow, …) — ignore.
return;
}
@@ -114,6 +76,5 @@ public final class GravityFlipWandInteraction extends SimpleInstantInteraction {
playerRef.sendMessage(Message.raw(
"[gravityflip] pos2 set: (%d, %d, %d)".formatted(bp.x, bp.y, bp.z)));
}
// Any other InteractionType (Ability1, Pick, Equipped, …) is ignored.
}
}
@@ -3,29 +3,12 @@ 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}.
*
* <p><b>Pure-data :</b> 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.
*
* <p><b>Thread-safety :</b> 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).
*
* <p><b>Lifecycle :</b> in-memory only — selections are discarded on plugin
* shutdown. Conscious design : a builder will not quit mid-{@code define}.
*/
/** Thread-safe per-player wand selection store (pos1/pos2 keyed by player UUID). */
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) {
@@ -43,7 +26,7 @@ public final class WandSelectionStore {
private final ConcurrentHashMap<UUID, Selection> byUuid = new ConcurrentHashMap<>();
/** Record the Primary-click corner for {@code uuid}. Keeps any existing pos2. */
/** Record the Primary-click corner for the given player. */
public void setPos1(UUID uuid, int x, int y, int z) {
if (uuid == null) return;
byUuid.compute(uuid, (k, prev) -> new Selection(
@@ -51,7 +34,7 @@ public final class WandSelectionStore {
prev != null ? prev.pos2 : null));
}
/** Record the Secondary-click corner for {@code uuid}. Keeps any existing pos1. */
/** Record the Secondary-click corner for the given player. */
public void setPos2(UUID uuid, int x, int y, int z) {
if (uuid == null) return;
byUuid.compute(uuid, (k, prev) -> new Selection(
@@ -59,23 +42,20 @@ public final class WandSelectionStore {
new int[]{x, y, z}));
}
/**
* Return the current selection for {@code uuid}. Never returns {@code null} :
* an unknown UUID yields a {@link Selection} with both corners {@code null}.
*/
/** Return the current selection for the given player (never null). */
public Selection get(UUID uuid) {
if (uuid == null) return EMPTY;
Selection s = byUuid.get(uuid);
return s != null ? s : EMPTY;
}
/** Forget the selection for {@code uuid} (e.g. after {@code /gravityflip define}). */
/** Forget the selection for the given player. */
public void clear(UUID uuid) {
if (uuid == null) return;
byUuid.remove(uuid);
}
/** Diagnostic : number of players with an in-flight selection. */
/** Number of players with an in-flight selection. */
public int size() {
return byUuid.size();
}