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"