Browse Source

Merge branch 'master' into Improvements/RandomGameEquipment

RedstoneFuture 2 years ago
parent
commit
09fe6e302c
31 changed files with 1174 additions and 511 deletions
  1. 25 0
      .github/workflows/java-ci.yaml
  2. BIN
      .mvn/wrapper/maven-wrapper.jar
  3. 18 0
      .mvn/wrapper/maven-wrapper.properties
  4. 11 4
      README.md
  5. 2 2
      missilewars-plugin/pom.xml
  6. 10 5
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/MissileWars.java
  7. 11 6
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/MWCommands.java
  8. 8 9
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/UserCommands.java
  9. 206 49
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Game.java
  10. 14 10
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/GameManager.java
  11. 1 2
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/timer/LobbyTimer.java
  12. 87 87
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/PlayerListener.java
  13. 25 3
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/SignListener.java
  14. 27 21
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/EndListener.java
  15. 1 1
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/GameBoundListener.java
  16. 59 95
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/GameListener.java
  17. 26 60
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/LobbyListener.java
  18. 0 54
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/signs/ClickListener.java
  19. 3 3
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/MathUtil.java
  20. 105 0
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/MissileWarsPlaceholder.java
  21. 11 5
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/SetupUtil.java
  22. 40 9
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/Arena.java
  23. 7 8
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/Lobby.java
  24. 13 1
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/event/PlayerArenaJoinEvent.java
  25. 0 61
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/event/PrePlayerArenaJoinEvent.java
  26. 3 10
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/game/Team.java
  27. 0 4
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/player/MWPlayer.java
  28. 1 1
      missilewars-plugin/src/main/resources-filtered/plugin.yml
  29. 286 0
      mvnw
  30. 161 0
      mvnw.cmd
  31. 13 1
      pom.xml

+ 25 - 0
.github/workflows/java-ci.yaml

@@ -0,0 +1,25 @@
+name: Java CI
+
+on:
+  pull_request:
+  push:
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up JDK 17
+        uses: actions/setup-java@v3
+        with:
+          java-version: '17'
+          distribution: 'adopt'
+          cache: 'maven'
+      - name: Build with Maven
+        run: mvn --batch-mode --update-snapshots package
+      - name: Archive artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: MissileWars jar
+          path: missilewars-plugin/target/MissileWars-*.jar

BIN
.mvn/wrapper/maven-wrapper.jar


+ 18 - 0
.mvn/wrapper/maven-wrapper.properties

@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

+ 11 - 4
README.md

@@ -2,11 +2,18 @@
 MissileWars is a famous, fun and fast minigame spigot-plugin for Minecraft
 
 ## Downloads
