From 591a56dd48be9617646963973096e06d7fce3db3 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 18:02:28 -0400 Subject: [PATCH 01/29] Register custom event --- src/main/java/net/cytonic/cytosis/events/EventHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/cytonic/cytosis/events/EventHandler.java b/src/main/java/net/cytonic/cytosis/events/EventHandler.java index 75c8a9a5..9da79601 100644 --- a/src/main/java/net/cytonic/cytosis/events/EventHandler.java +++ b/src/main/java/net/cytonic/cytosis/events/EventHandler.java @@ -192,5 +192,8 @@ private void setupInternalListeners() { GLOBAL_HANDLER.addListener(ClientPingServerEvent.class, (this::handleEvent)); GLOBAL_HANDLER.addListener(ServerListPingEvent.class, (this::handleEvent)); GLOBAL_HANDLER.addListener(ServerTickMonitorEvent.class, (this::handleEvent)); + + // Cytosis Events + GLOBAL_HANDLER.addListener(PlayerRankSetupEvent.class, (this::handleEvent)); } } From 693d8c167a53e6ee5a4bc6de84c295c17f5c915c Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 18:04:39 -0400 Subject: [PATCH 02/29] Reformatting --- .../net/cytonic/cytosis/events/ServerEventListeners.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java index 0e580c0d..d3012df6 100644 --- a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java +++ b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java @@ -5,10 +5,8 @@ import net.cytonic.cytosis.logging.Logger; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; -import net.minestom.server.event.Event; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.PlayerChatEvent; -import net.minestom.server.event.player.PlayerChatEvent; import net.minestom.server.event.player.PlayerSpawnEvent; public class ServerEventListeners { @@ -31,10 +29,10 @@ public static void initServerEvents() { }))); Logger.info("Registering player chat event."); - Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class,event ->{ + Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class, event -> { final Player player = event.getPlayer(); if (CytosisSettings.LOG_PLAYER_CHAT) - Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(),event.getMessage()); + Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(), event.getMessage()); })); } } \ No newline at end of file From 2cf604a1b7668760713ace655e2d8a270f972378 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:23:21 -0400 Subject: [PATCH 03/29] Delete operator command as it isn't used anymore --- .../cytosis/commands/OperatorCommand.java | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java diff --git a/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java b/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java deleted file mode 100644 index c8f3aa14..00000000 --- a/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.cytonic.cytosis.commands; - -import net.cytonic.cytosis.Cytosis; -import net.kyori.adventure.text.Component; -import net.minestom.server.command.CommandParser; -import net.minestom.server.command.builder.Command; -import net.minestom.server.command.builder.arguments.ArgumentType; -import net.minestom.server.entity.Player; -import java.util.List; - -public class OperatorCommand extends Command { - - public OperatorCommand() { - super("op"); - setCondition((sender, commandString) -> sender.hasPermission("cytosis.commands.operator")); - - var playerArg = ArgumentType.Word("player").from(getPlayerNames()); - playerArg.setCallback((sender, exception) -> { - final String input = exception.getInput(); - sender.sendMessage(STR."The player \{input} is invalid!"); - }); - - setDefaultExecutor((sender, context) -> { - if (sender == Cytosis.getConsoleSender()) { - CommandParser.Result result = Cytosis.getCommandManager().parseCommand(sender, context.getInput()); - System.out.println(result.args()); - return; - } - final String playerName = context.get(playerArg); - Cytosis.getPlayer(playerName).ifPresentOrElse(Cytosis::opPlayer, () -> sender.sendMessage(Component.text(STR."Couldn't find the player '\{playerName}'. Did you spell their name right?"))); - }); - } - - private String[] getPlayerNames() { - List players = Cytosis.getOnlinePlayers().stream().toList(); - String[] names = new String[players.size()]; - for (int i = 0; i < players.size(); i++) { - names[i] = players.get(i).getUsername(); - } - return names; - } -} \ No newline at end of file From ae579508372c0900b6d9f922d55ab7f1951653ea Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:24:10 -0400 Subject: [PATCH 04/29] Add events for handling ranks --- .../cytonic/cytosis/events/ServerEventListeners.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java index d3012df6..a8bff37d 100644 --- a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java +++ b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java @@ -7,6 +7,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.PlayerChatEvent; +import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerSpawnEvent; public class ServerEventListeners { @@ -26,13 +27,21 @@ public static void initServerEvents() { if (CytosisSettings.LOG_PLAYER_IPS) Logger.info(STR."\{event.getPlayer().getUsername()} (\{event.getPlayer().getUuid()}) joined with the ip: \{player.getPlayerConnection().getServerAddress()}"); else Logger.info(STR."\{event.getPlayer().getUsername()} (\{event.getPlayer().getUuid()}) joined."); + + Cytosis.getRankManager().addPlayer(player); }))); Logger.info("Registering player chat event."); - Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class, event -> { + Cytosis.getEventHandler().registerListener(new EventListener<>("core:player-chat", false, 1, PlayerChatEvent.class, event -> { final Player player = event.getPlayer(); if (CytosisSettings.LOG_PLAYER_CHAT) Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(), event.getMessage()); })); + + Logger.info("Registering player disconnect event."); + Cytosis.getEventHandler().registerListener(new EventListener<>("core:player-disconnect", false, 1, PlayerDisconnectEvent.class, event -> { + final Player player = event.getPlayer(); + Cytosis.getRankManager().removePlayer(player); + })); } } \ No newline at end of file From 78158a6f817221b9a08bf91f02d4a77176635e2d Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:24:31 -0400 Subject: [PATCH 05/29] Create events for rank activities --- .../cytosis/events/ranks/RankChangeEvent.java | 31 +++++++++++++++++++ .../cytosis/events/ranks/RankSetupEvent.java | 30 ++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java create mode 100644 src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java diff --git a/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java b/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java new file mode 100644 index 00000000..325788c1 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java @@ -0,0 +1,31 @@ +package net.cytonic.cytosis.events.ranks; + +import lombok.Getter; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerEvent; + +@Getter +public class RankChangeEvent implements PlayerEvent, CancellableEvent { + private boolean canceled; + private final PlayerRank newRank; + private final PlayerRank oldRank; + private final Player player; + + public RankChangeEvent(PlayerRank newRank, PlayerRank oldRank, Player player) { + this.newRank = newRank; + this.oldRank = oldRank; + this.player = player; + } + + @Override + public boolean isCancelled() { + return canceled; + } + + @Override + public void setCancelled(boolean cancel) { + this.canceled = cancel; + } +} diff --git a/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java b/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java new file mode 100644 index 00000000..fd06ecf1 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java @@ -0,0 +1,30 @@ +package net.cytonic.cytosis.events.ranks; + +import lombok.Getter; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerEvent; + +@Getter +public class RankSetupEvent implements PlayerEvent, CancellableEvent { + + private final Player player; + private final PlayerRank rank; + private boolean canceled; + + public RankSetupEvent(Player player, PlayerRank rank) { + this.player = player; + this.rank = rank; + } + + @Override + public boolean isCancelled() { + return canceled; + } + + @Override + public void setCancelled(boolean canceled) { + this.canceled = canceled; + } +} From bfda3cd1891ee2b9358f15d53cb2bff762d87760 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:00 -0400 Subject: [PATCH 06/29] Create Rank enum --- .../net/cytonic/cytosis/ranks/PlayerRank.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java diff --git a/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java b/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java new file mode 100644 index 00000000..15783d63 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java @@ -0,0 +1,70 @@ +package net.cytonic.cytosis.ranks; + +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import static net.cytonic.cytosis.utils.MiniMessageTemplate.MM; + +/** + * This class holds constants that represent player ranks + */ +@Getter +public enum PlayerRank { + // Staff ranks + OWNER(MM."[OWNER]", NamedTextColor.RED, NamedTextColor.WHITE, new String[]{"*"}), + ADMIN(MM."[ADMIN]", NamedTextColor.RED, NamedTextColor.WHITE, new String[]{"*"}), + MODERATOR(MM."[MOD]", NamedTextColor.GREEN, NamedTextColor.WHITE, new String[]{}), + HELPER(MM."[HELPER]", NamedTextColor.AQUA, NamedTextColor.WHITE, new String[]{}), + + // player ranks + ELYSIAN(MM."[ELYSIAN]", NamedTextColor.GOLD, NamedTextColor.WHITE, new String[]{}), + CELESTIAL(MM."[CELESTIAL]", NamedTextColor.DARK_AQUA, NamedTextColor.WHITE, new String[]{}), + MASTER(MM."[MASTER]", NamedTextColor.DARK_RED, NamedTextColor.WHITE, new String[]{}), + VALIENT(MM."[VALIENT]", NamedTextColor.DARK_GREEN, NamedTextColor.WHITE, new String[]{}), + NOBLE(MM."[NOBLE]", NamedTextColor.DARK_PURPLE, NamedTextColor.WHITE, new String[]{}), + DEFAULT(MM."[DEFAULT]", NamedTextColor.GRAY, NamedTextColor.GRAY, new String[]{"cytosis.commands.gamemode"}); + + private final Component prefix; + private final NamedTextColor teamColor; + private final NamedTextColor chatColor; + private final String[] permissions; + + /** + * Creates a Rank object + * + * @param prefix The prefix shown in chat and above the player's head + * @param teamColor The color of the player's name tag, and their name in chat + * @param chatColor The color of the player's chat message + * @param permissions The permissions that rank has + */ + PlayerRank(Component prefix, NamedTextColor teamColor, NamedTextColor chatColor, String[] permissions) { + this.prefix = prefix; + this.teamColor = teamColor; + this.chatColor = chatColor; + this.permissions = permissions; + } + + public static boolean isDemotion(PlayerRank currentRole, PlayerRank newRole) { + return newRole.ordinal() > currentRole.ordinal(); + } + + public static boolean isPromotion(PlayerRank currentRole, PlayerRank newRole) { + return newRole.ordinal() < currentRole.ordinal(); + } + + public static boolean canChangeRank(PlayerRank currentUserRole, PlayerRank targetOriginalRole, PlayerRank targetNewRole) { + if (currentUserRole == OWNER) return true; + if (isDemotion(targetOriginalRole, targetNewRole)) { + return currentUserRole.ordinal() <= targetOriginalRole.ordinal(); + } + + if (isPromotion(targetOriginalRole, targetNewRole)) { + return currentUserRole.ordinal() <= targetOriginalRole.ordinal(); + } + + // If it's neither promotion nor demotion, it's an invalid operation + return false; + } + +} From 9c98f32304fa73da0cf19e721c06caebb502ed05 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:10 -0400 Subject: [PATCH 07/29] Create command for changing ranks --- .../cytonic/cytosis/commands/RankCommand.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/commands/RankCommand.java diff --git a/src/main/java/net/cytonic/cytosis/commands/RankCommand.java b/src/main/java/net/cytonic/cytosis/commands/RankCommand.java new file mode 100644 index 00000000..906367f3 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/commands/RankCommand.java @@ -0,0 +1,82 @@ +package net.cytonic.cytosis.commands; + +import net.cytonic.cytosis.Cytosis; +import net.cytonic.cytosis.logging.Logger; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.arguments.ArgumentEnum; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.command.builder.suggestion.SuggestionEntry; +import net.minestom.server.entity.Player; + +import java.util.Locale; + +import static net.cytonic.cytosis.utils.MiniMessageTemplate.MM; + +public class RankCommand extends Command { + + public RankCommand() { + super("rank"); + setCondition((sender, _) -> sender.hasPermission("cytosis.commands.rank")); + setDefaultExecutor((sender, _) -> sender.sendMessage(MM."You must specify a valid rank and player!")); + + var rankArg = ArgumentType.Enum("rank", PlayerRank.class).setFormat(ArgumentEnum.Format.LOWER_CASED); + rankArg.setCallback((sender, exception) -> sender.sendMessage(STR."The rank \{exception.getInput()} is invalid!")); + rankArg.setSuggestionCallback((_, _, suggestion) -> { + for (PlayerRank rank : PlayerRank.values()) { + suggestion.addEntry(new SuggestionEntry(rank.name().toLowerCase(Locale.ROOT), rank.getPrefix())); + } + }); + + var playerArg = ArgumentType.Entity("player").onlyPlayers(true); + playerArg.setCallback((sender, exception) -> sender.sendMessage(STR."The player \{exception.getInput()} doesn't exist!")); + + + var group = ArgumentType.Group("rank-group", playerArg, rankArg); + + addSyntax((sender, context) -> { + final Player player = context.get(group).get(playerArg).findFirstPlayer(sender); + final PlayerRank newRank = context.get(group).get(rankArg); + if (player == null) { + sender.sendMessage(MM."The player \{context.get(group).getRaw("player")} doesn't exist!"); + return; + } + + if (player == sender) { + sender.sendMessage(MM."You cannot change your own rank!"); + return; + } + + Cytosis.getDatabaseManager().getDatabase().getPlayerRank(player.getUuid()).whenComplete((rank, throwable) -> { + if (throwable != null) { + sender.sendMessage("An error occured whilst fetching the old rank!"); + return; + } + + // if it's a console we don't care (There isn't a console impl) + if (sender instanceof Player s) { + PlayerRank senderRank = Cytosis.getRankManager().getPlayerRank(s.getUuid()).orElseThrow(); + if (!PlayerRank.canChangeRank(senderRank, rank, newRank)) { + sender.sendMessage(MM."You cannot do this!"); + return; + } + } + + setRank(player, newRank, sender); + }); + }, group); + } + + private void setRank(Player player, PlayerRank rank, CommandSender sender) { + Cytosis.getDatabaseManager().getDatabase().setPlayerRank(player.getUuid(), rank).whenComplete((_, t) -> { + if (t != null) { + sender.sendMessage(MM."An error occurred whilst setting \{player.getUsername()}'s rank! Check the console for more details."); + Logger.error(STR."An error occurred whilst setting \{player.getUsername()}'s rank! Check the console for more details.", t); + return; + } + Cytosis.getRankManager().changeRank(player, rank); + sender.sendMessage(MM."Successfully updated \{player.getUsername()}'s rank!"); + }); + } +} From 0261cdd3efb21c83ea9da98c049ac3a70662fc08 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:22 -0400 Subject: [PATCH 08/29] Create class for handling interactions with ranks --- .../cytonic/cytosis/ranks/RankManager.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/ranks/RankManager.java diff --git a/src/main/java/net/cytonic/cytosis/ranks/RankManager.java b/src/main/java/net/cytonic/cytosis/ranks/RankManager.java new file mode 100644 index 00000000..6028c580 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/ranks/RankManager.java @@ -0,0 +1,86 @@ +package net.cytonic.cytosis.ranks; + +import net.cytonic.cytosis.Cytosis; +import net.cytonic.cytosis.events.ranks.RankChangeEvent; +import net.cytonic.cytosis.events.ranks.RankSetupEvent; +import net.cytonic.cytosis.logging.Logger; +import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.Player; +import net.minestom.server.event.EventDispatcher; +import net.minestom.server.network.packet.server.play.TeamsPacket; +import net.minestom.server.permission.Permission; +import net.minestom.server.scoreboard.Team; +import net.minestom.server.scoreboard.TeamBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class RankManager { + ConcurrentHashMap rankMap = new ConcurrentHashMap<>(); + ConcurrentHashMap teamMap = new ConcurrentHashMap<>(); + + public void init() { + for (PlayerRank value : PlayerRank.values()) { + Team team = new TeamBuilder(value.ordinal() + value.name(), MinecraftServer.getTeamManager()) + .collisionRule(TeamsPacket.CollisionRule.NEVER) + .teamColor(value.getTeamColor()) + .prefix(value.getPrefix().appendSpace()) + .build(); + + teamMap.put(value, team); + } + } + + public void addPlayer(Player player) { + // cache the rank + Cytosis.getDatabaseManager().getDatabase().getPlayerRank(player.getUuid()).whenComplete((playerRank, throwable) -> { + if (throwable != null) { + Logger.error(STR."An error occured whilst fetching \{player.getUsername()}'s rank!", throwable); + return; + } + var event = new RankSetupEvent(player, playerRank); + EventDispatcher.call(event); + if (event.isCanceled()) return; + rankMap.put(player.getUuid(), playerRank); + setupCosmetics(player, playerRank); + }); + } + + public void changeRank(Player player, PlayerRank rank) { + if (!rankMap.containsKey(player.getUuid())) + throw new IllegalStateException(STR."The player \{player.getUsername()} is not yet initialized! Call addPlayer(Player) first!"); + PlayerRank old = rankMap.get(player.getUuid()); + var event = new RankChangeEvent(old, rank, player); + EventDispatcher.call(event); + if (event.isCanceled()) return; + removePermissions(player, old.getPermissions()); + rankMap.put(player.getUuid(), rank); + setupCosmetics(player, rank); + } + + private void setupCosmetics(Player player, PlayerRank rank) { + addPermissions(player, rank.getPermissions()); + teamMap.get(rank).addMember(player.getUsername()); + player.setCustomName(rank.getPrefix().appendSpace().append(player.getName())); + Cytosis.getCommandHandler().recalculateCommands(player); + } + + public void removePlayer(Player player) { + removePermissions(player, rankMap.getOrDefault(player.getUuid(), PlayerRank.DEFAULT).getPermissions()); + rankMap.remove(player.getUuid()); + } + + public final void addPermissions(@NotNull final Player player, @NotNull final String... nodes) { + for (String node : nodes) player.addPermission(new Permission(node)); + } + + public final void removePermissions(@NotNull final Player player, @NotNull final String... nodes) { + for (String node : nodes) player.removePermission(node); + } + + public Optional getPlayerRank(UUID uuid) { + return Optional.ofNullable(rankMap.get(uuid)); + } +} From 0fa6d4cf181ae8dd2831a6e500521ca4c3ed9676 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:38 -0400 Subject: [PATCH 09/29] Formatting --- .../java/net/cytonic/cytosis/commands/GamemodeCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java b/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java index 79f853f8..4e0ec15c 100644 --- a/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java +++ b/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java @@ -13,15 +13,15 @@ public class GamemodeCommand extends Command { public GamemodeCommand() { super("gamemode", "gm"); - setCondition((sender, commandString) -> sender.hasPermission("cytosis.commands.gamemode")); - setDefaultExecutor((sender, context) -> sender.sendMessage(Component.text("You must specify a gamemode!", NamedTextColor.RED))); + setCondition((sender, _) -> sender.hasPermission("cytosis.commands.gamemode")); + setDefaultExecutor((sender, _) -> sender.sendMessage(Component.text("You must specify a gamemode!", NamedTextColor.RED))); // using a gamemode as an argument var gameModeArgument = ArgumentType.Enum("gamemode", GameMode.class).setFormat(ArgumentEnum.Format.LOWER_CASED); gameModeArgument.setCallback((sender, exception) -> sender.sendMessage(STR."The gamemode \{exception.getInput()} is invalid!")); var shorthand = ArgumentType.Word("shorthand").from("c", "s", "sv", "a", "0", "1", "2", "3"); - shorthand.setSuggestionCallback((sender, context, suggestion) -> { + shorthand.setSuggestionCallback((_, _, suggestion) -> { suggestion.addEntry(new SuggestionEntry("c", Component.text("Represents the Creative gamemode"))); suggestion.addEntry(new SuggestionEntry("s", Component.text("Represents the Spectator gamemode"))); suggestion.addEntry(new SuggestionEntry("sv", Component.text("Represents the Survival gamemode"))); From 14a975457cc968abb0c1fcdee54386b0f42d22c7 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:53 -0400 Subject: [PATCH 10/29] Register new events with the event handler --- src/main/java/net/cytonic/cytosis/events/EventHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/events/EventHandler.java b/src/main/java/net/cytonic/cytosis/events/EventHandler.java index 9da79601..c278f5e8 100644 --- a/src/main/java/net/cytonic/cytosis/events/EventHandler.java +++ b/src/main/java/net/cytonic/cytosis/events/EventHandler.java @@ -1,5 +1,7 @@ package net.cytonic.cytosis.events; +import net.cytonic.cytosis.events.ranks.RankChangeEvent; +import net.cytonic.cytosis.events.ranks.RankSetupEvent; import net.minestom.server.event.Event; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.book.EditBookEvent; @@ -194,6 +196,7 @@ private void setupInternalListeners() { GLOBAL_HANDLER.addListener(ServerTickMonitorEvent.class, (this::handleEvent)); // Cytosis Events - GLOBAL_HANDLER.addListener(PlayerRankSetupEvent.class, (this::handleEvent)); + GLOBAL_HANDLER.addListener(RankSetupEvent.class, (this::handleEvent)); + GLOBAL_HANDLER.addListener(RankChangeEvent.class, (this::handleEvent)); } } From b24cab44d2b983736dad123b6ddb897a57c43a07 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:13 -0400 Subject: [PATCH 11/29] Tidying up --- .../net/cytonic/cytosis/DatabaseManager.java | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/DatabaseManager.java b/src/main/java/net/cytonic/cytosis/DatabaseManager.java index 2831affa..b9cc6a6c 100644 --- a/src/main/java/net/cytonic/cytosis/DatabaseManager.java +++ b/src/main/java/net/cytonic/cytosis/DatabaseManager.java @@ -1,34 +1,20 @@ package net.cytonic.cytosis; +import lombok.Getter; import net.cytonic.cytosis.data.Database; -import net.cytonic.cytosis.logging.Logger; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +@Getter public class DatabaseManager { - private final ExecutorService worker; private Database database; - public DatabaseManager() { - this.worker = Executors.newSingleThreadExecutor(Thread.ofVirtual().name("CytosisManagerWorker") - .uncaughtExceptionHandler((t, e) -> Logger.error(STR."An uncaught exception occoured on the thread: \{t.getName()}", e)).factory()); - } - public void shutdown() { - worker.submit(() -> { - database.disconnect(); - Logger.info("Good night!"); - }); + database.disconnect(); } public void setupDatabase() { - worker.submit(() -> { - database = new Database(); - database.connect(); - database.createChatTable(); - }); + database = new Database(); + database.connect(); + database.createTables(); } - - public Database getDatabase() {return database;} } \ No newline at end of file From 0b503aff3e8aafa947a7b859143702f2a2813201 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:30 -0400 Subject: [PATCH 12/29] Add rank database queries --- .../net/cytonic/cytosis/data/Database.java | 98 ++++++++++++++++--- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/data/Database.java b/src/main/java/net/cytonic/cytosis/data/Database.java index 34713025..f68b7377 100644 --- a/src/main/java/net/cytonic/cytosis/data/Database.java +++ b/src/main/java/net/cytonic/cytosis/data/Database.java @@ -2,12 +2,13 @@ import net.cytonic.cytosis.config.CytosisSettings; import net.cytonic.cytosis.logging.Logger; +import net.cytonic.cytosis.ranks.PlayerRank; import net.minestom.server.MinecraftServer; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; +import org.jetbrains.annotations.NotNull; + +import java.sql.*; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -33,8 +34,7 @@ public Database() { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { - Logger.error("Failed to load database driver"); - Logger.error(e.toString()); + Logger.error("Failed to load database driver", e); } } @@ -46,9 +46,10 @@ public void connect() { worker.submit(() -> { if (!isConnected()) { try { - connection = DriverManager.getConnection(STR."jdbc:mysql://\{host}:\{port}/\{database}?useSSL=\{ssl}&autoReconnect=true", username, password); + connection = DriverManager.getConnection(STR."jdbc:mysql://\{host}:\{port}/\{database}?useSSL=\{ssl}&autoReconnect=true&allowPublicKeyRetrieval=true", username, password); + Logger.info("Successfully connected to the MySQL Database!"); } catch (SQLException e) { - Logger.error("Invalid Database Credentials!",e); + Logger.error("Invalid Database Credentials!", e); MinecraftServer.stopCleanly(); } } @@ -63,17 +64,22 @@ public void disconnect() { connection.close(); Logger.info("Database connection closed!"); } catch (SQLException e) { - Logger.error("An error occurred whilst disconnecting from the database. Please report the following stacktrace to Foxikle: ",e); + Logger.error("An error occurred whilst disconnecting from the database. Please report the following stacktrace to Foxikle: ", e); } } }); } + public void createTables() { + createRanksTable(); + createChatTable(); + } + private Connection getConnection() { return connection; } - public void createChatTable() { + private void createChatTable() { worker.submit(() -> { if (isConnected()) { PreparedStatement ps; @@ -81,17 +87,85 @@ public void createChatTable() { ps = getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS cytonicchat (id INT NOT NULL AUTO_INCREMENT, timestamp TIMESTAMP, uuid VARCHAR(36), message TEXT, PRIMARY KEY(id))"); ps.executeUpdate(); } catch (SQLException e) { - Logger.error("An error occoured whilst fetching data from the database. Please report the following stacktrace to Foxikle:",e); + Logger.error("An error occoured whilst fetching data from the database. Please report the following stacktrace to Foxikle:", e); + } + } + }); + } + + private void createRanksTable() { + worker.submit(() -> { + if (isConnected()) { + PreparedStatement ps; + try { + ps = getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS cytonic_ranks (uuid VARCHAR(36), rank_id VARCHAR(16), PRIMARY KEY(uuid))"); + ps.executeUpdate(); + } catch (SQLException e) { + Logger.error("An error occoured whilst creating the `cytonic_ranks` table.", e); + } + } + }); + } + + /** + * Gets the player's rank. This returns {@link PlayerRank#DEFAULT} even if the player doesn't exist. + * + * @param uuid the player to fetch the id from + * @return The player's {@link PlayerRank} + * @throws IllegalStateException if the database isn't connected + */ + @NotNull + public CompletableFuture getPlayerRank(@NotNull final UUID uuid) { + CompletableFuture future = new CompletableFuture<>(); + if (!isConnected()) + throw new IllegalStateException("The database must have an open connection to fetch a player's rank!"); + worker.submit(() -> { + String id = "DEFAULT"; + try { + PreparedStatement ps = connection.prepareStatement("SELECT rank_id FROM cytonic_ranks WHERE uuid = ?"); + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + id = rs.getString("rank_id"); } + future.complete(PlayerRank.valueOf(id)); + } catch (SQLException e) { + Logger.error(STR."An error occurred whilst fetching the rank of '\{uuid}'"); + } + }); + return future; + } + + /** + * Sets the given player's rank to the specified rank. + * + * @param uuid The player's UUID + * @param rank The player's rank constant + * @throws IllegalStateException if the database isn't connected + */ + public CompletableFuture setPlayerRank(UUID uuid, PlayerRank rank) { + if (!isConnected()) + throw new IllegalStateException("The database must have an open connection to set a player's rank!"); + CompletableFuture future = new CompletableFuture<>(); + worker.submit(() -> { + try { + PreparedStatement ps = connection.prepareStatement("INSERT INTO cytonic_ranks (uuid, rank_id) VALUES (?, ?) ON DUPLICATE KEY UPDATE rank_id = VALUES(rank_id)"); + ps.setString(1, uuid.toString()); + ps.setString(2, rank.name()); + ps.executeUpdate(); + future.complete(null); + } catch (SQLException e) { + Logger.error(STR."An error occurred whilst setting the rank of '\{uuid}'"); } }); + return future; } public void addChat(UUID uuid, String message) { worker.submit(() -> { PreparedStatement ps; try { - ps = connection.prepareStatement("INSERT INTO cytonicchat (timestamp,uuid,message) VALUES (CURRENT_TIMESTAMP,?,?)"); + ps = connection.prepareStatement("INSERT INTO cytonicchat (timestamp, uuid, message) VALUES (CURRENT_TIMESTAMP,?,?)"); ps.setString(1, uuid.toString()); ps.setString(2, message); ps.executeUpdate(); From f5935370275d622e26f7923609503a9223fe8add Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:46 -0400 Subject: [PATCH 13/29] Just a bunch of stuff --- .../java/net/cytonic/cytosis/Cytosis.java | 118 +++++++++--------- 1 file changed, 56 insertions(+), 62 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/Cytosis.java b/src/main/java/net/cytonic/cytosis/Cytosis.java index 7d74daff..2c7d5a0d 100644 --- a/src/main/java/net/cytonic/cytosis/Cytosis.java +++ b/src/main/java/net/cytonic/cytosis/Cytosis.java @@ -1,5 +1,6 @@ package net.cytonic.cytosis; +import lombok.Getter; import net.cytonic.cytosis.commands.CommandHandler; import net.cytonic.cytosis.config.CytosisSettings; import net.cytonic.cytosis.events.EventHandler; @@ -7,6 +8,7 @@ import net.cytonic.cytosis.files.FileManager; import net.cytonic.cytosis.logging.Logger; import net.cytonic.cytosis.messaging.MessagingManager; +import net.cytonic.cytosis.ranks.RankManager; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; import net.minestom.server.command.ConsoleSender; @@ -19,24 +21,38 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.network.ConnectionManager; import net.minestom.server.permission.Permission; + import java.util.*; +@Getter public class Cytosis { // manager stuff - private static MinecraftServer MINECRAFT_SERVER; - private static InstanceManager INSTANCE_MANAGER; - private static InstanceContainer DEFAULT_INSTANCE; - private static EventHandler EVENT_HANDLER; - private static ConnectionManager CONNECTION_MANAGER; - private static CommandManager COMMAND_MANAGER; - private static CommandHandler COMMAND_HANDLER; - private static FileManager FILE_MANAGER; - private static DatabaseManager DATABASE_MANAGER; - private static MessagingManager MESSAGE_MANAGER; - private static ConsoleSender CONSOLE_SENDER; - private static int SERVER_PORT; + @Getter + private static MinecraftServer minecraftServer; + @Getter + private static InstanceManager instanceManager; + @Getter + private static InstanceContainer defaultInstance; + @Getter + private static EventHandler eventHandler; + @Getter + private static ConnectionManager connectionManager; + @Getter + private static CommandManager commandManager; + @Getter + private static CommandHandler commandHandler; + @Getter + private static FileManager fileManager; + @Getter + private static DatabaseManager databaseManager; + @Getter + private static MessagingManager messagingManager; + @Getter + private static ConsoleSender consoleSender; + @Getter + private static RankManager rankManager; private static List FLAGS; @@ -46,37 +62,37 @@ public static void main(String[] args) { long start = System.currentTimeMillis(); // Initialize the server Logger.info("Starting server."); - MINECRAFT_SERVER = MinecraftServer.init(); + minecraftServer = MinecraftServer.init(); MinecraftServer.setBrandName("Cytosis"); Logger.info("Starting instance manager."); - INSTANCE_MANAGER = MinecraftServer.getInstanceManager(); + instanceManager = MinecraftServer.getInstanceManager(); Logger.info("Starting connection manager."); - CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); + connectionManager = MinecraftServer.getConnectionManager(); Logger.info("Starting manager."); - DATABASE_MANAGER = new DatabaseManager(); + databaseManager = new DatabaseManager(); // Commands Logger.info("Starting command manager."); - COMMAND_MANAGER = MinecraftServer.getCommandManager(); + commandManager = MinecraftServer.getCommandManager(); Logger.info("Setting console command sender."); - CONSOLE_SENDER = COMMAND_MANAGER.getConsoleSender(); - CONSOLE_SENDER.addPermission(new Permission("*")); + consoleSender = commandManager.getConsoleSender(); + consoleSender.addPermission(new Permission("*")); // instances Logger.info("Creating instance container"); - DEFAULT_INSTANCE = INSTANCE_MANAGER.createInstanceContainer(); + defaultInstance = instanceManager.createInstanceContainer(); Logger.info("Creating file manager"); - FILE_MANAGER = new FileManager(); + fileManager = new FileManager(); // Everything after this point depends on config contents Logger.info("Initializing file manager"); - FILE_MANAGER.init().whenComplete((_, throwable) -> { + fileManager.init().whenComplete((_, throwable) -> { if (throwable != null) { Logger.error("An error occurred whilst initializing the file manager!", throwable); } else { @@ -91,29 +107,9 @@ public static void main(String[] args) { }); } - public static EventHandler getEventHandler() { - return EVENT_HANDLER; - } - - public static InstanceContainer getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - public static ConnectionManager getConnectionManager() { - return CONNECTION_MANAGER; - } - - public static CommandManager getCommandManager() { - return COMMAND_MANAGER; - } - - public static DatabaseManager getDatabaseManager() { - return DATABASE_MANAGER; - } - public static Set getOnlinePlayers() { Set players = new HashSet<>(); - INSTANCE_MANAGER.getInstances().forEach(instance -> players.addAll(instance.getPlayers())); + instanceManager.getInstances().forEach(instance -> players.addAll(instance.getPlayers())); return players; } @@ -140,10 +136,6 @@ public static void deopPlayer(Player player) { player.removePermission("*"); // remove every permission } - public static ConsoleSender getConsoleSender() { - return CONSOLE_SENDER; - } - public static void mojangAuth() { Logger.info("Initializing Mojang Authentication"); MojangAuth.init(); //VERY IMPORTANT! (This is online mode!) @@ -152,40 +144,42 @@ public static void mojangAuth() { public static void completeNonEssentialTasks(long start) { // basic world generator Logger.info("Generating basic world"); - DEFAULT_INSTANCE.setGenerator(unit -> unit.modifier().fillHeight(0, 1, Block.WHITE_STAINED_GLASS)); - DEFAULT_INSTANCE.setChunkSupplier(LightingChunk::new); + defaultInstance.setGenerator(unit -> unit.modifier().fillHeight(0, 1, Block.WHITE_STAINED_GLASS)); + defaultInstance.setChunkSupplier(LightingChunk::new); Logger.info("Setting up event handlers"); - EVENT_HANDLER = new EventHandler(MinecraftServer.getGlobalEventHandler()); - EVENT_HANDLER.init(); + eventHandler = new EventHandler(MinecraftServer.getGlobalEventHandler()); + eventHandler.init(); Logger.info("Initializing server events"); ServerEventListeners.initServerEvents(); Logger.info("Initializing database"); - DATABASE_MANAGER.setupDatabase(); + databaseManager.setupDatabase(); - MinecraftServer.getSchedulerManager().buildShutdownTask(() -> DATABASE_MANAGER.shutdown()); + MinecraftServer.getSchedulerManager().buildShutdownTask(() -> databaseManager.shutdown()); Logger.info("Initializing server commands"); - COMMAND_HANDLER = new CommandHandler(); - COMMAND_HANDLER.setupConsole(); - COMMAND_HANDLER.registerCystosisCommands(); + commandHandler = new CommandHandler(); + commandHandler.setupConsole(); + commandHandler.registerCystosisCommands(); - MESSAGE_MANAGER = new MessagingManager(); - MESSAGE_MANAGER.initialize().whenComplete((_, throwable) -> { + messagingManager = new MessagingManager(); + messagingManager.initialize().whenComplete((_, throwable) -> { if (throwable != null) { Logger.error("An error occurred whilst initializing the messaging manager!", throwable); } else { Logger.info("Messaging manager initialized!"); } }); - - SERVER_PORT = CytosisSettings.SERVER_PORT; + + Logger.info("Initializing Rank Manager"); + rankManager = new RankManager(); + rankManager.init(); // Start the server - Logger.info(STR."Server started on port \{SERVER_PORT}"); - MINECRAFT_SERVER.start("0.0.0.0", SERVER_PORT); + Logger.info(STR."Server started on port \{CytosisSettings.SERVER_PORT}"); + minecraftServer.start("0.0.0.0", CytosisSettings.SERVER_PORT); long end = System.currentTimeMillis(); Logger.info(STR."Server started in \{end - start}ms!"); From 3268582d8b8c78e08e8dba82a295a4b3836a3682 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:27:53 -0400 Subject: [PATCH 14/29] add method to recalculate command permissions, add rank command --- .../java/net/cytonic/cytosis/commands/CommandHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java b/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java index 8c42b9fd..f3b82273 100644 --- a/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java +++ b/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java @@ -2,6 +2,8 @@ import net.cytonic.cytosis.Cytosis; import net.minestom.server.command.CommandManager; +import net.minestom.server.entity.Player; + import java.util.Scanner; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -20,7 +22,11 @@ public CommandHandler() { public void registerCystosisCommands() { CommandManager cm = Cytosis.getCommandManager(); cm.register(new GamemodeCommand()); - cm.register(new OperatorCommand()); + cm.register(new RankCommand()); + } + + public void recalculateCommands(Player player) { + player.sendPacket(Cytosis.getCommandManager().createDeclareCommandsPacket(player)); } public void setupConsole() { From 6d41403528f013fa83cfb133fe410f947a18cac4 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 18:02:28 -0400 Subject: [PATCH 15/29] Register custom event --- src/main/java/net/cytonic/cytosis/events/EventHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/cytonic/cytosis/events/EventHandler.java b/src/main/java/net/cytonic/cytosis/events/EventHandler.java index 75c8a9a5..9da79601 100644 --- a/src/main/java/net/cytonic/cytosis/events/EventHandler.java +++ b/src/main/java/net/cytonic/cytosis/events/EventHandler.java @@ -192,5 +192,8 @@ private void setupInternalListeners() { GLOBAL_HANDLER.addListener(ClientPingServerEvent.class, (this::handleEvent)); GLOBAL_HANDLER.addListener(ServerListPingEvent.class, (this::handleEvent)); GLOBAL_HANDLER.addListener(ServerTickMonitorEvent.class, (this::handleEvent)); + + // Cytosis Events + GLOBAL_HANDLER.addListener(PlayerRankSetupEvent.class, (this::handleEvent)); } } From 18e90934ed63cccd714a79b92433a2fab51eddc1 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 18:04:39 -0400 Subject: [PATCH 16/29] Reformatting --- .../net/cytonic/cytosis/events/ServerEventListeners.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java index 0e580c0d..d3012df6 100644 --- a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java +++ b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java @@ -5,10 +5,8 @@ import net.cytonic.cytosis.logging.Logger; import net.minestom.server.coordinate.Pos; import net.minestom.server.entity.Player; -import net.minestom.server.event.Event; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.PlayerChatEvent; -import net.minestom.server.event.player.PlayerChatEvent; import net.minestom.server.event.player.PlayerSpawnEvent; public class ServerEventListeners { @@ -31,10 +29,10 @@ public static void initServerEvents() { }))); Logger.info("Registering player chat event."); - Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class,event ->{ + Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class, event -> { final Player player = event.getPlayer(); if (CytosisSettings.LOG_PLAYER_CHAT) - Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(),event.getMessage()); + Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(), event.getMessage()); })); } } \ No newline at end of file From 899420b2b1279b62458d80a74d8f060a8e075eca Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:23:21 -0400 Subject: [PATCH 17/29] Delete operator command as it isn't used anymore --- .../cytosis/commands/OperatorCommand.java | 42 ------------------- 1 file changed, 42 deletions(-) delete mode 100644 src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java diff --git a/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java b/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java deleted file mode 100644 index c8f3aa14..00000000 --- a/src/main/java/net/cytonic/cytosis/commands/OperatorCommand.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.cytonic.cytosis.commands; - -import net.cytonic.cytosis.Cytosis; -import net.kyori.adventure.text.Component; -import net.minestom.server.command.CommandParser; -import net.minestom.server.command.builder.Command; -import net.minestom.server.command.builder.arguments.ArgumentType; -import net.minestom.server.entity.Player; -import java.util.List; - -public class OperatorCommand extends Command { - - public OperatorCommand() { - super("op"); - setCondition((sender, commandString) -> sender.hasPermission("cytosis.commands.operator")); - - var playerArg = ArgumentType.Word("player").from(getPlayerNames()); - playerArg.setCallback((sender, exception) -> { - final String input = exception.getInput(); - sender.sendMessage(STR."The player \{input} is invalid!"); - }); - - setDefaultExecutor((sender, context) -> { - if (sender == Cytosis.getConsoleSender()) { - CommandParser.Result result = Cytosis.getCommandManager().parseCommand(sender, context.getInput()); - System.out.println(result.args()); - return; - } - final String playerName = context.get(playerArg); - Cytosis.getPlayer(playerName).ifPresentOrElse(Cytosis::opPlayer, () -> sender.sendMessage(Component.text(STR."Couldn't find the player '\{playerName}'. Did you spell their name right?"))); - }); - } - - private String[] getPlayerNames() { - List players = Cytosis.getOnlinePlayers().stream().toList(); - String[] names = new String[players.size()]; - for (int i = 0; i < players.size(); i++) { - names[i] = players.get(i).getUsername(); - } - return names; - } -} \ No newline at end of file From 617efba7cfa3d7f5238f4d83739e04ffdc0a4d7e Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:24:10 -0400 Subject: [PATCH 18/29] Add events for handling ranks --- .../cytonic/cytosis/events/ServerEventListeners.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java index d3012df6..a8bff37d 100644 --- a/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java +++ b/src/main/java/net/cytonic/cytosis/events/ServerEventListeners.java @@ -7,6 +7,7 @@ import net.minestom.server.entity.Player; import net.minestom.server.event.player.AsyncPlayerConfigurationEvent; import net.minestom.server.event.player.PlayerChatEvent; +import net.minestom.server.event.player.PlayerDisconnectEvent; import net.minestom.server.event.player.PlayerSpawnEvent; public class ServerEventListeners { @@ -26,13 +27,21 @@ public static void initServerEvents() { if (CytosisSettings.LOG_PLAYER_IPS) Logger.info(STR."\{event.getPlayer().getUsername()} (\{event.getPlayer().getUuid()}) joined with the ip: \{player.getPlayerConnection().getServerAddress()}"); else Logger.info(STR."\{event.getPlayer().getUsername()} (\{event.getPlayer().getUuid()}) joined."); + + Cytosis.getRankManager().addPlayer(player); }))); Logger.info("Registering player chat event."); - Cytosis.getEventHandler().registerListener(new EventListener("core:player-chat", false, 1, PlayerChatEvent.class, event -> { + Cytosis.getEventHandler().registerListener(new EventListener<>("core:player-chat", false, 1, PlayerChatEvent.class, event -> { final Player player = event.getPlayer(); if (CytosisSettings.LOG_PLAYER_CHAT) Cytosis.getDatabaseManager().getDatabase().addChat(player.getUuid(), event.getMessage()); })); + + Logger.info("Registering player disconnect event."); + Cytosis.getEventHandler().registerListener(new EventListener<>("core:player-disconnect", false, 1, PlayerDisconnectEvent.class, event -> { + final Player player = event.getPlayer(); + Cytosis.getRankManager().removePlayer(player); + })); } } \ No newline at end of file From bbba5df714abf48f8092b8d9c0705e39167b6584 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:24:31 -0400 Subject: [PATCH 19/29] Create events for rank activities --- .../cytosis/events/ranks/RankChangeEvent.java | 31 +++++++++++++++++++ .../cytosis/events/ranks/RankSetupEvent.java | 30 ++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java create mode 100644 src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java diff --git a/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java b/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java new file mode 100644 index 00000000..325788c1 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/events/ranks/RankChangeEvent.java @@ -0,0 +1,31 @@ +package net.cytonic.cytosis.events.ranks; + +import lombok.Getter; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerEvent; + +@Getter +public class RankChangeEvent implements PlayerEvent, CancellableEvent { + private boolean canceled; + private final PlayerRank newRank; + private final PlayerRank oldRank; + private final Player player; + + public RankChangeEvent(PlayerRank newRank, PlayerRank oldRank, Player player) { + this.newRank = newRank; + this.oldRank = oldRank; + this.player = player; + } + + @Override + public boolean isCancelled() { + return canceled; + } + + @Override + public void setCancelled(boolean cancel) { + this.canceled = cancel; + } +} diff --git a/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java b/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java new file mode 100644 index 00000000..fd06ecf1 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/events/ranks/RankSetupEvent.java @@ -0,0 +1,30 @@ +package net.cytonic.cytosis.events.ranks; + +import lombok.Getter; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.entity.Player; +import net.minestom.server.event.trait.CancellableEvent; +import net.minestom.server.event.trait.PlayerEvent; + +@Getter +public class RankSetupEvent implements PlayerEvent, CancellableEvent { + + private final Player player; + private final PlayerRank rank; + private boolean canceled; + + public RankSetupEvent(Player player, PlayerRank rank) { + this.player = player; + this.rank = rank; + } + + @Override + public boolean isCancelled() { + return canceled; + } + + @Override + public void setCancelled(boolean canceled) { + this.canceled = canceled; + } +} From 93c8275ab87aa604b1b631d41ca2f1430f49808f Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:00 -0400 Subject: [PATCH 20/29] Create Rank enum --- .../net/cytonic/cytosis/ranks/PlayerRank.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java diff --git a/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java b/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java new file mode 100644 index 00000000..15783d63 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/ranks/PlayerRank.java @@ -0,0 +1,70 @@ +package net.cytonic.cytosis.ranks; + +import lombok.Getter; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import static net.cytonic.cytosis.utils.MiniMessageTemplate.MM; + +/** + * This class holds constants that represent player ranks + */ +@Getter +public enum PlayerRank { + // Staff ranks + OWNER(MM."[OWNER]", NamedTextColor.RED, NamedTextColor.WHITE, new String[]{"*"}), + ADMIN(MM."[ADMIN]", NamedTextColor.RED, NamedTextColor.WHITE, new String[]{"*"}), + MODERATOR(MM."[MOD]", NamedTextColor.GREEN, NamedTextColor.WHITE, new String[]{}), + HELPER(MM."[HELPER]", NamedTextColor.AQUA, NamedTextColor.WHITE, new String[]{}), + + // player ranks + ELYSIAN(MM."[ELYSIAN]", NamedTextColor.GOLD, NamedTextColor.WHITE, new String[]{}), + CELESTIAL(MM."[CELESTIAL]", NamedTextColor.DARK_AQUA, NamedTextColor.WHITE, new String[]{}), + MASTER(MM."[MASTER]", NamedTextColor.DARK_RED, NamedTextColor.WHITE, new String[]{}), + VALIENT(MM."[VALIENT]", NamedTextColor.DARK_GREEN, NamedTextColor.WHITE, new String[]{}), + NOBLE(MM."[NOBLE]", NamedTextColor.DARK_PURPLE, NamedTextColor.WHITE, new String[]{}), + DEFAULT(MM."[DEFAULT]", NamedTextColor.GRAY, NamedTextColor.GRAY, new String[]{"cytosis.commands.gamemode"}); + + private final Component prefix; + private final NamedTextColor teamColor; + private final NamedTextColor chatColor; + private final String[] permissions; + + /** + * Creates a Rank object + * + * @param prefix The prefix shown in chat and above the player's head + * @param teamColor The color of the player's name tag, and their name in chat + * @param chatColor The color of the player's chat message + * @param permissions The permissions that rank has + */ + PlayerRank(Component prefix, NamedTextColor teamColor, NamedTextColor chatColor, String[] permissions) { + this.prefix = prefix; + this.teamColor = teamColor; + this.chatColor = chatColor; + this.permissions = permissions; + } + + public static boolean isDemotion(PlayerRank currentRole, PlayerRank newRole) { + return newRole.ordinal() > currentRole.ordinal(); + } + + public static boolean isPromotion(PlayerRank currentRole, PlayerRank newRole) { + return newRole.ordinal() < currentRole.ordinal(); + } + + public static boolean canChangeRank(PlayerRank currentUserRole, PlayerRank targetOriginalRole, PlayerRank targetNewRole) { + if (currentUserRole == OWNER) return true; + if (isDemotion(targetOriginalRole, targetNewRole)) { + return currentUserRole.ordinal() <= targetOriginalRole.ordinal(); + } + + if (isPromotion(targetOriginalRole, targetNewRole)) { + return currentUserRole.ordinal() <= targetOriginalRole.ordinal(); + } + + // If it's neither promotion nor demotion, it's an invalid operation + return false; + } + +} From 0a993ab70ec2aed1839f9f2b0a63b4c8a05c1bd4 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:10 -0400 Subject: [PATCH 21/29] Create command for changing ranks --- .../cytonic/cytosis/commands/RankCommand.java | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/commands/RankCommand.java diff --git a/src/main/java/net/cytonic/cytosis/commands/RankCommand.java b/src/main/java/net/cytonic/cytosis/commands/RankCommand.java new file mode 100644 index 00000000..906367f3 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/commands/RankCommand.java @@ -0,0 +1,82 @@ +package net.cytonic.cytosis.commands; + +import net.cytonic.cytosis.Cytosis; +import net.cytonic.cytosis.logging.Logger; +import net.cytonic.cytosis.ranks.PlayerRank; +import net.minestom.server.command.CommandSender; +import net.minestom.server.command.builder.Command; +import net.minestom.server.command.builder.arguments.ArgumentEnum; +import net.minestom.server.command.builder.arguments.ArgumentType; +import net.minestom.server.command.builder.suggestion.SuggestionEntry; +import net.minestom.server.entity.Player; + +import java.util.Locale; + +import static net.cytonic.cytosis.utils.MiniMessageTemplate.MM; + +public class RankCommand extends Command { + + public RankCommand() { + super("rank"); + setCondition((sender, _) -> sender.hasPermission("cytosis.commands.rank")); + setDefaultExecutor((sender, _) -> sender.sendMessage(MM."You must specify a valid rank and player!")); + + var rankArg = ArgumentType.Enum("rank", PlayerRank.class).setFormat(ArgumentEnum.Format.LOWER_CASED); + rankArg.setCallback((sender, exception) -> sender.sendMessage(STR."The rank \{exception.getInput()} is invalid!")); + rankArg.setSuggestionCallback((_, _, suggestion) -> { + for (PlayerRank rank : PlayerRank.values()) { + suggestion.addEntry(new SuggestionEntry(rank.name().toLowerCase(Locale.ROOT), rank.getPrefix())); + } + }); + + var playerArg = ArgumentType.Entity("player").onlyPlayers(true); + playerArg.setCallback((sender, exception) -> sender.sendMessage(STR."The player \{exception.getInput()} doesn't exist!")); + + + var group = ArgumentType.Group("rank-group", playerArg, rankArg); + + addSyntax((sender, context) -> { + final Player player = context.get(group).get(playerArg).findFirstPlayer(sender); + final PlayerRank newRank = context.get(group).get(rankArg); + if (player == null) { + sender.sendMessage(MM."The player \{context.get(group).getRaw("player")} doesn't exist!"); + return; + } + + if (player == sender) { + sender.sendMessage(MM."You cannot change your own rank!"); + return; + } + + Cytosis.getDatabaseManager().getDatabase().getPlayerRank(player.getUuid()).whenComplete((rank, throwable) -> { + if (throwable != null) { + sender.sendMessage("An error occured whilst fetching the old rank!"); + return; + } + + // if it's a console we don't care (There isn't a console impl) + if (sender instanceof Player s) { + PlayerRank senderRank = Cytosis.getRankManager().getPlayerRank(s.getUuid()).orElseThrow(); + if (!PlayerRank.canChangeRank(senderRank, rank, newRank)) { + sender.sendMessage(MM."You cannot do this!"); + return; + } + } + + setRank(player, newRank, sender); + }); + }, group); + } + + private void setRank(Player player, PlayerRank rank, CommandSender sender) { + Cytosis.getDatabaseManager().getDatabase().setPlayerRank(player.getUuid(), rank).whenComplete((_, t) -> { + if (t != null) { + sender.sendMessage(MM."An error occurred whilst setting \{player.getUsername()}'s rank! Check the console for more details."); + Logger.error(STR."An error occurred whilst setting \{player.getUsername()}'s rank! Check the console for more details.", t); + return; + } + Cytosis.getRankManager().changeRank(player, rank); + sender.sendMessage(MM."Successfully updated \{player.getUsername()}'s rank!"); + }); + } +} From 947f412dabd8e4852fb87a2784a9764941df90b1 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:22 -0400 Subject: [PATCH 22/29] Create class for handling interactions with ranks --- .../cytonic/cytosis/ranks/RankManager.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/ranks/RankManager.java diff --git a/src/main/java/net/cytonic/cytosis/ranks/RankManager.java b/src/main/java/net/cytonic/cytosis/ranks/RankManager.java new file mode 100644 index 00000000..6028c580 --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/ranks/RankManager.java @@ -0,0 +1,86 @@ +package net.cytonic.cytosis.ranks; + +import net.cytonic.cytosis.Cytosis; +import net.cytonic.cytosis.events.ranks.RankChangeEvent; +import net.cytonic.cytosis.events.ranks.RankSetupEvent; +import net.cytonic.cytosis.logging.Logger; +import net.minestom.server.MinecraftServer; +import net.minestom.server.entity.Player; +import net.minestom.server.event.EventDispatcher; +import net.minestom.server.network.packet.server.play.TeamsPacket; +import net.minestom.server.permission.Permission; +import net.minestom.server.scoreboard.Team; +import net.minestom.server.scoreboard.TeamBuilder; +import org.jetbrains.annotations.NotNull; + +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public class RankManager { + ConcurrentHashMap rankMap = new ConcurrentHashMap<>(); + ConcurrentHashMap teamMap = new ConcurrentHashMap<>(); + + public void init() { + for (PlayerRank value : PlayerRank.values()) { + Team team = new TeamBuilder(value.ordinal() + value.name(), MinecraftServer.getTeamManager()) + .collisionRule(TeamsPacket.CollisionRule.NEVER) + .teamColor(value.getTeamColor()) + .prefix(value.getPrefix().appendSpace()) + .build(); + + teamMap.put(value, team); + } + } + + public void addPlayer(Player player) { + // cache the rank + Cytosis.getDatabaseManager().getDatabase().getPlayerRank(player.getUuid()).whenComplete((playerRank, throwable) -> { + if (throwable != null) { + Logger.error(STR."An error occured whilst fetching \{player.getUsername()}'s rank!", throwable); + return; + } + var event = new RankSetupEvent(player, playerRank); + EventDispatcher.call(event); + if (event.isCanceled()) return; + rankMap.put(player.getUuid(), playerRank); + setupCosmetics(player, playerRank); + }); + } + + public void changeRank(Player player, PlayerRank rank) { + if (!rankMap.containsKey(player.getUuid())) + throw new IllegalStateException(STR."The player \{player.getUsername()} is not yet initialized! Call addPlayer(Player) first!"); + PlayerRank old = rankMap.get(player.getUuid()); + var event = new RankChangeEvent(old, rank, player); + EventDispatcher.call(event); + if (event.isCanceled()) return; + removePermissions(player, old.getPermissions()); + rankMap.put(player.getUuid(), rank); + setupCosmetics(player, rank); + } + + private void setupCosmetics(Player player, PlayerRank rank) { + addPermissions(player, rank.getPermissions()); + teamMap.get(rank).addMember(player.getUsername()); + player.setCustomName(rank.getPrefix().appendSpace().append(player.getName())); + Cytosis.getCommandHandler().recalculateCommands(player); + } + + public void removePlayer(Player player) { + removePermissions(player, rankMap.getOrDefault(player.getUuid(), PlayerRank.DEFAULT).getPermissions()); + rankMap.remove(player.getUuid()); + } + + public final void addPermissions(@NotNull final Player player, @NotNull final String... nodes) { + for (String node : nodes) player.addPermission(new Permission(node)); + } + + public final void removePermissions(@NotNull final Player player, @NotNull final String... nodes) { + for (String node : nodes) player.removePermission(node); + } + + public Optional getPlayerRank(UUID uuid) { + return Optional.ofNullable(rankMap.get(uuid)); + } +} From fabf3cfd420f13df5b17081e1bfa413fb38a8143 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:38 -0400 Subject: [PATCH 23/29] Formatting --- .../java/net/cytonic/cytosis/commands/GamemodeCommand.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java b/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java index 79f853f8..4e0ec15c 100644 --- a/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java +++ b/src/main/java/net/cytonic/cytosis/commands/GamemodeCommand.java @@ -13,15 +13,15 @@ public class GamemodeCommand extends Command { public GamemodeCommand() { super("gamemode", "gm"); - setCondition((sender, commandString) -> sender.hasPermission("cytosis.commands.gamemode")); - setDefaultExecutor((sender, context) -> sender.sendMessage(Component.text("You must specify a gamemode!", NamedTextColor.RED))); + setCondition((sender, _) -> sender.hasPermission("cytosis.commands.gamemode")); + setDefaultExecutor((sender, _) -> sender.sendMessage(Component.text("You must specify a gamemode!", NamedTextColor.RED))); // using a gamemode as an argument var gameModeArgument = ArgumentType.Enum("gamemode", GameMode.class).setFormat(ArgumentEnum.Format.LOWER_CASED); gameModeArgument.setCallback((sender, exception) -> sender.sendMessage(STR."The gamemode \{exception.getInput()} is invalid!")); var shorthand = ArgumentType.Word("shorthand").from("c", "s", "sv", "a", "0", "1", "2", "3"); - shorthand.setSuggestionCallback((sender, context, suggestion) -> { + shorthand.setSuggestionCallback((_, _, suggestion) -> { suggestion.addEntry(new SuggestionEntry("c", Component.text("Represents the Creative gamemode"))); suggestion.addEntry(new SuggestionEntry("s", Component.text("Represents the Spectator gamemode"))); suggestion.addEntry(new SuggestionEntry("sv", Component.text("Represents the Survival gamemode"))); From 9ab0facd894c8a0a00a821f0f0e62ed12012b7f7 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:25:53 -0400 Subject: [PATCH 24/29] Register new events with the event handler --- src/main/java/net/cytonic/cytosis/events/EventHandler.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/events/EventHandler.java b/src/main/java/net/cytonic/cytosis/events/EventHandler.java index 9da79601..c278f5e8 100644 --- a/src/main/java/net/cytonic/cytosis/events/EventHandler.java +++ b/src/main/java/net/cytonic/cytosis/events/EventHandler.java @@ -1,5 +1,7 @@ package net.cytonic.cytosis.events; +import net.cytonic.cytosis.events.ranks.RankChangeEvent; +import net.cytonic.cytosis.events.ranks.RankSetupEvent; import net.minestom.server.event.Event; import net.minestom.server.event.GlobalEventHandler; import net.minestom.server.event.book.EditBookEvent; @@ -194,6 +196,7 @@ private void setupInternalListeners() { GLOBAL_HANDLER.addListener(ServerTickMonitorEvent.class, (this::handleEvent)); // Cytosis Events - GLOBAL_HANDLER.addListener(PlayerRankSetupEvent.class, (this::handleEvent)); + GLOBAL_HANDLER.addListener(RankSetupEvent.class, (this::handleEvent)); + GLOBAL_HANDLER.addListener(RankChangeEvent.class, (this::handleEvent)); } } From cb60115eac675d051aed23ce99a5d5a23b56900f Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:13 -0400 Subject: [PATCH 25/29] rebasing --- .../net/cytonic/cytosis/DatabaseManager.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/main/java/net/cytonic/cytosis/DatabaseManager.java diff --git a/src/main/java/net/cytonic/cytosis/DatabaseManager.java b/src/main/java/net/cytonic/cytosis/DatabaseManager.java new file mode 100644 index 00000000..b9cc6a6c --- /dev/null +++ b/src/main/java/net/cytonic/cytosis/DatabaseManager.java @@ -0,0 +1,20 @@ +package net.cytonic.cytosis; + +import lombok.Getter; +import net.cytonic.cytosis.data.Database; + +@Getter +public class DatabaseManager { + + private Database database; + + public void shutdown() { + database.disconnect(); + } + + public void setupDatabase() { + database = new Database(); + database.connect(); + database.createTables(); + } +} \ No newline at end of file From e9511c905024361bfdfa3e0fee79f5abe1269cf0 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:30 -0400 Subject: [PATCH 26/29] Add rank database queries --- .../net/cytonic/cytosis/data/Database.java | 98 ++++++++++++++++--- 1 file changed, 86 insertions(+), 12 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/data/Database.java b/src/main/java/net/cytonic/cytosis/data/Database.java index 34713025..f68b7377 100644 --- a/src/main/java/net/cytonic/cytosis/data/Database.java +++ b/src/main/java/net/cytonic/cytosis/data/Database.java @@ -2,12 +2,13 @@ import net.cytonic.cytosis.config.CytosisSettings; import net.cytonic.cytosis.logging.Logger; +import net.cytonic.cytosis.ranks.PlayerRank; import net.minestom.server.MinecraftServer; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.SQLException; +import org.jetbrains.annotations.NotNull; + +import java.sql.*; import java.util.UUID; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -33,8 +34,7 @@ public Database() { try { Class.forName("com.mysql.cj.jdbc.Driver"); } catch (ClassNotFoundException e) { - Logger.error("Failed to load database driver"); - Logger.error(e.toString()); + Logger.error("Failed to load database driver", e); } } @@ -46,9 +46,10 @@ public void connect() { worker.submit(() -> { if (!isConnected()) { try { - connection = DriverManager.getConnection(STR."jdbc:mysql://\{host}:\{port}/\{database}?useSSL=\{ssl}&autoReconnect=true", username, password); + connection = DriverManager.getConnection(STR."jdbc:mysql://\{host}:\{port}/\{database}?useSSL=\{ssl}&autoReconnect=true&allowPublicKeyRetrieval=true", username, password); + Logger.info("Successfully connected to the MySQL Database!"); } catch (SQLException e) { - Logger.error("Invalid Database Credentials!",e); + Logger.error("Invalid Database Credentials!", e); MinecraftServer.stopCleanly(); } } @@ -63,17 +64,22 @@ public void disconnect() { connection.close(); Logger.info("Database connection closed!"); } catch (SQLException e) { - Logger.error("An error occurred whilst disconnecting from the database. Please report the following stacktrace to Foxikle: ",e); + Logger.error("An error occurred whilst disconnecting from the database. Please report the following stacktrace to Foxikle: ", e); } } }); } + public void createTables() { + createRanksTable(); + createChatTable(); + } + private Connection getConnection() { return connection; } - public void createChatTable() { + private void createChatTable() { worker.submit(() -> { if (isConnected()) { PreparedStatement ps; @@ -81,17 +87,85 @@ public void createChatTable() { ps = getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS cytonicchat (id INT NOT NULL AUTO_INCREMENT, timestamp TIMESTAMP, uuid VARCHAR(36), message TEXT, PRIMARY KEY(id))"); ps.executeUpdate(); } catch (SQLException e) { - Logger.error("An error occoured whilst fetching data from the database. Please report the following stacktrace to Foxikle:",e); + Logger.error("An error occoured whilst fetching data from the database. Please report the following stacktrace to Foxikle:", e); + } + } + }); + } + + private void createRanksTable() { + worker.submit(() -> { + if (isConnected()) { + PreparedStatement ps; + try { + ps = getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS cytonic_ranks (uuid VARCHAR(36), rank_id VARCHAR(16), PRIMARY KEY(uuid))"); + ps.executeUpdate(); + } catch (SQLException e) { + Logger.error("An error occoured whilst creating the `cytonic_ranks` table.", e); + } + } + }); + } + + /** + * Gets the player's rank. This returns {@link PlayerRank#DEFAULT} even if the player doesn't exist. + * + * @param uuid the player to fetch the id from + * @return The player's {@link PlayerRank} + * @throws IllegalStateException if the database isn't connected + */ + @NotNull + public CompletableFuture getPlayerRank(@NotNull final UUID uuid) { + CompletableFuture future = new CompletableFuture<>(); + if (!isConnected()) + throw new IllegalStateException("The database must have an open connection to fetch a player's rank!"); + worker.submit(() -> { + String id = "DEFAULT"; + try { + PreparedStatement ps = connection.prepareStatement("SELECT rank_id FROM cytonic_ranks WHERE uuid = ?"); + ps.setString(1, uuid.toString()); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + id = rs.getString("rank_id"); } + future.complete(PlayerRank.valueOf(id)); + } catch (SQLException e) { + Logger.error(STR."An error occurred whilst fetching the rank of '\{uuid}'"); + } + }); + return future; + } + + /** + * Sets the given player's rank to the specified rank. + * + * @param uuid The player's UUID + * @param rank The player's rank constant + * @throws IllegalStateException if the database isn't connected + */ + public CompletableFuture setPlayerRank(UUID uuid, PlayerRank rank) { + if (!isConnected()) + throw new IllegalStateException("The database must have an open connection to set a player's rank!"); + CompletableFuture future = new CompletableFuture<>(); + worker.submit(() -> { + try { + PreparedStatement ps = connection.prepareStatement("INSERT INTO cytonic_ranks (uuid, rank_id) VALUES (?, ?) ON DUPLICATE KEY UPDATE rank_id = VALUES(rank_id)"); + ps.setString(1, uuid.toString()); + ps.setString(2, rank.name()); + ps.executeUpdate(); + future.complete(null); + } catch (SQLException e) { + Logger.error(STR."An error occurred whilst setting the rank of '\{uuid}'"); } }); + return future; } public void addChat(UUID uuid, String message) { worker.submit(() -> { PreparedStatement ps; try { - ps = connection.prepareStatement("INSERT INTO cytonicchat (timestamp,uuid,message) VALUES (CURRENT_TIMESTAMP,?,?)"); + ps = connection.prepareStatement("INSERT INTO cytonicchat (timestamp, uuid, message) VALUES (CURRENT_TIMESTAMP,?,?)"); ps.setString(1, uuid.toString()); ps.setString(2, message); ps.executeUpdate(); From 715271632cc9d5d1bd7e15ba34a5f2bc597764a0 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:26:46 -0400 Subject: [PATCH 27/29] Just a bunch of stuff --- .../java/net/cytonic/cytosis/Cytosis.java | 118 +++++++++--------- 1 file changed, 56 insertions(+), 62 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/Cytosis.java b/src/main/java/net/cytonic/cytosis/Cytosis.java index 76a8f7dc..ab87fd6b 100644 --- a/src/main/java/net/cytonic/cytosis/Cytosis.java +++ b/src/main/java/net/cytonic/cytosis/Cytosis.java @@ -1,5 +1,6 @@ package net.cytonic.cytosis; +import lombok.Getter; import net.cytonic.cytosis.commands.CommandHandler; import net.cytonic.cytosis.config.CytosisSettings; import net.cytonic.cytosis.data.DatabaseManager; @@ -8,6 +9,7 @@ import net.cytonic.cytosis.files.FileManager; import net.cytonic.cytosis.logging.Logger; import net.cytonic.cytosis.messaging.MessagingManager; +import net.cytonic.cytosis.ranks.RankManager; import net.minestom.server.MinecraftServer; import net.minestom.server.command.CommandManager; import net.minestom.server.command.ConsoleSender; @@ -20,24 +22,38 @@ import net.minestom.server.instance.block.Block; import net.minestom.server.network.ConnectionManager; import net.minestom.server.permission.Permission; + import java.util.*; +@Getter public class Cytosis { // manager stuff - private static MinecraftServer MINECRAFT_SERVER; - private static InstanceManager INSTANCE_MANAGER; - private static InstanceContainer DEFAULT_INSTANCE; - private static EventHandler EVENT_HANDLER; - private static ConnectionManager CONNECTION_MANAGER; - private static CommandManager COMMAND_MANAGER; - private static CommandHandler COMMAND_HANDLER; - private static FileManager FILE_MANAGER; - private static DatabaseManager DATABASE_MANAGER; - private static MessagingManager MESSAGE_MANAGER; - private static ConsoleSender CONSOLE_SENDER; - private static int SERVER_PORT; + @Getter + private static MinecraftServer minecraftServer; + @Getter + private static InstanceManager instanceManager; + @Getter + private static InstanceContainer defaultInstance; + @Getter + private static EventHandler eventHandler; + @Getter + private static ConnectionManager connectionManager; + @Getter + private static CommandManager commandManager; + @Getter + private static CommandHandler commandHandler; + @Getter + private static FileManager fileManager; + @Getter + private static DatabaseManager databaseManager; + @Getter + private static MessagingManager messagingManager; + @Getter + private static ConsoleSender consoleSender; + @Getter + private static RankManager rankManager; private static List FLAGS; @@ -47,37 +63,37 @@ public static void main(String[] args) { long start = System.currentTimeMillis(); // Initialize the server Logger.info("Starting server."); - MINECRAFT_SERVER = MinecraftServer.init(); + minecraftServer = MinecraftServer.init(); MinecraftServer.setBrandName("Cytosis"); Logger.info("Starting instance manager."); - INSTANCE_MANAGER = MinecraftServer.getInstanceManager(); + instanceManager = MinecraftServer.getInstanceManager(); Logger.info("Starting connection manager."); - CONNECTION_MANAGER = MinecraftServer.getConnectionManager(); + connectionManager = MinecraftServer.getConnectionManager(); Logger.info("Starting manager."); - DATABASE_MANAGER = new DatabaseManager(); + databaseManager = new DatabaseManager(); // Commands Logger.info("Starting command manager."); - COMMAND_MANAGER = MinecraftServer.getCommandManager(); + commandManager = MinecraftServer.getCommandManager(); Logger.info("Setting console command sender."); - CONSOLE_SENDER = COMMAND_MANAGER.getConsoleSender(); - CONSOLE_SENDER.addPermission(new Permission("*")); + consoleSender = commandManager.getConsoleSender(); + consoleSender.addPermission(new Permission("*")); // instances Logger.info("Creating instance container"); - DEFAULT_INSTANCE = INSTANCE_MANAGER.createInstanceContainer(); + defaultInstance = instanceManager.createInstanceContainer(); Logger.info("Creating file manager"); - FILE_MANAGER = new FileManager(); + fileManager = new FileManager(); // Everything after this point depends on config contents Logger.info("Initializing file manager"); - FILE_MANAGER.init().whenComplete((_, throwable) -> { + fileManager.init().whenComplete((_, throwable) -> { if (throwable != null) { Logger.error("An error occurred whilst initializing the file manager!", throwable); } else { @@ -94,29 +110,9 @@ public static void main(String[] args) { }); } - public static EventHandler getEventHandler() { - return EVENT_HANDLER; - } - - public static InstanceContainer getDefaultInstance() { - return DEFAULT_INSTANCE; - } - - public static ConnectionManager getConnectionManager() { - return CONNECTION_MANAGER; - } - - public static CommandManager getCommandManager() { - return COMMAND_MANAGER; - } - - public static DatabaseManager getDatabaseManager() { - return DATABASE_MANAGER; - } - public static Set getOnlinePlayers() { Set players = new HashSet<>(); - INSTANCE_MANAGER.getInstances().forEach(instance -> players.addAll(instance.getPlayers())); + instanceManager.getInstances().forEach(instance -> players.addAll(instance.getPlayers())); return players; } @@ -143,10 +139,6 @@ public static void deopPlayer(Player player) { player.removePermission("*"); // remove every permission } - public static ConsoleSender getConsoleSender() { - return CONSOLE_SENDER; - } - public static void mojangAuth() { Logger.info("Initializing Mojang Authentication"); MojangAuth.init(); //VERY IMPORTANT! (This is online mode!) @@ -155,40 +147,42 @@ public static void mojangAuth() { public static void completeNonEssentialTasks(long start) { // basic world generator Logger.info("Generating basic world"); - DEFAULT_INSTANCE.setGenerator(unit -> unit.modifier().fillHeight(0, 1, Block.WHITE_STAINED_GLASS)); - DEFAULT_INSTANCE.setChunkSupplier(LightingChunk::new); + defaultInstance.setGenerator(unit -> unit.modifier().fillHeight(0, 1, Block.WHITE_STAINED_GLASS)); + defaultInstance.setChunkSupplier(LightingChunk::new); Logger.info("Setting up event handlers"); - EVENT_HANDLER = new EventHandler(MinecraftServer.getGlobalEventHandler()); - EVENT_HANDLER.init(); + eventHandler = new EventHandler(MinecraftServer.getGlobalEventHandler()); + eventHandler.init(); Logger.info("Initializing server events"); ServerEventListeners.initServerEvents(); Logger.info("Initializing database"); - DATABASE_MANAGER.setupDatabase(); + databaseManager.setupDatabase(); - MinecraftServer.getSchedulerManager().buildShutdownTask(() -> DATABASE_MANAGER.shutdown()); + MinecraftServer.getSchedulerManager().buildShutdownTask(() -> databaseManager.shutdown()); Logger.info("Initializing server commands"); - COMMAND_HANDLER = new CommandHandler(); - COMMAND_HANDLER.setupConsole(); - COMMAND_HANDLER.registerCystosisCommands(); + commandHandler = new CommandHandler(); + commandHandler.setupConsole(); + commandHandler.registerCystosisCommands(); - MESSAGE_MANAGER = new MessagingManager(); - MESSAGE_MANAGER.initialize().whenComplete((_, throwable) -> { + messagingManager = new MessagingManager(); + messagingManager.initialize().whenComplete((_, throwable) -> { if (throwable != null) { Logger.error("An error occurred whilst initializing the messaging manager!", throwable); } else { Logger.info("Messaging manager initialized!"); } }); - - SERVER_PORT = CytosisSettings.SERVER_PORT; + + Logger.info("Initializing Rank Manager"); + rankManager = new RankManager(); + rankManager.init(); // Start the server - Logger.info(STR."Server started on port \{SERVER_PORT}"); - MINECRAFT_SERVER.start("0.0.0.0", SERVER_PORT); + Logger.info(STR."Server started on port \{CytosisSettings.SERVER_PORT}"); + minecraftServer.start("0.0.0.0", CytosisSettings.SERVER_PORT); long end = System.currentTimeMillis(); Logger.info(STR."Server started in \{end - start}ms!"); From c27c30e625b013fd9022e713743c743c600ec6b4 Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:27:53 -0400 Subject: [PATCH 28/29] add method to recalculate command permissions, add rank command --- .../java/net/cytonic/cytosis/commands/CommandHandler.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java b/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java index 8c42b9fd..f3b82273 100644 --- a/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java +++ b/src/main/java/net/cytonic/cytosis/commands/CommandHandler.java @@ -2,6 +2,8 @@ import net.cytonic.cytosis.Cytosis; import net.minestom.server.command.CommandManager; +import net.minestom.server.entity.Player; + import java.util.Scanner; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -20,7 +22,11 @@ public CommandHandler() { public void registerCystosisCommands() { CommandManager cm = Cytosis.getCommandManager(); cm.register(new GamemodeCommand()); - cm.register(new OperatorCommand()); + cm.register(new RankCommand()); + } + + public void recalculateCommands(Player player) { + player.sendPacket(Cytosis.getCommandManager().createDeclareCommandsPacket(player)); } public void setupConsole() { From 2bf454dbe2213af5f6dc70a66671a3612c25f0db Mon Sep 17 00:00:00 2001 From: foxikle Date: Wed, 8 May 2024 21:34:45 -0400 Subject: [PATCH 29/29] Whoops fix compilation error, use getter --- src/main/java/net/cytonic/cytosis/data/DatabaseManager.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/cytonic/cytosis/data/DatabaseManager.java b/src/main/java/net/cytonic/cytosis/data/DatabaseManager.java index 82a93d0f..5c52469d 100644 --- a/src/main/java/net/cytonic/cytosis/data/DatabaseManager.java +++ b/src/main/java/net/cytonic/cytosis/data/DatabaseManager.java @@ -1,7 +1,9 @@ package net.cytonic.cytosis.data; +import lombok.Getter; import net.cytonic.cytosis.logging.Logger; +@Getter public class DatabaseManager { private Database database; @@ -17,8 +19,7 @@ public void shutdown() { public void setupDatabase() { database = new Database(); database.connect(); - database.createChatTable(); + database.createTables(); } - public Database getDatabase() {return database;} } \ No newline at end of file