feat: implemented InstantScript

This commit is contained in:
Andrey Terentev 2025-04-28 17:58:52 +07:00
parent 2fe9109178
commit 577896e48b
17 changed files with 198 additions and 90 deletions

View File

@ -5,6 +5,8 @@ import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextDecoration;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender; import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command; import net.minestom.server.command.builder.Command;
@ -12,11 +14,13 @@ import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentString; import net.minestom.server.command.builder.arguments.ArgumentString;
import net.minestom.server.command.builder.arguments.ArgumentType; import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.script.Script; import ru.dragonestia.msb3.api.script.ContinuousScript;
import ru.dragonestia.msb3.api.script.ScriptRegistry; import ru.dragonestia.msb3.api.script.ScriptRegistry;
import ru.dragonestia.msb3.api.script.ScriptService; import ru.dragonestia.msb3.api.script.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
public class ScriptCommand extends Command { public class ScriptCommand extends Command {
@ -57,8 +61,8 @@ public class ScriptCommand extends Command {
return; return;
} }
player.sendMessage(Component.text("Staring script '%s'...".formatted(scriptId), NamedTextColor.YELLOW)); player.sendMessage(Component.text("Staring script '%s'(%s)...".formatted(scriptId, script.get().getClass().getSimpleName()), NamedTextColor.YELLOW));
scriptService.start(script.get()); scriptService.start(script.get(), Params.EMPTY);
} }
private void executeStopScript(CommandSender sender, CommandContext ctx) { private void executeStopScript(CommandSender sender, CommandContext ctx) {
@ -72,16 +76,16 @@ public class ScriptCommand extends Command {
return; return;
} }
player.sendMessage(Component.text("Stopping script '%s'...".formatted(scriptId), NamedTextColor.YELLOW)); player.sendMessage(Component.text("Stopping continuous script '%s'(%s)...".formatted(scriptId, script.get().getClass().getSimpleName()), NamedTextColor.YELLOW));
scriptService.stop(script.get()); scriptService.stop(script.get());
} }
private void executeListActiveScripts(CommandSender sender, CommandContext ctx) { private void executeListActiveScripts(CommandSender sender, CommandContext ctx) {
var player = (Player) sender; var player = (Player) sender;
var scriptService = ScriptService.ofPlayer(player); var scriptService = ScriptService.ofPlayer(player);
var scripts = scriptService.getActiveScripts().stream().map(Script::getId).toList(); var scripts = scriptService.getActiveScripts().stream().map(ContinuousScript::getId).toList();
player.sendMessage(Component.text("Active scripts(%s): ".formatted(scripts.size())) player.sendMessage(Component.text("Active continuous scripts(%s): ".formatted(scripts.size()))
.append(listScriptIds(scripts))); .append(listScriptIds(scripts)));
} }
@ -97,8 +101,14 @@ public class ScriptCommand extends Command {
return Component.join( return Component.join(
JoinConfiguration.builder().separator(Component.text(", ")).build(), JoinConfiguration.builder().separator(Component.text(", ")).build(),
scriptIds.stream().map(id -> Component.text(id, NamedTextColor.GOLD) scriptIds.stream().map(id -> Component.text(id, NamedTextColor.GOLD)
.hoverEvent(HoverEvent.showText(Component.text("Click to copy"))) .hoverEvent(HoverEvent.showText(
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, id))) Component.text()
.append(Component.text("Click to copy", Style.style().color(NamedTextColor.YELLOW).decoration(TextDecoration.UNDERLINED, true).build()))
.appendNewline()
.appendNewline()
.append(Component.text(ScriptRegistry.findAndCreateScript(id).orElseThrow().getClass().getName()))
.build()
)).clickEvent(ClickEvent.clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, id)))
.toList() .toList()
); );
} }

View File

