feat: implemented navigator

This commit is contained in:
Andrey Terentev 2025-03-10 00:27:48 +07:00
parent 2b007d9897
commit b0c71740a3
6 changed files with 227 additions and 4 deletions

View File

@ -7,16 +7,18 @@ import net.kyori.adventure.key.Key;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
import net.minestom.server.event.player.PlayerDisconnectEvent; 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.entity.PickableItem;
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.KeyedBossBarContext;
import ru.dragonestia.msb3.api.player.defaults.NavigatorContext;
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.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;
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.api.util.ResourceFromJar;
import ru.dragonestia.msb3.resource.Resources; import ru.dragonestia.msb3.resource.Resources;
import ru.dragonestia.msb3.resource.glyph.GlyphCharacterFactory; import ru.dragonestia.msb3.resource.glyph.GlyphCharacterFactory;
@ -103,7 +105,7 @@ public final class ServerBootstrap {
private void initDefaultGlyphs() { private void initDefaultGlyphs() {
ClassPreLoader.preload(BlackScreen.class); ClassPreLoader.preload(BlackScreen.class);
ClassPreLoader.preload(Navigator.class); ClassPreLoader.preload(Navigators.class);
ClassPreLoader.preload(KeyedBossBars.class); ClassPreLoader.preload(KeyedBossBars.class);
MonologueResources.registerAvatar(MonologueResources.DEFAULT, ResourceFromJar.of(ServerBootstrap.CLASS_LOADER, "glyphs/monologue/default_avatar.png")); 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 -> { MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> {
var player = (MsbPlayer) event.getPlayer(); 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(); player.initContexts();
}).addListener(PlayerSpawnEvent.class, event -> {
var player = (MsbPlayer) event.getPlayer();
player.emitSpawnSignalForContexts();
}).addListener(PlayerDisconnectEvent.class, event -> { }).addListener(PlayerDisconnectEvent.class, event -> {
var player = (MsbPlayer) event.getPlayer(); 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(); player.disposeContexts();
}); });
// Default contexts // Default contexts
PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new); PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::new);
PlayerContextManager.registerContext(NavigatorContext.class, NavigatorContext::new);
} }
} }

View File

@ -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() { public void disposeContexts() {
for (var entry: contexts.entrySet()) { for (var entry: contexts.entrySet()) {
var ctxKey = entry.getKey(); var ctxKey = entry.getKey();

View File

@ -14,6 +14,8 @@ public abstract class PlayerContext {
public abstract void init(); public abstract void init();
public void firstSpawn() {}
public abstract void dispose(); public abstract void dispose();
public static <T extends PlayerContext> T of(Player player, Class<T> clazz) { public static <T extends PlayerContext> T of(Player player, Class<T> clazz) {

View File

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

View File

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

View File

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