-Get binary jars directly here: https://www.spigotmc.org/resources/62947/
-If you buy the resource and leave a comment, you will also receive an extra map + missiles
+You can download the jars in the "[Releases](https://github.com/Butzlabben/missilewars/releases)" 
+section on GitHub or build them yourself via `mvnw install`.
+
+However, if you [buy the resource](https://www.spigotmc.org/resources/62947) and leave a review, 
+you will also receive an extra map and some more missiles.
 
 ## Contributions
-- Contributions are always welcome, just fork this project, make your changes and create a pull request
+Contributions are always welcome, just fork this project, make your changes and create a pull request.
+
+## Documentation
+All information about the Commands, Permissions, Placeholders and the Configuration can be found 
+in our [Github Wiki](https://github.com/Butzlabben/missilewars/wiki).
 
 ## Donate
-[![Build Status](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=naegele_daniel%40web.de&currency_code=EUR&source=url)
+[![Donation link](https://www.paypalobjects.com/en_US/DK/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=naegele_daniel%40web.de&currency_code=EUR&source=url)

+ 2 - 2
missilewars-plugin/pom.xml

@@ -26,7 +26,7 @@
         <version>1.0</version>
     </parent>
 
-    <version>4.2.1</version>
+    <version>4.3.0</version>
 
     <modelVersion>4.0.0</modelVersion>
 
@@ -125,4 +125,4 @@
         <sourceDirectory>src/main/java</sourceDirectory>
     </build>
 
-</project>
+</project>

+ 10 - 5
missilewars-plugin/src/main/java/de/butzlabben/missilewars/MissileWars.java

@@ -25,9 +25,9 @@ import de.butzlabben.missilewars.cmd.UserCommands;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.listener.PlayerListener;
-import de.butzlabben.missilewars.listener.signs.ClickListener;
-import de.butzlabben.missilewars.listener.signs.ManageListener;
+import de.butzlabben.missilewars.listener.SignListener;
 import de.butzlabben.missilewars.util.ConnectionHolder;
+import de.butzlabben.missilewars.util.MissileWarsPlaceholder;
 import de.butzlabben.missilewars.util.MoneyUtil;
 import de.butzlabben.missilewars.util.SetupUtil;
 import de.butzlabben.missilewars.util.stats.PreFetcher;
@@ -108,7 +108,7 @@ public class MissileWars extends JavaPlugin {
         GameManager.getInstance().getGames().values().forEach(game -> {
             for (Player player : Bukkit.getOnlinePlayers()) {
                 if (!game.isIn(player.getLocation())) continue;
-                game.addPlayer(player);
+                game.teleportToLobbySpawn(player);
             }
         });
 
@@ -120,6 +120,12 @@ public class MissileWars extends JavaPlugin {
             PreFetcher.preFetchPlayers(new StatsFetcher(new Date(0L), ""));
         }
 
+        // Small check to make sure that PlaceholderAPI is installed
+        if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
+            new MissileWarsPlaceholder(this).register();
+            Logger.NORMAL.log("The PlaceholderAPI is installed. New placeholders are provided by MissileWars.");
+        }
+
         endTime = System.currentTimeMillis();
         Logger.SUCCESS.log("MissileWars was enabled in " + (endTime - startTime) + "ms");
     }
@@ -137,8 +143,7 @@ public class MissileWars extends JavaPlugin {
      */
     private void registerEvents() {
         Bukkit.getPluginManager().registerEvents(new PlayerListener(), this);
-        Bukkit.getPluginManager().registerEvents(new ClickListener(), this);
-        Bukkit.getPluginManager().registerEvents(new ManageListener(), this);
+        Bukkit.getPluginManager().registerEvents(new SignListener(), this);
     }
 
     /**

+ 11 - 6
missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/MWCommands.java

@@ -19,7 +19,13 @@
 package de.butzlabben.missilewars.cmd;
 
 import co.aikar.commands.BaseCommand;
-import co.aikar.commands.annotation.*;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Default;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MessageConfig;
@@ -33,14 +39,13 @@ import de.butzlabben.missilewars.wrapper.abstracts.Lobby;
 import de.butzlabben.missilewars.wrapper.abstracts.MapChooseProcedure;
 import de.butzlabben.missilewars.wrapper.missile.Missile;
 import de.butzlabben.missilewars.wrapper.missile.MissileFacing;
-import org.bukkit.Bukkit;
-import org.bukkit.command.CommandSender;
-import org.bukkit.entity.Player;
-
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
 
 @CommandAlias("mw|missilewars")
 public class MWCommands extends BaseCommand {
@@ -138,7 +143,7 @@ public class MWCommands extends BaseCommand {
                 }
                 if (mostVotes == null) throw new IllegalStateException("Most votes object was null");
                 Optional<Arena> arena = Arenas.getFromName(mostVotes.getKey());
-                if (!arena.isPresent()) throw new IllegalStateException("Voted arena is not present");
+                if (arena.isEmpty()) throw new IllegalStateException("Voted arena is not present");
                 game.setArena(arena.get());
                 player.sendMessage(MessageConfig.getPrefix() + "A map was elected. Use \"/mw start\" again to start the round");
             }

+ 8 - 9
missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/UserCommands.java

@@ -19,7 +19,12 @@
 package de.butzlabben.missilewars.cmd;
 
 import co.aikar.commands.BaseCommand;
-import co.aikar.commands.annotation.*;
+import co.aikar.commands.annotation.CommandAlias;
+import co.aikar.commands.annotation.CommandCompletion;
+import co.aikar.commands.annotation.CommandPermission;
+import co.aikar.commands.annotation.Description;
+import co.aikar.commands.annotation.Subcommand;
+import co.aikar.commands.annotation.Syntax;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.MessageConfig;
 import de.butzlabben.missilewars.game.Arenas;
@@ -30,12 +35,11 @@ import de.butzlabben.missilewars.wrapper.abstracts.Arena;
 import de.butzlabben.missilewars.wrapper.abstracts.MapChooseProcedure;
 import de.butzlabben.missilewars.wrapper.game.Team;
 import de.butzlabben.missilewars.wrapper.player.MWPlayer;
+import java.util.Optional;
 import org.bukkit.Location;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 
-import java.util.Optional;
-
 @CommandAlias("mw|missilewars")
 public class UserCommands extends BaseCommand {
 
@@ -60,11 +64,6 @@ public class UserCommands extends BaseCommand {
             return;
         }
 
-        if (args.length != 1) {
-            player.sendMessage(MessageConfig.getPrefix() + "§c/mw vote <arena>");
-            return;
-        }
-
         if (args.length != 1) {
             player.sendMessage(MessageConfig.getPrefix() + "§c/mw change <1|2>");
             return;
@@ -128,7 +127,7 @@ public class UserCommands extends BaseCommand {
 
         String arenaName = args[0];
         Optional<Arena> arena = Arenas.getFromName(arenaName);
-        if (!game.getVotes().containsKey(arenaName) || !arena.isPresent()) {
+        if (!game.getVotes().containsKey(arenaName) || arena.isEmpty()) {
             player.sendMessage(MessageConfig.getPrefix() + "§cNo map with this title was found");
             return;
         }

+ 206 - 49
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Game.java

@@ -27,11 +27,13 @@ import de.butzlabben.missilewars.game.timer.EndTimer;
 import de.butzlabben.missilewars.game.timer.GameTimer;
 import de.butzlabben.missilewars.game.timer.LobbyTimer;
 import de.butzlabben.missilewars.game.timer.Timer;
-import de.butzlabben.missilewars.listener.EndListener;
-import de.butzlabben.missilewars.listener.GameBoundListener;
-import de.butzlabben.missilewars.listener.GameListener;
-import de.butzlabben.missilewars.listener.LobbyListener;
+import de.butzlabben.missilewars.inventory.OrcItem;
+import de.butzlabben.missilewars.listener.game.EndListener;
+import de.butzlabben.missilewars.listener.game.GameBoundListener;
+import de.butzlabben.missilewars.listener.game.GameListener;
+import de.butzlabben.missilewars.listener.game.LobbyListener;
 import de.butzlabben.missilewars.util.MotdManager;
+import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.util.ScoreboardManager;
 import de.butzlabben.missilewars.util.serialization.Serializer;
 import de.butzlabben.missilewars.util.version.VersionUtil;
@@ -41,13 +43,13 @@ import de.butzlabben.missilewars.wrapper.abstracts.Lobby;
 import de.butzlabben.missilewars.wrapper.abstracts.MapChooseProcedure;
 import de.butzlabben.missilewars.wrapper.event.GameStartEvent;
 import de.butzlabben.missilewars.wrapper.event.GameStopEvent;
-import de.butzlabben.missilewars.wrapper.event.PlayerArenaJoinEvent;
 import de.butzlabben.missilewars.wrapper.game.MissileGameEquipment;
 import de.butzlabben.missilewars.wrapper.game.SpecialGameEquipment;
 import de.butzlabben.missilewars.wrapper.game.Team;
 import de.butzlabben.missilewars.wrapper.missile.Missile;
 import de.butzlabben.missilewars.wrapper.missile.MissileFacing;
 import de.butzlabben.missilewars.wrapper.player.MWPlayer;
+import de.butzlabben.missilewars.wrapper.signs.MWSign;
 import de.butzlabben.missilewars.wrapper.stats.FightStats;
 import lombok.Getter;
 import lombok.ToString;
@@ -56,10 +58,12 @@ import org.bukkit.enchantments.Enchantment;
 import org.bukkit.entity.Fireball;
 import org.bukkit.entity.Player;
 import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.PlayerTeleportEvent;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.inventory.meta.ItemMeta;
 import org.bukkit.scheduler.BukkitTask;
 import org.bukkit.scoreboard.Scoreboard;
+import org.bukkit.util.Vector;
 
 import java.util.HashMap;
 import java.util.List;
@@ -128,23 +132,14 @@ public class Game {
 
         gameWorld = new GameWorld(this, "");
 
-        players.clear();
-
         team1 = new Team(lobby.getTeam1Name(), lobby.getTeam1Color(), this);
         team2 = new Team(lobby.getTeam2Name(), lobby.getTeam2Color(), this);
-      
+
         team1.createTeamArmor();
         team2.createTeamArmor();
 
         Logger.DEBUG.log("Registering, teleporting, etc. all players");
 
-        for (Player all : Bukkit.getOnlinePlayers()) {
-            if (!isIn(all.getLocation()))
-                continue;
-            Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(),
-                    () -> Bukkit.getPluginManager().callEvent(new PlayerArenaJoinEvent(all, this)), 2);
-        }
-
         // Change MOTD
         if (!Config.isMultipleLobbies()) MotdManager.getInstance().updateMOTD(this);
 
@@ -156,6 +151,8 @@ public class Game {
         bt = Bukkit.getScheduler().runTaskTimer(MissileWars.getInstance(), timer, 0, 20);
         state = GameState.LOBBY;
 
+        Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> applyForAllPlayers(this::runTeleportEventForPlayer), 2);
+
         if (Config.isSetup()) {
             Logger.WARN.log("Did not fully initialize lobby " + lobby.getName() + " as the plugin is in setup mode");
             return;
@@ -194,11 +191,11 @@ public class Game {
         createGameItems();
     }
 
-    private void updateGameListener(GameBoundListener listener) {
+    private void updateGameListener(GameBoundListener newListener) {
         if (listener != null) HandlerList.unregisterAll(listener);
 
-        Bukkit.getPluginManager().registerEvents(listener, MissileWars.getInstance());
-        this.listener = listener;
+        Bukkit.getPluginManager().registerEvents(newListener, MissileWars.getInstance());
+        this.listener = newListener;
     }
 
     public Scoreboard getScoreboard() {
@@ -241,8 +238,7 @@ public class Game {
     }
 
     public void stopGame() {
-        if (Config.isSetup())
-            return;
+        if (Config.isSetup()) return;
 
         Logger.DEBUG.log("Stopping");
 
@@ -255,8 +251,7 @@ public class Game {
 
             Logger.DEBUG.log("Stopping for: " + player.getName());
             player.setGameMode(GameMode.SPECTATOR);
-            player.teleport(arena.getSpectatorSpawn());
-            player.setHealth(player.getMaxHealth());
+            teleportToArenaSpectatorSpawn(player);
 
         }
 
@@ -297,31 +292,140 @@ public class Game {
     }
 
     public void disableGameOnServerStop() {
-        sendPlayerToFallbackSpawn();
+
+        for (MWPlayer player : players.values()) {
+            playerLeaveFromGame(player);
+        }
+
         gameWorld.unload();
     }
 
-    private void sendPlayerToFallbackSpawn() {
+    /**
+     * This method adds the player to the game.
+     *
+     * @param player the target Player
+     * @param isSpectatorJoin should the player join as spectator or as normal player
+     */
+    public void playerJoinInGame(Player player, boolean isSpectatorJoin) {
 
-        for (Player player : Bukkit.getOnlinePlayers()) {
-            if (!isIn(player.getLocation())) continue;
+        PlayerDataProvider.getInstance().storeInventory(player);
+        MWPlayer mwPlayer = addPlayer(player);
 
-            player.teleport(Config.getFallbackSpawn());
+        if (state == GameState.LOBBY) {
+            assert !isSpectatorJoin : "wrong syntax";
 
+            teleportToLobbySpawn(player);
+            player.setGameMode(GameMode.ADVENTURE);
+        }
+
+        if (isSpectatorJoin) {
+            Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> teleportToArenaSpectatorSpawn(player), 2);
+            Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> player.setGameMode(GameMode.SPECTATOR), 35);
+
+            player.sendMessage(MessageConfig.getMessage("spectator"));
+            player.setDisplayName("§7" + player.getName() + "§r");
+
+        } else {
             player.getInventory().clear();
             player.setFoodLevel(20);
             player.setHealth(player.getMaxHealth());
+
+            Team team = getNextTeam();
+            team.addMember(mwPlayer);
+            player.sendMessage(MessageConfig.getMessage("team_assigned").replace("%team%", team.getFullname()));
+
+            broadcast(MessageConfig.getMessage("lobby_joined")
+                    .replace("%max_players%", Integer.toString(getLobby().getMaxSize()))
+                    .replace("%players%", Integer.toString(getPlayers().values().size()))
+                    .replace("%player%", player.getName()));
+        }
+
+        player.setScoreboard(getScoreboard());
+
+        if (state == GameState.LOBBY) {
+
+            // team change menu:
+            if (player.hasPermission("mw.change")) {
+                player.getInventory().setItem(0, VersionUtil.getGlassPlane(team1));
+                player.getInventory().setItem(8, VersionUtil.getGlassPlane(team2));
+            }
+
+            // map choose menu:
+            if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING && arena == null) {
+                player.getInventory().setItem(4, new OrcItem(Material.NETHER_STAR, "§3Vote Map").getItemStack());
+            }
+
+        } else if ((state == GameState.INGAME) && (!isSpectatorJoin)) {
+            startForPlayer(player);
+        }
+    }
+
+    /**
+     * This method handles the removal of the player from the game.
+     *
+     * @param mwPlayer the target missilewars player
+     */
+    public void playerLeaveFromGame(MWPlayer mwPlayer) {
+        Player player = mwPlayer.getPlayer();
+        Team team = mwPlayer.getTeam();
+
+        if (state == GameState.INGAME) {
+            BukkitTask task = getPlayerTasks().get(mwPlayer.getUuid());
+            if (task != null) task.cancel();
+
+            if (team != null) {
+                broadcast(MessageConfig.getMessage("player_left")
+                        .replace("%team%", team.getFullname())
+                        .replace("%player%", player.getName()));
+            }
+        }
+
+        PlayerDataProvider.getInstance().loadInventory(player);
+
+        if (team != null) {
+            team.removeMember(mwPlayer);
+            if (state == GameState.INGAME) checkTeamSize(team);
+        }
+
+        player.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
+        removePlayer(mwPlayer);
+    }
+
+    /**
+     * This method executes the PlayerTeleportEvent to run the basic game join process
+     * after the game is restarted.
+     *
+     * @param player target player
+     */
+    private void runTeleportEventForPlayer(Player player) {
+        Bukkit.getPluginManager().callEvent(new PlayerTeleportEvent(player,
+                Config.getFallbackSpawn(), getLobby().getSpawnPoint()));
+    }
+
+    private void checkTeamSize(Team team) {
+        int teamSize = team.getMembers().size();
+        if (teamSize == 0) {
+            Bukkit.getScheduler().runTask(MissileWars.getInstance(), () -> {
+                team.getEnemyTeam().setGameResult(GameResult.WIN);
+                team.setGameResult(GameResult.LOSE);
+                sendGameResult();
+                stopGame();
+            });
+            broadcast(MessageConfig.getMessage("team_offline").replace("%team%", team.getFullname()));
         }
     }
 
     public void resetGame() {
-        if (state == GameState.INGAME) stopGame();
+        if (state == GameState.INGAME) {
+            stopGame();
+            return;
+        }
 
         HandlerList.unregisterAll(listener);
 
         stopTimer();
 
-        applyForAllPlayers(player -> player.teleport(lobby.getAfterGameSpawn()));
+        applyForAllPlayers(this::teleportToAfterGameSpawn);
 
         if (gameWorld != null) {
             gameWorld.sendPlayersBack();
@@ -364,10 +468,25 @@ public class Game {
         return isInLobbyArea(location) || isInGameWorld(location);
     }
 
+    private MWPlayer addPlayer(Player player) {
+        if (players.containsKey(player.getUniqueId())) return players.get(player.getUniqueId());
+        MWPlayer mwPlayer = new MWPlayer(player, this);
+        players.put(player.getUniqueId(), mwPlayer);
+        return mwPlayer;
+    }
+
     public MWPlayer getPlayer(Player player) {
         return players.get(player.getUniqueId());
     }
 
+    /**
+     * This method finally removes the player from the game player array. Besides former
+     * team members, it also affects spectators.
+     */
+    private void removePlayer(MWPlayer mwPlayer) {
+        players.remove(mwPlayer.getUuid());
+    }
+
     public void broadcast(String message) {
         for (MWPlayer player : players.values()) {
             Player p = player.getPlayer();
@@ -389,7 +508,6 @@ public class Game {
 
         playerTasks.put(player.getUniqueId(),
                 Bukkit.getScheduler().runTaskTimer(MissileWars.getInstance(), mwPlayer, 40, 20));
-
     }
 
     /**
@@ -485,7 +603,7 @@ public class Game {
      *
      * @param player the target player
      */
-    public static void autoRespawnPlayer(Player player) {
+    public void autoRespawnPlayer(Player player) {
         Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> player.spigot().respawn(), 20L);
     }
 
@@ -547,10 +665,7 @@ public class Game {
             throw new IllegalStateException("The game cannot be started, when 0 missiles are configured");
         }
 
-        this.arena = arena.toBuilder().build();
-        this.arena.setSpectatorSpawn(arena.getSpectatorSpawn().clone());
-        this.arena.setTeam1Spawn(arena.getTeam1Spawn().clone());
-        this.arena.setTeam2Spawn(arena.getTeam2Spawn().clone());
+        this.arena = arena.clone();
 
         // Load world
         this.gameWorld = new GameWorld(this, this.arena.getTemplateWorld());
@@ -569,7 +684,7 @@ public class Game {
         if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING) {
             this.broadcast(MessageConfig.getMessage("vote.finished").replace("%map%", this.arena.getDisplayName()));
         }
-        applyForAllPlayers(p -> p.getInventory().setItem(4, new ItemStack(Material.AIR)));
+        applyForAllPlayers(player -> player.getInventory().setItem(4, new ItemStack(Material.AIR)));
 
         ready = true;
     }
@@ -581,13 +696,6 @@ public class Game {
         }
     }
 
-    public MWPlayer addPlayer(Player player) {
-        if (players.containsKey(player.getUniqueId())) return players.get(player.getUniqueId());
-        MWPlayer mwPlayer = new MWPlayer(player, this);
-        players.put(player.getUniqueId(), mwPlayer);
-        return mwPlayer;
-    }
-
     /**
      * This method manages the message output of the game result.
      * Each player who is currently in the arena world gets a
@@ -622,7 +730,6 @@ public class Game {
         }
     }
 
-
     /**
      * This method sends the players the title / subtitle of the
      * game result there are not in a team (= spectator).
@@ -648,13 +755,63 @@ public class Game {
         VersionUtil.sendTitle(player, title, subTitle);
     }
 
+    /**
+     * This method updates the MissileWars signs and the scoreboard.
+     */
+    public void updateGameInfo() {
+        MissileWars.getInstance().getSignRepository().getSigns(this).forEach(MWSign::update);
+        getScoreboardManager().resetScoreboard();
+        Logger.DEBUG.log("Updated signs and scoreboard.");
+    }
 
     /**
-     * This method removes players from the game. Besides former
-     * team members, it also affects spectators.
+     * This method returns the next matching team for the next player to
+     * join. It is always the smaller team.
+     *
+     * @return (Team) the matched team to join
      */
-    public void removePlayer(MWPlayer mwPlayer) {
-        if (mwPlayer.getTeam() != null) mwPlayer.getTeam().removeMember(mwPlayer);
-        players.remove(mwPlayer);
+    public Team getNextTeam() {
+        if (team1.getMembers().size() > team2.getMembers().size()) {
+            return team2;
+        } else {
+            return team1;
+        }
+    }
+
+    public boolean isPlayersMax() {
+        int maxSize = getLobby().getMaxSize();
+        int currentSize = team1.getMembers().size() + team2.getMembers().size();
+        return currentSize >= maxSize;
+    }
+
+    public boolean isSpectatorsMax() {
+        int maxSize = getArena().getMaxSpectators();
+
+        if (maxSize == -1) return false;
+
+        int currentSize = players.size();
+        return currentSize >= maxSize;
+    }
+
+    public static void knockbackEffect(Player player, Location from, Location to) {
+        Vector addTo = from.toVector().subtract(to.toVector()).multiply(3);
+        addTo.setY(0);
+        player.teleport(from.add(addTo));
+    }
+
+    public void teleportToFallbackSpawn(Player player) {
+        player.teleport(Config.getFallbackSpawn());
+    }
+
+    public void teleportToLobbySpawn(Player player) {
+        player.teleport(getLobby().getSpawnPoint());
+    }
+
+    public void teleportToArenaSpectatorSpawn(Player player) {
+        player.teleport(getArena().getSpectatorSpawn());
+    }
+
+    public void teleportToAfterGameSpawn(Player player) {
+        player.teleport(getLobby().getAfterGameSpawn());
     }
 }

+ 14 - 10
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/GameManager.java

@@ -18,7 +18,6 @@
 
 package de.butzlabben.missilewars.game;
 
-import com.google.common.base.Preconditions;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
@@ -105,24 +104,25 @@ public class GameManager {
         }
     }
 
-    public void disableGame(String name) {
-        Preconditions.checkNotNull(name);
-        Game game = getGame(name);
-        if (game == null)
-            return;
+    public void disableGame(String lobbyName) {
+        Game game = getGame(lobbyName);
+        if (game == null) return;
+
         game.resetGame();
-        games.remove(name);
+        games.remove(lobbyName);
+
+        Logger.DEBUG.log("Old Game disabled.");
     }
 
     public void restartGame(Lobby oldLobby) {
-        String name = oldLobby.getName();
-        disableGame(name);
+        String oldLobbyName = oldLobby.getName();
+        disableGame(oldLobbyName);
         try {
             Lobby lobby = Serializer.deserialize(oldLobby.getFile(), Lobby.class);
             lobby.setFile(oldLobby.getFile());
             // Save for possible new values
             Serializer.serialize(oldLobby.getFile(), lobby);
-            games.put(name, new Game(lobby));
+            games.put(oldLobbyName, new Game(lobby));
         } catch (IOException exception) {
             Logger.ERROR.log("Could not load lobby " + oldLobby.getName());
             exception.printStackTrace();
@@ -133,6 +133,10 @@ public class GameManager {
         return games.get(name);
     }
 
+    public int getGameAmount() {
+        return games.size();
+    }
+
     public Game getGame(Location location) {
         for (Game game : GameManager.getInstance().getGames().values()) {
             if (game.isIn(location)) {

+ 1 - 2
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/timer/LobbyTimer.java

@@ -25,7 +25,6 @@ import de.butzlabben.missilewars.util.version.VersionUtil;
 import de.butzlabben.missilewars.wrapper.abstracts.Arena;
 import de.butzlabben.missilewars.wrapper.abstracts.MapChooseProcedure;
 import de.butzlabben.missilewars.wrapper.player.MWPlayer;
-
 import java.util.Map;
 import java.util.Optional;
 
@@ -128,7 +127,7 @@ public class LobbyTimer extends Timer implements Runnable {
         }
         if (mostVotes == null) throw new IllegalStateException("Most votes object was null");
         Optional<Arena> arena = Arenas.getFromName(mostVotes.getKey());
-        if (!arena.isPresent()) throw new IllegalStateException("Voted arena is not present");
+        if (arena.isEmpty()) throw new IllegalStateException("Voted arena is not present");
         game.setArena(arena.get());
     }
 }

+ 87 - 87
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/PlayerListener.java

@@ -20,36 +20,28 @@ package de.butzlabben.missilewars.listener;
 
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.Logger;
-import de.butzlabben.missilewars.MessageConfig;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.GameManager;
-import de.butzlabben.missilewars.game.GameState;
 import de.butzlabben.missilewars.util.MotdManager;
-import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.wrapper.event.PlayerArenaJoinEvent;
 import de.butzlabben.missilewars.wrapper.event.PlayerArenaLeaveEvent;
-import de.butzlabben.missilewars.wrapper.event.PrePlayerArenaJoinEvent;
-import de.butzlabben.missilewars.wrapper.player.MWPlayer;
-import de.butzlabben.missilewars.wrapper.signs.MWSign;
 import org.bukkit.Bukkit;
 import org.bukkit.GameMode;
 import org.bukkit.Location;
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
 import org.bukkit.event.Listener;
 import org.bukkit.event.block.BlockPlaceEvent;
 import org.bukkit.event.entity.FoodLevelChangeEvent;
 import org.bukkit.event.player.*;
 import org.bukkit.event.server.ServerListPingEvent;
-import org.bukkit.util.Vector;
+import org.bukkit.scheduler.BukkitRunnable;
 
 /**
  * @author Butzlabben
  * @since 01.01.2018
  */
-@SuppressWarnings("deprecation")
 public class PlayerListener implements Listener {
 
     @EventHandler
@@ -92,117 +84,125 @@ public class PlayerListener implements Listener {
     }
 
     @EventHandler
-    public void onQuit(PlayerQuitEvent event) {
-        Player p = event.getPlayer();
-
-        Game game = getGame(p.getLocation());
-        if (game != null) {
+    public void onJoin(PlayerJoinEvent event) {
+        Game game = getGame(event.getPlayer().getLocation());
+        if (game == null) return;
 
-            if (!game.isIn(game.getLobby().getAfterGameSpawn()))
-                p.teleport(game.getLobby().getAfterGameSpawn());
-            else
-                p.teleport(Config.getFallbackSpawn());
-        }
+        Player player = event.getPlayer();
+        game.teleportToFallbackSpawn(player);
     }
 
     @EventHandler
-    public void onJoin(PlayerJoinEvent event) {
-        Player p = event.getPlayer();
+    public void onQuit(PlayerQuitEvent event) {
+        Game game = getGame(event.getPlayer().getLocation());
+        if (game == null) return;
 
-        Game game = getGame(p.getLocation());
-        if (game != null) {
+        Player player = event.getPlayer();
 
-            if (checkJoinOrLeave(p, null, game)) {
-                p.teleport(Config.getFallbackSpawn());
-            }
-        }
+        // old game handling:
+        registerPlayerArenaLeaveEvent(player, game);
+
+        game.teleportToFallbackSpawn(player);
     }
 
     @EventHandler
     public void onTeleport(PlayerTeleportEvent event) {
-        Game to = getGame(event.getTo());
-        Game from = getGame(event.getFrom());
-        if (checkJoinOrLeave(event.getPlayer(), from, to)) {
-            event.setCancelled(true);
-        }
-    }
+        Location from = event.getFrom();
+        Location to = event.getTo();
+        Game gameFrom = getGame(from);
+        Game gameTo = getGame(to);
 
-    @EventHandler
-    public void onMove(PlayerMoveEvent event) {
-        Game to = getGame(event.getTo());
-        Game from = getGame(event.getFrom());
-
-        if (checkJoinOrLeave(event.getPlayer(), from, to)) {
-            event.setCancelled(true);
-            Vector addTo = event.getFrom().toVector().subtract(event.getTo().toVector()).multiply(3);
-            addTo.setY(0);
-            event.getPlayer().teleport(event.getFrom().add(addTo));
-        }
+        // same game:
+        if (gameFrom == gameTo) return;
+
+        Player player = event.getPlayer();
 
+        // old game handling:
+        if (gameFrom != null) registerPlayerArenaLeaveEvent(player, gameFrom);
+
+        // new game handling (after a delay):
+        if (gameTo == null) return;
+        new BukkitRunnable() {
+            public void run() {
+                PlayerArenaJoinEvent joinEvent = registerPlayerArenaJoinEvent(player, gameTo);
+                if (joinEvent.isCancelled()) gameTo.teleportToFallbackSpawn(player);
+            }
+        }.runTaskLater(MissileWars.getInstance(), 2);
     }
 
-    @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
-    public void onLeaveWithStuff(PlayerArenaLeaveEvent event) {
-        Game game = event.getGame();
+    @EventHandler
+    public void onMove(PlayerMoveEvent event) {
+        Location from = event.getFrom();
+        Location to = event.getTo();
+        Game gameFrom = getGame(from);
+        Game gameTo = getGame(to);
 
-        MWPlayer mwPlayer = game.getPlayer(event.getPlayer());
-        Player player = mwPlayer.getPlayer();
+        // same game:
+        if (gameFrom == gameTo) return;
 
-        PlayerDataProvider.getInstance().loadInventory(player);
+        Player player = event.getPlayer();
 
-        game.removePlayer(mwPlayer);
+        // old game handling:
+        if (gameFrom != null) registerPlayerArenaLeaveEvent(player, gameFrom);
 
-        MissileWars.getInstance().getSignRepository().getSigns(game).forEach(MWSign::update);
+        // new game handling (after a delay):
+        if (gameTo == null) return;
+        new BukkitRunnable() {
+            public void run() {
+                PlayerArenaJoinEvent joinEvent = registerPlayerArenaJoinEvent(player, gameTo);
+                if (joinEvent.isCancelled()) Game.knockbackEffect(player, from, to);
+            }
+        }.runTaskLater(MissileWars.getInstance(), 2);
     }
 
-    // Internal stuff
+    private PlayerArenaJoinEvent registerPlayerArenaJoinEvent(Player player, Game game) {
+        PlayerArenaJoinEvent onJoinGame = new PlayerArenaJoinEvent(player, game);
+        Bukkit.getPluginManager().callEvent(onJoinGame);
 
-    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
-    public void onMaxPlayers(PrePlayerArenaJoinEvent event) {
-        if (event.getGame().getPlayers().size() >= event.getGame().getLobby().getMaxSize() && event.getGame().getState() == GameState.LOBBY)
-            event.setCancelled(true);
-    }
+        if (!onJoinGame.isCancelled()) {
+            game.updateGameInfo();
+            logEventDebugMessage(player, game);
+            Logger.NORMAL.log(player.getName() + " joint the MW game " + game.getLobby().getName());
+        } else {
+            Logger.DEBUG.log("Canceling game join for " + player.getName());
+        }
 
-    @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
-    public void onPreJoin(PrePlayerArenaJoinEvent event) {
-        Logger.DEBUG.log("PrePlayerArenaJoinEvent: " + event.getPlayer().getName());
-        Bukkit.getScheduler().runTask(MissileWars.getInstance(), () -> Bukkit.getPluginManager()
-                .callEvent(new PlayerArenaJoinEvent(event.getPlayer(), event.getGame())));
+        return onJoinGame;
     }
 
-    // Debugging staff
+    private PlayerArenaLeaveEvent registerPlayerArenaLeaveEvent(Player player, Game game) {
+        PlayerArenaLeaveEvent onLeaveGame = new PlayerArenaLeaveEvent(player, game);
+        Bukkit.getPluginManager().callEvent(onLeaveGame);
 
-    @EventHandler
-    public void onPlayerArenaJoin(PlayerArenaJoinEvent event) {
-        Logger.DEBUG.log("PlayerArenaJoinEvent: " + event.getPlayer().getName());
-    }
+        game.updateGameInfo();
+        logEventDebugMessage(player, game);
+        Logger.NORMAL.log(player.getName() + " left the MW game " + game.getLobby().getName());
 
-    @EventHandler
-    public void onPlayerArenaLeave(PlayerArenaLeaveEvent event) {
-        Logger.DEBUG.log("PlayerArenaLeaveEvent: " + event.getPlayer().getName());
+        return onLeaveGame;
     }
 
+    /**
+     * This method gets the game based of the location. It's either inside
+     * the game lobby (representing as an area) or inside the game arena
+     * (representing as a world).
+     *
+     * @param location (Location) of the player
+     * @return the Game Object if existing for the location
+     */
     private Game getGame(Location location) {
         if (GameManager.getInstance() == null) return null;
 
         return GameManager.getInstance().getGame(location);
     }
 
-    /**
-     * Checks if cancelled and spits out events
-     */
-    private boolean checkJoinOrLeave(Player player, Game from, Game to) {
-        if (to != null && to != from) {
-            PrePlayerArenaJoinEvent event = new PrePlayerArenaJoinEvent(player, to);
-            Bukkit.getPluginManager().callEvent(event);
-            if (event.isCancelled()) player.sendMessage(MessageConfig.getMessage("not_enter_arena"));
-            return event.isCancelled();
-        }
-        if (from != null && to != from) {
-            PlayerArenaLeaveEvent event = new PlayerArenaLeaveEvent(player, from);
-            Bukkit.getPluginManager().callEvent(event);
-        }
-        return false;
-    }
+    private void logEventDebugMessage(Player player, Game game) {
 
+        Logger.DEBUG.log("Location: " + player.getLocation());
+        Logger.DEBUG.log("Current game amount: " + GameManager.getInstance().getGameAmount());
+        Logger.DEBUG.log("Lobby: " + game.getLobby().getDisplayName());
+        Logger.DEBUG.log("Arena: " + game.getArena().getDisplayName());
+        Logger.DEBUG.log("Team 1: " + game.getTeam1());
+        Logger.DEBUG.log("Team 2: " + game.getTeam2());
+
+    }
 }

+ 25 - 3
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/signs/ManageListener.java → missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/SignListener.java

@@ -16,7 +16,7 @@
  * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package de.butzlabben.missilewars.listener.signs;
+package de.butzlabben.missilewars.listener;
 
 import de.butzlabben.missilewars.MessageConfig;
 import de.butzlabben.missilewars.MissileWars;
@@ -25,16 +25,38 @@ import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.util.version.VersionUtil;
 import de.butzlabben.missilewars.wrapper.signs.MWSign;
 import de.butzlabben.missilewars.wrapper.signs.SignRepository;
+import lombok.RequiredArgsConstructor;
+import org.bukkit.Location;
 import org.bukkit.block.Block;
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
+import org.bukkit.event.block.Action;
 import org.bukkit.event.block.BlockBreakEvent;
 import org.bukkit.event.block.SignChangeEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
 
 import java.util.Optional;
 
-public class ManageListener implements Listener {
+@RequiredArgsConstructor
+public class SignListener implements Listener {
+
+    @EventHandler
+    public void onSignClick(PlayerInteractEvent event) {
+        if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
+            Block block = event.getClickedBlock();
+            if (VersionUtil.isWallSignMaterial(block.getType())) {
+                Location location = block.getLocation();
+                Optional<MWSign> optional = MissileWars.getInstance().getSignRepository().getSign(location);
+                if (optional.isEmpty())
+                    return;
+                MWSign sign = optional.get();
+                Game game = GameManager.getInstance().getGame(sign.getLobby());
+                if (game == null) return;
+                event.getPlayer().teleport(game.getLobby().getSpawnPoint());
+            }
+        }
+    }
 
     @EventHandler
     public void onSignChange(SignChangeEvent event) {
@@ -62,7 +84,7 @@ public class ManageListener implements Listener {
     }
 
     @EventHandler
-    public void onBreak(BlockBreakEvent event) {
+    public void onSignBreak(BlockBreakEvent event) {
         Player player = event.getPlayer();
         if (player.hasPermission("mw.sign.manage")) {
             Block block = event.getBlock();

+ 27 - 21
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/EndListener.java → missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/EndListener.java

@@ -16,14 +16,13 @@
  * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package de.butzlabben.missilewars.listener;
+package de.butzlabben.missilewars.listener.game;
 
 import de.butzlabben.missilewars.MessageConfig;
-import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.wrapper.event.PlayerArenaJoinEvent;
-import org.bukkit.Bukkit;
+import de.butzlabben.missilewars.wrapper.event.PlayerArenaLeaveEvent;
+import de.butzlabben.missilewars.wrapper.player.MWPlayer;
 import org.bukkit.GameMode;
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
@@ -42,23 +41,6 @@ public class EndListener extends GameBoundListener {
         super(game);
     }
 
-    @SuppressWarnings("deprecation")
-    @EventHandler
-    public void onJoin(PlayerArenaJoinEvent event) {
-        Game game = event.getGame();
-        if (game != getGame()) return;
-
-        Player p = event.getPlayer();
-        PlayerDataProvider.getInstance().storeInventory(p);
-        p.sendMessage(MessageConfig.getMessage("spectator"));
-
-        Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.teleport(game.getArena().getSpectatorSpawn()), 2);
-
-        Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.setGameMode(GameMode.SPECTATOR), 35);
-        p.setDisplayName("§7" + p.getName() + "§r");
-        game.addPlayer(p);
-    }
-
     @EventHandler(priority = EventPriority.HIGH)
     public void onRespawn(PlayerRespawnEvent event) {
         if (!isInGameWorld(event.getPlayer().getLocation())) return;
@@ -85,4 +67,28 @@ public class EndListener extends GameBoundListener {
 
         if (player.getGameMode() != GameMode.CREATIVE) event.setCancelled(true);
     }
+
+    @EventHandler
+    public void onPlayerArenaJoin(PlayerArenaJoinEvent event) {
+        if (!getGame().isIn(event.getPlayer().getLocation())) return;
+
+        if (getGame().isSpectatorsMax()) {
+            event.setCancelled(true);
+            event.getPlayer().sendMessage(MessageConfig.getMessage("not_enter_arena"));
+            return;
+        }
+
+        Player player = event.getPlayer();
+        getGame().playerJoinInGame(player, true);
+    }
+
+    @EventHandler
+    public void onPlayerArenaLeave(PlayerArenaLeaveEvent event) {
+        if (!isInGameWorld(event.getPlayer().getLocation())) return;
+
+        Player player = event.getPlayer();
+        MWPlayer mwPlayer = event.getGame().getPlayer(player);
+
+        if (mwPlayer != null) getGame().playerLeaveFromGame(mwPlayer);
+    }
 }

+ 1 - 1
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/GameBoundListener.java → missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/GameBoundListener.java

@@ -16,7 +16,7 @@
  * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package de.butzlabben.missilewars.listener;
+package de.butzlabben.missilewars.listener.game;
 
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Game;

+ 59 - 95
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/GameListener.java → missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/GameListener.java

@@ -16,13 +16,11 @@
  * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package de.butzlabben.missilewars.listener;
+package de.butzlabben.missilewars.listener.game;
 
 import de.butzlabben.missilewars.MessageConfig;
-import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.GameResult;
-import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.util.version.VersionUtil;
 import de.butzlabben.missilewars.wrapper.abstracts.arena.FallProtectionConfiguration;
 import de.butzlabben.missilewars.wrapper.event.PlayerArenaJoinEvent;
@@ -31,7 +29,6 @@ import de.butzlabben.missilewars.wrapper.game.RespawnGoldBlock;
 import de.butzlabben.missilewars.wrapper.game.Shield;
 import de.butzlabben.missilewars.wrapper.game.Team;
 import de.butzlabben.missilewars.wrapper.player.MWPlayer;
-import org.bukkit.Bukkit;
 import org.bukkit.GameMode;
 import org.bukkit.Location;
 import org.bukkit.entity.EntityType;
@@ -50,41 +47,17 @@ import org.bukkit.event.player.PlayerMoveEvent;
 import org.bukkit.event.player.PlayerRespawnEvent;
 import org.bukkit.inventory.Inventory;
 import org.bukkit.inventory.ItemStack;
-import org.bukkit.scheduler.BukkitTask;
-import org.bukkit.util.Vector;
 
 /**
  * @author Butzlabben
  * @since 12.01.2018
  */
-@SuppressWarnings("deprecation")
 public class GameListener extends GameBoundListener {
 
     public GameListener(Game game) {
         super(game);
     }
 
-    @EventHandler
-    public void onMove(PlayerMoveEvent event) {
-        if (!isInGameWorld(event.getTo())) return;
-
-        Player p = event.getPlayer();
-        if ((event.getTo().getBlockY() >= getGame().getArena().getMaxHeight()) && (p.getGameMode() == GameMode.SURVIVAL)) {
-            p.teleport(event.getFrom());
-            p.sendMessage(MessageConfig.getMessage("not_higher"));
-        } else if ((event.getTo().getBlockY() <= getGame().getArena().getDeathHeight()) && (p.getGameMode() == GameMode.SURVIVAL)) {
-            p.setLastDamageCause(new EntityDamageEvent(p, EntityDamageEvent.DamageCause.FALL, 20));
-            p.damage(20.0D);
-        }
-        if (!getGame().isInGameArea(event.getTo())) {
-            event.setCancelled(true);
-            Vector addTo = event.getFrom().toVector().subtract(event.getTo().toVector()).multiply(3);
-            addTo.setY(0);
-            p.teleport(event.getFrom().add(addTo));
-            p.sendMessage(MessageConfig.getMessage("arena_leave"));
-        }
-    }
-
     @EventHandler
     public void onExplode(EntityExplodeEvent event) {
         if (!isInGameWorld(event.getLocation())) return;
@@ -142,40 +115,6 @@ public class GameListener extends GameBoundListener {
         if (itemStack.getType() == VersionUtil.getFireball()) {
 
             getGame().spawnFireball(player, itemStack);
-            return;
-        }
-    }
-
-    @EventHandler
-    public void onJoin(PlayerArenaJoinEvent event) {
-        Game game = event.getGame();
-        if (game != getGame()) return;
-
-        Player p = event.getPlayer();
-        MWPlayer mwPlayer = game.addPlayer(p);
-        PlayerDataProvider.getInstance().storeInventory(p);
-        p.getInventory().clear();
-
-        if (!game.getLobby().isJoinOngoingGame() || game.getPlayers().size() >= game.getLobby().getMaxSize()) {
-            p.sendMessage(MessageConfig.getMessage("spectator"));
-            Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.teleport(game.getArena().getSpectatorSpawn()), 2);
-            Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.setGameMode(GameMode.SPECTATOR), 35);;
-            p.setDisplayName("§7" + p.getName() + "§r");
-            p.setScoreboard(game.getScoreboard());
-        } else {
-            Team to;
-            int size1 = game.getTeam1().getMembers().size();
-            int size2 = game.getTeam2().getMembers().size();
-            if (size2 < size1)
-                to = getGame().getTeam2();
-            else
-                to = getGame().getTeam1();
-
-            // Adds the player to the new team.
-            to.addMember(mwPlayer);
-
-            p.sendMessage(MessageConfig.getMessage("team_assigned").replace("%team%", to.getFullname()));
-            game.startForPlayer(p);
         }
     }
 
@@ -263,51 +202,22 @@ public class GameListener extends GameBoundListener {
 
             if (player.getLastDamageCause() == null) return;
 
-            String deathBroadcastMessage;
+            String deathBroadcast;
             EntityDamageEvent.DamageCause damageCause = player.getLastDamageCause().getCause();
 
             if (damageCause == EntityDamageEvent.DamageCause.BLOCK_EXPLOSION || damageCause == EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) {
-                deathBroadcastMessage = MessageConfig.getNativeMessage("died_explosion").replace("%player%", player.getDisplayName());
+                deathBroadcast = MessageConfig.getNativeMessage("died_explosion").replace("%player%", player.getDisplayName());
             } else {
-                deathBroadcastMessage = MessageConfig.getNativeMessage("died").replace("%player%", player.getDisplayName());
+                deathBroadcast = MessageConfig.getNativeMessage("died").replace("%player%", player.getDisplayName());
             }
 
-            getGame().broadcast(deathBroadcastMessage);
+            getGame().broadcast(deathBroadcast);
         }
 
         event.setDeathMessage(null);
         if (getGame().getArena().isAutoRespawn()) getGame().autoRespawnPlayer(player);
     }
 
-    @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
-    public void onLeave(PlayerArenaLeaveEvent event) {
-        Game game = event.getGame();
-        if (game != getGame()) return;
-
-        MWPlayer player = getGame().getPlayer(event.getPlayer());
-        if (player == null) return;
-        BukkitTask task = game.getPlayerTasks().get(player.getUuid());
-        if (task != null) task.cancel();
-
-        Team team = player.getTeam();
-        if (team != null) {
-            getGame().broadcast(
-                    MessageConfig.getMessage("player_left").replace("%player%", event.getPlayer().getDisplayName()));
-            team.removeMember(getGame().getPlayer(event.getPlayer()));
-
-            int teamSize = team.getMembers().size();
-            if (teamSize == 0) {
-                Bukkit.getScheduler().runTask(MissileWars.getInstance(), () -> {
-                    team.getEnemyTeam().setGameResult(GameResult.WIN);
-                    team.setGameResult(GameResult.LOSE);
-                    game.sendGameResult();
-                    getGame().stopGame();
-                });
-                getGame().broadcast(MessageConfig.getMessage("team_offline").replace("%team%", team.getFullname()));
-            }
-        }
-    }
-
     @EventHandler
     public void onInventoryClick(InventoryClickEvent event) {
         if (!(event.getWhoClicked() instanceof Player)) return;
@@ -327,4 +237,58 @@ public class GameListener extends GameBoundListener {
         if ((event.getSlotType() != InventoryType.SlotType.CONTAINER) &&
                 (event.getSlotType() != InventoryType.SlotType.QUICKBAR)) event.setCancelled(true);
     }
+
+    @EventHandler
+    public void onMove(PlayerMoveEvent event) {
+        if (!isInGameWorld(event.getTo())) return;
+
+        Player player = event.getPlayer();
+        if (player.getGameMode() != GameMode.SURVIVAL) return;
+
+        int toY = event.getTo().getBlockY();
+        if (toY > getGame().getArena().getMaxHeight()) {
+            player.teleport(event.getFrom());
+            player.sendMessage(MessageConfig.getMessage("not_higher"));
+        } else if (toY < getGame().getArena().getDeathHeight()) {
+            player.setLastDamageCause(new EntityDamageEvent(player, EntityDamageEvent.DamageCause.FALL, 20));
+            player.damage(20.0D);
+        }
+
+        Location from = event.getFrom();
+        Location to = event.getTo();
+
+        if (!getGame().isInGameArea(event.getTo())) {
+            if (to != null) Game.knockbackEffect(player, from, to);
+            player.sendMessage(MessageConfig.getMessage("arena_leave"));
+        }
+    }
+
+    @EventHandler
+    public void onPlayerArenaJoin(PlayerArenaJoinEvent event) {
+        if (!getGame().isIn(event.getPlayer().getLocation())) return;
+
+        Player player = event.getPlayer();
+
+        if ((!getGame().getLobby().isJoinOngoingGame()) || (getGame().isPlayersMax())) {
+            if (getGame().isSpectatorsMax()) {
+                event.setCancelled(true);
+                event.getPlayer().sendMessage(MessageConfig.getMessage("not_enter_arena"));
+                return;
+            }
+            getGame().playerJoinInGame(player, true);
+            return;
+        }
+
+        getGame().playerJoinInGame(player, false);
+    }
+
+    @EventHandler
+    public void onPlayerArenaLeave(PlayerArenaLeaveEvent event) {
+        if (!isInGameWorld(event.getPlayer().getLocation())) return;
+
+        Player player = event.getPlayer();
+        MWPlayer mwPlayer = event.getGame().getPlayer(player);
+
+        if (mwPlayer != null) getGame().playerLeaveFromGame(mwPlayer);
+    }
 }

+ 26 - 60
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/LobbyListener.java → missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/LobbyListener.java

@@ -16,21 +16,15 @@
  * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-package de.butzlabben.missilewars.listener;
+package de.butzlabben.missilewars.listener.game;
 
 import de.butzlabben.missilewars.MessageConfig;
-import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.inventory.OrcItem;
 import de.butzlabben.missilewars.inventory.VoteInventory;
-import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.util.version.VersionUtil;
-import de.butzlabben.missilewars.wrapper.abstracts.MapChooseProcedure;
 import de.butzlabben.missilewars.wrapper.event.PlayerArenaJoinEvent;
-import de.butzlabben.missilewars.wrapper.game.Team;
+import de.butzlabben.missilewars.wrapper.event.PlayerArenaLeaveEvent;
 import de.butzlabben.missilewars.wrapper.player.MWPlayer;
-import de.butzlabben.missilewars.wrapper.signs.MWSign;
-import org.bukkit.Bukkit;
 import org.bukkit.GameMode;
 import org.bukkit.Material;
 import org.bukkit.entity.Player;
@@ -95,58 +89,6 @@ public class LobbyListener extends GameBoundListener {
         }
     }
 
-    @EventHandler
-    public void onJoin(PlayerArenaJoinEvent event) {
-        Game game = event.getGame();
-        if (game != getGame()) return;
-
-        Player p = event.getPlayer();
-        MWPlayer mw = game.addPlayer(p);
-
-        PlayerDataProvider.getInstance().storeInventory(p);
-
-        p.getInventory().clear();
-        p.setFoodLevel(20);
-        p.setHealth(p.getMaxHealth());
-
-        Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.setGameMode(GameMode.ADVENTURE), 10);
-        Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> p.teleport(game.getLobby().getSpawnPoint()), 2);
-
-        Team to;
-
-        int size1 = getGame().getTeam1().getMembers().size();
-        int size2 = getGame().getTeam2().getMembers().size();
-
-        if (size2 < size1) {
-            to = getGame().getTeam2();
-        } else {
-            to = getGame().getTeam1();
-        }
-
-        // Premium version
-        if (p.hasPermission("mw.change")) {
-            p.getInventory().setItem(0, VersionUtil.getGlassPlane(getGame().getTeam1()));
-            p.getInventory().setItem(8, VersionUtil.getGlassPlane(getGame().getTeam2()));
-        }
-
-        if (game.getLobby().getMapChooseProcedure() == MapChooseProcedure.MAPVOTING && game.getArena() == null) {
-            p.getInventory().setItem(4, new OrcItem(Material.NETHER_STAR, "§3Vote Map").getItemStack());
-        }
-
-        // Adds the player to the new team.
-        to.addMember(mw);
-
-        p.sendMessage(MessageConfig.getMessage("team_assigned").replace("%team%", to.getFullname()));
-
-        String name = p.getName();
-        String players = "" + game.getPlayers().values().size();
-        String maxPlayers = "" + game.getLobby().getMaxSize();
-        String message = MessageConfig.getMessage("lobby_joined").replace("%max_players%", maxPlayers).replace("%players%", players).replace("%player%", name);
-        game.broadcast(message);
-
-        MissileWars.getInstance().getSignRepository().getSigns(game).forEach(MWSign::update);
-    }
-
     @EventHandler
     public void onDamage(EntityDamageEvent event) {
         if (!(event.getEntity() instanceof Player)) return;
@@ -173,4 +115,28 @@ public class LobbyListener extends GameBoundListener {
 
         if (player.getGameMode() != GameMode.CREATIVE) event.setCancelled(true);
     }
+
+    @EventHandler
+    public void onPlayerArenaJoin(PlayerArenaJoinEvent event) {
+        if (!isInLobbyArea(event.getPlayer().getLocation())) return;
+
+        if (getGame().isPlayersMax()) {
+            event.setCancelled(true);
+            event.getPlayer().sendMessage(MessageConfig.getMessage("not_enter_arena"));
+            return;
+        }
+
+        Player player = event.getPlayer();
+        getGame().playerJoinInGame(player, false);
+    }
+
+    @EventHandler
+    public void onPlayerArenaLeave(PlayerArenaLeaveEvent event) {
+        if (!isInLobbyArea(event.getPlayer().getLocation())) return;
+
+        Player player = event.getPlayer();
+        MWPlayer mwPlayer = event.getGame().getPlayer(player);
+
+        if (mwPlayer != null) getGame().playerLeaveFromGame(mwPlayer);
+    }
 }

+ 0 - 54
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/signs/ClickListener.java

@@ -1,54 +0,0 @@
-/*
- * This file is part of MissileWars (https://github.com/Butzlabben/missilewars).
- * Copyright (c) 2018-2021 Daniel Nägele.
- *
- * MissileWars is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MissileWars is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-package de.butzlabben.missilewars.listener.signs;
-
-import de.butzlabben.missilewars.MissileWars;
-import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.game.GameManager;
-import de.butzlabben.missilewars.util.version.VersionUtil;
-import de.butzlabben.missilewars.wrapper.signs.MWSign;
-import java.util.Optional;
-import lombok.RequiredArgsConstructor;
-import org.bukkit.Location;
-import org.bukkit.block.Block;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.block.Action;
-import org.bukkit.event.player.PlayerInteractEvent;
-
-@RequiredArgsConstructor
-public class ClickListener implements Listener {
-
-    @EventHandler
-    public void onJoinClick(PlayerInteractEvent event) {
-        if (event.getAction() == Action.RIGHT_CLICK_BLOCK) {
-            Block block = event.getClickedBlock();
-            if (VersionUtil.isWallSignMaterial(block.getType())) {
-                Location location = block.getLocation();
-                Optional<MWSign> optional = MissileWars.getInstance().getSignRepository().getSign(location);
-                if (!optional.isPresent())
-                    return;
-                MWSign sign = optional.get();
-                Game game = GameManager.getInstance().getGame(sign.getLobby());
-                if (game == null) return;
-                event.getPlayer().teleport(game.getLobby().getSpawnPoint());
-            }
-        }
-    }
-}

+ 3 - 3
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/MathUtil.java

@@ -28,10 +28,10 @@ public class MathUtil {
      * As long as the difference is smaller than 1.0E-8D, it will return true. This value was chosen, as
      * {@link org.bukkit.util.Vector#equals(Object)} uses a more losen tolerance.
      *
-     * @param value1
-     * @param value2
+     * @param value1 the first double
+     * @param value2 the second double
      *
-     * @return
+     * @return true if the double values are close enough to be considered equal
      */
     public static boolean closeEnoughEquals(final double value1, final double value2) {
         return Math.abs(value1 - value2) < 1.0E-8D;

+ 105 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/MissileWarsPlaceholder.java

@@ -0,0 +1,105 @@
+/*
+ * This file is part of MissileWars (https://github.com/Butzlabben/missilewars).
+ * Copyright (c) 2018-2021 Daniel Nägele.
+ *
+ * MissileWars is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MissileWars is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package de.butzlabben.missilewars.util;
+
+import de.butzlabben.missilewars.MissileWars;
+import de.butzlabben.missilewars.game.Game;
+import de.butzlabben.missilewars.game.GameManager;
+import de.butzlabben.missilewars.wrapper.abstracts.Arena;
+import de.butzlabben.missilewars.wrapper.abstracts.Lobby;
+import me.clip.placeholderapi.expansion.PlaceholderExpansion;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.entity.Player;
+
+public class MissileWarsPlaceholder extends PlaceholderExpansion {
+
+    private final MissileWars plugin;
+
+    public MissileWarsPlaceholder(MissileWars plugin) {
+        this.plugin = plugin;
+    }
+
+    @Override
+    public String getAuthor() {
+        return "Daniel Nägele";
+    }
+
+    @Override
+    public String getIdentifier() {
+        return "missilewars";
+    }
+
+    @Override
+    public String getVersion() {
+        return "0.0.1";
+    }
+
+    // This is required or else PlaceholderAPI will unregister the Expansion on reload
+    @Override
+    public boolean persist() {
+        return true;
+    }
+
+    @Override
+    public String onRequest(OfflinePlayer offlinePlayer, String params) {
+
+        if (params.endsWith("_this")) {
+            if (!offlinePlayer.isOnline()) return "§c§oPlayer is not online!";
+
+            Player player = offlinePlayer.getPlayer();
+            Game playerGame = GameManager.getInstance().getGame(player.getLocation());
+
+            if (playerGame == null) {
+                if (params.startsWith("lobby_")) return "§c§oThis is not a lobby area!";
+                if (params.startsWith("arena_")) return "§c§oThis is not a game arena!";
+            }
+
+            if (params.startsWith("lobby_")) params.replace("_this", playerGame.getLobby().getName());
+            if (params.startsWith("arena_")) params.replace("_this", playerGame.getArena().getName());
+        }
+
+
+        for (Game game : GameManager.getInstance().getGames().values()) {
+            Lobby lobby = game.getLobby();
+
+            for (Arena arena : lobby.getArenas()) {
+
+                // %missilewars_lobby_displayname_<lobby name or 'this'>%
+                if (params.equalsIgnoreCase("lobby_displayname_" + lobby.getName())) {
+                    return lobby.getDisplayName();
+                }
+
+                // %missilewars_arena_displayname_<arena name or 'this'>%
+                if (params.equalsIgnoreCase("arena_displayname_" + arena.getName())) {
+                    return arena.getDisplayName();
+                }
+
+                // %missilewars_arena_missileamount_<arena name or 'this'>%
+                if (params.equalsIgnoreCase("arena_missileamount_" + arena.getName())) {
+                    return Integer.toString(arena.getMissileConfiguration().getMissiles().size());
+                }
+
+            }
+        }
+
+        // Placeholder is unknown by the Expansion
+        return null;
+    }
+
+}

+ 11 - 5
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/SetupUtil.java

@@ -23,10 +23,14 @@ import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.wrapper.abstracts.Arena;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.plugin.java.JavaPlugin;
-
-import java.io.*;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
@@ -35,6 +39,8 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.plugin.java.JavaPlugin;
 
 /**
  * @author Butzlabben
@@ -201,7 +207,7 @@ public class SetupUtil {
     /**
      * Extracts a zip entry (file entry)
      *
-     * @param zipIn
+     * @param zipIn    the input stream
      * @param filePath
      *
      * @throws IOException

+ 40 - 9
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/Arena.java

@@ -19,21 +19,25 @@
 package de.butzlabben.missilewars.wrapper.abstracts;
 
 import com.google.gson.annotations.SerializedName;
-import de.butzlabben.missilewars.wrapper.abstracts.arena.*;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.EquipmentIntervalConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.FallProtectionConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.FireballConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.GameRespawnConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.GameSpawnConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.MissileConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.MoneyConfiguration;
+import de.butzlabben.missilewars.wrapper.abstracts.arena.ShieldConfiguration;
 import de.butzlabben.missilewars.wrapper.geometry.FlatArea;
 import de.butzlabben.missilewars.wrapper.geometry.Plane;
-import lombok.Builder;
 import lombok.Getter;
-import lombok.RequiredArgsConstructor;
+import lombok.Setter;
 import lombok.ToString;
 import org.bukkit.Location;
 import org.bukkit.util.Vector;
 
 @Getter
 @ToString
-@RequiredArgsConstructor
-@Builder(toBuilder = true)
-public class Arena {
+public class Arena implements Cloneable {
 
     private String name = "arena0";
     @SerializedName("display_name") private String displayName = "&eDefault map";
@@ -56,10 +60,23 @@ public class Arena {
     @SerializedName("money") private MoneyConfiguration money = new MoneyConfiguration();
     @SerializedName("equipment_interval") private EquipmentIntervalConfiguration interval = new EquipmentIntervalConfiguration();
     @SerializedName("missile") private MissileConfiguration missileConfiguration = new MissileConfiguration();
-    @SerializedName("spectator_spawn") private Location spectatorSpawn = new Location(null, 0, 100, 0, 90, 0);
     @SerializedName("area") private FlatArea gameArea = new FlatArea(-30, -72, 30, 72);
-    @SerializedName("team1_spawn") private Location team1Spawn = new Location(null, 0.5, 100, 45.5, 180, 0);
-    @SerializedName("team2_spawn") private Location team2Spawn = new Location(null, 0.5, 100, -45.5, 0, 0);
+    
+    @SerializedName("spectator_spawn")
+    @Setter
+    private Location spectatorSpawn = new Location(null, 0, 100, 0, 90, 0);
+    
+    @SerializedName("team1_spawn")
+    @Setter
+    private Location team1Spawn = new Location(null, 0.5, 100, 45.5, 180, 0);
+    
+    @SerializedName("team2_spawn")
+    @Setter
+    private Location team2Spawn = new Location(null, 0.5, 100, -45.5, 0, 0);
+    
+    public Arena() {
+
+    }
 
     public Plane getPlane1() {
         Vector spawn1 = team1Spawn.toVector();
@@ -79,4 +96,18 @@ public class Arena {
         double distance2 = plane2.distanceSquared(point);
         return distanceBetween > distance1 + distance2;
     }
+
+    @Override
+    public Arena clone() {
+        try {
+            Arena clone = (Arena) super.clone();
+            clone.spectatorSpawn = spectatorSpawn.clone();
+            clone.team1Spawn = team1Spawn.clone();
+            clone.team2Spawn = team2Spawn.clone();
+
+            return clone;
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError();
+        }
+    }
 }

+ 7 - 8
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/Lobby.java

@@ -22,6 +22,11 @@ import com.google.gson.annotations.SerializedName;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.wrapper.geometry.Area;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.Setter;
@@ -30,12 +35,6 @@ import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.World;
 
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
 @Getter
 @ToString
 @RequiredArgsConstructor
@@ -56,7 +55,7 @@ public class Lobby {
     @SerializedName("after_game_spawn") private Location afterGameSpawn = Bukkit.getWorlds().get(0).getSpawnLocation();
     private Area area = Area.defaultAreaAround(Bukkit.getWorlds().get(0).getSpawnLocation());
     @SerializedName("map_choose_procedure") private MapChooseProcedure mapChooseProcedure = MapChooseProcedure.FIRST;
-    @SerializedName("possible_arenas") private List<String> possibleArenas = new ArrayList<String>() {{
+    @SerializedName("possible_arenas") private List<String> possibleArenas = new ArrayList<>() {{
         add("arena0");
     }};
 
@@ -74,7 +73,7 @@ public class Lobby {
     public void checkForWrongArenas() {
         for (String arenaName : possibleArenas) {
             Optional<Arena> arena = Arenas.getFromName(arenaName);
-            if (!arena.isPresent()) {
+            if (arena.isEmpty()) {
                 Logger.WARN.log("Could not find arena with name \"" + arenaName + "\" for lobby \"" + getName() + "\"");
             }
         }

+ 13 - 1
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/event/PlayerArenaJoinEvent.java

@@ -21,6 +21,7 @@ package de.butzlabben.missilewars.wrapper.event;
 import de.butzlabben.missilewars.game.Game;
 import lombok.Getter;
 import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
 import org.bukkit.event.HandlerList;
 import org.bukkit.event.player.PlayerEvent;
 
@@ -28,10 +29,11 @@ import org.bukkit.event.player.PlayerEvent;
  * Get's called, when a player has already entered an arena
  */
 @Getter
-public class PlayerArenaJoinEvent extends PlayerEvent {
+public class PlayerArenaJoinEvent extends PlayerEvent implements Cancellable {
 
     public final static HandlerList handlers = new HandlerList();
     private final Game game;
+    private boolean cancelled;
 
     public PlayerArenaJoinEvent(Player who, Game game) {
         super(who);
@@ -46,4 +48,14 @@ public class PlayerArenaJoinEvent extends PlayerEvent {
     public HandlerList getHandlers() {
         return handlers;
     }
+
+    @Override
+    public boolean isCancelled() {
+        return cancelled;
+    }
+
+    @Override
+    public void setCancelled(boolean b) {
+        cancelled = b;
+    }
 }

+ 0 - 61
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/event/PrePlayerArenaJoinEvent.java

@@ -1,61 +0,0 @@
-/*
- * This file is part of MissileWars (https://github.com/Butzlabben/missilewars).
- * Copyright (c) 2018-2021 Daniel Nägele.
- *
- * MissileWars is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * MissileWars is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with MissileWars.  If not, see <https://www.gnu.org/licenses/>.
- */
-
-package de.butzlabben.missilewars.wrapper.event;
-
-import de.butzlabben.missilewars.game.Game;
-import lombok.Getter;
-import org.bukkit.entity.Player;
-import org.bukkit.event.Cancellable;
-import org.bukkit.event.HandlerList;
-import org.bukkit.event.player.PlayerEvent;
-
-/**
- * Get's called, before a player want's to join an arena
- */
-@Getter
-public class PrePlayerArenaJoinEvent extends PlayerEvent implements Cancellable {
-
-    public final static HandlerList handlers = new HandlerList();
-    private final Game game;
-    private boolean cancelled;
-
-    public PrePlayerArenaJoinEvent(Player who, Game game) {
-        super(who);
-        this.game = game;
-    }
-
-    public static HandlerList getHandlerList() {
-        return handlers;
-    }
-
-    @Override
-    public HandlerList getHandlers() {
-        return handlers;
-    }
-
-    @Override
-    public boolean isCancelled() {
-        return cancelled;
-    }
-
-    @Override
-    public void setCancelled(boolean b) {
-        cancelled = b;
-    }
-}

+ 3 - 10
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/game/Team.java

@@ -30,7 +30,6 @@ import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.Setter;
 import lombok.ToString;
-import org.bukkit.Bukkit;
 import org.bukkit.Color;
 import org.bukkit.Location;
 import org.bukkit.Material;
@@ -68,7 +67,7 @@ public class Team {
             return game.getTeam2();
         return game.getTeam1();
     }
-    
+
     public void removeMember(MWPlayer player) {
         if (!isMember(player)) return;
 
@@ -80,11 +79,8 @@ public class Team {
         }
 
         members.removeIf(mp -> mp.getUuid().equals(player.getUuid()));
-
-        game.getScoreboardManager().resetScoreboard();
-        p.setScoreboard(Bukkit.getScoreboardManager().getMainScoreboard());
     }
-    
+
     public void addMember(MWPlayer player) {
         if (isMember(player)) return;
 
@@ -103,9 +99,6 @@ public class Team {
         player.setTeam(this);
         p.setDisplayName(getColorCode() + p.getName() + "§r");
         p.getInventory().setArmorContents(getTeamArmor());
-
-        game.getScoreboardManager().updateScoreboard();
-        p.setScoreboard(game.getScoreboard());
     }
 
     public String getFullname() {
@@ -233,7 +226,7 @@ public class Team {
         }
     }
 
-    // TODO Review for necessity (of an alternative), completeness and refactoring
+    // TODO Add new team buffer
     public void updateIntervals(int newInterval) {
         if (newInterval < currentInterval && currentInterval != 0) {
             getGame().broadcast(MessageConfig.getMessage("team_buffed").replace("%team%", getFullname()));

+ 0 - 4
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/player/MWPlayer.java

@@ -58,10 +58,6 @@ public class MWPlayer implements Runnable {
 
     @Override
     public void run() {
-
-        Player p = Bukkit.getPlayer(uuid);
-        if (p == null || !p.isOnline()) return;
-
         randomGameEquipment.tick();
     }
 

+ 1 - 1
missilewars-plugin/src/main/resources-filtered/plugin.yml

@@ -4,7 +4,7 @@ version: ${project.version}
 main: de.butzlabben.missilewars.MissileWars
 api-version: 1.13
 depend: [ WorldEdit ]
-softdepend: [ Vault, FastAsyncWorldEdit ]
+softdepend: [ Vault, FastAsyncWorldEdit, PlaceholderAPI ]
 
 permissions:
   mw.*:

+ 286 - 0
mvnw

@@ -0,0 +1,286 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven2 Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+  if [ -f /etc/mavenrc ] ; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ] ; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  MINGW*) mingw=true;;
+  Darwin*) darwin=true
+    # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+    # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+    if [ -z "$JAVA_HOME" ]; then
+      if [ -x "/usr/libexec/java_home" ]; then
+        export JAVA_HOME="`/usr/libexec/java_home`"
+      else
+        export JAVA_HOME="/Library/Java/Home"
+      fi
+    fi
+    ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+  if [ -r /etc/gentoo-release ] ; then
+    JAVA_HOME=`java-config --jre-home`
+  fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+      PRG="$link"
+    else
+      PRG="`dirname "$PRG"`/$link"
+    fi
+  done
+
+  saveddir=`pwd`
+
+  M2_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  M2_HOME=`cd "$M2_HOME" && pwd`
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --unix "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="`(cd "$M2_HOME"; pwd)`"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
+  # TODO classpath?
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="`which javac`"
+  if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=`which readlink`
+    if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
+      if $darwin ; then
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
+      else
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+      fi
+      javaHome="`dirname \"$javaExecutable\"`"
+      javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="`which java`"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]
+  then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ] ; do
+    if [ -d "$wdir"/.mvn ] ; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=`cd "$wdir/.."; pwd`
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' < "$1")"
+  fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+  exit 1;
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found .mvn/wrapper/maven-wrapper.jar"
+    fi
+else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+    fi
+    jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+    while IFS="=" read key value; do
+      case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+      esac
+    done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Downloading from: $jarUrl"
+    fi
+    wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+
+    if command -v wget > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found wget ... using wget"
+        fi
+        wget "$jarUrl" -O "$wrapperJarPath"
+    elif command -v curl > /dev/null; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Found curl ... using curl"
+        fi
+        curl -o "$wrapperJarPath" "$jarUrl"
+    else
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo "Falling back to using Java to download"
+        fi
+        javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+        if [ -e "$javaClass" ]; then
+            if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Compiling MavenWrapperDownloader.java ..."
+                fi
+                # Compiling the Java class
+                ("$JAVA_HOME/bin/javac" "$javaClass")
+            fi
+            if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+                # Running the downloader
+                if [ "$MVNW_VERBOSE" = true ]; then
+                  echo " - Running MavenWrapperDownloader.java ..."
+                fi
+                ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+            fi
+        fi
+    fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=`cygpath --path --windows "$M2_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
+fi
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 161 - 0
mvnw.cmd

@@ -0,0 +1,161 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven2 Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.4.2/maven-wrapper-0.4.2.jar"
+FOR /F "tokens=1,2 delims==" %%A IN (%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties) DO (
+	IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    echo Found %WRAPPER_JAR%
+) else (
+    echo Couldn't find %WRAPPER_JAR%, downloading it ...
+	echo Downloading from: %DOWNLOAD_URL%
+    powershell -Command "(New-Object Net.WebClient).DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"
+    echo Finished downloading %WRAPPER_JAR%
+)
+@REM End of extension
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 13 - 1
pom.xml

@@ -66,6 +66,10 @@
             <url>https://libraries.minecraft.net/</url>
         </repository>
 
+        <repository>
+            <id>placeholderapi</id>
+            <url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
+        </repository>
     </repositories>
 
     <dependencies>
@@ -111,7 +115,7 @@
         <dependency>
             <groupId>org.projectlombok</groupId>
             <artifactId>lombok</artifactId>
-            <version>1.18.22</version>
+            <version>1.18.24</version>
             <scope>provided</scope>
         </dependency>
 
@@ -122,6 +126,14 @@
             <version>1.5.21</version>
             <scope>provided</scope>
         </dependency>
+
+        <!-- repo placeholderapi -->
+        <dependency>
+            <groupId>me.clip</groupId>
+            <artifactId>placeholderapi</artifactId>
+            <version>2.11.2</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
 
     <build>