feat(03-06): add particle-system dump for discovery (Task 1)
- New DumpParticlesCommand utility: enumerates loaded ParticleSystem asset-ids via ParticleSystem.getAssetMap().getAssetMap().keySet(). - Wired in GravityFlipPlugin.start() behind GRAVITYFLIP_DUMP_PARTICLES env var (or -Dgravityflip.dumpParticles sysprop). No-op when unset. - Boot-time fallback approach (plan-allowed) -- proper CommandBase registration deferred to avoid i18n translation-key plumbing for a one-shot discovery dump. - See .planning/phases/03-gravity-physics/03-06-DUMP-NOTES.md for trigger instructions and the user checkpoint before Task 3.
This commit is contained in:
@@ -5,6 +5,7 @@ import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
|||||||
import com.hypixel.hytale.server.core.universe.Universe;
|
import com.hypixel.hytale.server.core.universe.Universe;
|
||||||
import com.hypixel.hytale.server.core.universe.world.World;
|
import com.hypixel.hytale.server.core.universe.world.World;
|
||||||
import com.hypixel.hytale.server.core.util.Config;
|
import com.hypixel.hytale.server.core.util.Config;
|
||||||
|
import com.mythlane.gravityflip.command.DumpParticlesCommand;
|
||||||
import com.mythlane.gravityflip.config.GravityFlipConfig;
|
import com.mythlane.gravityflip.config.GravityFlipConfig;
|
||||||
import com.mythlane.gravityflip.physics.FallDamageGuard;
|
import com.mythlane.gravityflip.physics.FallDamageGuard;
|
||||||
import com.mythlane.gravityflip.physics.FallDamageSuppressorSystem;
|
import com.mythlane.gravityflip.physics.FallDamageSuppressorSystem;
|
||||||
@@ -103,6 +104,22 @@ public class GravityFlipPlugin extends JavaPlugin {
|
|||||||
getLogger().at(Level.INFO).log(
|
getLogger().at(Level.INFO).log(
|
||||||
"Gravity Flip enabled — %d region(s) loaded, detector @100ms, gravity inversion active",
|
"Gravity Flip enabled — %d region(s) loaded, detector @100ms, gravity inversion active",
|
||||||
cfg.getRegions().size());
|
cfg.getRegions().size());
|
||||||
|
|
||||||
|
// Plan 03-06 Task 1 — one-shot ParticleSystem asset-id dump for default
|
||||||
|
// discovery. Enabled via env var GRAVITYFLIP_DUMP_PARTICLES=1 (or sysprop
|
||||||
|
// -Dgravityflip.dumpParticles=true). No-op otherwise. See
|
||||||
|
// DumpParticlesCommand javadoc for the chosen API rationale.
|
||||||
|
if (DumpParticlesCommand.isEnabled()) {
|
||||||
|
try {
|
||||||
|
int n = DumpParticlesCommand.dump(line ->
|
||||||
|
getLogger().at(Level.INFO).log("%s", line));
|
||||||
|
getLogger().at(Level.INFO).log(
|
||||||
|
"ParticleSystem dump complete: %d asset-ids logged", n);
|
||||||
|
} catch (Throwable th) {
|
||||||
|
getLogger().at(Level.WARNING).withCause(th)
|
||||||
|
.log("ParticleSystem dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package com.mythlane.gravityflip.command;
|
||||||
|
|
||||||
|
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
|
||||||
|
import com.hypixel.hytale.server.core.asset.type.particle.config.ParticleSystem;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discovery utility — dumps all loaded {@link ParticleSystem} asset-ids to the
|
||||||
|
* plugin logger. Used during Phase 3 plan 06 to pick a default
|
||||||
|
* {@code VisualParticleId} from the 561 particle systems shipped with the base
|
||||||
|
* Hytale asset pack (only two string-literals are visible in the decompiled
|
||||||
|
* source, so a runtime dump is the cleanest discovery path).
|
||||||
|
*
|
||||||
|
* <p><b>API used:</b> {@code ParticleSystem.getAssetMap().getAssetMap().keySet()}
|
||||||
|
* — static accessor resolved via {@link com.hypixel.hytale.assetstore.AssetRegistry}.
|
||||||
|
* See decompiled {@code ParticleSystem#getAssetStore()} and
|
||||||
|
* {@code DefaultAssetMap#getAssetMap()}; the underlying map uses a
|
||||||
|
* case-insensitive hash strategy.
|
||||||
|
*
|
||||||
|
* <p><b>Trigger:</b> set the environment variable
|
||||||
|
* {@code GRAVITYFLIP_DUMP_PARTICLES=1} (or {@code =true}) before launching the
|
||||||
|
* server. A proper {@code /gf dumpparticles} command can replace this later —
|
||||||
|
* the boot-time fallback is explicitly allowed by 03-06-PLAN.md Task 1.
|
||||||
|
*
|
||||||
|
* <p>This class intentionally does not extend
|
||||||
|
* {@code com.hypixel.hytale.server.core.command.system.basecommands.CommandBase}
|
||||||
|
* because the full command-registration path requires i18n translation keys
|
||||||
|
* ({@code Message.translation(...)}) for description and per-argument help,
|
||||||
|
* which is out-of-scope for a throwaway discovery task. Name "Command" is kept
|
||||||
|
* to satisfy the plan artifact path.
|
||||||
|
*/
|
||||||
|
public final class DumpParticlesCommand {
|
||||||
|
|
||||||
|
/** Env var (preferred, zero-config). */
|
||||||
|
public static final String ENV_VAR = "GRAVITYFLIP_DUMP_PARTICLES";
|
||||||
|
|
||||||
|
/** System property fallback (e.g. {@code -Dgravityflip.dumpParticles=true}). */
|
||||||
|
public static final String SYS_PROP = "gravityflip.dumpParticles";
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "[dumpparticles]";
|
||||||
|
|
||||||
|
private DumpParticlesCommand() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if either {@link #ENV_VAR} or {@link #SYS_PROP} is set to a
|
||||||
|
* truthy value ({@code "1"}, {@code "true"}, {@code "yes"}, case-insensitive).
|
||||||
|
*/
|
||||||
|
public static boolean isEnabled() {
|
||||||
|
return isTruthy(System.getenv(ENV_VAR)) || isTruthy(System.getProperty(SYS_PROP));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTruthy(String v) {
|
||||||
|
if (v == null) return false;
|
||||||
|
String s = v.trim().toLowerCase();
|
||||||
|
return s.equals("1") || s.equals("true") || s.equals("yes") || s.equals("on");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump all loaded ParticleSystem asset-ids via the supplied logger, one id
|
||||||
|
* per line prefixed with {@value #LOG_PREFIX}. Safe to call from
|
||||||
|
* {@code JavaPlugin.start()} (post-LoadAssetEvent). If the asset store has
|
||||||
|
* not yet populated (unlikely at start(), but defensive), logs a warning
|
||||||
|
* and returns zero.
|
||||||
|
*
|
||||||
|
* @return number of ids emitted (>= 0)
|
||||||
|
*/
|
||||||
|
public static int dump(Logger logger) {
|
||||||
|
return dump(line -> logger.info(line));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testable variant — accepts any line consumer (logger.info, println, list::add).
|
||||||
|
*/
|
||||||
|
public static int dump(Consumer<String> lineSink) {
|
||||||
|
DefaultAssetMap<String, ParticleSystem> map;
|
||||||
|
try {
|
||||||
|
map = ParticleSystem.getAssetMap();
|
||||||
|
} catch (Throwable th) {
|
||||||
|
lineSink.accept(LOG_PREFIX + " ERROR: ParticleSystem asset store not available: " + th);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (map == null) {
|
||||||
|
lineSink.accept(LOG_PREFIX + " ERROR: ParticleSystem.getAssetMap() returned null");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultAssetMap#getAssetMap() returns an unmodifiable view of the
|
||||||
|
// underlying Map<K, T>. Snapshot keys to avoid concurrent-modification
|
||||||
|
// (StampedLock is held only during the accessor call).
|
||||||
|
List<String> ids = new ArrayList<>(map.getAssetMap().keySet());
|
||||||
|
Collections.sort(ids);
|
||||||
|
|
||||||
|
lineSink.accept(LOG_PREFIX + " BEGIN — " + ids.size() + " ParticleSystem asset-ids loaded:");
|
||||||
|
for (String id : ids) {
|
||||||
|
lineSink.accept(LOG_PREFIX + " " + id);
|
||||||
|
}
|
||||||
|
lineSink.accept(LOG_PREFIX + " END");
|
||||||
|
return ids.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user