Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13828054e5 | ||
|
|
39c58ef4d9 |
No files matched your search
@@ -717,6 +717,7 @@ public final class FantasyStickPlugin extends JavaPlugin {
|
||||
eventLog::emit, Clock.systemUTC(), sessions, feedbackPreferences,
|
||||
failure -> getLogger().log(Level.WARNING, "Preference event publication proof failed", failure));
|
||||
particlesCommand.attachEvents(preferenceEvents);
|
||||
feedbackCommand.attachEvents(preferenceEvents);
|
||||
eventCapture.excludeOwnedDamage(fireball::ownsAcceptedDamage);
|
||||
eventCapture.excludeOwnedDamage(guardianBond::ownsAcceptedRedirect);
|
||||
eventCapture.excludeOwnedDamage(frostNova::ownsAcceptedDamage);
|
||||
|
||||
@@ -47,15 +47,18 @@ public final class PreferenceEventBridge {
|
||||
var action = new Action(UUID.randomUUID(), clock.instant(), new EventInput.Context(player.getUniqueId(),
|
||||
player.getName(), record == null ? null : record.id(), record == null ? null : record.name(),
|
||||
record == null ? null : active.session(), location), key, before,
|
||||
preferences.savedAllParticlesEnabled(player.getUniqueId()), requested, "player");
|
||||
key.equals("player-aura-particles") ? preferences.savedPlayerAurasEnabled(player.getUniqueId())
|
||||
: key.equals("combat_feedback") ? preferences.savedEnabled(player.getUniqueId())
|
||||
: preferences.savedAllParticlesEnabled(player.getUniqueId()), requested, "player");
|
||||
emit("action.observed", action, "observed", null, null, before, false);
|
||||
return action;
|
||||
}
|
||||
|
||||
public void refuseNonPlayer(org.bukkit.command.CommandSender sender) {
|
||||
public void refuseNonPlayer(org.bukkit.command.CommandSender sender, String key) {
|
||||
var kind = sender instanceof org.bukkit.command.ConsoleCommandSender ? "console"
|
||||
: sender instanceof org.bukkit.command.BlockCommandSender ? "command_block" : "system";
|
||||
var action = new Action(UUID.randomUUID(), clock.instant(), null, "all-particles", true, true, null, kind);
|
||||
boolean initial = !key.equals("player-aura-particles");
|
||||
var action = new Action(UUID.randomUUID(), clock.instant(), null, key, initial, initial, null, kind);
|
||||
emit("action.observed", action, "observed", null, null, true, false);
|
||||
emit("action.resolved", action, "refused", "player_required", null, true, false);
|
||||
}
|
||||
@@ -70,6 +73,7 @@ public final class PreferenceEventBridge {
|
||||
fields.put("before", Boolean.toString(action.before()));
|
||||
fields.put("saved_before", Boolean.toString(action.savedBefore()));
|
||||
fields.put("requested", Boolean.toString(action.requested()));
|
||||
fields.put("all_enabled", Boolean.toString(preferences.allParticlesEnabled(action.context().playerId())));
|
||||
if (action.context().characterId() != null) {
|
||||
fields.put("character", action.context().characterId().toString());
|
||||
fields.put("character_name", action.context().characterName());
|
||||
@@ -92,7 +96,8 @@ public final class PreferenceEventBridge {
|
||||
var row = intent.captured();
|
||||
UUID account = UUID.fromString(row.get("account"));
|
||||
if (!"true".equals(row.get("published"))
|
||||
|| Boolean.parseBoolean(row.get("before")) == Boolean.parseBoolean(row.get("requested"))) return;
|
||||
|| Boolean.parseBoolean(row.getOrDefault("saved_before", row.get("before")))
|
||||
== Boolean.parseBoolean(row.get("requested"))) return;
|
||||
UUID character = row.containsKey("character") ? UUID.fromString(row.get("character")) : null;
|
||||
EventInput.Location location = row.containsKey("world")
|
||||
? new EventInput.Location(UUID.fromString(row.get("world")), row.get("world_name"),
|
||||
@@ -104,8 +109,10 @@ public final class PreferenceEventBridge {
|
||||
Boolean.parseBoolean(row.get("before")), Boolean.parseBoolean(
|
||||
row.getOrDefault("saved_before", row.get("before"))),
|
||||
Boolean.parseBoolean(row.get("requested")), "player");
|
||||
emit("preference.changed", action, "accepted", "acknowledged", intent.sequence(),
|
||||
Boolean.parseBoolean(row.get("requested")), true);
|
||||
boolean after = Boolean.parseBoolean(row.get("requested"));
|
||||
boolean effective = row.get("key").equals("player-aura-particles")
|
||||
? after && Boolean.parseBoolean(row.get("all_enabled")) : after;
|
||||
emit("preference.changed", action, "accepted", "acknowledged", intent.sequence(), effective, true);
|
||||
} catch (RuntimeException ignored) { /* Invalid pending evidence cannot authorize a preference change. */ }
|
||||
}
|
||||
|
||||
@@ -120,7 +127,7 @@ public final class PreferenceEventBridge {
|
||||
emit("action.resolved", action, "refused", "superseded", update.sequence(), effective, false);
|
||||
return;
|
||||
}
|
||||
if (action.before() == effective || update.intent() == null) {
|
||||
if (update.intent() == null || action.savedBefore() == action.requested()) {
|
||||
if (update.intent() != null) preferences.acknowledgeEvent(action.id());
|
||||
emit("action.resolved", action, "refused", "unchanged", update.sequence(), effective, false);
|
||||
return;
|
||||
@@ -148,25 +155,33 @@ public final class PreferenceEventBridge {
|
||||
if (action == null) return;
|
||||
var data = new LinkedHashMap<String, Object>();
|
||||
data.put("action_kind", "preference_" + (action.requested() == null ? "read" : "change"));
|
||||
data.put("entry_point", "/fstick particles all");
|
||||
data.put("entry_point", action.key().equals("combat_feedback") ? "/fstick feedback"
|
||||
: "/fstick particles " + (action.key().equals("player-aura-particles") ? "players" : "all"));
|
||||
data.put("preference_key", action.key());
|
||||
if (action.context() == null) { data.put("actor_kind", action.actorKind()); data.put("location_status", "not_applicable"); }
|
||||
data.put("scope", "account");
|
||||
data.put("default_value", "true");
|
||||
data.put("default_value", Boolean.toString(!action.key().equals("player-aura-particles")));
|
||||
data.put("before", Boolean.toString(action.before()));
|
||||
data.put("saved_before", Boolean.toString(action.savedBefore()));
|
||||
data.put("effective_before", Boolean.toString(action.before()));
|
||||
if (action.requested() != null) data.put("requested", Boolean.toString(action.requested()));
|
||||
if (action.context() != null) {
|
||||
boolean latestSaved = type.equals("preference.changed") ? effective
|
||||
boolean latestSaved = type.equals("preference.changed") ? action.requested()
|
||||
: preferenceSequence == null ? action.savedBefore()
|
||||
: preferences.savedAllParticlesEnabled(action.context().playerId());
|
||||
: action.key().equals("player-aura-particles")
|
||||
? preferences.savedPlayerAurasEnabled(action.context().playerId())
|
||||
: action.key().equals("combat_feedback")
|
||||
? preferences.savedEnabled(action.context().playerId())
|
||||
: preferences.savedAllParticlesEnabled(action.context().playerId());
|
||||
data.put("saved_value", Boolean.toString(latestSaved));
|
||||
data.put("effective_value", Boolean.toString(effective));
|
||||
data.put("effective_after", Boolean.toString(effective));
|
||||
}
|
||||
if (preferenceSequence != null) data.put("preference_sequence", Long.toString(preferenceSequence));
|
||||
data.put("outcome", outcome);
|
||||
if (reason != null) data.put(type.startsWith("preference.") ? "acknowledgment" : "reason", reason);
|
||||
if (type.equals("preference.changed")) {
|
||||
data.put("after", Boolean.toString(effective));
|
||||
data.put("after", Boolean.toString(action.requested()));
|
||||
data.put("preference_revision", Long.toString(preferenceSequence));
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -100,8 +100,18 @@ public final class PlayerFeedbackPreferences implements AutoCloseable {
|
||||
|
||||
/** Existing routine combat/effect feedback preference. */
|
||||
public synchronized boolean enabled(UUID account) { return value(account, Preference.COMBAT_FEEDBACK); }
|
||||
public synchronized boolean savedEnabled(UUID account) {
|
||||
Objects.requireNonNull(account);
|
||||
return saved.getOrDefault(new AccountPreference(account, Preference.COMBAT_FEEDBACK),
|
||||
Preference.COMBAT_FEEDBACK.defaultValue);
|
||||
}
|
||||
public synchronized boolean playerAurasEnabled(UUID account) { return value(account, Preference.PLAYER_AURAS); }
|
||||
public synchronized boolean allParticlesEnabled(UUID account) { return value(account, Preference.ALL_PARTICLES); }
|
||||
public synchronized boolean savedPlayerAurasEnabled(UUID account) {
|
||||
Objects.requireNonNull(account);
|
||||
return saved.getOrDefault(new AccountPreference(account, Preference.PLAYER_AURAS),
|
||||
Preference.PLAYER_AURAS.defaultValue);
|
||||
}
|
||||
public synchronized boolean savedAllParticlesEnabled(UUID account) {
|
||||
Objects.requireNonNull(account);
|
||||
return saved.getOrDefault(new AccountPreference(account, Preference.ALL_PARTICLES),
|
||||
@@ -120,10 +130,18 @@ public final class PlayerFeedbackPreferences implements AutoCloseable {
|
||||
return set(account, Preference.COMBAT_FEEDBACK, enabled);
|
||||
}
|
||||
|
||||
public CompletionStage<Update> set(UUID account, boolean enabled, EventIntent intent) {
|
||||
return set(account, Preference.COMBAT_FEEDBACK, enabled, intent);
|
||||
}
|
||||
|
||||
public CompletionStage<Update> setPlayerAuras(UUID account, boolean enabled) {
|
||||
return set(account, Preference.PLAYER_AURAS, enabled);
|
||||
}
|
||||
|
||||
public CompletionStage<Update> setPlayerAuras(UUID account, boolean enabled, EventIntent intent) {
|
||||
return set(account, Preference.PLAYER_AURAS, enabled, intent);
|
||||
}
|
||||
|
||||
/** Shared persisted precedence for the separately delivered all-particles command. */
|
||||
public CompletionStage<Update> setAllParticles(UUID account, boolean enabled) {
|
||||
return set(account, Preference.ALL_PARTICLES, enabled);
|
||||
|
||||
@@ -15,8 +15,13 @@ public final class FantasyFeedbackCommand implements TabExecutor, AutoCloseable
|
||||
private final PlayerFeedbackPreferences preferences;
|
||||
private final Consumer<Runnable> nextTick;
|
||||
private final Consumer<Throwable> failures;
|
||||
private games.dmg.fantasystick.events.PreferenceEventBridge events;
|
||||
private boolean closed;
|
||||
|
||||
public void attachEvents(games.dmg.fantasystick.events.PreferenceEventBridge bridge) {
|
||||
this.events = Objects.requireNonNull(bridge);
|
||||
}
|
||||
|
||||
public FantasyFeedbackCommand(PlayerFeedbackPreferences preferences, Consumer<Runnable> nextTick,
|
||||
Consumer<Throwable> failures) {
|
||||
this.preferences = Objects.requireNonNull(preferences);
|
||||
@@ -26,32 +31,51 @@ public final class FantasyFeedbackCommand implements TabExecutor, AutoCloseable
|
||||
|
||||
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
if (events != null) try { events.refuseNonPlayer(sender, "combat_feedback"); }
|
||||
catch (RuntimeException ignored) { }
|
||||
sender.sendMessage("Fantasy feedback is a player-account preference.");
|
||||
return true;
|
||||
}
|
||||
if (args.length == 0) {
|
||||
var action = observe(player, null);
|
||||
player.sendMessage("Fantasy feedback is " + state(preferences.enabled(player.getUniqueId())) + ".");
|
||||
if (action != null) events.read(action);
|
||||
return true;
|
||||
}
|
||||
if (args.length != 1 || !(args[0].equals("on") || args[0].equals("off"))) return false;
|
||||
if (args.length != 1 || !(args[0].equals("on") || args[0].equals("off"))) {
|
||||
var action = observe(player, null);
|
||||
if (action != null) events.refused(action, "invalid_arguments");
|
||||
return false;
|
||||
}
|
||||
boolean enabled = args[0].equals("on");
|
||||
var account = player.getUniqueId();
|
||||
preferences.set(account, enabled).whenComplete((saved, failure) -> {
|
||||
var action = observe(player, enabled);
|
||||
var save = action == null ? preferences.set(account, enabled)
|
||||
: preferences.set(account, enabled, events.intent(action));
|
||||
save.whenComplete((saved, failure) -> {
|
||||
if (failure != null) report(failure);
|
||||
try {
|
||||
nextTick.accept(() -> {
|
||||
if (closed) return;
|
||||
if (closed) {
|
||||
if (action != null) events.failed(action, "closed");
|
||||
return;
|
||||
}
|
||||
if (failure != null) {
|
||||
if (action != null) events.failed(action, "save_failed");
|
||||
if (player.isOnline()) player.sendMessage(
|
||||
"Fantasy feedback preference could not be saved; it remains "
|
||||
+ state(preferences.enabled(account)) + ".");
|
||||
return;
|
||||
}
|
||||
boolean published = preferences.publish(saved);
|
||||
if (action != null) events.saved(action, saved, published, preferences.enabled(account));
|
||||
if (player.isOnline()) player.sendMessage("Fantasy feedback is "
|
||||
+ (published ? "now " : "already ") + state(preferences.enabled(account)) + ".");
|
||||
});
|
||||
} catch (RuntimeException rejected) { report(rejected); }
|
||||
} catch (RuntimeException rejected) {
|
||||
if (action != null) events.failed(action, "schedule_failed");
|
||||
report(rejected);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
@@ -62,6 +86,13 @@ public final class FantasyFeedbackCommand implements TabExecutor, AutoCloseable
|
||||
return List.of("off", "on").stream().filter(value -> value.startsWith(prefix)).toList();
|
||||
}
|
||||
|
||||
private games.dmg.fantasystick.events.PreferenceEventBridge.Action observe(Player player, Boolean requested) {
|
||||
var bridge = events;
|
||||
if (bridge == null) return null;
|
||||
try { return bridge.begin(player, "combat_feedback", preferences.enabled(player.getUniqueId()), requested); }
|
||||
catch (RuntimeException ignored) { return null; } // Auditing cannot refuse a player preference.
|
||||
}
|
||||
|
||||
private static String state(boolean enabled) { return enabled ? "on" : "off"; }
|
||||
private void report(Throwable failure) { try { failures.accept(failure); } catch (RuntimeException ignored) { } }
|
||||
@Override public void close() { closed = true; }
|
||||
|
||||
@@ -32,22 +32,22 @@ public final class FantasyParticlesCommand implements TabExecutor, AutoCloseable
|
||||
|
||||
@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (!(sender instanceof Player player)) {
|
||||
if (args.length > 0 && args[0].equals("all") && events != null) {
|
||||
try { events.refuseNonPlayer(sender); } catch (RuntimeException ignored) { }
|
||||
if (args.length > 0 && (args[0].equals("all") || args[0].equals("players")) && events != null) {
|
||||
try { events.refuseNonPlayer(sender, key(args[0])); } catch (RuntimeException ignored) { }
|
||||
}
|
||||
sender.sendMessage("Fantasy particles are a player-account preference.");
|
||||
return true;
|
||||
}
|
||||
if (args.length == 1 && (args[0].equals("players") || args[0].equals("all"))) {
|
||||
var action = args[0].equals("all") ? observe(player, null) : null;
|
||||
var action = observe(player, args[0], null);
|
||||
player.sendMessage(status(player.getUniqueId(), args[0]));
|
||||
if (action != null) events.read(action);
|
||||
return true;
|
||||
}
|
||||
if (args.length != 2 || !(args[0].equals("players") || args[0].equals("all"))
|
||||
|| !(args[1].equals("on") || args[1].equals("off"))) {
|
||||
if (args.length > 0 && args[0].equals("all")) {
|
||||
var action = observe(player, null);
|
||||
if (args.length > 0 && (args[0].equals("all") || args[0].equals("players"))) {
|
||||
var action = observe(player, args[0], null);
|
||||
if (action != null) events.refused(action, "invalid_arguments");
|
||||
}
|
||||
return false;
|
||||
@@ -55,10 +55,11 @@ public final class FantasyParticlesCommand implements TabExecutor, AutoCloseable
|
||||
String route = args[0];
|
||||
boolean enabled = args[1].equals("on");
|
||||
UUID account = player.getUniqueId();
|
||||
var action = route.equals("all") ? observe(player, enabled) : null;
|
||||
var action = observe(player, route, enabled);
|
||||
var save = route.equals("players")
|
||||
? preferences.setPlayerAuras(account, enabled) : action == null
|
||||
? preferences.setAllParticles(account, enabled)
|
||||
? action == null ? preferences.setPlayerAuras(account, enabled)
|
||||
: preferences.setPlayerAuras(account, enabled, events.intent(action))
|
||||
: action == null ? preferences.setAllParticles(account, enabled)
|
||||
: preferences.setAllParticles(account, enabled, events.intent(action));
|
||||
save.whenComplete((saved, failure) -> {
|
||||
if (failure != null) report(failure);
|
||||
@@ -76,7 +77,9 @@ public final class FantasyParticlesCommand implements TabExecutor, AutoCloseable
|
||||
return;
|
||||
}
|
||||
boolean published = preferences.publish(saved);
|
||||
if (action != null) events.saved(action, saved, published, preferences.allParticlesEnabled(account));
|
||||
if (action != null) events.saved(action, saved, published,
|
||||
route.equals("players") ? preferences.auraParticlesEnabled(account)
|
||||
: preferences.allParticlesEnabled(account));
|
||||
if (player.isOnline()) player.sendMessage(acknowledgement(account, route, published));
|
||||
});
|
||||
} catch (RuntimeException rejected) {
|
||||
@@ -95,13 +98,18 @@ public final class FantasyParticlesCommand implements TabExecutor, AutoCloseable
|
||||
return List.of();
|
||||
}
|
||||
|
||||
private games.dmg.fantasystick.events.PreferenceEventBridge.Action observe(Player player, Boolean requested) {
|
||||
private games.dmg.fantasystick.events.PreferenceEventBridge.Action observe(
|
||||
Player player, String route, Boolean requested) {
|
||||
var bridge = events;
|
||||
if (bridge == null) return null;
|
||||
try { return bridge.begin(player, "all-particles", preferences.allParticlesEnabled(player.getUniqueId()), requested); }
|
||||
try { return bridge.begin(player, key(route), route.equals("players")
|
||||
? preferences.auraParticlesEnabled(player.getUniqueId())
|
||||
: preferences.allParticlesEnabled(player.getUniqueId()), requested); }
|
||||
catch (RuntimeException ignored) { return null; } // Capture failure cannot refuse gameplay.
|
||||
}
|
||||
|
||||
private static String key(String route) { return route.equals("players") ? "player-aura-particles" : "all-particles"; }
|
||||
|
||||
private String status(UUID account, String route) {
|
||||
if (route.equals("all"))
|
||||
return "All Fantasy Stick particles are " + state(preferences.allParticlesEnabled(account)) + ".";
|
||||
|
||||
@@ -38,6 +38,11 @@ class FantasyFeedbackCommandNativeTest {
|
||||
assertEquals("Fantasy feedback is off.", player.messages.getLast());
|
||||
assertTrue(player.messageComponents.isEmpty(), "preference/status notices keep their existing presentation");
|
||||
}
|
||||
var records = feedbackRecords();
|
||||
assertEquals(2, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.read")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).count());
|
||||
assertEquals(1, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).count());
|
||||
}
|
||||
|
||||
@Test void consoleMalformedAndExtraArgumentsCannotChangeAnAccountPreference() throws Exception {
|
||||
@@ -60,6 +65,188 @@ class FantasyFeedbackCommandNativeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test void registeredFeedbackReadAndChangeReachAccountOwnedJsonl() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick", new String[]{"feedback"}));
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick", new String[]{"feedback", "off"}));
|
||||
fixture.tick();
|
||||
assertEquals("Fantasy feedback is now off.", actor.messages.getLast());
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = feedbackRecords();
|
||||
var read = records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.read"))
|
||||
.findFirst().orElseThrow().getAsJsonObject("data");
|
||||
assertEquals("combat_feedback", read.get("preference_key").getAsString());
|
||||
assertEquals(actor.core.id.toString(), read.get("player_id").getAsString());
|
||||
assertEquals("/fstick feedback", read.get("entry_point").getAsString());
|
||||
assertEquals("true", read.get("default_value").getAsString());
|
||||
assertEquals("true", read.get("effective_value").getAsString());
|
||||
var change = records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed"))
|
||||
.findFirst().orElseThrow().getAsJsonObject("data");
|
||||
assertEquals(actor.core.id.toString(), change.get("player_id").getAsString());
|
||||
assertEquals("true", change.get("before").getAsString());
|
||||
assertEquals("false", change.get("after").getAsString());
|
||||
assertTrue(change.get("preference_sequence").getAsLong() > 0);
|
||||
assertEquals(actor.world.getUID().toString(), change.getAsJsonObject("location").get("world_id").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test void feedbackRefusalsFailedAndSupersededWritesCannotClaimSuccess() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick", new String[]{"feedback", "off"}));
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick", new String[]{"feedback", "on"}));
|
||||
var preferences = (games.dmg.fantasystick.persistence.PlayerFeedbackPreferences)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackPreferences");
|
||||
preferences.set(java.util.UUID.randomUUID(), true).toCompletableFuture()
|
||||
.get(5, java.util.concurrent.TimeUnit.SECONDS);
|
||||
var callbacks = new java.util.ArrayList<Runnable>();
|
||||
fixture.tasks.drainTo(callbacks);
|
||||
assertEquals(2, callbacks.size());
|
||||
java.util.Collections.reverse(callbacks); callbacks.forEach(Runnable::run);
|
||||
assertFalse(root.onCommand(actor.player, fixture.playerCommand, "fstick", new String[]{"feedback", "OFF"}));
|
||||
assertTrue(root.onCommand(org.mockito.Mockito.mock(org.bukkit.command.ConsoleCommandSender.class),
|
||||
fixture.playerCommand, "fstick", new String[]{"feedback", "off"}));
|
||||
assertTrue(root.onCommand(org.mockito.Mockito.mock(org.bukkit.command.BlockCommandSender.class),
|
||||
fixture.playerCommand, "fstick", new String[]{"feedback", "off"}));
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = feedbackRecords().stream().filter(e -> e.getAsJsonObject("data").has("preference_key")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).toList();
|
||||
assertEquals(0, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
assertEquals(java.util.Set.of("superseded", "unchanged", "invalid_arguments", "player_required"),
|
||||
records.stream().filter(e -> e.get("type").getAsString().endsWith("action.resolved"))
|
||||
.map(e -> e.getAsJsonObject("data").get("reason").getAsString())
|
||||
.collect(java.util.stream.Collectors.toSet()));
|
||||
fixture.plugin.setEnabled(true);
|
||||
fixture.plugin.setEnabled(false);
|
||||
assertEquals(0, feedbackRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
}
|
||||
}
|
||||
|
||||
@Test void failedFeedbackSaveIsReportedWithoutChangingEffectiveAccountState() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
java.nio.file.Files.createDirectory(data.resolve("player-preferences.yml"));
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(actor.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"feedback", "off"}));
|
||||
fixture.tick();
|
||||
assertTrue(actor.messages.getLast().contains("could not be saved"));
|
||||
fixture.plugin.setEnabled(false);
|
||||
assertEquals(0, feedbackRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
assertTrue(feedbackRecords().stream().anyMatch(e -> e.get("type").getAsString().endsWith("action.resolved")
|
||||
&& e.getAsJsonObject("data").has("reason")
|
||||
&& e.getAsJsonObject("data").get("reason").getAsString().equals("save_failed")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test void closedFeedbackCommandCannotPublishUnacknowledgedPreferenceSuccess() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(actor.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"feedback", "off"}));
|
||||
var preferences = (games.dmg.fantasystick.persistence.PlayerFeedbackPreferences)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackPreferences");
|
||||
preferences.set(java.util.UUID.randomUUID(), true).toCompletableFuture()
|
||||
.get(5, java.util.concurrent.TimeUnit.SECONDS);
|
||||
((games.dmg.fantasystick.session.FantasyFeedbackCommand)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackCommand")).close();
|
||||
fixture.tick();
|
||||
assertTrue(preferences.enabled(actor.core.id), "Closed callback leaves the current effective value unchanged");
|
||||
fixture.plugin.setEnabled(false);
|
||||
fixture.plugin.setEnabled(true);
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = feedbackRecords();
|
||||
assertEquals(0, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
assertTrue(records.stream().anyMatch(e -> e.get("type").getAsString().endsWith("action.resolved")
|
||||
&& e.getAsJsonObject("data").has("reason")
|
||||
&& e.getAsJsonObject("data").get("reason").getAsString().equals("closed")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test void feedbackOutboxRecoversOriginalAcceptedChangeAfterSinkOutage() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
((games.dmg.fantasystick.events.LocalEventLog)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "eventLog")).close();
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(actor.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"feedback", "off"}));
|
||||
fixture.tick();
|
||||
var preferences = (games.dmg.fantasystick.persistence.PlayerFeedbackPreferences)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackPreferences");
|
||||
for (int i = 0; i < 80 && preferences.pendingEvents().stream()
|
||||
.noneMatch(p -> "true".equals(p.captured().get("published"))); i++) Thread.sleep(10);
|
||||
assertTrue(preferences.pendingEvents().stream()
|
||||
.anyMatch(p -> "true".equals(p.captured().get("published"))));
|
||||
fixture.plugin.setEnabled(false);
|
||||
fixture.plugin.setEnabled(true);
|
||||
for (int i = 0; i < 80 && feedbackRecords().stream()
|
||||
.noneMatch(e -> e.get("type").getAsString().endsWith("preference.changed")); i++) Thread.sleep(10);
|
||||
fixture.plugin.setEnabled(false);
|
||||
var changes = feedbackRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).toList();
|
||||
assertEquals(1, changes.stream().map(e -> e.get("id").getAsString()).distinct().count());
|
||||
assertEquals("false", changes.getFirst().getAsJsonObject("data").get("after").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test void feedbackPreferenceIsAccountOwnedAcrossCharacterTransfer() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var owner = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
var recipient = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(owner.core.id, owner.player); fixture.players.put(recipient.core.id, recipient.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(owner.player, fixture.playerCommand, "fstick", new String[]{"feedback", "off"}));
|
||||
fixture.tick();
|
||||
fixture.activate(owner);
|
||||
var sheet = owner.core.inventory.getItemInOffHand().clone();
|
||||
var sessions = (games.dmg.fantasystick.session.EquipmentSessions)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "sessions");
|
||||
sessions.exit(owner.player);
|
||||
fixture.awaitCharacterAndReceipt(owner);
|
||||
recipient.core.inventory.setItem(8, recipient.core.inventory.getItemInOffHand());
|
||||
recipient.core.inventory.setItemInOffHand(sheet);
|
||||
fixture.activate(recipient);
|
||||
assertTrue(root.onCommand(owner.player, fixture.playerCommand, "fstick", new String[]{"feedback"}));
|
||||
assertEquals("Fantasy feedback is off.", owner.messages.getLast());
|
||||
assertTrue(root.onCommand(recipient.player, fixture.playerCommand, "fstick", new String[]{"feedback"}));
|
||||
assertEquals("Fantasy feedback is on.", recipient.messages.getLast());
|
||||
fixture.plugin.setEnabled(false);
|
||||
var reads = feedbackRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.read"))
|
||||
.map(e -> e.getAsJsonObject("data"))
|
||||
.filter(d -> d.get("preference_key").getAsString().equals("combat_feedback")).toList();
|
||||
assertTrue(reads.stream().anyMatch(d -> d.get("player_id").getAsString().equals(owner.core.id.toString())
|
||||
&& d.get("effective_value").getAsString().equals("false") && !d.has("character_id")));
|
||||
assertTrue(reads.stream().anyMatch(d -> d.get("player_id").getAsString().equals(recipient.core.id.toString())
|
||||
&& d.get("effective_value").getAsString().equals("true")
|
||||
&& d.get("character_id").getAsString().equals(owner.core.record.id().toString())));
|
||||
assertEquals(1, feedbackRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).count());
|
||||
}
|
||||
}
|
||||
|
||||
private List<com.google.gson.JsonObject> feedbackRecords() throws Exception {
|
||||
var records = new java.util.ArrayList<com.google.gson.JsonObject>();
|
||||
try (var files = java.nio.file.Files.walk(data.resolve("events"))) {
|
||||
for (var path : files.filter(p -> p.getFileName().toString().endsWith(".jsonl")).toList())
|
||||
for (var line : java.nio.file.Files.readAllLines(path))
|
||||
if (!line.isBlank()) records.add(com.google.gson.JsonParser.parseString(line).getAsJsonObject());
|
||||
}
|
||||
return records;
|
||||
}
|
||||
|
||||
@Test void sourceAndAffectedAccountPreferencesIndependentlyFilterPerTickMessages() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
|
||||
@@ -593,6 +593,187 @@ class FantasyParticlesNativeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test void registeredAuraPreferenceRecordsDefaultOffAndSavedOptInUnderAllOff() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players"}));
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "all", "off"}));
|
||||
fixture.tick();
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players", "on"}));
|
||||
fixture.tick();
|
||||
assertTrue(actor.messages.getLast().contains("effectively off"));
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = preferenceRecords();
|
||||
var aura = records.stream().filter(e -> e.getAsJsonObject("data").has("preference_key")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("player-aura-particles"))
|
||||
.toList();
|
||||
var read = aura.stream().filter(e -> e.get("type").getAsString().endsWith("preference.read"))
|
||||
.findFirst().orElseThrow().getAsJsonObject("data");
|
||||
assertEquals("false", read.get("default_value").getAsString());
|
||||
assertEquals("false", read.get("saved_value").getAsString());
|
||||
assertEquals("false", read.get("effective_value").getAsString());
|
||||
assertEquals("/fstick particles players", read.get("entry_point").getAsString());
|
||||
var change = aura.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed"))
|
||||
.findFirst().orElseThrow().getAsJsonObject("data");
|
||||
assertEquals(actor.core.id.toString(), change.get("player_id").getAsString());
|
||||
assertEquals("false", change.get("saved_before").getAsString());
|
||||
assertEquals("true", change.get("saved_value").getAsString());
|
||||
assertEquals("false", change.get("effective_value").getAsString());
|
||||
assertEquals("true", change.get("after").getAsString());
|
||||
assertTrue(change.get("preference_sequence").getAsLong() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test void auraRefusalsAndSupersededWritesNeverClaimPreferenceSuccess() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players", "on"}));
|
||||
assertTrue(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players", "off"}));
|
||||
var preferences = (games.dmg.fantasystick.persistence.PlayerFeedbackPreferences)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackPreferences");
|
||||
preferences.setPlayerAuras(java.util.UUID.randomUUID(), true).toCompletableFuture()
|
||||
.get(5, java.util.concurrent.TimeUnit.SECONDS);
|
||||
var callbacks = new java.util.ArrayList<Runnable>();
|
||||
fixture.tasks.drainTo(callbacks);
|
||||
assertEquals(2, callbacks.size());
|
||||
java.util.Collections.reverse(callbacks); callbacks.forEach(Runnable::run);
|
||||
assertFalse(root.onCommand(actor.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players", "invalid"}));
|
||||
assertTrue(root.onCommand(org.mockito.Mockito.mock(org.bukkit.command.ConsoleCommandSender.class),
|
||||
fixture.playerCommand, "fstick", new String[]{"particles", "players", "on"}));
|
||||
assertTrue(root.onCommand(org.mockito.Mockito.mock(org.bukkit.command.BlockCommandSender.class),
|
||||
fixture.playerCommand, "fstick", new String[]{"particles", "players", "on"}));
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = preferenceRecords().stream().filter(e -> e.getAsJsonObject("data").has("preference_key")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("player-aura-particles")).toList();
|
||||
assertEquals(0, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
var reasons = records.stream().filter(e -> e.get("type").getAsString().endsWith("action.resolved"))
|
||||
.map(e -> e.getAsJsonObject("data").get("reason").getAsString()).toList();
|
||||
assertEquals(5, reasons.size());
|
||||
assertEquals(java.util.Set.of("superseded", "unchanged", "invalid_arguments", "player_required"),
|
||||
java.util.Set.copyOf(reasons));
|
||||
fixture.plugin.setEnabled(true);
|
||||
fixture.plugin.setEnabled(false);
|
||||
assertEquals(0, preferenceRecords().stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
}
|
||||
}
|
||||
|
||||
@Test void auraSaveFailureLeavesDefaultOffAndReportsFailure() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
java.nio.file.Files.createDirectory(data.resolve("player-preferences.yml"));
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(actor.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"particles", "players", "on"}));
|
||||
fixture.tick();
|
||||
assertTrue(actor.messages.getLast().contains("could not be saved"));
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = preferenceRecords().stream().filter(e -> e.getAsJsonObject("data").has("preference_key")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("player-aura-particles")).toList();
|
||||
assertEquals(0, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
assertEquals("save_failed", records.stream().filter(e -> e.get("type").getAsString().endsWith("action.resolved"))
|
||||
.findFirst().orElseThrow().getAsJsonObject("data").get("reason").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test void auraOptInSurvivesRestartAndCharacterTransferWithoutLeakingAccounts() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var owner = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
var receiver = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(owner.core.id, owner.player);
|
||||
fixture.players.put(receiver.core.id, receiver.player);
|
||||
var root = fixture.playerCommand.getExecutor();
|
||||
assertTrue(root.onCommand(owner.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players", "on"}));
|
||||
fixture.tick();
|
||||
fixture.activate(owner);
|
||||
var sheet = owner.core.inventory.getItemInOffHand().clone();
|
||||
var sessions = (games.dmg.fantasystick.session.EquipmentSessions)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "sessions");
|
||||
sessions.exit(owner.player);
|
||||
fixture.awaitCharacterAndReceipt(owner);
|
||||
receiver.core.inventory.setItem(8, receiver.core.inventory.getItemInOffHand());
|
||||
receiver.core.inventory.setItemInOffHand(sheet);
|
||||
fixture.activate(receiver);
|
||||
assertTrue(root.onCommand(receiver.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players"}));
|
||||
assertEquals("Active-character aura particles are off.", receiver.messages.getLast());
|
||||
assertTrue(root.onCommand(owner.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players"}));
|
||||
assertEquals("Active-character aura particles are on.", owner.messages.getLast());
|
||||
fixture.plugin.setEnabled(false);
|
||||
fixture.plugin.setEnabled(true);
|
||||
assertTrue(root.onCommand(receiver.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players"}));
|
||||
assertEquals("Active-character aura particles are off.", receiver.messages.getLast());
|
||||
assertTrue(root.onCommand(owner.player, fixture.playerCommand, "fstick",
|
||||
new String[]{"particles", "players"}));
|
||||
assertEquals("Active-character aura particles are on.", owner.messages.getLast());
|
||||
assertEquals(List.of("off"), fixture.playerCommand.getTabCompleter().onTabComplete(owner.player,
|
||||
fixture.playerCommand, "fstick", new String[]{"particles", "players", "of"}));
|
||||
fixture.plugin.setEnabled(false);
|
||||
var records = preferenceRecords().stream()
|
||||
.filter(e -> e.getAsJsonObject("data").has("preference_key")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("player-aura-particles"))
|
||||
.toList();
|
||||
assertEquals(1, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")).count());
|
||||
var receiverRead = records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.read"))
|
||||
.map(e -> e.getAsJsonObject("data"))
|
||||
.filter(d -> d.get("player_id").getAsString().equals(receiver.core.id.toString()))
|
||||
.findFirst().orElseThrow();
|
||||
assertEquals("false", receiverRead.get("saved_value").getAsString());
|
||||
assertEquals(owner.core.record.id().toString(), receiverRead.get("character_id").getAsString());
|
||||
assertTrue(records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.read"))
|
||||
.map(e -> e.getAsJsonObject("data"))
|
||||
.anyMatch(d -> d.get("player_id").getAsString().equals(owner.core.id.toString())
|
||||
&& d.get("saved_value").getAsString().equals("true")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test void acceptedAuraOptInRecoversSameEventAfterSinkOutage() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
fixture.players.put(actor.core.id, actor.player);
|
||||
((games.dmg.fantasystick.events.LocalEventLog)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "eventLog")).close();
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(actor.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"particles", "players", "on"}));
|
||||
fixture.tick();
|
||||
var preferences = (games.dmg.fantasystick.persistence.PlayerFeedbackPreferences)
|
||||
FantasyStickLifecycleNativeTest.Fixture.field(fixture.plugin, "feedbackPreferences");
|
||||
for (int i = 0; i < 80 && preferences.pendingEvents().stream()
|
||||
.noneMatch(p -> "true".equals(p.captured().get("published"))); i++) Thread.sleep(10);
|
||||
assertTrue(preferences.pendingEvents().stream()
|
||||
.anyMatch(p -> "true".equals(p.captured().get("published"))));
|
||||
fixture.plugin.setEnabled(false);
|
||||
fixture.plugin.setEnabled(true);
|
||||
for (int i = 0; i < 80 && preferenceRecords().stream()
|
||||
.noneMatch(e -> e.get("type").getAsString().endsWith("preference.changed")); i++) Thread.sleep(10);
|
||||
fixture.plugin.setEnabled(false);
|
||||
var changes = preferenceRecords().stream()
|
||||
.filter(e -> e.get("type").getAsString().endsWith("preference.changed")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("player-aura-particles"))
|
||||
.toList();
|
||||
assertEquals(1, changes.stream().map(e -> e.get("id").getAsString()).distinct().count());
|
||||
assertEquals("false", changes.getFirst().getAsJsonObject("data").get("saved_before").getAsString());
|
||||
assertEquals("true", changes.getFirst().getAsJsonObject("data").get("saved_value").getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
private java.util.List<com.google.gson.JsonObject> preferenceRecords() throws Exception {
|
||||
var records = new java.util.ArrayList<com.google.gson.JsonObject>();
|
||||
try (var files = java.nio.file.Files.walk(data.resolve("events"))) {
|
||||
|
||||
@@ -105,6 +105,30 @@ class MendingLightNativeTest {
|
||||
assertEquals("10", healed.getAsJsonObject("data").get("health_after").getAsString());
|
||||
}
|
||||
|
||||
@Test void accountFeedbackOptOutCannotHideAnAcceptedNativeHealEvent() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
var caster = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
var ally = new FantasyStickLifecycleNativeTest.Actor(data);
|
||||
prepareCaster(data, caster, true); setHealth(data, ally, BigDecimal.valueOf(4));
|
||||
fixture.players.put(caster.core.id, caster.player); fixture.players.put(ally.core.id, ally.player);
|
||||
fixture.activate(caster); fixture.activate(ally); party(fixture, caster, ally); aim(caster, ally, 8, true);
|
||||
assertTrue(fixture.playerCommand.getExecutor().onCommand(caster.player, fixture.playerCommand,
|
||||
"fstick", new String[]{"feedback", "off"}));
|
||||
fixture.tick();
|
||||
use(fixture, caster); drain(fixture, 3);
|
||||
assertFalse(caster.messages.contains("Mending Light healed " + ally.core.record.name() + " for 6 health."));
|
||||
assertTrue(ally.messages.contains("Mending Light healed " + ally.core.record.name() + " for 6 health."));
|
||||
fixture.plugin.setEnabled(false);
|
||||
}
|
||||
var records = events(data);
|
||||
assertEquals(1, records.stream().filter(e -> e.get("type").getAsString().endsWith("combat.healed")
|
||||
&& e.getAsJsonObject("data").has("ability_id")
|
||||
&& e.getAsJsonObject("data").get("ability_id").getAsString().equals("mending_light")).count());
|
||||
assertEquals(1, records.stream().filter(e -> e.get("type").getAsString().endsWith("preference.changed")
|
||||
&& e.getAsJsonObject("data").get("preference_key").getAsString().equals("combat_feedback")).count());
|
||||
}
|
||||
|
||||
@Test void actualHealingCreatesOnePersistedSupportShareForAnAllyDirectContributor() throws Exception {
|
||||
try (var fixture = new FantasyStickLifecycleNativeTest.Fixture(data)) {
|
||||
fixture.plugin.setEnabled(true);
|
||||
|
||||
Reference in new issue
Block a user