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..4320970 100644
--- a/zander-hub/pom.xml
+++ b/zander-hub/pom.xml
@@ -18,6 +18,16 @@
papermc
https://papermc.io/repo/repository/maven-public/
+
+
+ jitpack.io
+ https://jitpack.io
+
+
+
+ dmulloy2-repo
+ https://repo.dmulloy2.net/repository/public/
+
@@ -28,6 +38,38 @@
1.20.2-R0.1-SNAPSHOT
provided
+
+
+ com.github.LeonMangler
+ PremiumVanishAPI
+ 2.9.0-4
+ provided
+
+
+
+ com.comphenix.protocol
+ ProtocolLib
+ 5.3.0
+ provided
+
-
\ No newline at end of file
+
+
+
+ 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 4a7ea8c..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
@@ -1,59 +1,76 @@
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.HubLocationsConfig;
+import org.modularsoft.zander.hub.configs.MessagesConfig;
+import org.modularsoft.zander.hub.configs.MiscConfig;
-import java.io.File;
-
-public class ConfigurationManager {
+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 setupHubLocationsConfig() {
+ 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 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();
+ }
- //
- // Welcome File
- //
public static void setupWelcomeFile() {
- if (!ZanderHubMain.plugin.getDataFolder().exists()) {
- ZanderHubMain.plugin.getDataFolder().mkdir();
- }
- 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
- // goes next to the plugin.
+ 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);
- 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);
+ welcomeFile = YamlConfiguration.loadConfiguration(welcomeFileYML);
}
- public static FileConfiguration getWelcome() {
- if (welcomeFile == null) {
- setupWelcomeFile();
- }
- return welcomeFile;
+ public static HubLocationsConfig getHubLocations() {
+ if (hubLocationsConfig == null)
+ 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.setupMessagesConfig'");
+ return messagesConfig;
}
- 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);
-
- return new Location(hubworld, hubx, huby, hubz, hubyaw, hubpitch);
+ 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'");
+ 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 410afff..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
@@ -3,17 +3,19 @@
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;
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.bukkit.plugin.PluginManager;
-import org.bukkit.plugin.java.JavaPlugin;
+import org.modularsoft.zander.hub.utils.CopyResources;
public class ZanderHubMain extends JavaPlugin {
public static ZanderHubMain plugin;
@@ -21,15 +23,23 @@ public class ZanderHubMain extends JavaPlugin {
public void onEnable() {
plugin = this;
+ CopyResources.mirror("config.yml");
+ CopyResources.mirror("welcome.yml");
+
+ ConfigurationManager.setupHubLocationsConfig();
+ ConfigurationManager.setupMessagesConfig();
+ ConfigurationManager.setupMiscConfig();
+ ConfigurationManager.setupWelcomeFile();
+
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);
@@ -37,6 +47,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);
@@ -50,11 +61,10 @@ public void onEnable() {
// Command Registry
this.getCommand("fly").setExecutor(new fly());
-
- ConfigurationManager.getHubLocation();
- saveConfig();
}
+ // load defaults from the embedded resource & don't override existing values
@Override
- public void onDisable() {}
+ public void onDisable() {
+ }
}
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..5d5ddbe
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/HubLocationsConfig.java
@@ -0,0 +1,67 @@
+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 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();
+
+ 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(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 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
new file mode 100644
index 0000000..b265554
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/configs/MessagesConfig.java
@@ -0,0 +1,89 @@
+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 player join/leave messages.
+ /// Validates the entries in server 'config.yml' with fallback.
+ public void setupJoinLeave() {
+ FileConfiguration config = plugin.getConfig();
+
+ String fallbackJoin = "&7%p% ";
+ String fallbackLeave = "&7%p% ";
+ String fieldJoin = "messages.join";
+ String fieldLeave = "messages.leave";
+
+ validateConfig(config, fieldJoin, isValidJoinLeave, fallbackJoin,
+ template -> this.textCompJoinTemplate = template);
+ validateConfig(config, fieldLeave, isValidJoinLeave, fallbackLeave,
+ template -> this.textCompLeaveTemplate = template);
+
+ plugin.saveConfig(); // * save to external 'config.yml'
+
+ String textLegacyJoin = config.getString(fieldJoin);
+ String textLegacyLeave = config.getString(fieldLeave);
+
+ if (this.plugin.getServer().getPluginManager().getPlugin("PremiumVanish") != null) {
+ updatePremiumVanish(textLegacyJoin, textLegacyLeave);
+ VanishAPI.reloadConfig();
+ }
+ }
+
+ /// Get join message for `playerName`
+ public Component getPlayerJoin(Component playerName) {
+ return insertPlayerName(this.textCompJoinTemplate, playerName);
+ }
+
+ /// Get leave message for `playerName`
+ public Component getPlayerLeave(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%") // * validation 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/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 53eadde..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
@@ -1,97 +1,170 @@
package org.modularsoft.zander.hub.events;
-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 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;
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 java.util.List;
+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.items.NavigationCompassItem;
+import org.modularsoft.zander.hub.utils.Misc;
+import org.modularsoft.zander.hub.utils.WelcomeSounds;
public class HubPlayerJoin implements Listener {
- ZanderHubMain plugin;
- public HubPlayerJoin(ZanderHubMain plugin) {
+ // misc settings
+ private static final long ROUTINE_PLAYER_JOINED_DELAY = (long) (1.2f * 20); // ticks
+
+ // 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); // 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,
+ };
+
+ private final JavaPlugin plugin;
+
+ public HubPlayerJoin(JavaPlugin plugin) {
this.plugin = plugin;
}
+ /// Triggers when player's client first connects.
+ /// Good for validation and basic setup (permission, flags, etc).
@EventHandler
- public void onPlayerJoin(PlayerJoinEvent event) {
+ public void onPlayerLogin(PlayerLoginEvent event) {
Player player = event.getPlayer();
+ setPermissions(player);
+ }
- // Clear the Players inventory for miscellaneous items
- player.getInventory().clear();
+ /// Triggers when player's client has joined the world.
+ /// Good for initial player world interactions (gameplay state etc).
+ @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().getPlayerJoin(player.displayName()));
+ Bukkit.getScheduler().runTaskLater(plugin, () -> scheduledLogin(player), ROUTINE_PLAYER_JOINED_DELAY);
+ }
- // Teleport Player to Hub spawn point
- player.teleport(ConfigurationManager.getHubLocation());
-
- 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
+ /// Set special permission depending on the player.
+ private void setPermissions(Player player) {
+ if (player.hasPermission("zander.hub.fly")) {
+ player.setAllowFlight(true);
}
+ }
- // Play random sound
- int randomSoundIndex = (int) (Math.random() * Sound.values().length - 1);
- Sound randomChosenSound = Sound.values()[randomSoundIndex];
- player.playSound(player.getLocation(), randomChosenSound, 1f,1f);
+ /// 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().getSpawn());
+ player.getInventory().clear();
+ player.getInventory().setHeldItemSlot(compassSlot);
+ player.getInventory().setItem(compassSlot, NavigationCompassItem.createCompass());
+ }
- if (!isVanished(player)) {
- Component broadcastMessage = Component.empty()
- .color(NamedTextColor.GRAY)
- .append(event.getPlayer().name())
- .append(Component.text(" joined."));
+ /// 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);
+ }
- for (Player otherPlayer : Bukkit.getOnlinePlayers()) {
- otherPlayer.sendMessage(broadcastMessage);
- }
+ /// 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());
+ }
- player.setCollidable(true); // Disable player collision.
+ /// 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));
+ }
- // If user has the fly permission, enable flight
- if (player.hasPermission("zander.hub.fly")) {
- player.setAllowFlight(true);
- }
+ /// 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);
}
- private boolean isVanished(Player player) {
- for (MetadataValue meta : player.getMetadata("vanished")) {
- if (meta.asBoolean()) return true;
+ /// 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();
+
+ Random random = new Random();
+ Color[] FCP = FIREWORK_COLOR_PALETTE;
+
+ // 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)]);
}
- return false;
+
+ // 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, () -> {
+ 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
new file mode 100644
index 0000000..bbcfeee
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/events/HubPlayerLeave.java
@@ -0,0 +1,25 @@
+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.bukkit.plugin.java.JavaPlugin;
+import org.modularsoft.zander.hub.ConfigurationManager;
+import org.modularsoft.zander.hub.utils.Misc;
+
+public class HubPlayerLeave implements Listener {
+ private final JavaPlugin plugin;
+
+ public HubPlayerLeave(JavaPlugin plugin) {
+ this.plugin = plugin;
+ }
+
+ @EventHandler
+ public void onPlayerQuit(PlayerQuitEvent event) {
+ Player player = event.getPlayer();
+ if (Misc.isVanish(player))
+ return;
+ 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 c93d6f0..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
@@ -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().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/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/ConfigValidator.java b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java
new file mode 100644
index 0000000..5622ed7
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/ConfigValidator.java
@@ -0,0 +1,177 @@
+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 YAML fields in Bukkit config objects.
+ */
+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, `fallback` is used and instated in `config`
+ public static ValidationResult validateConfig(FileConfiguration config, String field,
+ Validator validator, Object fallback) {
+ ValidationResult result = validator.validate(config, field);
+ if (!result.isValid()) {
+ config.set(field, fallback);
+ plugin.getLogger().warning(
+ String.format("Invalid '%s' in config.yml, replaced by fallback '%s'", field, fallback));
+ 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 fallback, Consumer setter) {
+ ValidationResult result = validator.validate(config, field, setter);
+ if (!result.isValid()) {
+ config.set(field, fallback);
+ plugin.getLogger().warning(
+ 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))
+ return ValidationResult.failure(String.format("Reason '%s' wasn't a decimal number", field));
+ 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))
+ 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 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();
+ };
+
+ /// 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 '%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 '%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 '%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/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..670a970
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/Misc.java
@@ -0,0 +1,18 @@
+package org.modularsoft.zander.hub.utils;
+
+import de.myzelyam.api.vanish.VanishAPI;
+import org.bukkit.entity.Player;
+
+/**
+ * Utility class providing miscellaneous functions.
+ */
+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
new file mode 100644
index 0000000..b9773a7
--- /dev/null
+++ b/zander-hub/src/main/java/org/modularsoft/zander/hub/utils/WelcomeSounds.java
@@ -0,0 +1,63 @@
+package org.modularsoft.zander.hub.utils;
+
+import org.bukkit.Sound;
+
+/**
+ * Utility class providing functions on welcome sounds.
+ */
+public final 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,
+ };
+
+ private WelcomeSounds() {
+ throw new IllegalStateException("Utility class shouldn't be instantiated");
+ }
+
+ public static Sound getRandomSound() {
+ return SOUNDS[(int) (Math.random() * SOUNDS.length)];
+ }
+}
diff --git a/zander-hub/src/main/resources/config.yml b/zander-hub/src/main/resources/config.yml
index bd94f31..0398af2 100644
--- a/zander-hub/src/main/resources/config.yml
+++ b/zander-hub/src/main/resources/config.yml
@@ -1,8 +1,14 @@
velocitymultiplier: 3
hub:
- world: world
+ 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.'
+misc:
+ always_first_join: false
+ slot_hub_compass: 4
diff --git a/zander-hub/src/main/resources/plugin.yml b/zander-hub/src/main/resources/plugin.yml
index 951e542..d1a090b 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:
@@ -18,4 +19,4 @@ permissions:
zanderhub.administrator:
default: op
zanderhub.build:
- default: op
\ No newline at end of file
+ default: op
diff --git a/zander-hub/src/main/resources/welcome.yml b/zander-hub/src/main/resources/welcome.yml
index dcbd787..09b1fcc 100644
--- a/zander-hub/src/main/resources/welcome.yml
+++ b/zander-hub/src/main/resources/welcome.yml
@@ -1,9 +1,9 @@
welcome:
- "&e Welcome to My Server!"
-newplayerwelcome:
+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"
\ No newline at end of file
+ - "Make sure you read the rules. &cUse /rules to read the rules.&r"