@ -11,6 +11,7 @@ import ru.dragonestia.msb3.api.player.PlayerContext;
import ru.dragonestia.msb3.api.player.defaults.TalksContext; import ru.dragonestia.msb3.api.player.defaults.TalksContext;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.debug.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.util.Params;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -58,7 +59,7 @@ public class DialogButton {
boolean success; boolean success;
try { try {
DebugMessage.send(player, "Выполнение действия диалога actionId=%s params=%s".formatted(actionId, params)); DebugMessage.send(player, "Выполнение действия диалога actionId=%s params=%s".formatted(actionId, params));
action.get().handle(click, params); action.get().handle(click, Params.of(params));
success = true; success = true;
} catch (Exception ex) { } catch (Exception ex) {

View File

@ -8,6 +8,7 @@ import lombok.extern.log4j.Log4j2;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.debug.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map; import java.util.Map;
@ -33,7 +34,7 @@ public class DialogCondition {
} }
try { try {
return condition.get().check(player, dialog, button, renderer, params); return condition.get().check(player, dialog, button, renderer, Params.of(params));
} catch (Exception ex) { } catch (Exception ex) {
log.error(ex.getMessage(), ex); log.error(ex.getMessage(), ex);
DebugMessage.send(player, "Во время проверки условия для кнопки произошла ошибка conditionId=%s params=%s".formatted(conditionId, params)); DebugMessage.send(player, "Во время проверки условия для кнопки произошла ошибка conditionId=%s params=%s".formatted(conditionId, params));

View File

@ -1,13 +1,14 @@
package ru.dragonestia.msb3.api.dialog.action; package ru.dragonestia.msb3.api.dialog.action;
import ru.dragonestia.msb3.api.dialog.DialogButtonClick; import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map; import java.util.Map;
public class CloseDialogActionHandler implements DialogActionHandler { public class CloseDialogActionHandler implements DialogActionHandler {
@Override @Override
public void handle(DialogButtonClick click, Map<String, String> params) { public void handle(DialogButtonClick click, Params params) {
click.renderer().close(false); click.renderer().close(false);
} }
} }

View File

@ -1,10 +1,9 @@
package ru.dragonestia.msb3.api.dialog.action; package ru.dragonestia.msb3.api.dialog.action;
import ru.dragonestia.msb3.api.dialog.DialogButtonClick; import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
public interface DialogActionHandler { public interface DialogActionHandler {
void handle(DialogButtonClick click, Map<String, String> params); void handle(DialogButtonClick click, Params params);
} }

View File

@ -5,21 +5,21 @@ import net.kyori.adventure.key.Key;
import ru.dragonestia.msb3.api.dialog.DialogButtonClick; import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
import ru.dragonestia.msb3.api.dialog.DialogRegistry; import ru.dragonestia.msb3.api.dialog.DialogRegistry;
import ru.dragonestia.msb3.api.debug.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
@Log4j2 @Log4j2
public class DialogDialogActionHandler implements DialogActionHandler { public class DialogDialogActionHandler implements DialogActionHandler {
@Override @Override
public void handle(DialogButtonClick click, Map<String, String> params) { public void handle(DialogButtonClick click, Params params) {
var player = click.player(); var player = click.player();
var dialogId = params.get("dialogId");
if (dialogId == null) { if (params.contains("dialogId")) {
DebugMessage.sendError(player, "Отсутствует обязательный параметр dialogId для команды dialog"); DebugMessage.sendError(player, "Отсутствует обязательный параметр dialogId для команды dialog");
return; return;
} }
var dialogId = params.getString("dialogId");
var dialog = DialogRegistry.findDialog(Key.key(dialogId)); var dialog = DialogRegistry.findDialog(Key.key(dialogId));
if (dialog.isEmpty()) { if (dialog.isEmpty()) {
log.error("Dialog {} not found", dialogId); log.error("Dialog {} not found", dialogId);

View File

@ -5,21 +5,21 @@ import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.dialog.DialogButtonClick; import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
import ru.dragonestia.msb3.api.script.ScriptRegistry; import ru.dragonestia.msb3.api.script.ScriptRegistry;
import ru.dragonestia.msb3.api.script.ScriptService; import ru.dragonestia.msb3.api.script.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
@Log4j2 @Log4j2
public class ScriptDialogActionHandler implements DialogActionHandler { public class ScriptDialogActionHandler implements DialogActionHandler {
@Override @Override
public void handle(DialogButtonClick click, Map<String, String> params) { public void handle(DialogButtonClick click, Params params) {
var player = click.player(); var player = click.player();
var scriptId = params.get("scriptId");
if (scriptId == null) { if (params.contains("scriptId")) {
DebugMessage.sendError(player, "Отсутствует обязательный параметр scriptId для команды script"); DebugMessage.sendError(player, "Отсутствует обязательный параметр scriptId для команды script");
return; return;
} }
var scriptId = params.getString("scriptId");
var script = ScriptRegistry.findAndCreateScript(scriptId); var script = ScriptRegistry.findAndCreateScript(scriptId);
if (script.isEmpty()) { if (script.isEmpty()) {
log.error("Script {} not found", scriptId); log.error("Script {} not found", scriptId);
@ -27,6 +27,6 @@ public class ScriptDialogActionHandler implements DialogActionHandler {
return; return;
} }
ScriptService.ofPlayer(player).start(script.get()); ScriptService.ofPlayer(player).start(script.get(), params);
} }
} }

View File

@ -4,13 +4,12 @@ import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.dialog.Dialog; import ru.dragonestia.msb3.api.dialog.Dialog;
import ru.dragonestia.msb3.api.dialog.DialogButton; import ru.dragonestia.msb3.api.dialog.DialogButton;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
public class AlwaysDialogConditionHandler implements DialogConditionHandler { public class AlwaysDialogConditionHandler implements DialogConditionHandler {
@Override @Override
public boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Map<String, String> params) { public boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Params params) {
return true; return true;
} }
} }

View File

@ -4,10 +4,9 @@ import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.dialog.Dialog; import ru.dragonestia.msb3.api.dialog.Dialog;
import ru.dragonestia.msb3.api.dialog.DialogButton; import ru.dragonestia.msb3.api.dialog.DialogButton;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
public interface DialogConditionHandler { public interface DialogConditionHandler {
boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Map<String, String> params); boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Params params);
} }

View File

@ -4,13 +4,12 @@ import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.dialog.Dialog; import ru.dragonestia.msb3.api.dialog.Dialog;
import ru.dragonestia.msb3.api.dialog.DialogButton; import ru.dragonestia.msb3.api.dialog.DialogButton;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Map;
public class NeverDialogConditionHandler implements DialogConditionHandler { public class NeverDialogConditionHandler implements DialogConditionHandler {
@Override @Override
public boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Map<String, String> params) { public boolean check(Player player, Dialog dialog, DialogButton button, DialogueRenderer renderer, Params params) {
return false; return false;
} }
} }

View File

@ -4,8 +4,10 @@ import lombok.extern.log4j.Log4j2;
import ru.dragonestia.msb3.api.debug.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.player.PlayerContext; import ru.dragonestia.msb3.api.player.PlayerContext;
import ru.dragonestia.msb3.api.script.ContinuousScript;
import ru.dragonestia.msb3.api.script.Script; import ru.dragonestia.msb3.api.script.Script;
import ru.dragonestia.msb3.api.script.ScriptService; import ru.dragonestia.msb3.api.script.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
import ru.dragonestia.msb3.api.util.UncheckedRunnable; import ru.dragonestia.msb3.api.util.UncheckedRunnable;
import java.util.*; import java.util.*;
@ -14,7 +16,7 @@ import java.util.concurrent.ConcurrentHashMap;
@Log4j2 @Log4j2
public class PlayerScriptContext extends PlayerContext implements ScriptService { public class PlayerScriptContext extends PlayerContext implements ScriptService {
private final Map<String, Script> scripts = new ConcurrentHashMap<>(); private final Map<String, ContinuousScript> scripts = new ConcurrentHashMap<>();
public PlayerScriptContext(MsbPlayer player) { public PlayerScriptContext(MsbPlayer player) {
super(player); super(player);
@ -34,28 +36,36 @@ public class PlayerScriptContext extends PlayerContext implements ScriptService
} }
@Override @Override
public void start(Script script) { public void start(Script script, Params params) {
synchronized (this) { synchronized (this) {
var opt = findScript(script.getId()); if (script instanceof ContinuousScript continuousScript) startContinuousScript(continuousScript, params);
if (opt.isPresent()) { else startScript(script, params);
DebugMessage.send(getPlayer(), "Script '%s' already started".formatted(script.getId())); }
return; }
}
try { private void startContinuousScript(ContinuousScript script, Params params) {
script.start(getPlayer()); var opt = findScript(script.getId());
DebugMessage.send(getPlayer(), "Script '%s' started!".formatted(script.getId())); if (opt.isPresent()) {
} catch (Exception ex) { DebugMessage.send(getPlayer(), "Script '%s' already running".formatted(script.getId()));
DebugMessage.sendError(getPlayer(), "Script '%s' started with error %s: %s".formatted(script.getId(), ex.getClass().getSimpleName(), ex.getMessage())); return;
log.error(ex.getMessage(), ex); }
}
scripts.put(script.getId(), script); startScript(script, params);
scripts.put(script.getId(), script);
}
private void startScript(Script script, Params params) {
try {
script.start(getPlayer(), params);
DebugMessage.send(getPlayer(), "Script '%s' was started!".formatted(script.getId()));
} catch (Exception ex) {
DebugMessage.sendError(getPlayer(), "Script '%s' was started with error %s: %s".formatted(script.getId(), ex.getClass().getSimpleName(), ex.getMessage()));
log.error(ex.getMessage(), ex);
} }
} }
@Override @Override
public void stop(Script script) { public void stop(ContinuousScript script) {
synchronized (this) { synchronized (this) {
if (!scripts.containsKey(script.getId())) { if (!scripts.containsKey(script.getId())) {
DebugMessage.send(getPlayer(), "Script '%s' not started".formatted(script.getId())); DebugMessage.send(getPlayer(), "Script '%s' not started".formatted(script.getId()));
@ -75,12 +85,12 @@ public class PlayerScriptContext extends PlayerContext implements ScriptService
} }
@Override @Override
public Collection<Script> getActiveScripts() { public Collection<ContinuousScript> getActiveScripts() {
return scripts.values(); return scripts.values();
} }
@Override @Override
public Optional<Script> findScript(String id) { public Optional<ContinuousScript> findScript(String id) {
return Optional.ofNullable(scripts.get(id)); return Optional.ofNullable(scripts.get(id));
} }
} }

View File

@ -0,0 +1,35 @@
package ru.dragonestia.msb3.api.script;
import lombok.Getter;
import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.util.Params;
@Getter
public abstract class ContinuousScript implements Script {
private final String id;
public ContinuousScript(String id) {
this.id = id;
}
@Override
public abstract void start(MsbPlayer player, Params params);
public abstract void end(MsbPlayer player);
@Override
public final int hashCode() {
return id.hashCode();
}
@Override
public final boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof ContinuousScript other) {
return id.equals(other.id);
}
return false;
}
}

View File

@ -0,0 +1,18 @@
package ru.dragonestia.msb3.api.script;
import lombok.Getter;
import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.util.Params;
@Getter
public abstract class InstantScript implements Script {
private final String id;
public InstantScript(String id) {
this.id = id;
}
@Override
public abstract void start(MsbPlayer player, Params params);
}

View File

@ -1,33 +1,11 @@
package ru.dragonestia.msb3.api.script; package ru.dragonestia.msb3.api.script;
import lombok.Getter;
import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.util.Params;
@Getter public interface Script {
public abstract class Script {
private final String id; String getId();
public Script(String id) { void start(MsbPlayer player, Params params);
this.id = id;
}
public abstract void start(MsbPlayer player);
public abstract void end(MsbPlayer player);
@Override
public final int hashCode() {
return id.hashCode();
}
@Override
public final boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof Script other) {
return id.equals(other.id);
}
return false;
}
} }

View File

@ -3,19 +3,24 @@ package ru.dragonestia.msb3.api.script;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.player.PlayerContext; import ru.dragonestia.msb3.api.player.PlayerContext;
import ru.dragonestia.msb3.api.player.defaults.PlayerScriptContext; import ru.dragonestia.msb3.api.player.defaults.PlayerScriptContext;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Collection; import java.util.Collection;
import java.util.Optional; import java.util.Optional;
public interface ScriptService { public interface ScriptService {
void start(Script script); default void start(Script script) {
start(script, Params.EMPTY);
}
void stop(Script script); void start(Script script, Params params);
Collection<Script> getActiveScripts(); void stop(ContinuousScript script);
Optional<Script> findScript(String id); Collection<ContinuousScript> getActiveScripts();
Optional<ContinuousScript> findScript(String id);
static ScriptService ofPlayer(Player player) { static ScriptService ofPlayer(Player player) {
return PlayerContext.of(player, PlayerScriptContext.class); return PlayerContext.of(player, PlayerScriptContext.class);

View File

@ -1,21 +1,17 @@
package ru.dragonestia.msb3.api.script.defaults; package ru.dragonestia.msb3.api.script.defaults;
import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.script.Script; import ru.dragonestia.msb3.api.script.InstantScript;
import ru.dragonestia.msb3.api.util.Params;
public class HelloWorldScript extends Script { public class HelloWorldScript extends InstantScript {
public HelloWorldScript() { public HelloWorldScript() {
super("hello_world"); super("hello_world");
} }
@Override @Override
public void start(MsbPlayer player) { public void start(MsbPlayer player, Params params) {
player.sendMessage("Hello World!"); player.sendMessage("Hello World!");
} }
@Override
public void end(MsbPlayer player) {
player.sendMessage("Goodbye my dear friend!");
}
} }

View File

@ -0,0 +1,57 @@
package ru.dragonestia.msb3.api.util;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public final class Params {
public static final Params EMPTY = of(new HashMap<>());
private final Map<String, String> params;
private Params(Map<String, String> params) {
this.params = new HashMap<>(params);
}
public static Params of(Map<String, String> params) {
return new Params(params);
}
public boolean contains(String key) {
return params.containsKey(key);
}
public Collection<String> keys() {
return params.keySet();
}
@Override
public String toString() {
return params.toString();
}
public String getString(String key) {
return params.get(key);
}
public boolean getBoolean(String key) {
return Boolean.parseBoolean(getString(key));
}
public int getInt(String key) {
return Integer.parseInt(getString(key));
}
public long getLong(String key) {
return Long.parseLong(getString(key));
}
public double getDouble(String key) {
return Double.parseDouble(getString(key));
}
public float getFloat(String key) {
return Float.parseFloat(getString(key));
}
}