From b0c71740a3cff55fcecdb6a2de8f58f17928c762 Mon Sep 17 00:00:00 2001 From: ScarletRedMan Date: Mon, 10 Mar 2025 00:27:48 +0700 Subject: [PATCH] feat: implemented navigator --- .../msb3/api/boot/ServerBootstrap.java | 15 +- .../msb3/api/player/MsbPlayer.java | 9 ++ .../msb3/api/player/PlayerContext.java | 2 + .../api/player/defaults/NavigatorContext.java | 129 ++++++++++++++++++ .../msb3/api/ui/navigator/Navigator.java | 12 ++ .../msb3/api/ui/navigator/Navigators.java | 64 +++++++++ 6 files changed, 227 insertions(+), 4 deletions(-) create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/player/defaults/NavigatorContext.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigator.java create mode 100644 api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigators.java diff --git a/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java b/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java index 8de6f42..02b5174 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/boot/ServerBootstrap.java @@ -7,16 +7,18 @@ import net.kyori.adventure.key.Key; import net.minestom.server.MinecraftServer; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.PlayerDisconnectEvent; +import net.minestom.server.event.player.PlayerSpawnEvent; import ru.dragonestia.msb3.api.entity.PickableItem; import ru.dragonestia.msb3.api.item.ItemUtil; import ru.dragonestia.msb3.api.player.MsbPlayer; import ru.dragonestia.msb3.api.player.PlayerContextManager; import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext; +import ru.dragonestia.msb3.api.player.defaults.NavigatorContext; import ru.dragonestia.msb3.api.resource.DialogueResources; import ru.dragonestia.msb3.api.resource.MonologueResources; import ru.dragonestia.msb3.api.ui.BlackScreen; import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars; -import ru.dragonestia.msb3.api.ui.navigator.Navigator; +import ru.dragonestia.msb3.api.ui.navigator.Navigators; import ru.dragonestia.msb3.api.util.ResourceFromJar; import ru.dragonestia.msb3.resource.Resources; import ru.dragonestia.msb3.resource.glyph.GlyphCharacterFactory; @@ -103,7 +105,7 @@ public final class ServerBootstrap { private void initDefaultGlyphs() { ClassPreLoader.preload(BlackScreen.class); - ClassPreLoader.preload(Navigator.class); + ClassPreLoader.preload(Navigators.class); ClassPreLoader.preload(KeyedBossBars.class); MonologueResources.registerAvatar(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png")); @@ -160,16 +162,21 @@ public final class ServerBootstrap { MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> { var player = (MsbPlayer) event.getPlayer(); - log.info("Connected player {} (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); + log.info("Connected player '{}' (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); player.initContexts(); + }).addListener(PlayerSpawnEvent.class, event -> { + var player = (MsbPlayer) event.getPlayer(); + + player.emitSpawnSignalForContexts(); }).addListener(PlayerDisconnectEvent.class, event -> { var player = (MsbPlayer) event.getPlayer(); - log.info("Disconnected player {} (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); + log.info("Disconnected player '{}' (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); player.disposeContexts(); }); // Default contexts PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new); + PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new); } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/player/MsbPlayer.java b/api/src/main/java/ru/dragonestia/msb3/api/player/MsbPlayer.java index 475ba13..6470443 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/player/MsbPlayer.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/MsbPlayer.java @@ -35,6 +35,15 @@ public class MsbPlayer extends Player { } } + public void emitSpawnSignalForContexts() { + for (var entry: contexts.entrySet()) { + var ctxKey = entry.getKey(); + var ctx = entry.getValue(); + + UncheckedRunnable.runIgnoreException(ctx::firstSpawn); + } + } + public void disposeContexts() { for (var entry: contexts.entrySet()) { var ctxKey = entry.getKey(); diff --git a/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContext.java b/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContext.java index e1a43bd..c0e882a 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContext.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContext.java @@ -14,6 +14,8 @@ public abstract class PlayerContext { public abstract void init(); + public void firstSpawn() {} + public abstract void dispose(); public static T of(Player player, Class clazz) { diff --git a/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/NavigatorContext.java b/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/NavigatorContext.java new file mode 100644 index 0000000..6a8d5b3 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/NavigatorContext.java @@ -0,0 +1,129 @@ +package ru.dragonestia.msb3.api.player.defaults; + +import lombok.Setter; +import net.kyori.adventure.bossbar.BossBar; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.TextColor; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; +import net.minestom.server.event.player.PlayerMoveEvent; +import ru.dragonestia.msb3.api.player.MsbPlayer; +import ru.dragonestia.msb3.api.player.PlayerContext; +import ru.dragonestia.msb3.api.ui.bossbar.KeyedBossBars; +import ru.dragonestia.msb3.api.ui.navigator.Navigator; +import ru.dragonestia.msb3.api.ui.navigator.Navigators; +import ru.dragonestia.msb3.resource.glyph.GlyphComponentBuilder; +import ru.dragonestia.msb3.resource.glyph.GlyphImage; +import ru.dragonestia.msb3.resource.glyph.MinecraftFont; + +public class NavigatorContext extends PlayerContext implements Navigator { + + private final static Component EMPTY = Component.text(" "); + + private BossBar bossBar; + private Point target; + private String text; + private MinecraftFont.Text cachedText; + private boolean prevTargetEmpty = true; + @Setter private String format = "[ %s ]"; + @Setter private TextColor textColor = TextColor.color(0xF8FF91); + + public NavigatorContext(MsbPlayer player) { + super(player); + } + + @Override + public void init() {} + + @Override + public void firstSpawn() { + getPlayer().eventNode().addListener(PlayerMoveEvent.class, event -> update()); + + bossBar = KeyedBossBars.showText(getPlayer(), "msb3_navigator", EMPTY); + } + + @Override + public void dispose() { + target = null; + text = null; + } + + @Override + public void setTarget(Point point) { + setTarget(point, null); + } + + @Override + public synchronized void setTarget(Point target, String text) { + prevTargetEmpty = this.target == null; + this.target = target; + this.text = text; + + update(); + } + + @Override + public void removeTarget() { + setTarget(null); + } + + private void update() { + if (prevTargetEmpty && target == null) return; + + bossBar.name(render()); + } + + private Component render() { + if (target == null) { + return EMPTY; + } + + var builder = new GlyphComponentBuilder(); + + // Text + if (text != null) { + builder.setColorTo(textColor); + if (cachedText == null) { + cachedText = MinecraftFont.translateByLineNumber(0, format.formatted(text)); + } + builder.append((int) (cachedText.width() / -2.2), cachedText); + builder.resetColor(); + } + + // Arrow + builder.append(0, getArrowGlyph()); + + return builder.build(); + } + + private GlyphImage getArrowGlyph() { + var playerPos = getPlayer().getPosition(); + var angle = computeAngle(playerPos.asVec(), playerPos.direction(), Vec.fromPoint(target)); + + if (!(angle < -2.749) && !(angle >= 2.749)) { + if (angle < -1.963) return Navigators.GLYPH_DOWN_RIGHT; + if (angle < -1.178) return Navigators.GLYPH_RIGHT; + if (angle < -0.393) return Navigators.GLYPH_UP_RIGHT; + if (angle < 0.393) return Navigators.GLYPH_UP; + if (angle < 1.178) return Navigators.GLYPH_UP_LEFT; + if (angle < 1.963) return Navigators.GLYPH_LEFT; + if (angle < 2.749) return Navigators.GLYPH_DOWN_LEFT; + } + return Navigators.GLYPH_DOWN; + } + + private double computeAngle(Vec start, Vec direction, Vec destination) { + Vec destinationDirection = destination.sub(start); + double playerAngle = Math.atan2(direction.z(), direction.x()); + double locAngle = Math.atan2(destinationDirection.z(), destinationDirection.x()); + + double angle; + for (angle = playerAngle - locAngle; angle > Math.PI; angle -= (Math.PI * 2D)) {} + + while (angle < -Math.PI) { + angle += (Math.PI * 2D); + } + + return angle; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigator.java b/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigator.java new file mode 100644 index 0000000..268c762 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigator.java @@ -0,0 +1,12 @@ +package ru.dragonestia.msb3.api.ui.navigator; + +import net.minestom.server.coordinate.Point; + +public interface Navigator { + + void setTarget(Point target); + + void setTarget(Point target, String text); + + void removeTarget(); +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigators.java b/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigators.java new file mode 100644 index 0000000..cf6bec4 --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/ui/navigator/Navigators.java @@ -0,0 +1,64 @@ +package ru.dragonestia.msb3.api.ui.navigator; + +import net.kyori.adventure.key.Key; +import net.minestom.server.entity.Player; +import ru.dragonestia.msb3.api.player.PlayerContext; +import ru.dragonestia.msb3.api.player.defaults.NavigatorContext; +import ru.dragonestia.msb3.api.util.ResourceFromJar; +import ru.dragonestia.msb3.resource.Resources; +import ru.dragonestia.msb3.resource.glyph.GlyphImage; + +public class Navigators { + + public final static GlyphImage GLYPH_UP = Resources.createGlyph( + Key.key("msb3", "navigator/up"), + ResourceFromJar.of("glyphs/navigator/arrow/up.png"), + 16, + 0); + + public final static GlyphImage GLYPH_UP_RIGHT = Resources.createGlyph( + Key.key("msb3", "navigator/up_right"), + ResourceFromJar.of("glyphs/navigator/arrow/up_right.png"), + 16, + 0); + + public final static GlyphImage GLYPH_RIGHT = Resources.createGlyph( + Key.key("msb3", "navigator/right"), + ResourceFromJar.of("glyphs/navigator/arrow/right.png"), + 16, + 0); + + public final static GlyphImage GLYPH_DOWN_RIGHT = Resources.createGlyph( + Key.key("msb3", "navigator/down_right"), + ResourceFromJar.of("glyphs/navigator/arrow/down_right.png"), + 16, + 0); + + public final static GlyphImage GLYPH_DOWN = Resources.createGlyph( + Key.key("msb3", "navigator/down"), + ResourceFromJar.of("glyphs/navigator/arrow/down.png"), + 16, + 0); + + public final static GlyphImage GLYPH_DOWN_LEFT = Resources.createGlyph( + Key.key("msb3", "navigator/down_left"), + ResourceFromJar.of("glyphs/navigator/arrow/down_left.png"), + 16, + 0); + + public final static GlyphImage GLYPH_LEFT = Resources.createGlyph( + Key.key("msb3", "navigator/left"), + ResourceFromJar.of("glyphs/navigator/arrow/left.png"), + 16, + 0); + + public final static GlyphImage GLYPH_UP_LEFT = Resources.createGlyph( + Key.key("msb3", "navigator/up_left"), + ResourceFromJar.of("glyphs/navigator/arrow/up_left.png"), + 16, + 0); + + public static Navigator of(Player player) { + return PlayerContext.of(player, NavigatorContext.class); + } +}