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 f050e50..8de6f42 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 @@ -5,9 +5,13 @@ import lombok.experimental.UtilityClass; import lombok.extern.log4j.Log4j2; 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 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.resource.DialogueResources; import ru.dragonestia.msb3.api.resource.MonologueResources; import ru.dragonestia.msb3.api.ui.BlackScreen; @@ -78,11 +82,7 @@ public final class ServerBootstrap { compileResourcePack(); initializer.onResourcePackCompiled(Resources.getResourcePack()); - MinecraftServer.getGlobalEventHandler().addListener(PlayerDisconnectEvent.class, event -> { - var player = event.getPlayer(); - - KeyedBossBars.hideAll(player); - }); + initPlayerContextManager(); } private void initDefaultModules() { @@ -153,4 +153,23 @@ public final class ServerBootstrap { Resources.getResourcePack().language(Language.language(Key.key("minecraft", lang), locales)); } } + + private void initPlayerContextManager() { + MinecraftServer.getConnectionManager().setPlayerProvider(PlayerContextManager::create); + + MinecraftServer.getGlobalEventHandler().addListener(AsyncPlayerConfigurationEvent.class, event -> { + var player = (MsbPlayer) event.getPlayer(); + + log.info("Connected player {} (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); + player.initContexts(); + }).addListener(PlayerDisconnectEvent.class, event -> { + var player = (MsbPlayer) event.getPlayer(); + + log.info("Disconnected player {} (uuid: {}, address: {})", player.getUsername(), player.getUuid(), player.getPlayerConnection().getRemoteAddress()); + player.disposeContexts(); + }); + + // Default contexts + PlayerContextManager.registerContext(KeyedBossBarContext.class, KeyedBossBarContext::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 new file mode 100644 index 0000000..41779bf --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/MsbPlayer.java @@ -0,0 +1,45 @@ +package ru.dragonestia.msb3.api.player; + +import net.minestom.server.entity.Player; +import net.minestom.server.network.player.GameProfile; +import net.minestom.server.network.player.PlayerConnection; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; + +public class MsbPlayer extends Player { + + private final Map contexts = new HashMap<>(); + + public MsbPlayer(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) { + super(playerConnection, gameProfile); + } + + void putContext(PlayerContext ctx) { + contexts.put(ctx.getClass().getName(), ctx); + } + + @SuppressWarnings("unchecked") + public T getContext(Class clazz) { + return (T) contexts.get(clazz.getName()); + } + + public void initContexts() { + for (var entry: contexts.entrySet()) { + var ctxKey = entry.getKey(); + var ctx = entry.getValue(); + + ctx.init(); + } + } + + public void disposeContexts() { + for (var entry: contexts.entrySet()) { + var ctxKey = entry.getKey(); + var ctx = entry.getValue(); + + ctx.dispose(); + } + } +} 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 new file mode 100644 index 0000000..62fdedd --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContext.java @@ -0,0 +1,17 @@ +package ru.dragonestia.msb3.api.player; + +import lombok.Getter; + +@Getter +public abstract class PlayerContext { + + private final MsbPlayer player; + + public PlayerContext(MsbPlayer player) { + this.player = player; + } + + public abstract void init(); + + public abstract void dispose(); +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContextManager.java b/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContextManager.java new file mode 100644 index 0000000..d540deb --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/PlayerContextManager.java @@ -0,0 +1,45 @@ +package ru.dragonestia.msb3.api.player; + +import lombok.experimental.UtilityClass; +import lombok.extern.log4j.Log4j2; +import net.minestom.server.entity.Player; +import net.minestom.server.network.player.GameProfile; +import net.minestom.server.network.player.PlayerConnection; +import org.jetbrains.annotations.NotNull; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +@Log4j2 +@UtilityClass +public class PlayerContextManager { + + private final Map> contexts = new HashMap<>(); + + public MsbPlayer create(@NotNull PlayerConnection playerConnection, @NotNull GameProfile gameProfile) { + var player = new MsbPlayer(playerConnection, gameProfile); + + for (var entry: contexts.entrySet()) { + var ctxKey = entry.getKey(); + var ctx = entry.getValue().apply(player); + + player.putContext(ctx); + } + + return player; + } + + public void registerContext(Class clazz, Function ctxProvider) { + var key = clazz.getName(); + var prev = contexts.put(key, ctxProvider); + + if (prev != null) { + log.warn("Duplicate context registration for {}", key); + } + } + + public T getContext(Player player, Class clazz) { + return ((MsbPlayer) player).getContext(clazz); + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/KeyedBossBarContext.java b/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/KeyedBossBarContext.java new file mode 100644 index 0000000..6e4223c --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/player/defaults/KeyedBossBarContext.java @@ -0,0 +1,45 @@ +package ru.dragonestia.msb3.api.player.defaults; + +import net.kyori.adventure.bossbar.BossBar; +import ru.dragonestia.msb3.api.player.MsbPlayer; +import ru.dragonestia.msb3.api.player.PlayerContext; + +import java.util.HashMap; +import java.util.Map; + +public class KeyedBossBarContext extends PlayerContext { + + private final Map bossBars = new HashMap<>(); + + public KeyedBossBarContext(MsbPlayer player) { + super(player); + } + + @Override + public void init() { + + } + + @Override + public void dispose() { + hideAll(); + } + + public void hideAll() { + bossBars.forEach((barId, bossBar) -> getPlayer().hideBossBar(bossBar)); + } + + public void hide(String bossBarId) { + var bossBar = bossBars.remove(bossBarId); + if (bossBar == null) return; + + getPlayer().hideBossBar(bossBar); + } + + public BossBar show(String bossBarId, BossBar bossBar) { + var prev = bossBars.put(bossBarId, bossBar); + if (prev != null) getPlayer().hideBossBar(prev); + getPlayer().showBossBar(bossBar); + return bossBar; + } +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/ui/bossbar/KeyedBossBars.java b/api/src/main/java/ru/dragonestia/msb3/api/ui/bossbar/KeyedBossBars.java index 537ec7d..1605a12 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/ui/bossbar/KeyedBossBars.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/ui/bossbar/KeyedBossBars.java @@ -5,13 +5,11 @@ import net.kyori.adventure.bossbar.BossBar; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.minestom.server.entity.Player; +import ru.dragonestia.msb3.api.player.PlayerContextManager; +import ru.dragonestia.msb3.api.player.defaults.KeyedBossBarContext; import ru.dragonestia.msb3.api.util.ResourceFromJar; import ru.dragonestia.msb3.resource.Resources; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; - @UtilityClass public class KeyedBossBars { @@ -24,34 +22,23 @@ public class KeyedBossBars { ResourceFromJar.of("glyphs/navigator/notched_20_progress.png")); } - private final Map> bars = new ConcurrentHashMap<>(); - public void hideAll(Player player) { - var map = bars.remove(player.getUuid()); - if (map == null) return; - - map.forEach((barId, bossBar) -> player.hideBossBar(bossBar)); + contextOf(player).hideAll(); } public void hide(Player player, String bossBarId) { - var map = bars.get(player.getUuid()); - if (map == null) return; - - var bossBar = map.remove(bossBarId); - if (bossBar == null) return; - - player.hideBossBar(bossBar); + contextOf(player).hide(bossBarId); } public BossBar show(Player player, String bossBarId, BossBar bossBar) { - var playerBars = bars.computeIfAbsent(player.getUuid(), uuid -> new ConcurrentHashMap<>()); - var prev = playerBars.put(bossBarId, bossBar); - if (prev != null) player.hideBossBar(prev); - player.showBossBar(bossBar); - return bossBar; + return contextOf(player).show(bossBarId, bossBar); } public BossBar showText(Player player, String bossBarId, Component text) { return show(player, bossBarId, BossBar.bossBar(text, 1, BossBar.Color.WHITE, BossBar.Overlay.PROGRESS)); } + + private KeyedBossBarContext contextOf(Player player) { + return PlayerContextManager.getContext(player, KeyedBossBarContext.class); + } }