From 6c4f0dfd58a9f6b7eb805e06041b1868b8717ef4 Mon Sep 17 00:00:00 2001 From: palsmo <78181042+palsmo@users.noreply.github.com> Date: Tue, 31 Dec 2024 23:20:40 +0100 Subject: [PATCH 01/15] version number prints at server startup instead of ${plugin.version} (#22) --- .gitignore | 8 +++++++- zander-hub/pom.xml | 11 ++++++++++- .../org/modularsoft/zander/hub/ZanderHubMain.java | 9 +++++---- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0561558..ae98d03 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,10 @@ .idea /target /.idea -*.iml \ No newline at end of file +*.iml + +# jdtls generation +.classpath +.factorypath +.project +.settings/ diff --git a/zander-hub/pom.xml b/zander-hub/pom.xml index 10b1c99..5328b49 100644 --- a/zander-hub/pom.xml +++ b/zander-hub/pom.xml @@ -12,6 +12,15 @@ zander-hub 1.0 + + + + src/main/resources + true + + + + @@ -30,4 +39,4 @@ - \ No newline at end of file + diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java index 410afff..a3fc8d1 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java @@ -22,14 +22,14 @@ public void onEnable() { plugin = this; this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); -// this.getServer().getMessenger().registerIncomingPluginChannel(this, "BungeeCord", new PluginMessageChannel(this)); + // this.getServer().getMessenger().registerIncomingPluginChannel(this, + // "BungeeCord", new PluginMessageChannel(this)); // Init Message TextComponent enabledMessage = Component.empty() .color(NamedTextColor.GREEN) .append(Component.text("\n\nZander Hub has been enabled.\n")) -// .append(Component.text("Running Version " + ZanderHubMain.class.getPackage().getImplementationVersion() + "\n")) - .append(Component.text("Running Version " + plugin.getDescription().getVersion() + "\n")) + .append(Component.text("Running Version " + plugin.getPluginMeta().getVersion() + "\n")) .append(Component.text("GitHub Repository: https://github.com/ModularSoftAU/zander\n")) .append(Component.text("Created by Modular Software\n\n", NamedTextColor.DARK_PURPLE)); getServer().sendMessage(enabledMessage); @@ -56,5 +56,6 @@ public void onEnable() { } @Override - public void onDisable() {} + public void onDisable() { + } } From d28121f46641b09a38d434cf59530a5402684cfc Mon Sep 17 00:00:00 2001 From: palsmo Date: Wed, 1 Jan 2025 17:49:57 +0100 Subject: [PATCH 02/15] Code quality, enhancments and better maintainability --- zander-hub/pom.xml | 1 - .../zander/hub/events/HubPlayerJoin.java | 231 ++++++++++++++---- zander-hub/src/main/resources/plugin.yml | 2 +- 3 files changed, 178 insertions(+), 56 deletions(-) diff --git a/zander-hub/pom.xml b/zander-hub/pom.xml index 5328b49..7ecd804 100644 --- a/zander-hub/pom.xml +++ b/zander-hub/pom.xml @@ -38,5 +38,4 @@ provided - diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 53eadde..2900497 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -2,96 +2,219 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; -import org.modularsoft.zander.hub.ConfigurationManager; -import org.modularsoft.zander.hub.ZanderHubMain; -import org.modularsoft.zander.hub.items.NavigationCompassItem; import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; import org.bukkit.Color; import org.bukkit.FireworkEffect; +import org.bukkit.Location; import org.bukkit.Sound; import org.bukkit.entity.Firework; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.inventory.meta.FireworkMeta; import org.bukkit.metadata.MetadataValue; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; +import org.modularsoft.zander.hub.ConfigurationManager; +import org.modularsoft.zander.hub.ZanderHubMain; +import org.modularsoft.zander.hub.items.NavigationCompassItem; +import java.util.ArrayList; import java.util.List; +import java.util.Random; public class HubPlayerJoin implements Listener { - ZanderHubMain plugin; + // Misc settings + private static final boolean TEST_ALWAYS_FIRST_JOIN = false; // * default false + private static final int NAV_COMPASS_SLOT = 4; + private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.5f * 20); + + // Firework settings + private static final double FIREWORK_GROUND_HEIGHT = 3; // blocks + private static final long FIREWORK_DETONATE_DELAY = (long) (0.3f * 20); + private static final Color[] FIREWORK_COLOR_PALETTE = { + Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.PURPLE, + Color.ORANGE, Color.WHITE, Color.AQUA, Color.LIME, + }; + + // Sound settings + private static final float SOUND_PITCH = 1.0f; + private static final float SOUND_VOLUME = 1.0f; + private static final Sound[] WELCOME_SOUNDS = { + Sound.BLOCK_AMETHYST_BLOCK_FALL, + Sound.BLOCK_AMETHYST_CLUSTER_BREAK, + Sound.BLOCK_BEACON_ACTIVATE, + Sound.BLOCK_BELL_RESONATE, + Sound.BLOCK_CAMPFIRE_CRACKLE, + Sound.BLOCK_COMPOSTER_FILL_SUCCESS, + Sound.BLOCK_COPPER_FALL, + Sound.BLOCK_DECORATED_POT_HIT, + Sound.BLOCK_ENCHANTMENT_TABLE_USE, + Sound.BLOCK_ENDER_CHEST_OPEN, + Sound.BLOCK_END_PORTAL_FRAME_FILL, + Sound.BLOCK_NETHERITE_BLOCK_STEP, + Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, + Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, + Sound.BLOCK_SCAFFOLDING_PLACE, + Sound.BLOCK_WATER_AMBIENT, + Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM, + Sound.ENTITY_ARMOR_STAND_BREAK, + Sound.ENTITY_AXOLOTL_SPLASH, + Sound.ENTITY_CAT_PURREOW, + Sound.ENTITY_CHICKEN_DEATH, + Sound.ENTITY_CREEPER_PRIMED, + Sound.ENTITY_DOLPHIN_ATTACK, + Sound.ENTITY_ENDERMAN_TELEPORT, + Sound.ENTITY_ENDER_DRAGON_HURT, + Sound.ENTITY_EVOKER_DEATH, + Sound.ENTITY_FROG_DEATH, + Sound.ENTITY_GLOW_SQUID_AMBIENT, + Sound.ENTITY_GOAT_AMBIENT, + Sound.ENTITY_HOGLIN_AMBIENT, + Sound.ENTITY_PIGLIN_ADMIRING_ITEM, + Sound.ENTITY_PIG_AMBIENT, + Sound.ENTITY_PLAYER_BIG_FALL, + Sound.ENTITY_PLAYER_HURT_FREEZE, + Sound.ENTITY_SHULKER_SHOOT, + Sound.ENTITY_VILLAGER_HURT, + Sound.ENTITY_WANDERING_TRADER_AMBIENT, + Sound.ENTITY_WANDERING_TRADER_DISAPPEARED, + Sound.ENTITY_WANDERING_TRADER_REAPPEARED, + Sound.ENTITY_WARDEN_NEARBY_CLOSER, + Sound.ENTITY_WITCH_HURT, + Sound.ENTITY_WITHER_SKELETON_STEP, + Sound.ENTITY_ZOMBIE_INFECT, + }; + + private final ZanderHubMain plugin; + public HubPlayerJoin(ZanderHubMain plugin) { this.plugin = plugin; } + /// Triggers when player's client first connects. + /// Best used for validation and basic setup (permission, flags, etc). + @EventHandler + public void onPlayerLogin(PlayerLoginEvent event) { + Player player = event.getPlayer(); + setPermissions(player); + } + + /// Triggers when player's client has joined the world. + /// Best used for initial world interactions (player world state changes etc). @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { + event.joinMessage(null); // * suppress default Player player = event.getPlayer(); + setInitialState(player); + Bukkit.getScheduler().runTaskLater(plugin, () -> { + // * bukkit uses 'world/playerdata' dir for tracking + if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { + chatWelcomeMessage(player); + spawnWelcomeFirework(player); + } + chatJoinMessage(player); + playWelcomeSound(player); + }, ROUTINE_PLAYER_JOINED_DELAY); + } - // Clear the Players inventory for miscellaneous items - player.getInventory().clear(); + /// Set special permission depending on the player. + private void setPermissions(Player player) { + if (player.hasPermission("zander.hub.fly")) { + player.setAllowFlight(true); + } + } - // Teleport Player to Hub spawn point + /// Set the initial state of the player in the world. + private void setInitialState(Player player) { + setupNoCollision(player); player.teleport(ConfigurationManager.getHubLocation()); + player.getInventory().clear(); + player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); + NavigationCompassItem.giveCompass(player); + } - NavigationCompassItem.giveCompass(player); // Give player navigation compass - player.getInventory().setHeldItemSlot(4); // Set the players current slot to the navigation compass - - event.joinMessage(null); -// event.setJoinMessage(null); // disable default join message - - if (!player.hasPlayedBefore()) { - // New user Joins for first time celebratory firework - Firework firework = event.getPlayer().getWorld().spawn(event.getPlayer().getLocation(), Firework.class); - FireworkMeta fireworkmeta = firework.getFireworkMeta(); - fireworkmeta.addEffect(FireworkEffect.builder() - .flicker(false) - .trail(true) - .with(FireworkEffect.Type.CREEPER) - .withColor(Color.GREEN) - .withFade(Color.BLUE) - .build()); - fireworkmeta.setPower(3); - firework.setFireworkMeta(fireworkmeta); - - // Send new user a MOTD seperate to the main MOTD - List newplayermotd = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); - for (String s : newplayermotd) { - event.getPlayer().sendMessage(ChatColor.translateAlternateColorCodes('&', s)); - } - event.getPlayer().sendMessage(" "); // Separate between messages + /// Disable entity collision for player. + /// Correct way as mentioned at: + /// https://hub.spigotmc.org/javadocs/bukkit/org/bukkit/entity/LivingEntity.html#setCollidable(boolean) + private void setupNoCollision(Player player) { + Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + Team team = scoreboard.getTeam("nocollision"); + if (team == null) { + team = scoreboard.registerNewTeam("nocollision"); + team.setOption(Team.Option.COLLISION_RULE, Team.OptionStatus.NEVER); } + team.addEntry(player.getName()); + } - // Play random sound - int randomSoundIndex = (int) (Math.random() * Sound.values().length - 1); - Sound randomChosenSound = Sound.values()[randomSoundIndex]; - player.playSound(player.getLocation(), randomChosenSound, 1f,1f); + /// Spawn a pretty firework where the player is. + private void spawnWelcomeFirework(Player player) { + Location spawnLoc = player.getLocation().add(0, FIREWORK_GROUND_HEIGHT, 0); + Firework firework = player.getWorld().spawn(spawnLoc, Firework.class); + FireworkMeta meta = firework.getFireworkMeta(); - if (!isVanished(player)) { - Component broadcastMessage = Component.empty() - .color(NamedTextColor.GRAY) - .append(event.getPlayer().name()) - .append(Component.text(" joined.")); + Random random = new Random(); + Color[] FCP = FIREWORK_COLOR_PALETTE; - for (Player otherPlayer : Bukkit.getOnlinePlayers()) { - otherPlayer.sendMessage(broadcastMessage); - } + // random primary-colors + List colors = new ArrayList<>(); + int numColors = random.nextInt(3) + 2; // 2-4 colors + for (int i = 0; i < numColors; i++) { + colors.add(FCP[random.nextInt(FCP.length)]); } + // random fade-color and firework type + Color fadeColor = FCP[random.nextInt(FCP.length)]; + FireworkEffect.Type[] types = FireworkEffect.Type.values(); + FireworkEffect.Type type = types[random.nextInt(types.length)]; + + FireworkEffect effect = FireworkEffect.builder() + .flicker(false) + .trail(true) + .with(type) + .withColor(colors) + .withFade(fadeColor) + .build(); + meta.addEffect(effect); + meta.setPower(1); + firework.setFireworkMeta(meta); + + Bukkit.getScheduler().runTaskLater(plugin, () -> { + firework.detonate(); + }, FIREWORK_DETONATE_DELAY); + } - player.setCollidable(true); // Disable player collision. - - // If user has the fly permission, enable flight - if (player.hasPermission("zander.hub.fly")) { - player.setAllowFlight(true); + /// Send a welcome message in player's chat. + private void chatWelcomeMessage(Player player) { + List message = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); + for (String row : message) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); } + player.sendMessage(""); + } + + /// Send a join message in player's chat. + private void chatJoinMessage(Player player) { + if (isVanished(player)) + return; + Component message = Component.empty() + .color(NamedTextColor.GRAY) + .append(player.name()) + .append(Component.text(" joined.")); + Bukkit.getOnlinePlayers().forEach(p -> p.sendMessage(message)); } + /// Play a random sound for the player. + private void playWelcomeSound(Player player) { + Sound randomSound = WELCOME_SOUNDS[(int) (Math.random() * WELCOME_SOUNDS.length)]; + player.playSound(player.getLocation(), randomSound, SOUND_VOLUME, SOUND_PITCH); + } + + /// Check if the player is currently vanished. private boolean isVanished(Player player) { - for (MetadataValue meta : player.getMetadata("vanished")) { - if (meta.asBoolean()) return true; - } - return false; + return player.getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean); + } } diff --git a/zander-hub/src/main/resources/plugin.yml b/zander-hub/src/main/resources/plugin.yml index 951e542..d08eaef 100644 --- a/zander-hub/src/main/resources/plugin.yml +++ b/zander-hub/src/main/resources/plugin.yml @@ -18,4 +18,4 @@ permissions: zanderhub.administrator: default: op zanderhub.build: - default: op \ No newline at end of file + default: op From cff0dfd72179eaddc1a85ab51504544ed7605f02 Mon Sep 17 00:00:00 2001 From: palsmo Date: Wed, 1 Jan 2025 20:51:55 +0100 Subject: [PATCH 03/15] join message should not be delayed --- .../org/modularsoft/zander/hub/events/HubPlayerJoin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 2900497..545c814 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -30,7 +30,7 @@ public class HubPlayerJoin implements Listener { // Misc settings private static final boolean TEST_ALWAYS_FIRST_JOIN = false; // * default false private static final int NAV_COMPASS_SLOT = 4; - private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.5f * 20); + private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.2f * 20); // Firework settings private static final double FIREWORK_GROUND_HEIGHT = 3; // blocks @@ -107,8 +107,9 @@ public void onPlayerLogin(PlayerLoginEvent event) { /// Best used for initial world interactions (player world state changes etc). @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { - event.joinMessage(null); // * suppress default Player player = event.getPlayer(); + event.joinMessage(null); // * suppress default + chatJoinMessage(player); setInitialState(player); Bukkit.getScheduler().runTaskLater(plugin, () -> { // * bukkit uses 'world/playerdata' dir for tracking @@ -116,7 +117,6 @@ public void onPlayerJoin(PlayerJoinEvent event) { chatWelcomeMessage(player); spawnWelcomeFirework(player); } - chatJoinMessage(player); playWelcomeSound(player); }, ROUTINE_PLAYER_JOINED_DELAY); } From 9a23f5b55a9f4b7ba07127b852255218d51175e3 Mon Sep 17 00:00:00 2001 From: palsmo Date: Wed, 1 Jan 2025 22:44:37 +0100 Subject: [PATCH 04/15] Extracted welcome sounds into its own class & wrote a friendly welcome message (replacing the placeholder text) --- .../zander/hub/events/HubPlayerJoin.java | 58 +++---------------- .../zander/hub/utils/WelcomeSounds.java | 55 ++++++++++++++++++ zander-hub/src/main/resources/welcome.yml | 18 +++--- zander-hub/src/main/resources/welcome_old.yml | 9 +++ 4 files changed, 82 insertions(+), 58 deletions(-) create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java create mode 100644 zander-hub/src/main/resources/welcome_old.yml diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 545c814..97014b3 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -18,9 +18,11 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Team; + import org.modularsoft.zander.hub.ConfigurationManager; import org.modularsoft.zander.hub.ZanderHubMain; import org.modularsoft.zander.hub.items.NavigationCompassItem; +import org.modularsoft.zander.hub.utils.WelcomeSounds; import java.util.ArrayList; import java.util.List; @@ -32,6 +34,10 @@ public class HubPlayerJoin implements Listener { private static final int NAV_COMPASS_SLOT = 4; private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.2f * 20); + // Sound settings + private static final float SOUND_PITCH = 1.0f; + private static final float SOUND_VOLUME = 1.0f; + // Firework settings private static final double FIREWORK_GROUND_HEIGHT = 3; // blocks private static final long FIREWORK_DETONATE_DELAY = (long) (0.3f * 20); @@ -40,55 +46,6 @@ public class HubPlayerJoin implements Listener { Color.ORANGE, Color.WHITE, Color.AQUA, Color.LIME, }; - // Sound settings - private static final float SOUND_PITCH = 1.0f; - private static final float SOUND_VOLUME = 1.0f; - private static final Sound[] WELCOME_SOUNDS = { - Sound.BLOCK_AMETHYST_BLOCK_FALL, - Sound.BLOCK_AMETHYST_CLUSTER_BREAK, - Sound.BLOCK_BEACON_ACTIVATE, - Sound.BLOCK_BELL_RESONATE, - Sound.BLOCK_CAMPFIRE_CRACKLE, - Sound.BLOCK_COMPOSTER_FILL_SUCCESS, - Sound.BLOCK_COPPER_FALL, - Sound.BLOCK_DECORATED_POT_HIT, - Sound.BLOCK_ENCHANTMENT_TABLE_USE, - Sound.BLOCK_ENDER_CHEST_OPEN, - Sound.BLOCK_END_PORTAL_FRAME_FILL, - Sound.BLOCK_NETHERITE_BLOCK_STEP, - Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, - Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, - Sound.BLOCK_SCAFFOLDING_PLACE, - Sound.BLOCK_WATER_AMBIENT, - Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM, - Sound.ENTITY_ARMOR_STAND_BREAK, - Sound.ENTITY_AXOLOTL_SPLASH, - Sound.ENTITY_CAT_PURREOW, - Sound.ENTITY_CHICKEN_DEATH, - Sound.ENTITY_CREEPER_PRIMED, - Sound.ENTITY_DOLPHIN_ATTACK, - Sound.ENTITY_ENDERMAN_TELEPORT, - Sound.ENTITY_ENDER_DRAGON_HURT, - Sound.ENTITY_EVOKER_DEATH, - Sound.ENTITY_FROG_DEATH, - Sound.ENTITY_GLOW_SQUID_AMBIENT, - Sound.ENTITY_GOAT_AMBIENT, - Sound.ENTITY_HOGLIN_AMBIENT, - Sound.ENTITY_PIGLIN_ADMIRING_ITEM, - Sound.ENTITY_PIG_AMBIENT, - Sound.ENTITY_PLAYER_BIG_FALL, - Sound.ENTITY_PLAYER_HURT_FREEZE, - Sound.ENTITY_SHULKER_SHOOT, - Sound.ENTITY_VILLAGER_HURT, - Sound.ENTITY_WANDERING_TRADER_AMBIENT, - Sound.ENTITY_WANDERING_TRADER_DISAPPEARED, - Sound.ENTITY_WANDERING_TRADER_REAPPEARED, - Sound.ENTITY_WARDEN_NEARBY_CLOSER, - Sound.ENTITY_WITCH_HURT, - Sound.ENTITY_WITHER_SKELETON_STEP, - Sound.ENTITY_ZOMBIE_INFECT, - }; - private final ZanderHubMain plugin; public HubPlayerJoin(ZanderHubMain plugin) { @@ -189,6 +146,7 @@ private void spawnWelcomeFirework(Player player) { /// Send a welcome message in player's chat. private void chatWelcomeMessage(Player player) { List message = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); + player.sendMessage(""); for (String row : message) { player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); } @@ -208,7 +166,7 @@ private void chatJoinMessage(Player player) { /// Play a random sound for the player. private void playWelcomeSound(Player player) { - Sound randomSound = WELCOME_SOUNDS[(int) (Math.random() * WELCOME_SOUNDS.length)]; + Sound randomSound = WelcomeSounds.getRandomSound(); player.playSound(player.getLocation(), randomSound, SOUND_VOLUME, SOUND_PITCH); } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java new file mode 100644 index 0000000..f273f8b --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -0,0 +1,55 @@ +package org.modularsoft.zander.hub.utils; + +import org.bukkit.Sound; + +public class WelcomeSounds { + private static final Sound[] SOUNDS = { + Sound.BLOCK_AMETHYST_BLOCK_FALL, + Sound.BLOCK_AMETHYST_CLUSTER_BREAK, + Sound.BLOCK_BEACON_ACTIVATE, + Sound.BLOCK_BELL_RESONATE, + Sound.BLOCK_CAMPFIRE_CRACKLE, + Sound.BLOCK_COMPOSTER_FILL_SUCCESS, + Sound.BLOCK_COPPER_FALL, + Sound.BLOCK_DECORATED_POT_HIT, + Sound.BLOCK_ENCHANTMENT_TABLE_USE, + Sound.BLOCK_ENDER_CHEST_OPEN, + Sound.BLOCK_END_PORTAL_FRAME_FILL, + Sound.BLOCK_NETHERITE_BLOCK_STEP, + Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, + Sound.BLOCK_RESPAWN_ANCHOR_DEPLETE, + Sound.BLOCK_SCAFFOLDING_PLACE, + Sound.BLOCK_WATER_AMBIENT, + Sound.ENTITY_ALLAY_AMBIENT_WITHOUT_ITEM, + Sound.ENTITY_ARMOR_STAND_BREAK, + Sound.ENTITY_AXOLOTL_SPLASH, + Sound.ENTITY_CAT_PURREOW, + Sound.ENTITY_CHICKEN_DEATH, + Sound.ENTITY_CREEPER_PRIMED, + Sound.ENTITY_DOLPHIN_ATTACK, + Sound.ENTITY_ENDERMAN_TELEPORT, + Sound.ENTITY_ENDER_DRAGON_HURT, + Sound.ENTITY_EVOKER_DEATH, + Sound.ENTITY_FROG_DEATH, + Sound.ENTITY_GLOW_SQUID_AMBIENT, + Sound.ENTITY_GOAT_AMBIENT, + Sound.ENTITY_HOGLIN_AMBIENT, + Sound.ENTITY_PIGLIN_ADMIRING_ITEM, + Sound.ENTITY_PIG_AMBIENT, + Sound.ENTITY_PLAYER_BIG_FALL, + Sound.ENTITY_PLAYER_HURT_FREEZE, + Sound.ENTITY_SHULKER_SHOOT, + Sound.ENTITY_VILLAGER_HURT, + Sound.ENTITY_WANDERING_TRADER_AMBIENT, + Sound.ENTITY_WANDERING_TRADER_DISAPPEARED, + Sound.ENTITY_WANDERING_TRADER_REAPPEARED, + Sound.ENTITY_WARDEN_NEARBY_CLOSER, + Sound.ENTITY_WITCH_HURT, + Sound.ENTITY_WITHER_SKELETON_STEP, + Sound.ENTITY_ZOMBIE_INFECT, + }; + + public static Sound getRandomSound() { + return SOUNDS[(int) (Math.random() * SOUNDS.length)]; + } +} diff --git a/zander-hub/src/main/resources/welcome.yml b/zander-hub/src/main/resources/welcome.yml index dcbd787..dfbdae3 100644 --- a/zander-hub/src/main/resources/welcome.yml +++ b/zander-hub/src/main/resources/welcome.yml @@ -1,9 +1,11 @@ -welcome: - - "&e Welcome to My Server!" newplayerwelcome: - - "&e&l ========== My Server ========== &r" - - "&9&l Website:&r http://myserver.net" - - "&9&l Discord:&r http://myserver.net/discord" - - " " - - "By joining My Server you agree to our Rules, TOS, and Privacy Policy." - - "Make sure you read the rules. &cUse /rules to read the rules.&r" \ No newline at end of file + - "&e&l ============ Welcome to CFC ============ &r" + - "" + - "&f Crafting-For-Christ is a community of christian minecraft players." + - "&f You don't need to be christian to play on this server." + - "&f We expect you to follow our guidelines and spread kindness \u2764" + - "" + - "&c (view /rules)&r" + - "" + - "&e Website:&r &7https://craftingforchrist.net&r" + - "&e Discord:&r &7https://craftingforchrist.net/discord&r" diff --git a/zander-hub/src/main/resources/welcome_old.yml b/zander-hub/src/main/resources/welcome_old.yml new file mode 100644 index 0000000..34a643f --- /dev/null +++ b/zander-hub/src/main/resources/welcome_old.yml @@ -0,0 +1,9 @@ +welcome: + - "&e Welcome to My Server!" +newplayerwelcome: + - "&e&l ========== My Server ========== &r" + - "&9&l Website:&r http://myserver.net" + - "&9&l Discord:&r http://myserver.net/discord" + - " " + - "By joining My Server you agree to our Rules, TOS, and Privacy Policy." + - "Make sure you read the rules. &cUse /rules to read the rules.&r" From f387bdde76a686b599a0e1bd53d2308ab2c670ce Mon Sep 17 00:00:00 2001 From: palsmo Date: Wed, 1 Jan 2025 22:57:13 +0100 Subject: [PATCH 05/15] Utility class should not be instantiated --- .../java/org/modularsoft/zander/hub/utils/WelcomeSounds.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java index f273f8b..5788b09 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -3,6 +3,11 @@ import org.bukkit.Sound; public class WelcomeSounds { + + private WelcomeSounds() { + throw new IllegalStateException("Utility class cannot be instantiated"); + } + private static final Sound[] SOUNDS = { Sound.BLOCK_AMETHYST_BLOCK_FALL, Sound.BLOCK_AMETHYST_CLUSTER_BREAK, From 841390bea053257bc9043fb463f388b86793ee14 Mon Sep 17 00:00:00 2001 From: palsmo Date: Wed, 1 Jan 2025 22:59:13 +0100 Subject: [PATCH 06/15] Better exception message. --- .../java/org/modularsoft/zander/hub/utils/WelcomeSounds.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java index 5788b09..02828c8 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -5,7 +5,7 @@ public class WelcomeSounds { private WelcomeSounds() { - throw new IllegalStateException("Utility class cannot be instantiated"); + throw new IllegalStateException("Utility class shouldn't be instantiated"); } private static final Sound[] SOUNDS = { From 6388477de404dc59ddc7a1613b17dedf8d5680fb Mon Sep 17 00:00:00 2001 From: palsmo Date: Thu, 2 Jan 2025 23:55:04 +0100 Subject: [PATCH 07/15] Fixed good integration with PremiumVanish, server will have uniform join/leave messages which can be configured from 'config.yml' --- zander-hub/pom.xml | 24 +++++ .../zander/hub/ConfigurationManager.java | 43 +++++++- .../modularsoft/zander/hub/ZanderHubMain.java | 8 +- .../zander/hub/configs/MessageConfig.java | 99 +++++++++++++++++++ .../zander/hub/events/HubPlayerJoin.java | 75 ++++++-------- .../zander/hub/events/HubPlayerLeave.java | 27 +++++ .../zander/hub/protection/HubProtection.java | 13 +-- .../modularsoft/zander/hub/utils/Misc.java | 15 +++ .../zander/hub/utils/WelcomeSounds.java | 2 +- zander-hub/src/main/resources/plugin.yml | 1 + 10 files changed, 241 insertions(+), 66 deletions(-) create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java diff --git a/zander-hub/pom.xml b/zander-hub/pom.xml index 7ecd804..0b39146 100644 --- a/zander-hub/pom.xml +++ b/zander-hub/pom.xml @@ -27,6 +27,16 @@ papermc https://papermc.io/repo/repository/maven-public/ + + + jitpack.io + https://jitpack.io + + + + dmulloy2-repo + https://repo.dmulloy2.net/repository/public/ + @@ -37,5 +47,19 @@ 1.20.2-R0.1-SNAPSHOT provided + + + com.github.LeonMangler + PremiumVanishAPI + 2.9.0-4 + provided + + + + com.comphenix.protocol + ProtocolLib + 5.3.0 + provided + diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java index 4a7ea8c..f8473a0 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java @@ -7,11 +7,13 @@ import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.modularsoft.zander.hub.configs.MessageConfig; import java.io.File; public class ConfigurationManager { private static FileConfiguration welcomeFile; + private static MessageConfig messageConfig; // // Welcome File @@ -23,7 +25,8 @@ public static void setupWelcomeFile() { File existingWelcomeFile = new File(ZanderHubMain.plugin.getDataFolder(), "welcome.yml"); if (!existingWelcomeFile.exists()) { - // Reads the welcome.yml from the resource folder and then creates the file in the folder that + // Reads the welcome.yml from the resource folder and then creates the file in + // the folder that // goes next to the plugin. ZanderHubMain.plugin.saveResource("welcome.yml", false); Bukkit.getServer().getConsoleSender().sendMessage( @@ -43,17 +46,47 @@ public static FileConfiguration getWelcome() { public static Location getHubLocation() { World defaultworld = Bukkit.getServer().getWorlds().get(0); - World hubworld = Bukkit.getWorld(ZanderHubMain.plugin.getConfig().getString("hub.world", defaultworld.getName())); + World hubworld = Bukkit + .getWorld(ZanderHubMain.plugin.getConfig().getString("hub.world", defaultworld.getName())); if (hubworld == null) { - Bukkit.getLogger().warning("No world by the name of " + defaultworld.getName() + " was found! Assuming default world."); + Bukkit.getLogger().warning( + "No world by the name of " + defaultworld.getName() + " was found! Assuming default world."); hubworld = Bukkit.getServer().getWorlds().get(0); } double hubx = ZanderHubMain.plugin.getConfig().getDouble("hub.x", defaultworld.getSpawnLocation().getX()); double huby = ZanderHubMain.plugin.getConfig().getDouble("hub.y", defaultworld.getSpawnLocation().getY()); double hubz = ZanderHubMain.plugin.getConfig().getDouble("hub.z", defaultworld.getSpawnLocation().getZ()); - float hubyaw = (float)ZanderHubMain.plugin.getConfig().getDouble("hub.yaw", 0); - float hubpitch = (float)ZanderHubMain.plugin.getConfig().getDouble("hub.pitch", 0); + float hubyaw = (float) ZanderHubMain.plugin.getConfig().getDouble("hub.yaw", 0); + float hubpitch = (float) ZanderHubMain.plugin.getConfig().getDouble("hub.pitch", 0); return new Location(hubworld, hubx, huby, hubz, hubyaw, hubpitch); } + + public static void setupMessage() { + FileConfiguration config = ZanderHubMain.plugin.getConfig(); + + // ensure join/leave messages exist in 'config.yml' + boolean hasModifiedConfig = false; + if (!config.contains("messages.join")) { + ZanderHubMain.plugin.getConfig().set("messages.join", "&7%p% joined."); + hasModifiedConfig = true; + } + if (!config.contains("messages.leave")) { + ZanderHubMain.plugin.getConfig().set("messages.leave", "&7%p% left."); + hasModifiedConfig = true; + } + if (hasModifiedConfig) { + ZanderHubMain.plugin.saveConfig(); + } + + String messageJoin = ZanderHubMain.plugin.getConfig().getString("messages.join"); + String messageLeave = ZanderHubMain.plugin.getConfig().getString("messages.leave"); + + messageConfig = new MessageConfig(ZanderHubMain.plugin); + messageConfig.setupJoinLeave(messageJoin, messageLeave); + } + + public static MessageConfig getMessage() { + return messageConfig; + } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java index a3fc8d1..040898e 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java @@ -3,6 +3,8 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; import org.modularsoft.zander.hub.commands.fly; import org.modularsoft.zander.hub.events.HubBoosterPlate; import org.modularsoft.zander.hub.events.HubPlayerJoin; @@ -12,8 +14,7 @@ import org.modularsoft.zander.hub.protection.HubCreatureSpawnProtection; import org.modularsoft.zander.hub.protection.HubInteractionProtection; import org.modularsoft.zander.hub.protection.HubProtection; -import org.bukkit.plugin.PluginManager; -import org.bukkit.plugin.java.JavaPlugin; +import org.modularsoft.zander.hub.events.HubPlayerLeave; public class ZanderHubMain extends JavaPlugin { public static ZanderHubMain plugin; @@ -37,6 +38,7 @@ public void onEnable() { // Event Registry PluginManager pluginmanager = this.getServer().getPluginManager(); pluginmanager.registerEvents(new HubPlayerJoin(this), this); + pluginmanager.registerEvents(new HubPlayerLeave(this), this); pluginmanager.registerEvents(new HubPlayerVoid(this), this); pluginmanager.registerEvents(new HubBoosterPlate(this), this); pluginmanager.registerEvents(new HubPlayerJoinChristmas(this), this); @@ -53,6 +55,8 @@ public void onEnable() { ConfigurationManager.getHubLocation(); saveConfig(); + + ConfigurationManager.setupMessage(); } @Override diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java new file mode 100644 index 0000000..9604f7a --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java @@ -0,0 +1,99 @@ +package org.modularsoft.zander.hub.configs; + +import java.io.File; +import java.io.IOException; +import java.util.function.Consumer; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.configuration.file.YamlConfiguration; +import org.modularsoft.zander.hub.ZanderHubMain; + +public class MessageConfig { + private final ZanderHubMain plugin; + + // holds pre-deserialized legacy text + private TextComponent textCompJoinTemplate; + private TextComponent textCompLeaveTemplate; + + public MessageConfig(ZanderHubMain plugin) { + this.plugin = plugin; + } + + /// Configure the join/leave messages to be provided for the entire project. + /// Example string: "&7%p% joined the game" (%p% gets replaced by player name). + public void setupJoinLeave(String textLegacyJoin, String textLegacyLeave) { + int rc1 = parseToTemplate(textLegacyJoin, "&7%p% ", // * relied to be correct + (template) -> this.textCompJoinTemplate = template); + int rc2 = parseToTemplate(textLegacyLeave, "&7%p% ", // * relied to be correct + (template) -> this.textCompLeaveTemplate = template); + if (rc1 == 0 && rc2 == 0) { + if (plugin.getServer().getPluginManager().getPlugin("PremiumVanish") != null) { + setPremiumVanish(textLegacyJoin, textLegacyLeave); + } + } + } + + /// Retrieve the 'join' message for `playerName`. + public Component playerJoin(Component playerName) { + if (textCompJoinTemplate == null) + throw new IllegalStateException("Message not initialized, please run 'setupJoinLeave'"); + return insertPlayerName(this.textCompJoinTemplate, playerName); + } + + /// Retrieve the 'leave' message for `playerName`. + public Component playerLeave(Component playerName) { + if (textCompLeaveTemplate == null) + throw new IllegalStateException("Message not initialized, please run 'setupJoinLeave'"); + return insertPlayerName(this.textCompLeaveTemplate, playerName); + } + + /// Insert component `playerName` into `template`. + // * depends on 'parseToTemplate' who ensures `template` contains single '%p%' + private Component insertPlayerName(TextComponent template, Component playerName) { + return template.replaceText(builder -> builder + .match("%p%") + .replacement(playerName)); // * correctly retains any style + } + + /// Parse `textLegacy` (+ verify placeholder %p%) into TextComponent template. + /// Returns 0 if successful (use custom text), 1 if failed (use default text). + private int parseToTemplate(String textLegacy, String textLegacyDefault, Consumer setter) { + LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand(); + try { + // verify the '%p%' exists exactly once + int placeholderIndex = textLegacy.indexOf("%p%"); + if (placeholderIndex == -1 || placeholderIndex != textLegacy.lastIndexOf("%p%")) { + throw new IllegalArgumentException("Should contain a single '%p%' (placeholder player name)"); + } + TextComponent template = serializer.deserialize(textLegacy); + setter.accept(template); + return 0; + } catch (Exception e) { + plugin.getLogger().warning(String.format("Invalid legacy string \"%s\"", textLegacy)); + plugin.getLogger().warning(String.format("%s", e.getMessage())); + + // * fall back to default + TextComponent template = serializer.deserialize(textLegacyDefault); + setter.accept(template); + return 1; + } + } + + /// Set join/leave messages in PremiumVanish 'messages.yml' file. + private void setPremiumVanish(String textLegacyJoin, String textLegacyLeave) { + File pvYML = new File(plugin.getDataFolder().getParentFile(), "PremiumVanish/messages.yml"); + if (pvYML.exists()) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(pvYML); + config.set("Messages.DiscordSRVFakeJoin", textLegacyJoin); + config.set("Messages.DiscordSRVFakeQuit", textLegacyLeave); + config.set("Messages.ReappearMessage", textLegacyJoin); + config.set("Messages.VanishMessage", textLegacyLeave); + try { + config.save(pvYML); + } catch (IOException e) { + plugin.getLogger().warning("Failed to update PremiumVanish messages.yml"); + } + } + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 97014b3..09b0306 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -1,7 +1,8 @@ package org.modularsoft.zander.hub.events; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; import net.md_5.bungee.api.ChatColor; import org.bukkit.Bukkit; import org.bukkit.Color; @@ -15,19 +16,14 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.inventory.meta.FireworkMeta; -import org.bukkit.metadata.MetadataValue; import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Team; - import org.modularsoft.zander.hub.ConfigurationManager; import org.modularsoft.zander.hub.ZanderHubMain; import org.modularsoft.zander.hub.items.NavigationCompassItem; +import org.modularsoft.zander.hub.utils.Misc; import org.modularsoft.zander.hub.utils.WelcomeSounds; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - public class HubPlayerJoin implements Listener { // Misc settings private static final boolean TEST_ALWAYS_FIRST_JOIN = false; // * default false @@ -53,7 +49,7 @@ public HubPlayerJoin(ZanderHubMain plugin) { } /// Triggers when player's client first connects. - /// Best used for validation and basic setup (permission, flags, etc). + /// Good for validation and basic setup (permission, flags, etc). @EventHandler public void onPlayerLogin(PlayerLoginEvent event) { Player player = event.getPlayer(); @@ -61,13 +57,17 @@ public void onPlayerLogin(PlayerLoginEvent event) { } /// Triggers when player's client has joined the world. - /// Best used for initial world interactions (player world state changes etc). + /// Good for initial player world interactions (gameplay state etc). @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - event.joinMessage(null); // * suppress default - chatJoinMessage(player); - setInitialState(player); + + setInitialState(player); // * just be aware, runs before checking vanish + if (Misc.isVanish(player)) + return; + + event.joinMessage(ConfigurationManager.getMessage().playerJoin(player.displayName())); + Bukkit.getScheduler().runTaskLater(plugin, () -> { // * bukkit uses 'world/playerdata' dir for tracking if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { @@ -107,6 +107,22 @@ private void setupNoCollision(Player player) { team.addEntry(player.getName()); } + /// Play a random sound for the player. + private void playWelcomeSound(Player player) { + Sound randomSound = WelcomeSounds.getRandomSound(); + player.playSound(player.getLocation(), randomSound, SOUND_VOLUME, SOUND_PITCH); + } + + /// Send a welcome message in player's chat. + private void chatWelcomeMessage(Player player) { + List message = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); + player.sendMessage(""); + for (String row : message) { + player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); + } + player.sendMessage(""); + } + /// Spawn a pretty firework where the player is. private void spawnWelcomeFirework(Player player) { Location spawnLoc = player.getLocation().add(0, FIREWORK_GROUND_HEIGHT, 0); @@ -142,37 +158,4 @@ private void spawnWelcomeFirework(Player player) { firework.detonate(); }, FIREWORK_DETONATE_DELAY); } - - /// Send a welcome message in player's chat. - private void chatWelcomeMessage(Player player) { - List message = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); - player.sendMessage(""); - for (String row : message) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); - } - player.sendMessage(""); - } - - /// Send a join message in player's chat. - private void chatJoinMessage(Player player) { - if (isVanished(player)) - return; - Component message = Component.empty() - .color(NamedTextColor.GRAY) - .append(player.name()) - .append(Component.text(" joined.")); - Bukkit.getOnlinePlayers().forEach(p -> p.sendMessage(message)); - } - - /// Play a random sound for the player. - private void playWelcomeSound(Player player) { - Sound randomSound = WelcomeSounds.getRandomSound(); - player.playSound(player.getLocation(), randomSound, SOUND_VOLUME, SOUND_PITCH); - } - - /// Check if the player is currently vanished. - private boolean isVanished(Player player) { - return player.getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean); - - } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java new file mode 100644 index 0000000..544d122 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java @@ -0,0 +1,27 @@ +package org.modularsoft.zander.hub.events; + +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerQuitEvent; +import org.modularsoft.zander.hub.ConfigurationManager; +import org.modularsoft.zander.hub.ZanderHubMain; +import org.modularsoft.zander.hub.utils.Misc; + +public class HubPlayerLeave implements Listener { + + private final ZanderHubMain plugin; + + public HubPlayerLeave(ZanderHubMain plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + Player player = event.getPlayer(); + if (Misc.isVanish(player)) { + return; + } + event.quitMessage(ConfigurationManager.getMessage().playerLeave(player.displayName())); + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/protection/HubProtection.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/protection/HubProtection.java index 92c6b8d..615fa2e 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/protection/HubProtection.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/protection/HubProtection.java @@ -15,6 +15,7 @@ public class HubProtection implements Listener { ZanderHubMain plugin; + public HubProtection(ZanderHubMain plugin) { this.plugin = plugin; } @@ -148,18 +149,6 @@ public void death(final PlayerDeathEvent event) { event.getDrops().clear(); } - // Hide the normal join message - @EventHandler(priority = EventPriority.HIGH) - public void HideJoinMessage(PlayerJoinEvent event) { - event.joinMessage(null); - } - - // Hide the normal leave message - @EventHandler(priority = EventPriority.HIGH) - public void HideLeaveMessage(PlayerQuitEvent event) { - event.quitMessage(null); - } - // Hide all death messages @EventHandler(priority = EventPriority.HIGH) public void HideDeathMessage(PlayerDeathEvent event) { diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java new file mode 100644 index 0000000..2235ad4 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java @@ -0,0 +1,15 @@ +package org.modularsoft.zander.hub.utils; + +import de.myzelyam.api.vanish.VanishAPI; +import org.bukkit.entity.Player; + +public final class Misc { + + private Misc() { + throw new IllegalStateException("Utility class shouldn't be instantiated"); + } + + public static boolean isVanish(Player player) { + return (player != null && VanishAPI.isInvisible(player)); + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java index 02828c8..06ab7ea 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -2,7 +2,7 @@ import org.bukkit.Sound; -public class WelcomeSounds { +public final class WelcomeSounds { private WelcomeSounds() { throw new IllegalStateException("Utility class shouldn't be instantiated"); diff --git a/zander-hub/src/main/resources/plugin.yml b/zander-hub/src/main/resources/plugin.yml index d08eaef..0f074d9 100644 --- a/zander-hub/src/main/resources/plugin.yml +++ b/zander-hub/src/main/resources/plugin.yml @@ -3,6 +3,7 @@ name: zander-hub version: ${project.version} author: ModularSoft api-version: 1.19 +#depend: [PremiumVanish, ProtocolLib] commands: fly: From f19ee3801e5a7aae260ce471dec426e16f1249fb Mon Sep 17 00:00:00 2001 From: palsmo Date: Thu, 2 Jan 2025 23:58:27 +0100 Subject: [PATCH 08/15] Uncomment dependencies --- zander-hub/src/main/resources/plugin.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zander-hub/src/main/resources/plugin.yml b/zander-hub/src/main/resources/plugin.yml index 0f074d9..d1a090b 100644 --- a/zander-hub/src/main/resources/plugin.yml +++ b/zander-hub/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ name: zander-hub version: ${project.version} author: ModularSoft api-version: 1.19 -#depend: [PremiumVanish, ProtocolLib] +depend: [PremiumVanish, ProtocolLib] commands: fly: From b41d6789a26120c037d09142283259b2eee9dcc8 Mon Sep 17 00:00:00 2001 From: palsmo Date: Fri, 3 Jan 2025 00:13:24 +0100 Subject: [PATCH 09/15] Using 'isConnected()' is most appropriate here, would correcly solve edge-cases and prevent multiple welcomes --- .../java/org/modularsoft/zander/hub/events/HubPlayerJoin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 09b0306..51557bd 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -61,7 +61,6 @@ public void onPlayerLogin(PlayerLoginEvent event) { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - setInitialState(player); // * just be aware, runs before checking vanish if (Misc.isVanish(player)) return; @@ -69,6 +68,8 @@ public void onPlayerJoin(PlayerJoinEvent event) { event.joinMessage(ConfigurationManager.getMessage().playerJoin(player.displayName())); Bukkit.getScheduler().runTaskLater(plugin, () -> { + if (!player.isConnected()) + return; // * bukkit uses 'world/playerdata' dir for tracking if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { chatWelcomeMessage(player); From 0c15e0106a813719c07a4c50f4d0bfcaaecfdf89 Mon Sep 17 00:00:00 2001 From: palsmo Date: Fri, 3 Jan 2025 00:37:09 +0100 Subject: [PATCH 10/15] Added check for if the firework would have been despawned etc. before trying to .detonate() --- .../java/org/modularsoft/zander/hub/events/HubPlayerJoin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 51557bd..b58cce6 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -156,7 +156,9 @@ private void spawnWelcomeFirework(Player player) { firework.setFireworkMeta(meta); Bukkit.getScheduler().runTaskLater(plugin, () -> { - firework.detonate(); + if (firework.isValid()) { + firework.detonate(); + } }, FIREWORK_DETONATE_DELAY); } } From 82eb9c60cdc68840a66ec0139a76bd1e91525fdf Mon Sep 17 00:00:00 2001 From: palsmo Date: Fri, 3 Jan 2025 00:58:33 +0100 Subject: [PATCH 11/15] Moved fields to top of class, as is standard. --- .../org/modularsoft/zander/hub/utils/WelcomeSounds.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java index 06ab7ea..95395bd 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -4,10 +4,6 @@ public final class WelcomeSounds { - private WelcomeSounds() { - throw new IllegalStateException("Utility class shouldn't be instantiated"); - } - private static final Sound[] SOUNDS = { Sound.BLOCK_AMETHYST_BLOCK_FALL, Sound.BLOCK_AMETHYST_CLUSTER_BREAK, @@ -54,6 +50,10 @@ private WelcomeSounds() { Sound.ENTITY_ZOMBIE_INFECT, }; + private WelcomeSounds() { + throw new IllegalStateException("Utility class shouldn't be instantiated"); + } + public static Sound getRandomSound() { return SOUNDS[(int) (Math.random() * SOUNDS.length)]; } From f2649aff2724725af6f85ae4740b49a4f58e26a2 Mon Sep 17 00:00:00 2001 From: palsmo Date: Fri, 3 Jan 2025 15:47:13 +0100 Subject: [PATCH 12/15] Small simplifications, added checks for functions that require their 'setup' counterpart to run first --- .../zander/hub/ConfigurationManager.java | 15 +++++++++------ .../zander/hub/configs/MessageConfig.java | 5 ++++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java index f8473a0..a35800e 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java @@ -68,25 +68,28 @@ public static void setupMessage() { // ensure join/leave messages exist in 'config.yml' boolean hasModifiedConfig = false; if (!config.contains("messages.join")) { - ZanderHubMain.plugin.getConfig().set("messages.join", "&7%p% joined."); + config.set("messages.join", "&7%p% joined."); hasModifiedConfig = true; } if (!config.contains("messages.leave")) { - ZanderHubMain.plugin.getConfig().set("messages.leave", "&7%p% left."); + config.set("messages.leave", "&7%p% left."); hasModifiedConfig = true; } if (hasModifiedConfig) { ZanderHubMain.plugin.saveConfig(); } - String messageJoin = ZanderHubMain.plugin.getConfig().getString("messages.join"); - String messageLeave = ZanderHubMain.plugin.getConfig().getString("messages.leave"); - + // create an instance of the config and instate join/leave messages messageConfig = new MessageConfig(ZanderHubMain.plugin); - messageConfig.setupJoinLeave(messageJoin, messageLeave); + messageConfig.setupJoinLeave( + config.getString("messages.join"), + config.getString("messages.leave")); } public static MessageConfig getMessage() { + if (messageConfig == null) { + throw new IllegalStateException("Bad order of execution, first run 'setupMessage'"); + } return messageConfig; } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java index 9604f7a..48cbceb 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java @@ -51,9 +51,12 @@ public Component playerLeave(Component playerName) { /// Insert component `playerName` into `template`. // * depends on 'parseToTemplate' who ensures `template` contains single '%p%' private Component insertPlayerName(TextComponent template, Component playerName) { + if (template == null) { + throw new IllegalStateException("Bad order of execution, first run 'setupJoinLeave'"); + } return template.replaceText(builder -> builder .match("%p%") - .replacement(playerName)); // * correctly retains any style + .replacement(playerName)); // * style is handled as expected } /// Parse `textLegacy` (+ verify placeholder %p%) into TextComponent template. From 98183d092c7600e4a63631959b94f4f6b7f2f8f0 Mon Sep 17 00:00:00 2001 From: palsmo Date: Sun, 5 Jan 2025 02:00:25 +0100 Subject: [PATCH 13/15] Fixed proper integration 'config.yml' + spilled out to a few other files, added robustness checks on a few places, updated pom.xml to specify java 21 --- zander-hub/pom.xml | 28 ++-- .../zander/hub/ConfigurationManager.java | 116 +++++-------- .../modularsoft/zander/hub/ZanderHubMain.java | 31 +++- .../hub/configs/HubLocationsConfig.java | 59 +++++++ .../zander/hub/configs/MessageConfig.java | 102 ------------ .../zander/hub/configs/MessagesConfig.java | 86 ++++++++++ .../zander/hub/events/HubPlayerJoin.java | 32 ++-- .../zander/hub/events/HubPlayerLeave.java | 12 +- .../zander/hub/events/HubPlayerVoid.java | 8 +- .../zander/hub/utils/ConfigValidator.java | 153 ++++++++++++++++++ .../modularsoft/zander/hub/utils/Misc.java | 3 + .../zander/hub/utils/WelcomeSounds.java | 3 + zander-hub/src/main/resources/config.yml | 7 +- 13 files changed, 427 insertions(+), 213 deletions(-) create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java delete mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java diff --git a/zander-hub/pom.xml b/zander-hub/pom.xml index 0b39146..4320970 100644 --- a/zander-hub/pom.xml +++ b/zander-hub/pom.xml @@ -12,15 +12,6 @@ zander-hub 1.0 - - - - src/main/resources - true - - - - @@ -62,4 +53,23 @@ provided + + + + + src/main/resources + true + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + + + + diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java index a35800e..22481bc 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java @@ -1,95 +1,61 @@ package org.modularsoft.zander.hub; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.World; +import java.io.File; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.modularsoft.zander.hub.configs.MessageConfig; - -import java.io.File; +import org.modularsoft.zander.hub.configs.MessagesConfig; +import org.modularsoft.zander.hub.configs.HubLocationsConfig; -public class ConfigurationManager { +public final class ConfigurationManager { private static FileConfiguration welcomeFile; - private static MessageConfig messageConfig; + private static HubLocationsConfig hubLocationsConfig; + private static MessagesConfig messagesConfig; - // - // Welcome File - // - public static void setupWelcomeFile() { - if (!ZanderHubMain.plugin.getDataFolder().exists()) { - ZanderHubMain.plugin.getDataFolder().mkdir(); - } - File existingWelcomeFile = new File(ZanderHubMain.plugin.getDataFolder(), "welcome.yml"); + private ConfigurationManager() { + throw new IllegalStateException("Utility class shouldn't be instantiated"); + } - if (!existingWelcomeFile.exists()) { - // Reads the welcome.yml from the resource folder and then creates the file in - // the folder that - // goes next to the plugin. - ZanderHubMain.plugin.saveResource("welcome.yml", false); - Bukkit.getServer().getConsoleSender().sendMessage( - Component.text("The welcome.yml file has been created.", NamedTextColor.GREEN)); - } - // Reopen the file since it should exist now - existingWelcomeFile = new File(ZanderHubMain.plugin.getDataFolder(), "welcome.yml"); - welcomeFile = YamlConfiguration.loadConfiguration(existingWelcomeFile); + public static void setupHubLocations() { + if (hubLocationsConfig != null) + throw new IllegalStateException("Already setup, ensure there's a single call"); + hubLocationsConfig = new HubLocationsConfig(ZanderHubMain.plugin); + hubLocationsConfig.setupSpawn(); + // future? hubLocationsConfig.setupParkour(); } - public static FileConfiguration getWelcome() { - if (welcomeFile == null) { - setupWelcomeFile(); - } - return welcomeFile; + public static void setupMessages() { + if (messagesConfig != null) + throw new IllegalStateException("Already setup, ensure there's a single call"); + messagesConfig = new MessagesConfig(ZanderHubMain.plugin); + messagesConfig.setupJoinLeave(); } - public static Location getHubLocation() { - World defaultworld = Bukkit.getServer().getWorlds().get(0); - World hubworld = Bukkit - .getWorld(ZanderHubMain.plugin.getConfig().getString("hub.world", defaultworld.getName())); - if (hubworld == null) { - Bukkit.getLogger().warning( - "No world by the name of " + defaultworld.getName() + " was found! Assuming default world."); - hubworld = Bukkit.getServer().getWorlds().get(0); - } - double hubx = ZanderHubMain.plugin.getConfig().getDouble("hub.x", defaultworld.getSpawnLocation().getX()); - double huby = ZanderHubMain.plugin.getConfig().getDouble("hub.y", defaultworld.getSpawnLocation().getY()); - double hubz = ZanderHubMain.plugin.getConfig().getDouble("hub.z", defaultworld.getSpawnLocation().getZ()); - float hubyaw = (float) ZanderHubMain.plugin.getConfig().getDouble("hub.yaw", 0); - float hubpitch = (float) ZanderHubMain.plugin.getConfig().getDouble("hub.pitch", 0); + public static void setupWelcomeFile() { + if (welcomeFile != null) + throw new IllegalStateException("Already setup, ensure there's a single call"); + File dataFolder = ZanderHubMain.plugin.getDataFolder(); + File welcomeFileYML = new File(dataFolder, "welcome.yml"); + if (!welcomeFileYML.exists()) + ZanderHubMain.plugin.saveResource("welcome.yml", false); - return new Location(hubworld, hubx, huby, hubz, hubyaw, hubpitch); + ConfigurationManager.welcomeFile = YamlConfiguration.loadConfiguration(welcomeFileYML); } - public static void setupMessage() { - FileConfiguration config = ZanderHubMain.plugin.getConfig(); - - // ensure join/leave messages exist in 'config.yml' - boolean hasModifiedConfig = false; - if (!config.contains("messages.join")) { - config.set("messages.join", "&7%p% joined."); - hasModifiedConfig = true; - } - if (!config.contains("messages.leave")) { - config.set("messages.leave", "&7%p% left."); - hasModifiedConfig = true; - } - if (hasModifiedConfig) { - ZanderHubMain.plugin.saveConfig(); - } + public static HubLocationsConfig getHubLocations() { + if (hubLocationsConfig == null) + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupHubLocations'"); + return hubLocationsConfig; + } - // create an instance of the config and instate join/leave messages - messageConfig = new MessageConfig(ZanderHubMain.plugin); - messageConfig.setupJoinLeave( - config.getString("messages.join"), - config.getString("messages.leave")); + public static MessagesConfig getMessages() { + if (messagesConfig == null) + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupMessages'"); + return messagesConfig; } - public static MessageConfig getMessage() { - if (messageConfig == null) { - throw new IllegalStateException("Bad order of execution, first run 'setupMessage'"); - } - return messageConfig; + public static FileConfiguration getWelcome() { + if (welcomeFile == null) + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupWelcomeFile'"); + return welcomeFile; } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java index 040898e..4b5cd96 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java @@ -1,20 +1,23 @@ package org.modularsoft.zander.hub; +import java.io.InputStreamReader; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.modularsoft.zander.hub.commands.fly; import org.modularsoft.zander.hub.events.HubBoosterPlate; import org.modularsoft.zander.hub.events.HubPlayerJoin; import org.modularsoft.zander.hub.events.HubPlayerJoinChristmas; +import org.modularsoft.zander.hub.events.HubPlayerLeave; import org.modularsoft.zander.hub.events.HubPlayerVoid; import org.modularsoft.zander.hub.gui.HubCompassItem; import org.modularsoft.zander.hub.protection.HubCreatureSpawnProtection; import org.modularsoft.zander.hub.protection.HubInteractionProtection; import org.modularsoft.zander.hub.protection.HubProtection; -import org.modularsoft.zander.hub.events.HubPlayerLeave; public class ZanderHubMain extends JavaPlugin { public static ZanderHubMain plugin; @@ -22,6 +25,11 @@ public class ZanderHubMain extends JavaPlugin { public void onEnable() { plugin = this; + copyConfigsResourceToServer(); + ConfigurationManager.setupHubLocations(); + ConfigurationManager.setupMessages(); + ConfigurationManager.setupWelcomeFile(); + this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); // this.getServer().getMessenger().registerIncomingPluginChannel(this, // "BungeeCord", new PluginMessageChannel(this)); @@ -52,14 +60,25 @@ public void onEnable() { // Command Registry this.getCommand("fly").setExecutor(new fly()); - - ConfigurationManager.getHubLocation(); - saveConfig(); - - ConfigurationManager.setupMessage(); } + // load defaults from the embedded resource & don't override existing values @Override public void onDisable() { } + + /// Copy _missing_ fields from resources/config.yml to server config.yml + /// Bukkit ensures auto-created empty config.yml file in plugin's folder. + private void copyConfigsResourceToServer() { + // 1. load the server 'config.yml' + // 2. load embedded default config ('resources/config.yml') + // 3. adjust configuration object in memory + // 4. reflect changes to external file + FileConfiguration config = this.getConfig(); + FileConfiguration defaultConfig = YamlConfiguration.loadConfiguration( + new InputStreamReader(this.getResource("config.yml"))); + config.setDefaults(defaultConfig); + config.options().copyDefaults(true); + this.saveConfig(); + } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java new file mode 100644 index 0000000..a9119dd --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java @@ -0,0 +1,59 @@ +package org.modularsoft.zander.hub.configs; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidDouble; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidPitch; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidWorld; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidYaw; +import static org.modularsoft.zander.hub.utils.ConfigValidator.validateConfig; + +/** + * Manages hub locations for the plugin, and their persistence. + * Handles loading, validation, and access to managed data. + */ +public class HubLocationsConfig { + private final JavaPlugin plugin; + + private Location locationSpawn; + // future? private Location locationParkour; + + public HubLocationsConfig(JavaPlugin plugin) { + this.plugin = plugin; + } + + /// Configure the spawn location, ensures valid entries in 'config.yml' + public void setupSpawn() { + FileConfiguration config = plugin.getConfig(); + + // * access server's primary world (guaranteed by Bukkit to exist) + Location defaultSpawn = Bukkit.getServer().getWorlds().get(0).getSpawnLocation(); + + validateConfig(config, "hub.world", isValidWorld, defaultSpawn.getWorld().getName()); + validateConfig(config, "hub.x", isValidDouble, defaultSpawn.getX()); + validateConfig(config, "hub.y", isValidDouble, defaultSpawn.getY()); + validateConfig(config, "hub.z", isValidDouble, defaultSpawn.getZ()); + validateConfig(config, "hub.pitch", isValidPitch, defaultSpawn.getPitch()); + validateConfig(config, "hub.yaw", isValidYaw, defaultSpawn.getYaw()); + + plugin.saveConfig(); // * save to external 'config.yml' + + World hubWorld = Bukkit.getWorld(config.getString("hub.world")); + double hubX = config.getDouble("hub.x"); + double hubY = config.getDouble("hub.y"); + double hubZ = config.getDouble("hub.z"); + float hubYaw = (float) config.getDouble("hub.yaw"); + float hubPitch = (float) config.getDouble("hub.pitch"); + this.locationSpawn = new Location(hubWorld, hubX, hubY, hubZ, hubYaw, hubPitch); + } + + /// Retrieve the spawn location. + public Location spawn() { + if (this.locationSpawn == null) + throw new IllegalStateException("Missing setup, first run 'HubLocationsConfig.setupSpawn'"); + return this.locationSpawn.clone(); + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java deleted file mode 100644 index 48cbceb..0000000 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessageConfig.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.modularsoft.zander.hub.configs; - -import java.io.File; -import java.io.IOException; -import java.util.function.Consumer; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.TextComponent; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.bukkit.configuration.file.YamlConfiguration; -import org.modularsoft.zander.hub.ZanderHubMain; - -public class MessageConfig { - private final ZanderHubMain plugin; - - // holds pre-deserialized legacy text - private TextComponent textCompJoinTemplate; - private TextComponent textCompLeaveTemplate; - - public MessageConfig(ZanderHubMain plugin) { - this.plugin = plugin; - } - - /// Configure the join/leave messages to be provided for the entire project. - /// Example string: "&7%p% joined the game" (%p% gets replaced by player name). - public void setupJoinLeave(String textLegacyJoin, String textLegacyLeave) { - int rc1 = parseToTemplate(textLegacyJoin, "&7%p% ", // * relied to be correct - (template) -> this.textCompJoinTemplate = template); - int rc2 = parseToTemplate(textLegacyLeave, "&7%p% ", // * relied to be correct - (template) -> this.textCompLeaveTemplate = template); - if (rc1 == 0 && rc2 == 0) { - if (plugin.getServer().getPluginManager().getPlugin("PremiumVanish") != null) { - setPremiumVanish(textLegacyJoin, textLegacyLeave); - } - } - } - - /// Retrieve the 'join' message for `playerName`. - public Component playerJoin(Component playerName) { - if (textCompJoinTemplate == null) - throw new IllegalStateException("Message not initialized, please run 'setupJoinLeave'"); - return insertPlayerName(this.textCompJoinTemplate, playerName); - } - - /// Retrieve the 'leave' message for `playerName`. - public Component playerLeave(Component playerName) { - if (textCompLeaveTemplate == null) - throw new IllegalStateException("Message not initialized, please run 'setupJoinLeave'"); - return insertPlayerName(this.textCompLeaveTemplate, playerName); - } - - /// Insert component `playerName` into `template`. - // * depends on 'parseToTemplate' who ensures `template` contains single '%p%' - private Component insertPlayerName(TextComponent template, Component playerName) { - if (template == null) { - throw new IllegalStateException("Bad order of execution, first run 'setupJoinLeave'"); - } - return template.replaceText(builder -> builder - .match("%p%") - .replacement(playerName)); // * style is handled as expected - } - - /// Parse `textLegacy` (+ verify placeholder %p%) into TextComponent template. - /// Returns 0 if successful (use custom text), 1 if failed (use default text). - private int parseToTemplate(String textLegacy, String textLegacyDefault, Consumer setter) { - LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand(); - try { - // verify the '%p%' exists exactly once - int placeholderIndex = textLegacy.indexOf("%p%"); - if (placeholderIndex == -1 || placeholderIndex != textLegacy.lastIndexOf("%p%")) { - throw new IllegalArgumentException("Should contain a single '%p%' (placeholder player name)"); - } - TextComponent template = serializer.deserialize(textLegacy); - setter.accept(template); - return 0; - } catch (Exception e) { - plugin.getLogger().warning(String.format("Invalid legacy string \"%s\"", textLegacy)); - plugin.getLogger().warning(String.format("%s", e.getMessage())); - - // * fall back to default - TextComponent template = serializer.deserialize(textLegacyDefault); - setter.accept(template); - return 1; - } - } - - /// Set join/leave messages in PremiumVanish 'messages.yml' file. - private void setPremiumVanish(String textLegacyJoin, String textLegacyLeave) { - File pvYML = new File(plugin.getDataFolder().getParentFile(), "PremiumVanish/messages.yml"); - if (pvYML.exists()) { - YamlConfiguration config = YamlConfiguration.loadConfiguration(pvYML); - config.set("Messages.DiscordSRVFakeJoin", textLegacyJoin); - config.set("Messages.DiscordSRVFakeQuit", textLegacyLeave); - config.set("Messages.ReappearMessage", textLegacyJoin); - config.set("Messages.VanishMessage", textLegacyLeave); - try { - config.save(pvYML); - } catch (IOException e) { - plugin.getLogger().warning("Failed to update PremiumVanish messages.yml"); - } - } - } -} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java new file mode 100644 index 0000000..e7b6de6 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java @@ -0,0 +1,86 @@ +package org.modularsoft.zander.hub.configs; + +import de.myzelyam.api.vanish.VanishAPI; +import java.io.File; +import java.io.IOException; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.TextComponent; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidJoinLeave; +import static org.modularsoft.zander.hub.utils.ConfigValidator.validateConfig; + +/** + * Manages server messages for the plugin, and their persistence. + * Handles loading, validation, and uniformity of managed data. + */ +public class MessagesConfig { + private final JavaPlugin plugin; + + private TextComponent textCompJoinTemplate; + private TextComponent textCompLeaveTemplate; + + public MessagesConfig(JavaPlugin plugin) { + this.plugin = plugin; + } + + /// Configure the join/leave messages, ensures valid entries in 'config.yml' + public void setupJoinLeave() { + FileConfiguration config = plugin.getConfig(); + + String defaultJoin = "&7%p% "; + String defaultLeave = "&7%p% "; + + validateConfig(config, "messages.join", isValidJoinLeave, defaultJoin, + template -> this.textCompJoinTemplate = template); + validateConfig(config, "messages.leave", isValidJoinLeave, defaultLeave, + template -> this.textCompLeaveTemplate = template); + + plugin.saveConfig(); // * save to external 'config.yml' + + String textLegacyJoin = config.getString("messages.join"); + String textLegacyLeave = config.getString("messages.leave"); + + if (this.plugin.getServer().getPluginManager().getPlugin("PremiumVanish") != null) { + updatePremiumVanish(textLegacyJoin, textLegacyLeave); + VanishAPI.reloadConfig(); + } + } + + /// Retrieve join message for `playerName` + public Component playerJoin(Component playerName) { + return insertPlayerName(this.textCompJoinTemplate, playerName); + } + + /// Retrieve leave message for `playerName` + public Component playerLeave(Component playerName) { + return insertPlayerName(this.textCompLeaveTemplate, playerName); + } + + /// Insert component `playerName` into `template` + private Component insertPlayerName(TextComponent template, Component playerName) { + if (template == null) + throw new IllegalStateException("Missing setup, first run 'MessagesConfig.setupJoinLeave'"); + return template.replaceText(builder -> builder + .match("%p%") // * 'parseToTemplate' ensured single %p% + .replacement(playerName)); // * style is handled as expected + } + + /// Replace join/leave messages in PremiumVanish 'messages.yml' file. + private void updatePremiumVanish(String textLegacyJoin, String textLegacyLeave) { + File pvYML = new File(this.plugin.getDataFolder().getParentFile(), "PremiumVanish/messages.yml"); + if (pvYML.exists()) { + YamlConfiguration config = YamlConfiguration.loadConfiguration(pvYML); + config.set("Messages.DiscordSRVFakeJoin", textLegacyJoin); + config.set("Messages.DiscordSRVFakeQuit", textLegacyLeave); + config.set("Messages.ReappearMessage", textLegacyJoin); + config.set("Messages.VanishMessage", textLegacyLeave); + try { + config.save(pvYML); + } catch (IOException e) { + this.plugin.getLogger().warning("Failed to update PremiumVanish messages.yml"); + } + } + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index b58cce6..1609681 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -16,10 +16,10 @@ import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerLoginEvent; import org.bukkit.inventory.meta.FireworkMeta; +import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scoreboard.Scoreboard; import org.bukkit.scoreboard.Team; import org.modularsoft.zander.hub.ConfigurationManager; -import org.modularsoft.zander.hub.ZanderHubMain; import org.modularsoft.zander.hub.items.NavigationCompassItem; import org.modularsoft.zander.hub.utils.Misc; import org.modularsoft.zander.hub.utils.WelcomeSounds; @@ -42,9 +42,9 @@ public class HubPlayerJoin implements Listener { Color.ORANGE, Color.WHITE, Color.AQUA, Color.LIME, }; - private final ZanderHubMain plugin; + private final JavaPlugin plugin; - public HubPlayerJoin(ZanderHubMain plugin) { + public HubPlayerJoin(JavaPlugin plugin) { this.plugin = plugin; } @@ -61,17 +61,17 @@ public void onPlayerLogin(PlayerLoginEvent event) { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); + setInitialState(player); // * just be aware, runs before checking vanish if (Misc.isVanish(player)) return; - event.joinMessage(ConfigurationManager.getMessage().playerJoin(player.displayName())); + event.joinMessage(ConfigurationManager.getMessages().playerJoin(player.displayName())); Bukkit.getScheduler().runTaskLater(plugin, () -> { if (!player.isConnected()) return; - // * bukkit uses 'world/playerdata' dir for tracking - if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { + if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { // * bukkit uses 'world/playerdata' dir chatWelcomeMessage(player); spawnWelcomeFirework(player); } @@ -89,10 +89,24 @@ private void setPermissions(Player player) { /// Set the initial state of the player in the world. private void setInitialState(Player player) { setupNoCollision(player); - player.teleport(ConfigurationManager.getHubLocation()); + Location spawn = ConfigurationManager.getHubLocations().spawn(); + plugin.getLogger().info(String.format( + "DEBUG Spawn: world='%s', x=%.2f, y=%.2f, z=%.2f, yaw=%.2f, pitch=%.2f", + spawn.getWorld() != null ? spawn.getWorld().getName() : "null", + spawn.getX(), + spawn.getY(), + spawn.getZ(), + spawn.getYaw(), + spawn.getPitch())); + player.teleport(spawn); player.getInventory().clear(); player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); NavigationCompassItem.giveCompass(player); + // setupNoCollision(player); + // player.teleport(ConfigurationManager.getHubLocations().spawn()); + // player.getInventory().clear(); + // player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); + // NavigationCompassItem.giveCompass(player); } /// Disable entity collision for player. @@ -139,6 +153,7 @@ private void spawnWelcomeFirework(Player player) { for (int i = 0; i < numColors; i++) { colors.add(FCP[random.nextInt(FCP.length)]); } + // random fade-color and firework type Color fadeColor = FCP[random.nextInt(FCP.length)]; FireworkEffect.Type[] types = FireworkEffect.Type.values(); @@ -156,9 +171,8 @@ private void spawnWelcomeFirework(Player player) { firework.setFireworkMeta(meta); Bukkit.getScheduler().runTaskLater(plugin, () -> { - if (firework.isValid()) { + if (firework.isValid()) firework.detonate(); - } }, FIREWORK_DETONATE_DELAY); } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java index 544d122..0c548af 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java @@ -4,24 +4,22 @@ import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; import org.modularsoft.zander.hub.ConfigurationManager; -import org.modularsoft.zander.hub.ZanderHubMain; import org.modularsoft.zander.hub.utils.Misc; public class HubPlayerLeave implements Listener { + private final JavaPlugin plugin; - private final ZanderHubMain plugin; - - public HubPlayerLeave(ZanderHubMain plugin) { + public HubPlayerLeave(JavaPlugin plugin) { this.plugin = plugin; } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); - if (Misc.isVanish(player)) { + if (Misc.isVanish(player)) return; - } - event.quitMessage(ConfigurationManager.getMessage().playerLeave(player.displayName())); + event.quitMessage(ConfigurationManager.getMessages().playerLeave(player.displayName())); } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java index c93d6f0..adcd1e1 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java @@ -13,6 +13,7 @@ public class HubPlayerVoid implements Listener { ZanderHubMain plugin; + public HubPlayerVoid(ZanderHubMain plugin) { this.plugin = plugin; } @@ -25,12 +26,13 @@ public void onPlayerMove(PlayerMoveEvent event) { World defaultworld = Bukkit.getServer().getWorlds().get(0); World hubworld = Bukkit.getWorld(plugin.getConfig().getString("hub.world", defaultworld.getName())); if (hubworld == null) { - Bukkit.getLogger().warning("No world by the name of " + defaultworld.getName() + " was found! Assuming default world."); -// hubworld = Bukkit.getServer().getWorlds().get(0); + Bukkit.getLogger().warning( + "No world by the name of " + defaultworld.getName() + " was found! Assuming default world."); + // hubworld = Bukkit.getServer().getWorlds().get(0); } if (location.getBlockY() <= 0) { - player.teleport(ConfigurationManager.getHubLocation()); + player.teleport(ConfigurationManager.getHubLocations().spawn()); } } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java new file mode 100644 index 0000000..f3a57c7 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java @@ -0,0 +1,153 @@ +package org.modularsoft.zander.hub.utils; + +import java.util.function.Consumer; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.modularsoft.zander.hub.ZanderHubMain; + +/** + * Utility class providing validators for fields on Bukkit config object. + */ +public final class ConfigValidator { + private static final JavaPlugin plugin = ZanderHubMain.plugin; + + private ConfigValidator() { + throw new IllegalStateException("Utility class shouldn't be instantiated"); + } + + public static class ValidationResult { + private final boolean valid; + private final String logMsg; + + private ValidationResult(boolean valid, String logMsg) { + this.valid = valid; + this.logMsg = logMsg; + } + + public static ValidationResult success() { + return new ValidationResult(true, ""); + } + + public static ValidationResult failure(String logMsg) { + return new ValidationResult(false, logMsg); + } + + public boolean isValid() { + return valid; + } + + public String getLog() { + return logMsg; + } + } + + @FunctionalInterface + public static interface Validator { + ValidationResult validate(FileConfiguration config, String field); + } + + @FunctionalInterface + public static interface ValidatorWithSetter { + ValidationResult validate(FileConfiguration config, String field, Consumer setter); + } + + /// Validate `field` in `config` with custom `validator` function. + /// When validator fails, `defaultValue` is used and instated in `config` + public static ValidationResult validateConfig(FileConfiguration config, String field, + Validator validator, Object defaultValue) { + ValidationResult result = validator.validate(config, field); + if (!result.isValid()) { + config.set(field, defaultValue); + plugin.getLogger().warning( + String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, defaultValue)); + plugin.getLogger().warning(result.getLog()); + } + return result; + } + + /// Overload for `validator` that want integration with setting values. + public static ValidationResult validateConfig(FileConfiguration config, String field, + ValidatorWithSetter validator, Object defaultValue, Consumer setter) { + ValidationResult result = validator.validate(config, field, setter); + if (!result.isValid()) { + config.set(field, defaultValue); + plugin.getLogger().warning( + String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, defaultValue)); + plugin.getLogger().warning(result.getLog()); + } + return result; + } + + /// Validate `field` value is a double decimal. + public static final Validator isValidDouble = (config, field) -> { + if (!config.isDouble(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't a decimal number", field)); + return ValidationResult.success(); + }; + + /// Validate `field` value is proper pitch rotation. + public static final Validator isValidPitch = (config, field) -> { + if (!config.isDouble(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't a decimal number", field)); + double value = config.getDouble(field); + if (value < -90.0 || value > 90.0) { + return ValidationResult + .failure(String.format("Reason '%s' wasn't between -90.0 and 90.0", field)); + } + return ValidationResult.success(); + }; + + /// Validate `field` value is proper yaw rotation. + public static final Validator isValidYaw = (config, field) -> { + if (!config.isDouble(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't a decimal number", field)); + double value = config.getDouble(field); + if (value < -180.0 || value > 180.0) { + return ValidationResult + .failure(String.format("Reason '%s' wasn't between -180.0 and 180.0", field)); + } + return ValidationResult.success(); + }; + + /// Validate `field` value is the name of a valid world. + public static final Validator isValidWorld = (config, field) -> { + if (!config.isString(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't a text string", field)); + String worldName = config.getString(field); + if (Bukkit.getWorld(worldName) == null) { + return ValidationResult + .failure(String.format("Reason '%s' refers to non-existent world '%s'", field, worldName)); + } + return ValidationResult.success(); + }; + + /// Validate `field` value is legacy string properly formatted with single %p% + public static final ValidatorWithSetter isValidJoinLeave = (config, field, setter) -> { + // 1. verify field value value is string + // 2. verify field value has single %p% placeholder + // 3. verify field value can be converted to adventure text component + // 4. use setter on parsed value (efficient no parse again in parent) + if (!config.isString(field)) + return ValidationResult.failure(String.format("Reason was '%s' must be a text string", field)); + + String textLegacy = config.getString(field); + try { + int placeholderIndex = textLegacy.indexOf("%p%"); + if (placeholderIndex == -1 || placeholderIndex != textLegacy.lastIndexOf("%p%")) { + return ValidationResult + .failure(String.format("Reason was '%s' must contain exactly one '%p%' placeholder", field)); + } + LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand(); + TextComponent template = serializer.deserialize(textLegacy); + setter.accept(template); + return ValidationResult.success(); + + } catch (Exception e) { + return ValidationResult + .failure(String.format("Reason was '%s' has invalid format; %s", field, e.getMessage())); + } + }; +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java index 2235ad4..670a970 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java @@ -3,6 +3,9 @@ import de.myzelyam.api.vanish.VanishAPI; import org.bukkit.entity.Player; +/** + * Utility class providing miscellaneous functions. + */ public final class Misc { private Misc() { diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java index 95395bd..b9773a7 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java @@ -2,6 +2,9 @@ import org.bukkit.Sound; +/** + * Utility class providing functions on welcome sounds. + */ public final class WelcomeSounds { private static final Sound[] SOUNDS = { diff --git a/zander-hub/src/main/resources/config.yml b/zander-hub/src/main/resources/config.yml index bd94f31..dde7035 100644 --- a/zander-hub/src/main/resources/config.yml +++ b/zander-hub/src/main/resources/config.yml @@ -2,7 +2,10 @@ velocitymultiplier: 3 hub: world: world x: 0.5 - y: 33 + y: 33.0 z: 0.5 yaw: -180.0 - pitch: 0.0 \ No newline at end of file + pitch: 0.0 +messages: + join: '&7%p% joined.' + leave: '&7%p% left.' From 0440e839d61838c07f672e1291763f969a7f108d Mon Sep 17 00:00:00 2001 From: palsmo Date: Sun, 5 Jan 2025 02:21:27 +0100 Subject: [PATCH 14/15] Removed debug code accidentally left behind --- .../zander/hub/events/HubPlayerJoin.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index 1609681..fb16b53 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -89,24 +89,10 @@ private void setPermissions(Player player) { /// Set the initial state of the player in the world. private void setInitialState(Player player) { setupNoCollision(player); - Location spawn = ConfigurationManager.getHubLocations().spawn(); - plugin.getLogger().info(String.format( - "DEBUG Spawn: world='%s', x=%.2f, y=%.2f, z=%.2f, yaw=%.2f, pitch=%.2f", - spawn.getWorld() != null ? spawn.getWorld().getName() : "null", - spawn.getX(), - spawn.getY(), - spawn.getZ(), - spawn.getYaw(), - spawn.getPitch())); - player.teleport(spawn); + player.teleport(ConfigurationManager.getHubLocations().spawn()); player.getInventory().clear(); player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); NavigationCompassItem.giveCompass(player); - // setupNoCollision(player); - // player.teleport(ConfigurationManager.getHubLocations().spawn()); - // player.getInventory().clear(); - // player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); - // NavigationCompassItem.giveCompass(player); } /// Disable entity collision for player. From a07194f761b37a0115e6e50fdf303c45de1c306a Mon Sep 17 00:00:00 2001 From: palsmo Date: Mon, 6 Jan 2025 01:31:58 +0100 Subject: [PATCH 15/15] Proper mirroring of resource files, new configs 'slot_hub_compass' & 'always_first_join', reverted to old 'welcome.yml' and refined some existing code. --- .../zander/hub/ConfigurationManager.java | 29 ++++++-- .../modularsoft/zander/hub/ZanderHubMain.java | 28 ++------ .../hub/configs/HubLocationsConfig.java | 38 ++++++---- .../zander/hub/configs/MessagesConfig.java | 27 +++---- .../zander/hub/configs/MiscConfig.java | 54 ++++++++++++++ .../zander/hub/events/HubPlayerJoin.java | 72 ++++++++++--------- .../zander/hub/events/HubPlayerLeave.java | 2 +- .../zander/hub/events/HubPlayerVoid.java | 2 +- .../hub/items/NavigationCompassItem.java | 22 ++---- .../zander/hub/utils/ConfigValidator.java | 54 ++++++++++---- .../zander/hub/utils/CopyResources.java | 56 +++++++++++++++ .../zander/hub/utils/ItemBuilder.java | 40 +++++++++++ zander-hub/src/main/resources/config.yml | 5 +- zander-hub/src/main/resources/welcome.yml | 20 +++--- zander-hub/src/main/resources/welcome_old.yml | 9 --- 15 files changed, 316 insertions(+), 142 deletions(-) create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MiscConfig.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/utils/CopyResources.java create mode 100644 zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ItemBuilder.java delete mode 100644 zander-hub/src/main/resources/welcome_old.yml diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java index 22481bc..7b3e301 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ConfigurationManager.java @@ -3,19 +3,21 @@ import java.io.File; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.modularsoft.zander.hub.configs.MessagesConfig; import org.modularsoft.zander.hub.configs.HubLocationsConfig; +import org.modularsoft.zander.hub.configs.MessagesConfig; +import org.modularsoft.zander.hub.configs.MiscConfig; public final class ConfigurationManager { private static FileConfiguration welcomeFile; private static HubLocationsConfig hubLocationsConfig; private static MessagesConfig messagesConfig; + private static MiscConfig miscConfig; private ConfigurationManager() { throw new IllegalStateException("Utility class shouldn't be instantiated"); } - public static void setupHubLocations() { + public static void setupHubLocationsConfig() { if (hubLocationsConfig != null) throw new IllegalStateException("Already setup, ensure there's a single call"); hubLocationsConfig = new HubLocationsConfig(ZanderHubMain.plugin); @@ -23,13 +25,21 @@ public static void setupHubLocations() { // future? hubLocationsConfig.setupParkour(); } - public static void setupMessages() { + public static void setupMessagesConfig() { if (messagesConfig != null) throw new IllegalStateException("Already setup, ensure there's a single call"); messagesConfig = new MessagesConfig(ZanderHubMain.plugin); messagesConfig.setupJoinLeave(); } + public static void setupMiscConfig() { + if (miscConfig != null) + throw new IllegalStateException("Already setup, ensure there's a single call"); + miscConfig = new MiscConfig(ZanderHubMain.plugin); + miscConfig.setupSlotHubCompass(); + miscConfig.setupAlwaysFirstJoin(); + } + public static void setupWelcomeFile() { if (welcomeFile != null) throw new IllegalStateException("Already setup, ensure there's a single call"); @@ -37,22 +47,27 @@ public static void setupWelcomeFile() { File welcomeFileYML = new File(dataFolder, "welcome.yml"); if (!welcomeFileYML.exists()) ZanderHubMain.plugin.saveResource("welcome.yml", false); - - ConfigurationManager.welcomeFile = YamlConfiguration.loadConfiguration(welcomeFileYML); + welcomeFile = YamlConfiguration.loadConfiguration(welcomeFileYML); } public static HubLocationsConfig getHubLocations() { if (hubLocationsConfig == null) - throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupHubLocations'"); + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupHubLocationsConfig'"); return hubLocationsConfig; } public static MessagesConfig getMessages() { if (messagesConfig == null) - throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupMessages'"); + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupMessagesConfig'"); return messagesConfig; } + public static MiscConfig getMisc() { + if (miscConfig == null) + throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupMiscConfig'"); + return miscConfig; + } + public static FileConfiguration getWelcome() { if (welcomeFile == null) throw new IllegalStateException("Missing setup, first run 'ConfigurationManager.setupWelcomeFile'"); diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java index 4b5cd96..b85dc30 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/ZanderHubMain.java @@ -1,11 +1,8 @@ package org.modularsoft.zander.hub; -import java.io.InputStreamReader; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; import org.modularsoft.zander.hub.commands.fly; @@ -18,6 +15,7 @@ import org.modularsoft.zander.hub.protection.HubCreatureSpawnProtection; import org.modularsoft.zander.hub.protection.HubInteractionProtection; import org.modularsoft.zander.hub.protection.HubProtection; +import org.modularsoft.zander.hub.utils.CopyResources; public class ZanderHubMain extends JavaPlugin { public static ZanderHubMain plugin; @@ -25,9 +23,12 @@ public class ZanderHubMain extends JavaPlugin { public void onEnable() { plugin = this; - copyConfigsResourceToServer(); - ConfigurationManager.setupHubLocations(); - ConfigurationManager.setupMessages(); + CopyResources.mirror("config.yml"); + CopyResources.mirror("welcome.yml"); + + ConfigurationManager.setupHubLocationsConfig(); + ConfigurationManager.setupMessagesConfig(); + ConfigurationManager.setupMiscConfig(); ConfigurationManager.setupWelcomeFile(); this.getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord"); @@ -66,19 +67,4 @@ public void onEnable() { @Override public void onDisable() { } - - /// Copy _missing_ fields from resources/config.yml to server config.yml - /// Bukkit ensures auto-created empty config.yml file in plugin's folder. - private void copyConfigsResourceToServer() { - // 1. load the server 'config.yml' - // 2. load embedded default config ('resources/config.yml') - // 3. adjust configuration object in memory - // 4. reflect changes to external file - FileConfiguration config = this.getConfig(); - FileConfiguration defaultConfig = YamlConfiguration.loadConfiguration( - new InputStreamReader(this.getResource("config.yml"))); - config.setDefaults(defaultConfig); - config.options().copyDefaults(true); - this.saveConfig(); - } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java index a9119dd..5d5ddbe 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java @@ -25,33 +25,41 @@ public HubLocationsConfig(JavaPlugin plugin) { this.plugin = plugin; } - /// Configure the spawn location, ensures valid entries in 'config.yml' + /// Configure the hub spawn location. + /// Validates the entries in server 'config.yml' with fallback. public void setupSpawn() { FileConfiguration config = plugin.getConfig(); // * access server's primary world (guaranteed by Bukkit to exist) Location defaultSpawn = Bukkit.getServer().getWorlds().get(0).getSpawnLocation(); - validateConfig(config, "hub.world", isValidWorld, defaultSpawn.getWorld().getName()); - validateConfig(config, "hub.x", isValidDouble, defaultSpawn.getX()); - validateConfig(config, "hub.y", isValidDouble, defaultSpawn.getY()); - validateConfig(config, "hub.z", isValidDouble, defaultSpawn.getZ()); - validateConfig(config, "hub.pitch", isValidPitch, defaultSpawn.getPitch()); - validateConfig(config, "hub.yaw", isValidYaw, defaultSpawn.getYaw()); + String fieldWorld = "hub.world"; + String fieldX = "hub.x"; + String fieldY = "hub.y"; + String fieldZ = "hub.z"; + String fieldPitch = "hub.pitch"; + String fieldYaw = "hub.yaw"; + + validateConfig(config, fieldWorld, isValidWorld, defaultSpawn.getWorld().getName()); + validateConfig(config, fieldX, isValidDouble, defaultSpawn.getX()); + validateConfig(config, fieldY, isValidDouble, defaultSpawn.getY()); + validateConfig(config, fieldZ, isValidDouble, defaultSpawn.getZ()); + validateConfig(config, fieldPitch, isValidPitch, defaultSpawn.getPitch()); + validateConfig(config, fieldYaw, isValidYaw, defaultSpawn.getYaw()); plugin.saveConfig(); // * save to external 'config.yml' - World hubWorld = Bukkit.getWorld(config.getString("hub.world")); - double hubX = config.getDouble("hub.x"); - double hubY = config.getDouble("hub.y"); - double hubZ = config.getDouble("hub.z"); - float hubYaw = (float) config.getDouble("hub.yaw"); - float hubPitch = (float) config.getDouble("hub.pitch"); + World hubWorld = Bukkit.getWorld(config.getString(fieldWorld)); + double hubX = config.getDouble(fieldX); + double hubY = config.getDouble(fieldY); + double hubZ = config.getDouble(fieldZ); + float hubYaw = (float) config.getDouble(fieldYaw); + float hubPitch = (float) config.getDouble(fieldPitch); this.locationSpawn = new Location(hubWorld, hubX, hubY, hubZ, hubYaw, hubPitch); } - /// Retrieve the spawn location. - public Location spawn() { + /// Retrieve the hub spawn location. + public Location getSpawn() { if (this.locationSpawn == null) throw new IllegalStateException("Missing setup, first run 'HubLocationsConfig.setupSpawn'"); return this.locationSpawn.clone(); diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java index e7b6de6..b265554 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java @@ -25,22 +25,25 @@ public MessagesConfig(JavaPlugin plugin) { this.plugin = plugin; } - /// Configure the join/leave messages, ensures valid entries in 'config.yml' + /// Configure the player join/leave messages. + /// Validates the entries in server 'config.yml' with fallback. public void setupJoinLeave() { FileConfiguration config = plugin.getConfig(); - String defaultJoin = "&7%p% "; - String defaultLeave = "&7%p% "; + String fallbackJoin = "&7%p% "; + String fallbackLeave = "&7%p% "; + String fieldJoin = "messages.join"; + String fieldLeave = "messages.leave"; - validateConfig(config, "messages.join", isValidJoinLeave, defaultJoin, + validateConfig(config, fieldJoin, isValidJoinLeave, fallbackJoin, template -> this.textCompJoinTemplate = template); - validateConfig(config, "messages.leave", isValidJoinLeave, defaultLeave, + validateConfig(config, fieldLeave, isValidJoinLeave, fallbackLeave, template -> this.textCompLeaveTemplate = template); plugin.saveConfig(); // * save to external 'config.yml' - String textLegacyJoin = config.getString("messages.join"); - String textLegacyLeave = config.getString("messages.leave"); + String textLegacyJoin = config.getString(fieldJoin); + String textLegacyLeave = config.getString(fieldLeave); if (this.plugin.getServer().getPluginManager().getPlugin("PremiumVanish") != null) { updatePremiumVanish(textLegacyJoin, textLegacyLeave); @@ -48,13 +51,13 @@ public void setupJoinLeave() { } } - /// Retrieve join message for `playerName` - public Component playerJoin(Component playerName) { + /// Get join message for `playerName` + public Component getPlayerJoin(Component playerName) { return insertPlayerName(this.textCompJoinTemplate, playerName); } - /// Retrieve leave message for `playerName` - public Component playerLeave(Component playerName) { + /// Get leave message for `playerName` + public Component getPlayerLeave(Component playerName) { return insertPlayerName(this.textCompLeaveTemplate, playerName); } @@ -63,7 +66,7 @@ private Component insertPlayerName(TextComponent template, Component playerName) if (template == null) throw new IllegalStateException("Missing setup, first run 'MessagesConfig.setupJoinLeave'"); return template.replaceText(builder -> builder - .match("%p%") // * 'parseToTemplate' ensured single %p% + .match("%p%") // * validation ensured single %p% .replacement(playerName)); // * style is handled as expected } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MiscConfig.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MiscConfig.java new file mode 100644 index 0000000..20eae00 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MiscConfig.java @@ -0,0 +1,54 @@ +package org.modularsoft.zander.hub.configs; + +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidBoolean; +import static org.modularsoft.zander.hub.utils.ConfigValidator.isValidHotbarSlot; +import static org.modularsoft.zander.hub.utils.ConfigValidator.validateConfig; + +/** + * Manages miscellaneous settings for the plugin, and their persistance. + * Handles loading, validation, and access to managed data. + */ +public class MiscConfig { + private final JavaPlugin plugin; + + private int slotHubCompass; // * default 0 + private boolean alwaysFirstJoin; // * default false + + public MiscConfig(JavaPlugin plugin) { + this.plugin = plugin; + } + + /// Configure the inventory slot for the hub compass. + /// Validates the entry in server 'config.yml' with fallback. + public void setupSlotHubCompass() { + FileConfiguration config = plugin.getConfig(); + int fallback = 4; + String field = "misc.slot_hub_compass"; + validateConfig(config, field, isValidHotbarSlot, fallback); + plugin.saveConfig(); // * save to external 'config.yml' + this.slotHubCompass = config.getInt(field); + } + + /// Configure the setting for "always trigger first join". + /// Validates the entry in server 'config.yml' with fallback. + public void setupAlwaysFirstJoin() { + FileConfiguration config = plugin.getConfig(); + boolean fallback = false; + String field = "misc.always_first_join"; + validateConfig(config, field, isValidBoolean, fallback); + plugin.saveConfig(); // * save to external 'config.yml' + this.alwaysFirstJoin = config.getBoolean(field); + } + + /// Get the inventory slot for the hub compass. + public int getSlotHubCompass() { + return this.slotHubCompass; + } + + /// Get the setting for "always trigger first join". + public boolean getAlwaysFirstJoin() { + return this.alwaysFirstJoin; + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java index fb16b53..5c78aee 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerJoin.java @@ -25,18 +25,16 @@ import org.modularsoft.zander.hub.utils.WelcomeSounds; public class HubPlayerJoin implements Listener { - // Misc settings - private static final boolean TEST_ALWAYS_FIRST_JOIN = false; // * default false - private static final int NAV_COMPASS_SLOT = 4; - private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.2f * 20); + // misc settings + private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.2f * 20); // ticks - // Sound settings + // sound settings private static final float SOUND_PITCH = 1.0f; private static final float SOUND_VOLUME = 1.0f; - // Firework settings + // firework settings private static final double FIREWORK_GROUND_HEIGHT = 3; // blocks - private static final long FIREWORK_DETONATE_DELAY = (long) (0.3f * 20); + private static final long FIREWORK_DETONATE_DELAY = (long) (0.3f * 20); // ticks private static final Color[] FIREWORK_COLOR_PALETTE = { Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.PURPLE, Color.ORANGE, Color.WHITE, Color.AQUA, Color.LIME, @@ -61,22 +59,11 @@ public void onPlayerLogin(PlayerLoginEvent event) { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player player = event.getPlayer(); - setInitialState(player); // * just be aware, runs before checking vanish if (Misc.isVanish(player)) return; - - event.joinMessage(ConfigurationManager.getMessages().playerJoin(player.displayName())); - - Bukkit.getScheduler().runTaskLater(plugin, () -> { - if (!player.isConnected()) - return; - if (!player.hasPlayedBefore() || TEST_ALWAYS_FIRST_JOIN) { // * bukkit uses 'world/playerdata' dir - chatWelcomeMessage(player); - spawnWelcomeFirework(player); - } - playWelcomeSound(player); - }, ROUTINE_PLAYER_JOINED_DELAY); + event.joinMessage(ConfigurationManager.getMessages().getPlayerJoin(player.displayName())); + Bukkit.getScheduler().runTaskLater(plugin, () -> scheduledLogin(player), ROUTINE_PLAYER_JOINED_DELAY); } /// Set special permission depending on the player. @@ -88,11 +75,26 @@ private void setPermissions(Player player) { /// Set the initial state of the player in the world. private void setInitialState(Player player) { + int compassSlot = ConfigurationManager.getMisc().getSlotHubCompass(); setupNoCollision(player); - player.teleport(ConfigurationManager.getHubLocations().spawn()); + player.teleport(ConfigurationManager.getHubLocations().getSpawn()); player.getInventory().clear(); - player.getInventory().setHeldItemSlot(NAV_COMPASS_SLOT); - NavigationCompassItem.giveCompass(player); + player.getInventory().setHeldItemSlot(compassSlot); + player.getInventory().setItem(compassSlot, NavigationCompassItem.createCompass()); + } + + /// Delayed login triggers (logic that's not immediate on login). + private void scheduledLogin(Player player) { + if (!player.isConnected()) + return; + // * bukkit determines by reading 'world/playerdata' + if (!player.hasPlayedBefore() || ConfigurationManager.getMisc().getAlwaysFirstJoin()) { + chatWelcomeMessageFirst(player); + spawnWelcomeFirework(player); + } else { + chatWelcomeMessageNormal(player); + } + playWelcomeSound(player); } /// Disable entity collision for player. @@ -108,22 +110,26 @@ private void setupNoCollision(Player player) { team.addEntry(player.getName()); } + /// Send a 'normal welcome' message in player's chat. + private void chatWelcomeMessageNormal(Player player) { + List message = ConfigurationManager.getWelcome().getStringList("welcome"); + for (String row : message) + player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); + } + + /// Send a 'new player welcome' message in player's chat. + private void chatWelcomeMessageFirst(Player player) { + List message = ConfigurationManager.getWelcome().getStringList("welcome_newplayer"); + for (String row : message) + player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); + } + /// Play a random sound for the player. private void playWelcomeSound(Player player) { Sound randomSound = WelcomeSounds.getRandomSound(); player.playSound(player.getLocation(), randomSound, SOUND_VOLUME, SOUND_PITCH); } - /// Send a welcome message in player's chat. - private void chatWelcomeMessage(Player player) { - List message = ConfigurationManager.getWelcome().getStringList("newplayerwelcome"); - player.sendMessage(""); - for (String row : message) { - player.sendMessage(ChatColor.translateAlternateColorCodes('&', row)); - } - player.sendMessage(""); - } - /// Spawn a pretty firework where the player is. private void spawnWelcomeFirework(Player player) { Location spawnLoc = player.getLocation().add(0, FIREWORK_GROUND_HEIGHT, 0); diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java index 0c548af..bbcfeee 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java @@ -20,6 +20,6 @@ public void onPlayerQuit(PlayerQuitEvent event) { Player player = event.getPlayer(); if (Misc.isVanish(player)) return; - event.quitMessage(ConfigurationManager.getMessages().playerLeave(player.displayName())); + event.quitMessage(ConfigurationManager.getMessages().getPlayerLeave(player.displayName())); } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java index adcd1e1..bd8bc6c 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerVoid.java @@ -32,7 +32,7 @@ public void onPlayerMove(PlayerMoveEvent event) { } if (location.getBlockY() <= 0) { - player.teleport(ConfigurationManager.getHubLocations().spawn()); + player.teleport(ConfigurationManager.getHubLocations().getSpawn()); } } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/items/NavigationCompassItem.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/items/NavigationCompassItem.java index 3ba575a..b1d1eb8 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/items/NavigationCompassItem.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/items/NavigationCompassItem.java @@ -4,25 +4,15 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import org.bukkit.Material; -import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.List; +import org.modularsoft.zander.hub.utils.ItemBuilder; public class NavigationCompassItem implements Listener { - public static void giveCompass(Player player) { - ItemStack navcompass = new ItemStack(Material.COMPASS); - ItemMeta meta = navcompass.getItemMeta(); - - meta.displayName(Component.text("Navigation Compass", NamedTextColor.AQUA, TextDecoration.BOLD)); - - List lore = new ArrayList<>(); - lore.add(Component.text("Right Click me to access Servers", NamedTextColor.YELLOW)); - meta.lore(lore); - navcompass.setItemMeta(meta); - player.getInventory().setItem(4, navcompass); // Put the compass into the players middle hand + public static ItemStack createCompass() { + return ItemBuilder.of(Material.COMPASS) + .name(Component.text("Navigation Compass", NamedTextColor.AQUA, TextDecoration.BOLD)) + .lore(Component.text("Right Click me to access Servers", NamedTextColor.YELLOW)) + .build(); } } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java index f3a57c7..5622ed7 100644 --- a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java @@ -9,7 +9,7 @@ import org.modularsoft.zander.hub.ZanderHubMain; /** - * Utility class providing validators for fields on Bukkit config object. + * Utility class providing validators for YAML fields in Bukkit config objects. */ public final class ConfigValidator { private static final JavaPlugin plugin = ZanderHubMain.plugin; @@ -55,14 +55,14 @@ public static interface ValidatorWithSetter { } /// Validate `field` in `config` with custom `validator` function. - /// When validator fails, `defaultValue` is used and instated in `config` + /// When validator fails, `fallback` is used and instated in `config` public static ValidationResult validateConfig(FileConfiguration config, String field, - Validator validator, Object defaultValue) { + Validator validator, Object fallback) { ValidationResult result = validator.validate(config, field); if (!result.isValid()) { - config.set(field, defaultValue); + config.set(field, fallback); plugin.getLogger().warning( - String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, defaultValue)); + String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, fallback)); plugin.getLogger().warning(result.getLog()); } return result; @@ -70,17 +70,26 @@ public static ValidationResult validateConfig(FileConfiguration config, String f /// Overload for `validator` that want integration with setting values. public static ValidationResult validateConfig(FileConfiguration config, String field, - ValidatorWithSetter validator, Object defaultValue, Consumer setter) { + ValidatorWithSetter validator, Object fallback, Consumer setter) { ValidationResult result = validator.validate(config, field, setter); if (!result.isValid()) { - config.set(field, defaultValue); + config.set(field, fallback); plugin.getLogger().warning( - String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, defaultValue)); + String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, fallback)); plugin.getLogger().warning(result.getLog()); } return result; } + /// Validate `field` value is a boolean. + /// Yaml true: [y, Y, yes, Yes, YES, true, True, TRUE, on, On, ON] + /// Yaml fale: [n, N, no, No, NO, false, False, FALSE, off, Off, OFF] + public static final Validator isValidBoolean = (config, field) -> { + if (!config.isBoolean(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't a boolean value", field)); + return ValidationResult.success(); + }; + /// Validate `field` value is a double decimal. public static final Validator isValidDouble = (config, field) -> { if (!config.isDouble(field)) @@ -88,6 +97,13 @@ public static ValidationResult validateConfig(FileConfiguration config, Stri return ValidationResult.success(); }; + /// Validate `field` value is an integer decimal. + public static final Validator isValidInt = (config, field) -> { + if (!config.isInt(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't an integer number", field)); + return ValidationResult.success(); + }; + /// Validate `field` value is proper pitch rotation. public static final Validator isValidPitch = (config, field) -> { if (!config.isDouble(field)) @@ -105,10 +121,18 @@ public static ValidationResult validateConfig(FileConfiguration config, Stri if (!config.isDouble(field)) return ValidationResult.failure(String.format("Reason '%s' wasn't a decimal number", field)); double value = config.getDouble(field); - if (value < -180.0 || value > 180.0) { - return ValidationResult - .failure(String.format("Reason '%s' wasn't between -180.0 and 180.0", field)); - } + if (value < -180.0 || value > 180.0) + return ValidationResult.failure(String.format("Reason '%s' wasn't between -180.0 and 180.0", field)); + return ValidationResult.success(); + }; + + /// Validate `field` value is proper hotbar index. + public static final Validator isValidHotbarSlot = (config, field) -> { + if (!config.isInt(field)) + return ValidationResult.failure(String.format("Reason '%s' wasn't an integer number", field)); + int value = config.getInt(field); + if (value < 0 || value > 8) + return ValidationResult.failure(String.format("Reason '%s' wasn't between 0 and 8", field)); return ValidationResult.success(); }; @@ -131,14 +155,14 @@ public static ValidationResult validateConfig(FileConfiguration config, Stri // 3. verify field value can be converted to adventure text component // 4. use setter on parsed value (efficient no parse again in parent) if (!config.isString(field)) - return ValidationResult.failure(String.format("Reason was '%s' must be a text string", field)); + return ValidationResult.failure(String.format("Reason '%s' wasn't a text string", field)); String textLegacy = config.getString(field); try { int placeholderIndex = textLegacy.indexOf("%p%"); if (placeholderIndex == -1 || placeholderIndex != textLegacy.lastIndexOf("%p%")) { return ValidationResult - .failure(String.format("Reason was '%s' must contain exactly one '%p%' placeholder", field)); + .failure(String.format("Reason '%s' must contain exactly one '%p%' placeholder", field)); } LegacyComponentSerializer serializer = LegacyComponentSerializer.legacyAmpersand(); TextComponent template = serializer.deserialize(textLegacy); @@ -147,7 +171,7 @@ public static ValidationResult validateConfig(FileConfiguration config, Stri } catch (Exception e) { return ValidationResult - .failure(String.format("Reason was '%s' has invalid format; %s", field, e.getMessage())); + .failure(String.format("Reason '%s' has invalid format; %s", field, e.getMessage())); } }; } diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/CopyResources.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/CopyResources.java new file mode 100644 index 0000000..b4a0881 --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/CopyResources.java @@ -0,0 +1,56 @@ +package org.modularsoft.zander.hub.utils; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.logging.Level; +import java.io.InputStreamReader; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.modularsoft.zander.hub.ZanderHubMain; + +/** + * Utility class for doing respectful copies of resources to server environment. + */ +public final class CopyResources { + private static final JavaPlugin plugin = ZanderHubMain.plugin; + + private CopyResources() { + throw new IllegalStateException("Utility class shouldn't be instantiated"); + } + + /// Mirror hierarchy and fields from embedded file at 'resources/'. + /// Server environment will have a corresponding file in plugins data folder. + public static void mirror(String filepath) { + InputStream resource = plugin.getResource(filepath); + + if (resource == null) { + plugin.getLogger().warning(String.format("Missing resource '%s'", filepath)); + return; + } + + File targetFile = new File(plugin.getDataFolder(), filepath); + + targetFile.getParentFile().mkdirs(); // * create any parent directories + + InputStreamReader reader = new InputStreamReader(resource); + FileConfiguration serverConf = YamlConfiguration.loadConfiguration(targetFile); + FileConfiguration templateConf = YamlConfiguration.loadConfiguration(reader); + + try { + reader.close(); + } catch (IOException e) { + plugin.getLogger().log(Level.SEVERE, String.format("Could not close reader for '%s'", filepath), e); + } + + serverConf.setDefaults(templateConf); + serverConf.options().copyDefaults(true); + + try { + serverConf.save(targetFile); + } catch (IOException e) { + plugin.getLogger().log(Level.SEVERE, String.format("Could not save '%s'", filepath), e); + } + } +} diff --git a/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ItemBuilder.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ItemBuilder.java new file mode 100644 index 0000000..f8e54ce --- /dev/null +++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ItemBuilder.java @@ -0,0 +1,40 @@ +package org.modularsoft.zander.hub.utils; + +import java.util.Arrays; +import net.kyori.adventure.text.Component; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +/** + * Utility class providing fluent item creation and modification. + * Simplifies building ItemStacks through method chaining. + */ +public class ItemBuilder { + private final ItemStack item; + private final ItemMeta meta; + + private ItemBuilder(Material material) { + this.item = new ItemStack(material); + this.meta = item.getItemMeta(); + } + + public static ItemBuilder of(Material material) { + return new ItemBuilder(material); + } + + public ItemBuilder name(Component name) { + meta.displayName(name); + return this; + } + + public ItemBuilder lore(Component... lore) { + meta.lore(Arrays.asList(lore)); + return this; + } + + public ItemStack build() { + item.setItemMeta(meta); + return item; + } +} diff --git a/zander-hub/src/main/resources/config.yml b/zander-hub/src/main/resources/config.yml index dde7035..0398af2 100644 --- a/zander-hub/src/main/resources/config.yml +++ b/zander-hub/src/main/resources/config.yml @@ -1,6 +1,6 @@ velocitymultiplier: 3 hub: - world: world + world: 'world' x: 0.5 y: 33.0 z: 0.5 @@ -9,3 +9,6 @@ hub: messages: join: '&7%p% joined.' leave: '&7%p% left.' +misc: + always_first_join: false + slot_hub_compass: 4 diff --git a/zander-hub/src/main/resources/welcome.yml b/zander-hub/src/main/resources/welcome.yml index dfbdae3..09b1fcc 100644 --- a/zander-hub/src/main/resources/welcome.yml +++ b/zander-hub/src/main/resources/welcome.yml @@ -1,11 +1,9 @@ -newplayerwelcome: - - "&e&l ============ Welcome to CFC ============ &r" - - "" - - "&f Crafting-For-Christ is a community of christian minecraft players." - - "&f You don't need to be christian to play on this server." - - "&f We expect you to follow our guidelines and spread kindness \u2764" - - "" - - "&c (view /rules)&r" - - "" - - "&e Website:&r &7https://craftingforchrist.net&r" - - "&e Discord:&r &7https://craftingforchrist.net/discord&r" +welcome: + - "&e Welcome to My Server!" +welcome_newplayer: + - "&e&l ========== My Server ========== &r" + - "&9&l Website:&r http://myserver.net" + - "&9&l Discord:&r http://myserver.net/discord" + - " " + - "By joining My Server you agree to our Rules, TOS, and Privacy Policy." + - "Make sure you read the rules. &cUse /rules to read the rules.&r" diff --git a/zander-hub/src/main/resources/welcome_old.yml b/zander-hub/src/main/resources/welcome_old.yml deleted file mode 100644 index 34a643f..0000000 --- a/zander-hub/src/main/resources/welcome_old.yml +++ /dev/null @@ -1,9 +0,0 @@ -welcome: - - "&e Welcome to My Server!" -newplayerwelcome: - - "&e&l ========== My Server ========== &r" - - "&9&l Website:&r http://myserver.net" - - "&9&l Discord:&r http://myserver.net/discord" - - " " - - "By joining My Server you agree to our Rules, TOS, and Privacy Policy." - - "Make sure you read the rules. &cUse /rules to read the rules.&r"