Merge pull request 'Feat: Implemented degub tools' (#1) from feat/debug-render into master

Reviewed-on: http://git.dragonestia.ru/MSB/msb3/pulls/1
This commit is contained in:
Andrey Terentev 2025-04-28 18:21:15 +07:00
commit fc04f223a4
45 changed files with 1260 additions and 43 deletions

2
README.md Normal file
View File

@ -0,0 +1,2 @@
msb3
-----------

View File

@ -2,6 +2,8 @@ plugins {
id 'com.github.johnrengelman.shadow' version '7.0.0' id 'com.github.johnrengelman.shadow' version '7.0.0'
} }
version = MSB3_VERSION
tasks { tasks {
shadowJar { shadowJar {
manifest { manifest {
@ -27,7 +29,7 @@ tasks {
dependencies { dependencies {
api project(":resource-compiler") api project(":resource-compiler")
api 'net.minestom:minestom-snapshots:1_21_4-6490538291' api 'net.minestom:minestom-snapshots:8209a1d1e9' // stableVersion: 1_21_4-6490538291
api 'org.slf4j:slf4j-api:2.0.16' api 'org.slf4j:slf4j-api:2.0.16'
api 'org.apache.logging.log4j:log4j-slf4j2-impl:2.24.0' api 'org.apache.logging.log4j:log4j-slf4j2-impl:2.24.0'
@ -48,3 +50,32 @@ dependencies {
implementation 'org.apache.commons:commons-text:1.10.0' implementation 'org.apache.commons:commons-text:1.10.0'
} }
publishing {
publications {
maven(MavenPublication) {
groupId = 'ru.dragonestia.msb3'
artifactId = 'msb-api'
version = MSB3_VERSION
from components.java
}
}
repositories {
maven {
name = "Gitea"
url = uri(MSB3_MAVEN_REPOSITORY)
allowInsecureProtocol = true
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = "token ${dragonestiaGiteaToken}"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}

View File

@ -20,7 +20,7 @@ public class DefaultBootstrap extends ServerInitializer {
@Override @Override
public void onDefaultModulesLoaded() { public void onDefaultModulesLoaded() {
MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>"); MotdModule.init("logo.png", "<gradient:#ff0059:#e06806><bold>msb3 server</bold></gradient>");
FlatWorldModule.init(GameMode.ADVENTURE); FlatWorldModule.init(GameMode.CREATIVE);
PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500)); PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500));
} }
@ -33,4 +33,9 @@ public class DefaultBootstrap extends ServerInitializer {
public void onResourcePackCompiled(ResourcePack resourcePack) { public void onResourcePackCompiled(ResourcePack resourcePack) {
ResourcePackRepositoryModule.init("0.0.0.0", 7270); ResourcePackRepositoryModule.init("0.0.0.0", 7270);
} }
@Override
public void onServerStarted() {
}
} }

View File

@ -12,6 +12,7 @@ import ru.dragonestia.msb3.api.command.DebugCommands;
import ru.dragonestia.msb3.api.dialog.DialogRegistry; import ru.dragonestia.msb3.api.dialog.DialogRegistry;
import ru.dragonestia.msb3.api.dialog.action.CloseDialogActionHandler; import ru.dragonestia.msb3.api.dialog.action.CloseDialogActionHandler;
import ru.dragonestia.msb3.api.dialog.action.DialogDialogActionHandler; import ru.dragonestia.msb3.api.dialog.action.DialogDialogActionHandler;
import ru.dragonestia.msb3.api.dialog.action.ScriptDialogActionHandler;
import ru.dragonestia.msb3.api.dialog.condition.AlwaysDialogConditionHandler; import ru.dragonestia.msb3.api.dialog.condition.AlwaysDialogConditionHandler;
import ru.dragonestia.msb3.api.dialog.condition.NeverDialogConditionHandler; import ru.dragonestia.msb3.api.dialog.condition.NeverDialogConditionHandler;
import ru.dragonestia.msb3.api.entity.PickableItem; import ru.dragonestia.msb3.api.entity.PickableItem;
@ -19,11 +20,11 @@ import ru.dragonestia.msb3.api.event.NPCClickEvent;
import ru.dragonestia.msb3.api.item.ItemUtil; import ru.dragonestia.msb3.api.item.ItemUtil;
import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.player.PlayerContextManager; import ru.dragonestia.msb3.api.player.PlayerContextManager;
import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext; import ru.dragonestia.msb3.api.player.defaults.*;
import ru.dragonestia.msb3.api.player.defaults.NavigatorContext;
import ru.dragonestia.msb3.api.player.defaults.TalksContext;
import ru.dragonestia.msb3.api.resource.DialogueResources; import ru.dragonestia.msb3.api.resource.DialogueResources;
import ru.dragonestia.msb3.api.resource.MonologueResources; import ru.dragonestia.msb3.api.resource.MonologueResources;
import ru.dragonestia.msb3.api.script.ScriptRegistry;
import ru.dragonestia.msb3.api.script.defaults.HelloWorldScript;
import ru.dragonestia.msb3.api.skin.SkinStorage; import ru.dragonestia.msb3.api.skin.SkinStorage;
import ru.dragonestia.msb3.api.ui.BlackScreen; import ru.dragonestia.msb3.api.ui.BlackScreen;
import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars; import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars;
@ -102,6 +103,7 @@ public final class ServerBootstrap {
initDefaultDialogActionsAndConditions(); initDefaultDialogActionsAndConditions();
NPCClickEvent.init(); NPCClickEvent.init();
DebugCommands.init(); DebugCommands.init();
initDefaultScripts();
} }
private void compileResourcePack() { private void compileResourcePack() {
@ -191,6 +193,8 @@ public final class ServerBootstrap {
PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new); PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new);
PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new); PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new);
PlayerContextManager.registerContext(TalksContext.class, TalksContext::new); PlayerContextManager.registerContext(TalksContext.class, TalksContext::new);
PlayerContextManager.registerContext(DebugParamsContext.class, DebugParamsContext::new);
PlayerContextManager.registerContext(PlayerScriptContext.class, PlayerScriptContext::new);
} }
private void initDefaultSkins() { private void initDefaultSkins() {
@ -200,8 +204,13 @@ public final class ServerBootstrap {
private void initDefaultDialogActionsAndConditions() { private void initDefaultDialogActionsAndConditions() {
DialogRegistry.registerActionHandler("close", new CloseDialogActionHandler()); DialogRegistry.registerActionHandler("close", new CloseDialogActionHandler());
DialogRegistry.registerActionHandler("dialog", new DialogDialogActionHandler()); DialogRegistry.registerActionHandler("dialog", new DialogDialogActionHandler());
DialogRegistry.registerActionHandler("script", new ScriptDialogActionHandler());
DialogRegistry.registerConditionHandler("always", new AlwaysDialogConditionHandler()); DialogRegistry.registerConditionHandler("always", new AlwaysDialogConditionHandler());
DialogRegistry.registerConditionHandler("never", new NeverDialogConditionHandler()); DialogRegistry.registerConditionHandler("never", new NeverDialogConditionHandler());
} }
private void initDefaultScripts() {
ScriptRegistry.register(HelloWorldScript::new);
}
} }

View File

@ -0,0 +1,49 @@
package ru.dragonestia.msb3.api.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentBoolean;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.player.PlayerContext;
import ru.dragonestia.msb3.api.player.defaults.DebugParamsContext;
public class DebugAICommand extends Command {
private final ArgumentBoolean argShowPathValue = ArgumentType.Boolean("show");
public DebugAICommand() {
super("debug_ai");
setDefaultExecutor(this::defaultExecutor);
addSyntax(this::executeShowPath, ArgumentType.Literal("show_path"), argShowPathValue);
}
public static void register() {
MinecraftServer.getCommandManager().register(new DebugAICommand());
}
private void defaultExecutor(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
player.sendMessage("Debug Render commands:");
player.sendMessage("/debug_ai show_path <true/false> - Change debug renderer for path finder");
}
private void executeShowPath(CommandSender sender, CommandContext ctx) {
var player = (MsbPlayer) sender;
var show = ctx.get(argShowPathValue);
PlayerContext.of(player, DebugParamsContext.class).setShowDebugRendererAiPathFinder(show);
if (show) {
player.sendMessage(Component.text("Enabled debug renderer for path finder", NamedTextColor.YELLOW));
} else {
player.sendMessage(Component.text("Disabled debug renderer for path finder", NamedTextColor.YELLOW));
}
}
}

View File

@ -29,6 +29,9 @@ public class DebugCommands {
registerPosCommand(); registerPosCommand();
registerTeleportCommand(); registerTeleportCommand();
registerGameModeCommand(); registerGameModeCommand();
DebugRendererCommand.register();
DebugAICommand.register();
ScriptCommand.register();
log.info("Registered debug commands"); log.info("Registered debug commands");
} }

View File

@ -0,0 +1,62 @@
package ru.dragonestia.msb3.api.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentEnum;
import net.minestom.server.command.builder.arguments.ArgumentString;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.player.MsbPlayer;
public class DebugRendererCommand extends Command {
private final ArgumentEnum<Option> argOption = ArgumentType.Enum("option", Option.class).setFormat(ArgumentEnum.Format.LOWER_CASED);
private final ArgumentString argLayerName = ArgumentType.String("layer");
public DebugRendererCommand() {
super("debug_render");
setDefaultExecutor(this::defaultExecutor);
argOption.setCallback((sender, ex) -> {
sender.sendMessage(Component.text("Invalid option", NamedTextColor.RED));
});
addSyntax(this::execute, argOption, argLayerName);
}
public static void register() {
MinecraftServer.getCommandManager().register(new DebugRendererCommand());
}
private void defaultExecutor(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
player.sendMessage("Debug Render commands:");
player.sendMessage("/debug_render show <layer> - Show debug layer");
player.sendMessage("/debug_render hide <layer> - Hide debug layer");
}
private void execute(CommandSender sender, CommandContext ctx) {
var player = (MsbPlayer) sender;
var option = ctx.get(argOption);
var layerName = ctx.get(argLayerName);
if (option == Option.SHOW) {
player.addDebugRenderLayer(layerName);
player.sendMessage(Component.text("Debug layer '%s' was showed for you".formatted(layerName), NamedTextColor.YELLOW));
return;
}
player.removeDebugRenderLayer(layerName);
player.sendMessage(Component.text("Debug layer '%s' was hidden for you".formatted(layerName), NamedTextColor.YELLOW));
}
public enum Option {
SHOW,
HIDE,
}
}

View File

@ -0,0 +1,115 @@
package ru.dragonestia.msb3.api.command;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.JoinConfiguration;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
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.command.CommandSender;
import net.minestom.server.command.builder.Command;
import net.minestom.server.command.builder.CommandContext;
import net.minestom.server.command.builder.arguments.ArgumentString;
import net.minestom.server.command.builder.arguments.ArgumentType;
import net.minestom.server.entity.Player;
import ru.dragonestia.msb3.api.script.ContinuousScript;
import ru.dragonestia.msb3.api.script.ScriptRegistry;
import ru.dragonestia.msb3.api.script.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
import java.util.Collection;
import java.util.HashMap;
public class ScriptCommand extends Command {
private final ArgumentString argScriptId = ArgumentType.String("scriptId");
public ScriptCommand() {
super("script");
setDefaultExecutor(this::defaultExecutor);
addSyntax(this::executeStartScript, ArgumentType.Literal("start"), argScriptId);
addSyntax(this::executeStopScript, ArgumentType.Literal("stop"), argScriptId);
addSyntax(this::executeListActiveScripts, ArgumentType.Literal("active"));
addSyntax(this::executeListRegisteredScripts, ArgumentType.Literal("registered"));
}
public static void register() {
MinecraftServer.getCommandManager().register(new ScriptCommand());
}
private void defaultExecutor(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
player.sendMessage("Script commands:");
player.sendMessage("/script start <scriptId> - Start script");
player.sendMessage("/script stop <scriptId> - Stop script");
player.sendMessage("/script active - List active scripts");
player.sendMessage("/script registered - List registered scripts");
}
private void executeStartScript(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
var scriptService = ScriptService.ofPlayer(player);
var scriptId = ctx.get(argScriptId);
var script = ScriptRegistry.findAndCreateScript(scriptId);
if (script.isEmpty()) {
player.sendMessage(Component.text("Script '%s' is not registered".formatted(scriptId), NamedTextColor.RED));
return;
}
player.sendMessage(Component.text("Staring script '%s'(%s)...".formatted(scriptId, script.get().getClass().getSimpleName()), NamedTextColor.YELLOW));
scriptService.start(script.get(), Params.EMPTY);
}
private void executeStopScript(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
var scriptService = ScriptService.ofPlayer(player);
var scriptId = ctx.get(argScriptId);
var script = scriptService.findScript(scriptId);
if (script.isEmpty()) {
player.sendMessage(Component.text("Active script '%s' not found".formatted(scriptId), NamedTextColor.RED));
return;
}
player.sendMessage(Component.text("Stopping continuous script '%s'(%s)...".formatted(scriptId, script.get().getClass().getSimpleName()), NamedTextColor.YELLOW));
scriptService.stop(script.get());
}
private void executeListActiveScripts(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
var scriptService = ScriptService.ofPlayer(player);
var scripts = scriptService.getActiveScripts().stream().map(ContinuousScript::getId).toList();
player.sendMessage(Component.text("Active continuous scripts(%s): ".formatted(scripts.size()))
.append(listScriptIds(scripts)));
}
private void executeListRegisteredScripts(CommandSender sender, CommandContext ctx) {
var player = (Player) sender;
var scripts = ScriptRegistry.listRegisteredScripts();
player.sendMessage(Component.text("Registered scripts(%s): ".formatted(scripts.size()))
.append(listScriptIds(scripts)));
}
private Component listScriptIds(Collection<String> scriptIds) {
return Component.join(
JoinConfiguration.builder().separator(Component.text(", ")).build(),
scriptIds.stream().map(id -> Component.text(id, NamedTextColor.GOLD)
.hoverEvent(HoverEvent.showText(
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()
);
}
}

View File

@ -0,0 +1,11 @@
package ru.dragonestia.msb3.api.debug;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import ru.dragonestia.msb3.api.util.Env;
@UtilityClass
public class Debug {
@Getter private final boolean enabled = Env.bool("MSB3_DEBUG").orElse(false);
}

View File

@ -1,4 +1,4 @@
package ru.dragonestia.msb3.api.util; package ru.dragonestia.msb3.api.debug;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import net.kyori.adventure.sound.Sound; import net.kyori.adventure.sound.Sound;
@ -11,10 +11,8 @@ import net.minestom.server.sound.SoundEvent;
@UtilityClass @UtilityClass
public class DebugMessage { public class DebugMessage {
private final boolean disabled = !Env.bool("MSB3_DEBUG").orElse(false);
public void send(Player player, String message) { public void send(Player player, String message) {
if (disabled) return; if (!Debug.isEnabled()) return;
player.sendMessage(Component.text() player.sendMessage(Component.text()
.append(Component.text("[DEBUG] ", TextColor.color(0xFFC909), TextDecoration.BOLD)) .append(Component.text("[DEBUG] ", TextColor.color(0xFFC909), TextDecoration.BOLD))
@ -23,7 +21,7 @@ public class DebugMessage {
} }
public void sendError(Player player, String message) { public void sendError(Player player, String message) {
if (disabled) return; if (!Debug.isEnabled()) return;
var sound = Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_BIT, Sound.Source.NEUTRAL, 1f, 0f); var sound = Sound.sound(SoundEvent.BLOCK_NOTE_BLOCK_BIT, Sound.Source.NEUTRAL, 1f, 0f);
player.playSound(sound); player.playSound(sound);

View File

@ -0,0 +1,129 @@
package ru.dragonestia.msb3.api.debug;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minestom.server.coordinate.Vec;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class Graph {
private final static AtomicInteger freeId = new AtomicInteger(1);
private final Map<Integer, Node> nodes = new HashMap<>();
public Node addNode(Vec pos) {
var node = new Node(pos);
nodes.put(node.getId(), node);
return node;
}
public boolean addNodeLink(Node from, Node to) {
return from.addLink(to) && to.addLink(from);
}
public void removeLink(Node from, Node to) {
from.removeLink(to);
to.removeLink(from);
}
public void removeNode(Node node) {
nodes.remove(node.getId());
node.getLinks().forEach(other -> removeLink(other, node));
}
public Collection<Node> getNodes() {
return nodes.values();
}
public Optional<Node> findNode(int id) {
return Optional.ofNullable(nodes.get(id));
}
public SerializedGraph serialize() {
var nodes = new ArrayList<SerializedNode>();
var links = new HashSet<IntPair>();
for (var node: getNodes()) {
nodes.add(new SerializedNode(node.id, node.position.x(), node.position.y(), node.position.z()));
for (var otherNode: node.getLinks()) {
if (links.contains(new IntPair(otherNode.getId(), node.getId()))) continue;
links.add(new IntPair(node.getId(), otherNode.getId()));
}
}
return new SerializedGraph(nodes, links);
}
public static Graph deserialize(SerializedGraph serializedGraph) {
var graph = new Graph();
for (var node: serializedGraph.nodes) {
graph.addNode(new Vec(node.x(), node.y(), node.z()));
}
for (var link: serializedGraph.links) {
graph.addNodeLink(graph.findNode(link.a).orElseThrow(), graph.findNode(link.b).orElseThrow());
}
return graph;
}
@RequiredArgsConstructor
public static class Node {
@Getter private final int id = freeId.getAndIncrement();
@Getter private final Vec position;
private final Map<Node, Double> links = new HashMap<>();
private boolean addLink(Node node) {
if (id == node.getId()) return false;
return links.put(node, position.distance(node.position)) == null;
}
private void removeLink(Node node) {
links.remove(node);
node.links.remove(this);
}
public boolean hasLink(Node node) {
if (id == node.getId()) return true;
return links.containsKey(node);
}
public Set<Node> getLinks() {
return links.keySet();
}
public Collection<DistancePair> getLinksWithDistance() {
return links.entrySet().stream()
.map(entry -> new DistancePair(entry.getKey(), entry.getValue()))
.toList();
}
public double getDistanceTo(Node node) {
return links.getOrDefault(node, Double.NaN);
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null) return false;
if (obj instanceof Node other) {
return id == other.id;
}
return false;
}
}
public record SerializedGraph(List<SerializedNode> nodes, Set<IntPair> links) {}
public record SerializedNode(int id, double x, double y, double z) {}
public record IntPair(int a, int b) {}
public record DistancePair(Node node, double distance) {}
}

View File

@ -14,7 +14,7 @@ import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
import ru.dragonestia.msb3.api.ui.TalksThemes; import ru.dragonestia.msb3.api.ui.TalksThemes;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer; import ru.dragonestia.msb3.api.ui.dialogue.DialogueRenderer;
import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme; import ru.dragonestia.msb3.api.ui.dialogue.DialogueTheme;
import ru.dragonestia.msb3.api.util.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;

View File

@ -10,7 +10,8 @@ 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.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.util.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

@ -7,7 +7,8 @@ import lombok.Setter;
import lombok.extern.log4j.Log4j2; 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.util.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

@ -4,22 +4,22 @@ import lombok.extern.log4j.Log4j2;
import net.kyori.adventure.key.Key; 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.util.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

@ -0,0 +1,32 @@
package ru.dragonestia.msb3.api.dialog.action;
import lombok.extern.log4j.Log4j2;
import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.dialog.DialogButtonClick;
import ru.dragonestia.msb3.api.script.ScriptRegistry;
import ru.dragonestia.msb3.api.script.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
@Log4j2
public class ScriptDialogActionHandler implements DialogActionHandler {
@Override
public void handle(DialogButtonClick click, Params params) {
var player = click.player();
if (params.contains("scriptId")) {
DebugMessage.sendError(player, "Отсутствует обязательный параметр scriptId для команды script");
return;
}
var scriptId = params.getString("scriptId");
var script = ScriptRegistry.findAndCreateScript(scriptId);
if (script.isEmpty()) {
log.error("Script {} not found", scriptId);
DebugMessage.sendError(player, "Скрипт с идентификатором %s не найден".formatted(scriptId));
return;
}
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

@ -0,0 +1,10 @@
package ru.dragonestia.msb3.api.entity;
import net.minestom.server.coordinate.Point;
public interface OffsetPosition {
void setOffsetPosition(Point offset);
Point getOffsetPosition();
}

View File

@ -0,0 +1,29 @@
package ru.dragonestia.msb3.api.entity.debug;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.instance.block.Block;
public class DebugCollider extends DebugRendererEntity {
private DebugHologram hologram;
public DebugCollider(String layerName, Block block, Point size) {
super(EntityType.BLOCK_DISPLAY, layerName);
var meta = (BlockDisplayMeta) getEntityMeta();
meta.setScale(Vec.fromPoint(size));
meta.setBlockState(block);
meta.setScale(Vec.fromPoint(size));
}
public DebugCollider(String layerName, Block block, Point size, Component text) {
this(layerName, block, size);
var meta = (BlockDisplayMeta) getEntityMeta();
addNestedEntity(hologram = new DebugHologram(layerName, text), meta.getScale().div(2));
}
}

View File

@ -0,0 +1,18 @@
package ru.dragonestia.msb3.api.entity.debug;
import net.kyori.adventure.text.Component;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.AbstractDisplayMeta;
import net.minestom.server.entity.metadata.display.TextDisplayMeta;
public class DebugHologram extends DebugRendererEntity {
public DebugHologram(String layerName, Component text) {
super(EntityType.TEXT_DISPLAY, layerName);
var meta = (TextDisplayMeta) getEntityMeta();
meta.setText(text);
meta.setBillboardRenderConstraints(AbstractDisplayMeta.BillboardConstraints.CENTER);
meta.setSeeThrough(true);
}
}

View File

@ -0,0 +1,53 @@
package ru.dragonestia.msb3.api.entity.debug;
import lombok.Getter;
import lombok.extern.log4j.Log4j2;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
import ru.dragonestia.msb3.api.math.Quaternion;
@Getter
@Log4j2
public class DebugLine extends DebugRendererEntity {
private Vec normalWithLength;
private Block block;
protected DebugLine(String layerName, Vec normalWithLength, Block block) {
super(EntityType.BLOCK_DISPLAY, layerName);
var meta = getMeta();
meta.setBrightnessOverride(15);
meta.setBrightness(15, 15);
updateNormalWithLength(normalWithLength);
updateBlock(block);
}
public void updateNormalWithLength(Vec newValue) {
normalWithLength = newValue;
var meta = getMeta();
meta.setScale(new Vec(0.1, normalWithLength.length(), 0.1));
meta.setLeftRotation(Quaternion.DEFAULT
.rotate(newValue.normalize())
.toFloatArray());
}
public void updateBlock(Block block) {
getMeta().setBlockState(this.block = block);
}
private BlockDisplayMeta getMeta() {
return (BlockDisplayMeta) getEntityMeta();
}
public static DebugLine spawn(Instance instance, Point start, Point end, Block block, String layerName) {
var entity = new DebugLine(layerName, Vec.fromPoint(end.sub(start)), block);
entity.setInstance(instance, start);
return entity;
}
}

View File

@ -0,0 +1,33 @@
package ru.dragonestia.msb3.api.entity.debug;
import net.kyori.adventure.text.Component;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.AbstractDisplayMeta;
import net.minestom.server.entity.metadata.display.BlockDisplayMeta;
import net.minestom.server.instance.block.Block;
public class DebugMarker extends DebugRendererEntity {
public DebugMarker(String layerName, Block block) {
super(EntityType.BLOCK_DISPLAY, layerName);
var meta = (BlockDisplayMeta) getEntityMeta();
meta.setBlockState(block);
meta.setBillboardRenderConstraints(AbstractDisplayMeta.BillboardConstraints.CENTER);
meta.setTranslation(new Pos(-0.5, -0.5, -0.5));
meta.setLeftRotation(new float[] { 0.33333f, 0.33333f, 0.33333f, 0.8f });
}
public DebugMarker(String layerName, Block block, Component text) {
this(layerName, block);
addNestedEntity(new DebugHologram(layerName, text));
}
public void setScale(double x, double y, double z) {
var meta = (BlockDisplayMeta) getEntityMeta();
meta.setScale(new Vec(x, y, z));
}
}

View File

@ -0,0 +1,96 @@
package ru.dragonestia.msb3.api.entity.debug;
import lombok.Getter;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.display.AbstractDisplayMeta;
import net.minestom.server.instance.Instance;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.dragonestia.msb3.api.debug.Debug;
import ru.dragonestia.msb3.api.entity.OffsetPosition;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Getter
public class DebugRendererEntity extends Entity implements OffsetPosition {
private final String layerName;
private final List<Entity> nestedEntities = new ArrayList<>();
private Point offsetPosition = Pos.ZERO;
public DebugRendererEntity(@NotNull EntityType entityType, String layerName) {
super(entityType);
this.layerName = layerName;
setNoGravity(true);
if (getEntityMeta() instanceof AbstractDisplayMeta meta) {
meta.setBrightnessOverride(1);
meta.setBrightness(15, 15);
}
}
public void addNestedEntity(Entity entity) {
nestedEntities.add(entity);
}
public void addNestedEntity(Entity entity, Point offset) {
if (entity instanceof OffsetPosition target) target.setOffsetPosition(offset);
nestedEntities.add(entity);
}
@Override
public CompletableFuture<Void> setInstance(@NotNull Instance instance, @NotNull Pos spawnPosition) {
return super.setInstance(instance, spawnPosition)
.thenRun(() -> {
if (!Debug.isEnabled()) {
remove();
return;
}
for (var entity: nestedEntities) {
if (entity instanceof OffsetPosition offsetPosition) {
entity.setInstance(instance, spawnPosition.add(offsetPosition.getOffsetPosition()));
} else {
entity.setInstance(instance, spawnPosition);
}
}
});
}
@Override
public @NotNull CompletableFuture<Void> teleport(@NotNull Pos position, long @Nullable [] chunks, int flags, boolean shouldConfirm) {
return super.teleport(position, chunks, flags, shouldConfirm)
.thenRun(() -> {
for (var entity: nestedEntities) {
if (entity instanceof OffsetPosition offsetPosition) {
entity.teleport(position.add(offsetPosition.getOffsetPosition()), chunks, flags, shouldConfirm);
} else {
entity.teleport(position, chunks, flags, shouldConfirm);
}
}
});
}
@Override
public void remove() {
super.remove();
for (var entity: nestedEntities) {
entity.remove();
}
}
@Override
public void setOffsetPosition(Point offset) {
offsetPosition = offset;
}
@Override
public Point getOffsetPosition() {
return offsetPosition;
}
}

View File

@ -0,0 +1,31 @@
package ru.dragonestia.msb3.api.entity.rule;
import net.minestom.server.entity.Entity;
import java.util.UUID;
public abstract class PlayerViewerRule {
private final UUID id = UUID.randomUUID();
public final UUID getId() {
return id;
}
public abstract boolean canView(Entity entity);
@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 PlayerViewerRule other) {
return this.id.equals(other.id);
}
return false;
}
}

View File

@ -0,0 +1,74 @@
package ru.dragonestia.msb3.api.math;
import net.minestom.server.coordinate.Vec;
public record Quaternion(double x, double y, double z, double w) {
public static final Quaternion DEFAULT = new Quaternion(0, 0, 0, 1);
public static Quaternion fromVec (Vec vec, double realPart) {
return new Quaternion(vec.x(), vec.y(), vec.z(), realPart);
}
public float[] toFloatArray() {
return new float[] { (float) x, (float) y, (float) z, (float) w };
}
public double length() {
return Math.sqrt(x * x + y * y + z * z + w * w);
}
public Quaternion normalize() {
var length = length();
return new Quaternion(x / length, y / length, z / length, w / length);
}
public Quaternion conjugate() {
return new Quaternion(-x, -y, -z, w);
}
public double dotProduct(Quaternion target) {
return (x * target.x + y * target.y + z * target.z + w * target.w
);
}
public Quaternion mul(Quaternion target) {
return new Quaternion(
w() * target.x() + x() * target.w() + y() * target.z() - z() * target.y(),
w() * target.y() - x() * target.z() + y() * target.w() + z() * target.x(),
w() * target.z() + x() * target.y() - y() * target.x() + z() * target.w(),
w() * target.w() - x() * target.x() - y() * target.y() - z() * target.z()
);
}
public Quaternion add(Quaternion target) {
return new Quaternion(
x + target.x,
y + target.y,
z + target.z,
w + target.w
);
}
public Quaternion sub(Quaternion target) {
return new Quaternion(
x - target.x,
y - target.y,
z - target.z,
w - target.w
);
}
public Quaternion rotate(Vec normal) {
var defaultQuaternion = new Quaternion(0, 0, 0, 1);
var defaultVector = new Vec(0, 1, 0);
var feta = Math.acos(defaultVector.dot(normal));
var r = defaultVector.cross(normal);
if (r.isZero()){
r = new Vec(0, 0, 1);
}
var rotation = fromVec(r.normalize().mul(Math.sin(feta / 2)), Math.cos(feta / 2));
return defaultQuaternion.mul(rotation);
}
}

View File

@ -5,19 +5,23 @@ import net.minestom.server.entity.Player;
import net.minestom.server.network.player.GameProfile; import net.minestom.server.network.player.GameProfile;
import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.network.player.PlayerConnection;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import ru.dragonestia.msb3.api.entity.debug.DebugRendererEntity;
import ru.dragonestia.msb3.api.entity.rule.PlayerViewerRule;
import ru.dragonestia.msb3.api.util.UncheckedRunnable; import ru.dragonestia.msb3.api.util.UncheckedRunnable;
import java.time.Instant; import java.time.Instant;
import java.util.LinkedHashMap; import java.util.*;
import java.util.Map;
public class MsbPlayer extends Player { public class MsbPlayer extends Player {
private final Map<String, PlayerContext> contexts = new LinkedHashMap<>(); private final Map<String, PlayerContext> contexts = new LinkedHashMap<>();
@Getter private final Instant startSessionTime = Instant.now(); @Getter private final Instant startSessionTime = Instant.now();
private final Set<PlayerViewerRule> viewerRules = Collections.synchronizedSet(new HashSet<>());
private final Set<String> debugRenderLayers = Collections.synchronizedSet(new HashSet<>());
public MsbPlayer(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) { public MsbPlayer(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) {
super(playerConnection, gameProfile); super(playerConnection, gameProfile);
updateViewerRules();
} }
void putContext(PlayerContext ctx) { void putContext(PlayerContext ctx) {
@ -55,4 +59,38 @@ public class MsbPlayer extends Player {
UncheckedRunnable.runIgnoreException(ctx::dispose); UncheckedRunnable.runIgnoreException(ctx::dispose);
} }
} }
public void addDebugRenderLayer(String layerName) {
debugRenderLayers.add(layerName);
updateViewerRules();
}
public void removeDebugRenderLayer(String layerName) {
debugRenderLayers.remove(layerName);
updateViewerRules();
}
public void appendViewRule(PlayerViewerRule rule) {
viewerRules.add(rule);
updateViewerRules();
}
public void removeViewRule(PlayerViewerRule rule) {
viewerRules.remove(rule);
updateViewerRules();
}
private void updateViewerRules() {
updateViewerRule(entity -> {
if (entity instanceof DebugRendererEntity debugRenderer) {
return debugRenderLayers.contains(debugRenderer.getLayerName());
}
for (var rule: viewerRules) {
if (!rule.canView(entity)) return false;
}
return true;
});
}
} }

View File

@ -0,0 +1,59 @@
package ru.dragonestia.msb3.api.player.defaults;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.entity.EntityCreature;
import net.minestom.server.network.packet.server.play.ParticlePacket;
import net.minestom.server.particle.Particle;
import net.minestom.server.timer.Task;
import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.player.PlayerContext;
import ru.dragonestia.msb3.api.scheduler.Scheduler;
import java.time.Duration;
public class DebugParamsContext extends PlayerContext {
private final static Particle PARTICLE_AI_PATH = Particle.DUST
.withScale(2f)
.withColor(NamedTextColor.GREEN);
private Task taskDebugRendererAiPathFinder;
public DebugParamsContext(MsbPlayer player) {
super(player);
}
@Override
public void init() {
}
@Override
public void dispose() {
}
public synchronized void setShowDebugRendererAiPathFinder(boolean value) {
if (value) {
if (taskDebugRendererAiPathFinder != null) return;
taskDebugRendererAiPathFinder = Scheduler.ofPlayer(getPlayer()).repeatingTask(() -> {
for (var entity: getPlayer().getInstance().getNearbyEntities(getPlayer().getPosition(), 32)) {
if (entity instanceof EntityCreature creature) {
var nodes = creature.getNavigator().getNodes();
if (nodes == null) continue;
for (var point: nodes) {
var packet = new ParticlePacket(PARTICLE_AI_PATH, point.x(), point.y() + 0.5, point.z(), 0, 0, 0, 0, 1);
entity.sendPacketToViewers(packet);
}
}
}
}, Duration.ofMillis(500));
return;
}
if (taskDebugRendererAiPathFinder == null) return;
taskDebugRendererAiPathFinder.cancel();
taskDebugRendererAiPathFinder = null;
}
}

View File

@ -0,0 +1,96 @@
package ru.dragonestia.msb3.api.player.defaults;
import lombok.extern.log4j.Log4j2;
import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.player.MsbPlayer;
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.ScriptService;
import ru.dragonestia.msb3.api.util.Params;
import ru.dragonestia.msb3.api.util.UncheckedRunnable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Log4j2
public class PlayerScriptContext extends PlayerContext implements ScriptService {
private final Map<String, ContinuousScript> scripts = new ConcurrentHashMap<>();
public PlayerScriptContext(MsbPlayer player) {
super(player);
}
@Override
public void init() {}
@Override
public void dispose() {
synchronized (this) {
for (var script : scripts.values()) {
UncheckedRunnable.runIgnoreException(() -> script.end(getPlayer()));
}
scripts.clear();
}
}
@Override
public void start(Script script, Params params) {
synchronized (this) {
if (script instanceof ContinuousScript continuousScript) startContinuousScript(continuousScript, params);
else startScript(script, params);
}
}
private void startContinuousScript(ContinuousScript script, Params params) {
var opt = findScript(script.getId());
if (opt.isPresent()) {
DebugMessage.send(getPlayer(), "Script '%s' already running".formatted(script.getId()));
return;
}
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
public void stop(ContinuousScript script) {
synchronized (this) {
if (!scripts.containsKey(script.getId())) {
DebugMessage.send(getPlayer(), "Script '%s' not started".formatted(script.getId()));
return;
}
scripts.remove(script.getId());
try {
script.end(getPlayer());
DebugMessage.send(getPlayer(), "Script '%s' stopped".formatted(script.getId()));
} catch (Exception ex) {
DebugMessage.sendError(getPlayer(), "Script '%s' stopped with error %s: %s".formatted(script.getId(), ex.getClass().getSimpleName(), ex.getMessage()));
log.error(ex.getMessage(), ex);
}
}
}
@Override
public Collection<ContinuousScript> getActiveScripts() {
return scripts.values();
}
@Override
public Optional<ContinuousScript> findScript(String 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

@ -0,0 +1,11 @@
package ru.dragonestia.msb3.api.script;
import ru.dragonestia.msb3.api.player.MsbPlayer;
import ru.dragonestia.msb3.api.util.Params;
public interface Script {
String getId();
void start(MsbPlayer player, Params params);
}

View File

@ -0,0 +1,30 @@
package ru.dragonestia.msb3.api.script;
import lombok.experimental.UtilityClass;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
@UtilityClass
public class ScriptRegistry {
private final Map<String, Supplier<Script>> scripts = new ConcurrentHashMap<>();
public void register(Supplier<Script> scriptSupplier) {
var prev = scripts.put(scriptSupplier.get().getId(), scriptSupplier);
if (prev != null) {
throw new IllegalStateException("Script already registered: " + scriptSupplier.get().getId());
}
}
public Optional<Script> findAndCreateScript(String id) {
return Optional.ofNullable(scripts.get(id)).map(Supplier::get);
}
public Collection<String> listRegisteredScripts() {
return scripts.keySet();
}
}

View File

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

View File

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

View File

@ -24,7 +24,7 @@ import ru.dragonestia.msb3.api.item.ItemUtil;
import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber; import ru.dragonestia.msb3.api.resource.dialog.ButtonNumber;
import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions; import ru.dragonestia.msb3.api.resource.dialog.GlyphPositions;
import ru.dragonestia.msb3.api.resource.dialog.TextureProperties; import ru.dragonestia.msb3.api.resource.dialog.TextureProperties;
import ru.dragonestia.msb3.api.util.DebugMessage; import ru.dragonestia.msb3.api.debug.DebugMessage;
import ru.dragonestia.msb3.api.util.StringUtil; import ru.dragonestia.msb3.api.util.StringUtil;
import ru.dragonestia.msb3.resource.glyph.GlyphComponent; import ru.dragonestia.msb3.resource.glyph.GlyphComponent;
import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder; import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder;

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));
}
}

View File

@ -2,6 +2,7 @@ package ru.dragonestia.msb3.api.world.loader.anvil;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.*; import net.kyori.adventure.nbt.*;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.instance.DynamicChunk; import net.minestom.server.instance.DynamicChunk;
@ -11,9 +12,7 @@ import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockHandler; import net.minestom.server.instance.block.BlockHandler;
import net.minestom.server.instance.palette.Palettes; import net.minestom.server.instance.palette.Palettes;
import net.minestom.server.registry.DynamicRegistry; import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.utils.ArrayUtils;
import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.NamespaceID;
import net.minestom.server.utils.validate.Check; import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.biome.Biome; import net.minestom.server.world.biome.Biome;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -26,7 +25,7 @@ import java.util.*;
public class AnvilRegionLoader { public class AnvilRegionLoader {
private static final DynamicRegistry<Biome> BIOME_REGISTRY = MinecraftServer.getBiomeRegistry(); private static final DynamicRegistry<Biome> BIOME_REGISTRY = MinecraftServer.getBiomeRegistry();
private final static int PLAINS_ID = BIOME_REGISTRY.getId(NamespaceID.from("minecraft:plains")); private final static int PLAINS_ID = BIOME_REGISTRY.getId(Key.key("minecraft:plains"));
private final File worldDir; private final File worldDir;
@ -178,7 +177,7 @@ public class AnvilRegionLoader {
if (blockName.equals("minecraft:air")) { if (blockName.equals("minecraft:air")) {
convertedPalette[i] = Block.AIR; convertedPalette[i] = Block.AIR;
} else { } else {
Block block = Objects.requireNonNull(Block.fromNamespaceId(blockName), "Unknown block " + blockName); Block block = Objects.requireNonNull(Block.fromKey(blockName), "Unknown block " + blockName);
// Properties // Properties
final Map<String, String> properties = new HashMap<>(); final Map<String, String> properties = new HashMap<>();
CompoundBinaryTag propertiesNBT = paletteEntry.getCompound("Properties"); CompoundBinaryTag propertiesNBT = paletteEntry.getCompound("Properties");
@ -205,7 +204,7 @@ public class AnvilRegionLoader {
int[] convertedPalette = new int[paletteTag.size()]; int[] convertedPalette = new int[paletteTag.size()];
for (int i = 0; i < convertedPalette.length; i++) { for (int i = 0; i < convertedPalette.length; i++) {
final String name = paletteTag.getString(i); final String name = paletteTag.getString(i);
int biomeId = BIOME_REGISTRY.getId(NamespaceID.from(name)); int biomeId = BIOME_REGISTRY.getId(Key.key(name));
if (biomeId == -1) biomeId = PLAINS_ID; if (biomeId == -1) biomeId = PLAINS_ID;
convertedPalette[i] = biomeId; convertedPalette[i] = biomeId;
} }

View File

@ -4,7 +4,6 @@ subprojects {
apply plugin: 'maven-publish' apply plugin: 'maven-publish'
group = 'ru.dragonestia.msb3' group = 'ru.dragonestia.msb3'
version = '3.0'
java { java {
toolchain { toolchain {

View File

@ -33,3 +33,7 @@ dependencyManagement {
mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}" mavenBom "com.vaadin:vaadin-bom:${vaadinVersion}"
} }
} }
sourcesJar.dependsOn({
vaadinPrepareFrontend
})

2
gradle.properties Normal file
View File

@ -0,0 +1,2 @@
MSB3_MAVEN_REPOSITORY=http://git.dragonestia.ru/api/packages/MSB/maven
MSB3_VERSION=3.0.1

View File

@ -1,3 +1,9 @@
ext {
ext.set("VERSION", '1.0.0')
}
version = ext.get("VERSION")
dependencies { dependencies {
api 'net.kyori:adventure-api:4.17.0' api 'net.kyori:adventure-api:4.17.0'
api 'net.kyori:adventure-text-minimessage:4.17.0' api 'net.kyori:adventure-text-minimessage:4.17.0'
@ -5,3 +11,32 @@ dependencies {
api 'team.unnamed:creative-serializer-minecraft:1.7.3' api 'team.unnamed:creative-serializer-minecraft:1.7.3'
api 'team.unnamed:creative-server:1.7.3' api 'team.unnamed:creative-server:1.7.3'
} }
publishing {
publications {
maven(MavenPublication) {
groupId = 'ru.dragonestia.msb3'
artifactId = 'resource-compiler'
version = project.ext.get("VERSION")
from components.java
}
}
repositories {
maven {
name = "Gitea"
url = uri(MSB3_MAVEN_REPOSITORY)
allowInsecureProtocol = true
credentials(HttpHeaderCredentials) {
name = "Authorization"
value = "token ${dragonestiaGiteaToken}"
}
authentication {
header(HttpHeaderAuthentication)
}
}
}
}