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"