浏览代码

Merge pull request #98 from RedstoneFuture/Improvements/map-voting

Reimplementation / Improving of map-vote
Daniel 1 年之前
父节点
当前提交
d74d5e3b60
共有 21 个文件被更改,包括 416 次插入224 次删除
  1. 15 0
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommandCompletions.java
  2. 12 29
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommands.java
  3. 9 31
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/UserCommands.java
  4. 6 11
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Lobby.java
  5. 8 5
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Messages.java
  6. 17 11
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Arenas.java
  7. 56 36
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Game.java
  8. 6 4
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/GameManager.java
  9. 169 0
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/MapVoting.java
  10. 30 0
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/enums/VoteState.java
  11. 23 10
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/misc/ScoreboardManager.java
  12. 11 26
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/timer/LobbyTimer.java
  13. 6 4
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/inventory/OrcInventory.java
  14. 5 3
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/inventory/OrcListener.java
  15. 6 7
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/PlayerListener.java
  16. 4 2
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/LobbyListener.java
  17. 8 5
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/player/MWPlayer.java
  18. 5 3
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/PlayerDataProvider.java
  19. 5 11
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/SetupUtil.java
  20. 9 9
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/geometry/GameArea.java
  21. 6 17
      missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/stats/GameProfileBuilder.java

+ 15 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommandCompletions.java

@@ -35,6 +35,7 @@ public class MWCommandCompletions {
 
 
         registerGamesResult();
         registerGamesResult();
         registerMissilesResult();
         registerMissilesResult();
+        registerArenasResult();
     }
     }
 
 
     private void registerGamesResult() {
     private void registerGamesResult() {
@@ -55,4 +56,18 @@ public class MWCommandCompletions {
         });
         });
     }
     }
 
 
+    private void registerArenasResult() {
+        commandCompletions.registerCompletion("arenas", c -> {
+            CommandSender sender = c.getSender();
+
+            if (!(sender instanceof Player)) return null;
+            Player player = (Player) sender;
+
+            Game game = GameManager.getInstance().getGame(player.getLocation());
+            if (game == null) return null;
+
+            return game.getLobby().getPossibleArenas();
+        });
+    }
+
 }
 }

+ 12 - 29
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/MWCommands.java

@@ -19,27 +19,20 @@
 package de.butzlabben.missilewars.commands;
 package de.butzlabben.missilewars.commands;
 
 
 import co.aikar.commands.BaseCommand;
 import co.aikar.commands.BaseCommand;
-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.*;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Messages;
 import de.butzlabben.missilewars.configuration.Messages;
-import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.game.enums.GameResult;
 import de.butzlabben.missilewars.game.enums.GameResult;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.game.enums.GameState;
-import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
+import de.butzlabben.missilewars.game.enums.VoteState;
 import de.butzlabben.missilewars.game.missile.Missile;
 import de.butzlabben.missilewars.game.missile.Missile;
 import de.butzlabben.missilewars.game.missile.MissileFacing;
 import de.butzlabben.missilewars.game.missile.MissileFacing;
-import java.util.Map;
-import java.util.Optional;
+import de.butzlabben.missilewars.game.timer.LobbyTimer;
 import org.bukkit.command.CommandSender;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 
 
@@ -52,7 +45,7 @@ public class MWCommands extends BaseCommand {
 
 
         sender.sendMessage(Messages.getPrefix() + "MissileWars v" + MissileWars.getInstance().version + " by Butzlabben");
         sender.sendMessage(Messages.getPrefix() + "MissileWars v" + MissileWars.getInstance().version + " by Butzlabben");
 
 
-        sendHelpMessage(sender, "mw.vote", "/mw vote", "Vote for a arena.");
+        sendHelpMessage(sender, "mw.vote", "/mw vote <arena>", "Vote for a arena.");
         sendHelpMessage(sender, "mw.change", "/mw change <1|2>", "Changes your team.");
         sendHelpMessage(sender, "mw.change", "/mw change <1|2>", "Changes your team.");
         sendHelpMessage(sender, "mw.quit", "/mw quit", "Quit a game.");
         sendHelpMessage(sender, "mw.quit", "/mw quit", "Quit a game.");
 
 
@@ -161,27 +154,17 @@ public class MWCommands extends BaseCommand {
             return;
             return;
         }
         }
 
 
-        if (game.isReady())
-            game.startGame();
-        else {
-            if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING && game.getArena() == null) {
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_CAN_NOT_STARTET));
+        if (!game.isReady()) {
+            if (game.getMapVoting().getState() == VoteState.RUNNING) {
+                game.getMapVoting().setVotedArena();
             } else {
             } else {
-                Map.Entry<String, Integer> mostVotes = null;
-                for (Map.Entry<String, Integer> arena : game.getVotes().entrySet()) {
-                    if (mostVotes == null) {
-                        mostVotes = arena;
-                        continue;
-                    }
-                    if (arena.getValue() > mostVotes.getValue()) mostVotes = arena;
-                }
-                if (mostVotes == null) throw new IllegalStateException("Most votes object was null");
-                Optional<Arena> arena = Arenas.getFromName(mostVotes.getKey());
-                if (arena.isEmpty()) throw new IllegalStateException("Voted arena is not present");
-                game.setArena(arena.get());
-                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_MAP_SELECTED));
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_CAN_NOT_STARTET));
+                return;
             }
             }
         }
         }
+
+        LobbyTimer lobbyTimer = (LobbyTimer) game.getTaskManager().getTimer();
+        lobbyTimer.executeGameStart();
     }
     }
 
 
     @Subcommand("stop")
     @Subcommand("stop")

+ 9 - 31
missilewars-plugin/src/main/java/de/butzlabben/missilewars/commands/UserCommands.java

@@ -25,15 +25,11 @@ import co.aikar.commands.annotation.CommandPermission;
 import co.aikar.commands.annotation.Subcommand;
 import co.aikar.commands.annotation.Subcommand;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.configuration.Messages;
 import de.butzlabben.missilewars.configuration.Messages;
-import de.butzlabben.missilewars.configuration.arena.Arena;
-import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.game.GameManager;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.game.enums.GameState;
-import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
 import de.butzlabben.missilewars.player.MWPlayer;
 import de.butzlabben.missilewars.player.MWPlayer;
-import java.util.Optional;
 import org.bukkit.command.CommandSender;
 import org.bukkit.command.CommandSender;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 
 
@@ -41,14 +37,19 @@ import org.bukkit.entity.Player;
 public class UserCommands extends BaseCommand {
 public class UserCommands extends BaseCommand {
 
 
     @Subcommand("vote")
     @Subcommand("vote")
-    @CommandCompletion("@nothing")
+    @CommandCompletion("@arenas")
     @CommandPermission("mw.vote")
     @CommandPermission("mw.vote")
     public void voteCommand(CommandSender sender, String[] args) {
     public void voteCommand(CommandSender sender, String[] args) {
 
 
         if (!MWCommands.senderIsPlayer(sender)) return;
         if (!MWCommands.senderIsPlayer(sender)) return;
         Player player = (Player) sender;
         Player player = (Player) sender;
 
 
-        if (args.length > 0) {
+        if (args.length < 1) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_MAP_NEEDED));
+            return;
+        }
+
+        if (args.length > 1) {
             player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_TO_MANY_ARGUMENTS));
             player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_TO_MANY_ARGUMENTS));
             return;
             return;
         }
         }
@@ -58,31 +59,8 @@ public class UserCommands extends BaseCommand {
             player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_NOT_IN_GAME_AREA));
             player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.GAME_NOT_IN_GAME_AREA));
             return;
             return;
         }
         }
-
-        if (game.getState() != GameState.LOBBY) {
-            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CHANGE_TEAM_NOT_NOW));
-            return;
-        }
-
-        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING) {
-            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CANT_VOTE));
-            return;
-        }
-
-        if (game.getArena() != null) {
-            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CHANGE_TEAM_NO_LONGER_NOW));
-            return;
-        }
-
-        String arenaName = args[0];
-        Optional<Arena> arena = Arenas.getFromName(arenaName);
-        if (!game.getVotes().containsKey(arenaName) || arena.isEmpty()) {
-            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_INVALID_MAP));
-            return;
-        }
-
-        game.getVotes().put(arenaName, game.getVotes().get(arenaName) + 1);
-        player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_SUCCESS).replace("%map%", arena.get().getDisplayName()));
+        
+        game.getMapVoting().addVote(player, args[0]);
     }
     }
 
 
     @Subcommand("change")
     @Subcommand("change")

+ 6 - 11
missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Lobby.java

@@ -40,6 +40,12 @@ import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Location;
 import org.bukkit.World;
 import org.bukkit.World;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
 @Getter
 @Getter
 @ToString
 @ToString
 @RequiredArgsConstructor
 @RequiredArgsConstructor
@@ -83,21 +89,10 @@ public class Lobby {
         return Bukkit.getWorlds().get(0);
         return Bukkit.getWorlds().get(0);
     }
     }
 
 
-    public void checkForWrongArenas() {
-        for (String arenaName : possibleArenas) {
-            Optional<Arena> arena = Arenas.getFromName(arenaName);
-            if (arena.isEmpty()) {
-                Logger.WARN.log("Could not find arena with name \"" + arenaName + "\" for lobby \"" + getName() + "\"");
-            }
-        }
-    }
-
     public List<Arena> getArenas() {
     public List<Arena> getArenas() {
         return possibleArenas
         return possibleArenas
                 .stream()
                 .stream()
                 .map(Arenas::getFromName)
                 .map(Arenas::getFromName)
-                .filter(Optional::isPresent)
-                .map(Optional::get)
                 .collect(Collectors.toList());
                 .collect(Collectors.toList());
     }
     }
 
 

+ 8 - 5
missilewars-plugin/src/main/java/de/butzlabben/missilewars/configuration/Messages.java

@@ -20,11 +20,12 @@ package de.butzlabben.missilewars.configuration;
 
 
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.util.SetupUtil;
 import de.butzlabben.missilewars.util.SetupUtil;
-import java.io.File;
 import lombok.Getter;
 import lombok.Getter;
 import org.bukkit.ChatColor;
 import org.bukkit.ChatColor;
 import org.bukkit.configuration.file.YamlConfiguration;
 import org.bukkit.configuration.file.YamlConfiguration;
 
 
+import java.io.File;
+
 
 
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
@@ -91,13 +92,13 @@ public class Messages {
         COMMAND_ONLY_PLAYERS("command.only_players", "&cYou are not a player."),
         COMMAND_ONLY_PLAYERS("command.only_players", "&cYou are not a player."),
         COMMAND_TO_MANY_ARGUMENTS("command.to_many_arguments", "&cToo many arguments."),
         COMMAND_TO_MANY_ARGUMENTS("command.to_many_arguments", "&cToo many arguments."),
         COMMAND_INVALID_MISSILE("command.invalid_missile", "&cThe specified missile was not found."),
         COMMAND_INVALID_MISSILE("command.invalid_missile", "&cThe specified missile was not found."),
+        COMMAND_MISSILE_NEEDED("command.missile_needed", "&cPlease specify the missile."),
         COMMAND_INVALID_GAME("command.invalid_game", "&cThe specified game was not found."),
         COMMAND_INVALID_GAME("command.invalid_game", "&cThe specified game was not found."),
         COMMAND_INVALID_MAP("command.invalid_map", "&cThe specified map was not found."),
         COMMAND_INVALID_MAP("command.invalid_map", "&cThe specified map was not found."),
+        COMMAND_MAP_NEEDED("command.map_needed", "&cPlease specify the map."),
         COMMAND_INVALID_TEAM_NUMBER("command.invalid_team_number", "&cThe team number is invalid. Use \"1\" or \"2\" to specify the target team."),
         COMMAND_INVALID_TEAM_NUMBER("command.invalid_team_number", "&cThe team number is invalid. Use \"1\" or \"2\" to specify the target team."),
-        COMMAND_MISSILE_NEEDED("command.missile_needed", "&cPlease specify the missile."),
         COMMAND_TEAM_NUMBER_NEEDED("command.team_number_needed", "&cPlease specify the team number."),
         COMMAND_TEAM_NUMBER_NEEDED("command.team_number_needed", "&cPlease specify the team number."),
-
-        GAME_MAP_SELECTED("game.map_selected", "&7A map was selected. Use \"/mw start\" again to start the round."),
+        
         GAME_PLAYER_JOINED("game.player_joined", "&e%player% &7joined the game (%team%&7)."),
         GAME_PLAYER_JOINED("game.player_joined", "&e%player% &7joined the game (%team%&7)."),
         GAME_PLAYER_LEFT("game.player_left", "&e%player% &7left the game (%team%&7)."),
         GAME_PLAYER_LEFT("game.player_left", "&e%player% &7left the game (%team%&7)."),
         GAME_LEFT("game.left", "&7You left the running MissileWars game."),
         GAME_LEFT("game.left", "&7You left the running MissileWars game."),
@@ -155,11 +156,13 @@ public class Messages {
         GAME_RESULT_MONEY("game_result.money", "&7You received &e%money% &7coins."),
         GAME_RESULT_MONEY("game_result.money", "&7You received &e%money% &7coins."),
 
 
         VOTE_SUCCESS("vote.success", "&7You successfully voted for the map %map%."),
         VOTE_SUCCESS("vote.success", "&7You successfully voted for the map %map%."),
-        VOTE_FINISHED("vote.finished", "&7The map %map% &7was elected."),
+        VOTE_FINISHED("vote.finished", "&7The map %map% &7was selected."),
         VOTE_GUI("vote.gui", "Vote for a map"),
         VOTE_GUI("vote.gui", "Vote for a map"),
         VOTE_CANT_VOTE("vote.cant_vote", "&cYou can not vote in this game."),
         VOTE_CANT_VOTE("vote.cant_vote", "&cYou can not vote in this game."),
         VOTE_CHANGE_TEAM_NOT_NOW("vote.change_team_not_now", "&cThe game is not in the right state to vote right now."),
         VOTE_CHANGE_TEAM_NOT_NOW("vote.change_team_not_now", "&cThe game is not in the right state to vote right now."),
         VOTE_CHANGE_TEAM_NO_LONGER_NOW("vote.change_team_no_longer_now", "&cA map was already selected."),
         VOTE_CHANGE_TEAM_NO_LONGER_NOW("vote.change_team_no_longer_now", "&cA map was already selected."),
+        VOTE_MAP_NOT_AVAILABLE("vote.map_not_available", "&cThe selected map is not available for this game."),
+        VOTE_ARENA_ALREADY_SELECTED("vote.arena_already_selected", "&cYou have already voted for this arena."),
 
 
         SIGNEDIT_SIGN_CREATED("signedit.sign_created", "&7Sign was successfully created and connected."),
         SIGNEDIT_SIGN_CREATED("signedit.sign_created", "&7Sign was successfully created and connected."),
         SIGNEDIT_SIGN_REMOVED("signedit.sign_removed", "&7You have successfully removed this missilewars sign."),
         SIGNEDIT_SIGN_REMOVED("signedit.sign_removed", "&7You have successfully removed this missilewars sign."),

+ 17 - 11
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Arenas.java

@@ -24,20 +24,21 @@ import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.util.SetupUtil;
 import de.butzlabben.missilewars.util.SetupUtil;
 import de.butzlabben.missilewars.util.serialization.Serializer;
 import de.butzlabben.missilewars.util.serialization.Serializer;
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Optional;
 import lombok.Getter;
 import lombok.Getter;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 public class Arenas {
 public class Arenas {
 
 
-    @Getter private static final List<Arena> arenas = new ArrayList<>();
+    @Getter
+    private static final Map<String, Arena> ARENAS = new HashMap<>();
 
 
     public static void load() {
     public static void load() {
-        arenas.clear();
+        ARENAS.clear();
 
 
         File folder = new File(Config.getArenasFolder());
         File folder = new File(Config.getArenasFolder());
 
 
@@ -66,13 +67,13 @@ public class Arenas {
             try {
             try {
                 Arena arena = Serializer.deserialize(config, Arena.class);
                 Arena arena = Serializer.deserialize(config, Arena.class);
                 arena.setFile(config);
                 arena.setFile(config);
-                if (getFromName(arena.getName()).isPresent()) {
+                if (existsArena(arena.getName())) {
                     Logger.WARN.log("There are several arenas configured with the name \"" + arena.getName() + "\". Arenas must have a unique name");
                     Logger.WARN.log("There are several arenas configured with the name \"" + arena.getName() + "\". Arenas must have a unique name");
                     continue;
                     continue;
                 }
                 }
                 SetupUtil.checkMap(arena.getTemplateWorld());
                 SetupUtil.checkMap(arena.getTemplateWorld());
                 arena.updateConfig();
                 arena.updateConfig();
-                arenas.add(arena);
+                ARENAS.put(arena.getName(), arena);
             } catch (IOException exception) {
             } catch (IOException exception) {
                 Logger.ERROR.log("Could not load config for arena " + config.getName());
                 Logger.ERROR.log("Could not load config for arena " + config.getName());
                 exception.printStackTrace();
                 exception.printStackTrace();
@@ -80,7 +81,12 @@ public class Arenas {
         }
         }
     }
     }
 
 
-    public static Optional<Arena> getFromName(String name) {
-        return arenas.stream().filter(arena -> arena.getName().equalsIgnoreCase(name)).findFirst();
+    public static Arena getFromName(String arenaName) {
+        if (ARENAS.containsKey(arenaName)) return ARENAS.get(arenaName);
+        return null;
+    }
+
+    public static boolean existsArena(String arenaName) {
+        return ARENAS.containsKey(arenaName);
     }
     }
 }
 }

+ 56 - 36
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/Game.java

@@ -18,7 +18,6 @@
 
 
 package de.butzlabben.missilewars.game;
 package de.butzlabben.missilewars.game;
 
 
-import com.google.common.base.Preconditions;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Config;
@@ -30,7 +29,9 @@ import de.butzlabben.missilewars.event.GameStopEvent;
 import de.butzlabben.missilewars.game.enums.GameResult;
 import de.butzlabben.missilewars.game.enums.GameResult;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
 import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
+import de.butzlabben.missilewars.game.enums.VoteState;
 import de.butzlabben.missilewars.game.equipment.EquipmentManager;
 import de.butzlabben.missilewars.game.equipment.EquipmentManager;
+import de.butzlabben.missilewars.game.equipment.PlayerEquipmentRandomizer;
 import de.butzlabben.missilewars.game.misc.MotdManager;
 import de.butzlabben.missilewars.game.misc.MotdManager;
 import de.butzlabben.missilewars.game.misc.ScoreboardManager;
 import de.butzlabben.missilewars.game.misc.ScoreboardManager;
 import de.butzlabben.missilewars.game.missile.Missile;
 import de.butzlabben.missilewars.game.missile.Missile;
@@ -51,19 +52,9 @@ import de.butzlabben.missilewars.util.PlayerDataProvider;
 import de.butzlabben.missilewars.util.geometry.GameArea;
 import de.butzlabben.missilewars.util.geometry.GameArea;
 import de.butzlabben.missilewars.util.geometry.Geometry;
 import de.butzlabben.missilewars.util.geometry.Geometry;
 import de.butzlabben.missilewars.util.serialization.Serializer;
 import de.butzlabben.missilewars.util.serialization.Serializer;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.function.Consumer;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.ToString;
 import lombok.ToString;
-import org.bukkit.Bukkit;
-import org.bukkit.GameMode;
-import org.bukkit.Location;
-import org.bukkit.Material;
-import org.bukkit.Sound;
-import org.bukkit.World;
+import org.bukkit.*;
 import org.bukkit.entity.Fireball;
 import org.bukkit.entity.Fireball;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 import org.bukkit.event.HandlerList;
 import org.bukkit.event.HandlerList;
@@ -72,6 +63,12 @@ import org.bukkit.inventory.ItemStack;
 import org.bukkit.scheduler.BukkitTask;
 import org.bukkit.scheduler.BukkitTask;
 import org.bukkit.util.Vector;
 import org.bukkit.util.Vector;
 
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Consumer;
+
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
  * @since 01.01.2018
  * @since 01.01.2018
@@ -84,9 +81,9 @@ public class Game {
     private static final Map<String, Integer> cycles = new HashMap<>();
     private static final Map<String, Integer> cycles = new HashMap<>();
     private static int fights = 0;
     private static int fights = 0;
     private final Map<UUID, MWPlayer> players = new HashMap<>();
     private final Map<UUID, MWPlayer> players = new HashMap<>();
-    private final Map<String, Integer> votes = new HashMap<>(); // Votes for the maps.
+    private final MapVoting mapVoting = new MapVoting(this);
     private final Lobby lobby;
     private final Lobby lobby;
-    private final HashMap<UUID, BukkitTask> playerTasks = new HashMap<>();
+    private final Map<UUID, BukkitTask> playerTasks = new HashMap<>();
     private GameState state = GameState.LOBBY;
     private GameState state = GameState.LOBBY;
     private Team team1;
     private Team team1;
     private Team team2;
     private Team team2;
@@ -104,29 +101,29 @@ public class Game {
     private int remainingGameDuration;
     private int remainingGameDuration;
 
 
     public Game(Lobby lobby) {
     public Game(Lobby lobby) {
-        Logger.BOOT.log("Loading lobby " + lobby.getName());
+        Logger.BOOT.log("Loading lobby \"" + lobby.getName() + "\".");
         this.lobby = lobby;
         this.lobby = lobby;
 
 
         if (lobby.getBukkitWorld() == null) {
         if (lobby.getBukkitWorld() == null) {
-            Logger.ERROR.log("Lobby world in arena \"" + lobby.getName() + "\" must not be null");
+            Logger.ERROR.log("Lobby world \"" + lobby.getName() + "\" must not be null");
             return;
             return;
         }
         }
 
 
         try {
         try {
             Serializer.setWorldAtAllLocations(lobby, lobby.getBukkitWorld());
             Serializer.setWorldAtAllLocations(lobby, lobby.getBukkitWorld());
         } catch (Exception exception) {
         } catch (Exception exception) {
-            Logger.ERROR.log("Could not inject world object at lobby " + lobby.getName());
+            Logger.ERROR.log("Could not inject world object at lobby \"" + lobby.getName() + "\".");
             exception.printStackTrace();
             exception.printStackTrace();
             return;
             return;
         }
         }
 
 
         if (lobby.getPossibleArenas().size() == 0) {
         if (lobby.getPossibleArenas().size() == 0) {
-            Logger.ERROR.log("At least one valid arena must be set at lobby " + lobby.getName());
+            Logger.ERROR.log("At least one valid arena must be set at lobby \"" + lobby.getName() + "\".");
             return;
             return;
         }
         }
 
 
-        if (lobby.getPossibleArenas().stream().noneMatch(a -> Arenas.getFromName(a).isPresent())) {
-            Logger.ERROR.log("None of the specified arenas match a real arena for the lobby " + lobby.getName());
+        if (lobby.getPossibleArenas().stream().noneMatch(Arenas::existsArena)) {
+            Logger.ERROR.log("None of the specified arenas match a real arena for the lobby \"" + lobby.getName() + "\".");
             return;
             return;
         }
         }
 
 
@@ -152,40 +149,64 @@ public class Game {
         Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> applyForAllPlayers(this::runTeleportEventForPlayer), 2);
         Bukkit.getScheduler().runTaskLater(MissileWars.getInstance(), () -> applyForAllPlayers(this::runTeleportEventForPlayer), 2);
 
 
         if (Config.isSetup()) {
         if (Config.isSetup()) {
-            Logger.WARN.log("Did not fully initialize lobby " + lobby.getName() + " as the plugin is in setup mode");
+            Logger.WARN.log("Did not fully initialize lobby \"" + lobby.getName() + "\" as the plugin is in setup mode");
             return;
             return;
         }
         }
 
 
         // choose the game arena
         // choose the game arena
         if (lobby.getMapChooseProcedure() == MapChooseProcedure.FIRST) {
         if (lobby.getMapChooseProcedure() == MapChooseProcedure.FIRST) {
             setArena(lobby.getArenas().get(0));
             setArena(lobby.getArenas().get(0));
+            prepareGame();
+
         } else if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPCYCLE) {
         } else if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPCYCLE) {
             final int lastMapIndex = cycles.getOrDefault(lobby.getName(), -1);
             final int lastMapIndex = cycles.getOrDefault(lobby.getName(), -1);
             List<Arena> arenas = lobby.getArenas();
             List<Arena> arenas = lobby.getArenas();
             int index = lastMapIndex >= arenas.size() - 1 ? 0 : lastMapIndex + 1;
             int index = lastMapIndex >= arenas.size() - 1 ? 0 : lastMapIndex + 1;
             cycles.put(lobby.getName(), index);
             cycles.put(lobby.getName(), index);
             setArena(arenas.get(index));
             setArena(arenas.get(index));
+            prepareGame();
+
         } else if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING) {
         } else if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING) {
-            if (lobby.getArenas().size() == 1) {
+            if (mapVoting.onlyOneArenaFound()) {
                 setArena(lobby.getArenas().get(0));
                 setArena(lobby.getArenas().get(0));
+                Logger.WARN.log("Only one arena was found for the lobby \"" + lobby.getName() + "\". The configured map voting was skipped.");
+                prepareGame();
+            } else {
+                mapVoting.startVote();
             }
             }
-            lobby.getArenas().forEach(arena -> votes.put(arena.getName(), 0));
         }
         }
 
 
         scoreboardManager = new ScoreboardManager(this);
         scoreboardManager = new ScoreboardManager(this);
         scoreboardManager.createScoreboard();
         scoreboardManager.createScoreboard();
+    }
+
+    /**
+     * This method performs the final preparations for the game start.
+     * <p>
+     * It is necessary that the arena - even in the case of a map vote - is
+     * now already defined.
+     */
+    public void prepareGame() {
+        if (this.arena == null) {
+            throw new IllegalStateException("The arena is not yet set");
+        }
+
+        // Clear the player inventory
+        applyForAllPlayers(player -> player.getInventory().setItem(4, new ItemStack(Material.AIR)));
+
+        scoreboardManager.resetScoreboard();
 
 
         equipmentManager = new EquipmentManager(this);
         equipmentManager = new EquipmentManager(this);
+        equipmentManager.createGameItems();
 
 
         Logger.DEBUG.log("Making game ready");
         Logger.DEBUG.log("Making game ready");
         ++fights;
         ++fights;
-        if (fights >= Config.getFightRestart())
-            restart = true;
+        if (fights >= Config.getFightRestart()) restart = true;
 
 
         FightStats.checkTables();
         FightStats.checkTables();
         Logger.DEBUG.log("Fights: " + fights);
         Logger.DEBUG.log("Fights: " + fights);
 
 
-        equipmentManager.createGameItems();
+        ready = true;
     }
     }
 
 
     private void updateGameListener(GameBoundListener newListener) {
     private void updateGameListener(GameBoundListener newListener) {
@@ -352,8 +373,10 @@ public class Game {
             }
             }
 
 
             // map choose menu:
             // map choose menu:
-            if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING && arena == null) {
-                player.getInventory().setItem(4, new OrcItem(Material.NETHER_STAR, "§3Vote Map").getItemStack());
+            if (mapVoting.getState() == VoteState.RUNNING) {
+                if (player.hasPermission("mw.vote")) {
+                    player.getInventory().setItem(4, new OrcItem(Material.NETHER_STAR, "§3Vote Map").getItemStack());
+                }
             }
             }
 
 
         } else if ((state == GameState.INGAME) && (!isSpectatorJoin)) {
         } else if ((state == GameState.INGAME) && (!isSpectatorJoin)) {
@@ -499,6 +522,9 @@ public class Game {
      * @return true, if it's in the game world
      * @return true, if it's in the game world
      */
      */
     public boolean isInGameWorld(Location location) {
     public boolean isInGameWorld(Location location) {
+        // Is possible during the map voting phase:
+        if (gameArea == null) return false;
+
         return Geometry.isInWorld(location, gameArea.getWorld());
         return Geometry.isInWorld(location, gameArea.getWorld());
     }
     }
 
 
@@ -552,6 +578,8 @@ public class Game {
         equipmentManager.sendGameItems(player, false);
         equipmentManager.sendGameItems(player, false);
         setPlayerAttributes(player);
         setPlayerAttributes(player);
 
 
+        mwPlayer.setRandomGameEquipment(new PlayerEquipmentRandomizer(mwPlayer, this));
+
         playerTasks.put(player.getUniqueId(),
         playerTasks.put(player.getUniqueId(),
                 Bukkit.getScheduler().runTaskTimer(MissileWars.getInstance(), mwPlayer, 40, 20));
                 Bukkit.getScheduler().runTaskTimer(MissileWars.getInstance(), mwPlayer, 40, 20));
     }
     }
@@ -623,7 +651,6 @@ public class Game {
     }
     }
 
 
     public void setArena(Arena arena) {
     public void setArena(Arena arena) {
-        Preconditions.checkNotNull(arena);
         if (this.arena != null) {
         if (this.arena != null) {
             throw new IllegalStateException("Arena already set");
             throw new IllegalStateException("Arena already set");
         }
         }
@@ -649,13 +676,6 @@ public class Game {
         }
         }
 
 
         createInnerGameArea();
         createInnerGameArea();
-
-        if (lobby.getMapChooseProcedure() == MapChooseProcedure.MAPVOTING) {
-            this.broadcast(Messages.getMessage(true, Messages.MessageEnum.VOTE_FINISHED).replace("%map%", this.arena.getDisplayName()));
-        }
-        applyForAllPlayers(player -> player.getInventory().setItem(4, new ItemStack(Material.AIR)));
-
-        ready = true;
     }
     }
 
 
     private void createInnerGameArea() {
     private void createInnerGameArea() {

+ 6 - 4
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/GameManager.java

@@ -24,19 +24,21 @@ import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Lobby;
 import de.butzlabben.missilewars.configuration.Lobby;
 import de.butzlabben.missilewars.util.geometry.GameArea;
 import de.butzlabben.missilewars.util.geometry.GameArea;
 import de.butzlabben.missilewars.util.serialization.Serializer;
 import de.butzlabben.missilewars.util.serialization.Serializer;
-import java.io.File;
-import java.io.IOException;
-import java.util.HashMap;
 import lombok.Getter;
 import lombok.Getter;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Location;
 
 
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
 @Getter
 @Getter
 public class GameManager {
 public class GameManager {
 
 
     @Getter
     @Getter
     private static final GameManager instance = new GameManager();
     private static final GameManager instance = new GameManager();
-    private final HashMap<String, Game> games = new HashMap<>();
+    private final Map<String, Game> games = new HashMap<>();
 
 
 
 
     public void disableAll() {
     public void disableAll() {

+ 169 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/MapVoting.java

@@ -0,0 +1,169 @@
+/*
+ * 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.game;
+
+import de.butzlabben.missilewars.configuration.Messages;
+import de.butzlabben.missilewars.configuration.arena.Arena;
+import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
+import de.butzlabben.missilewars.game.enums.VoteState;
+import de.butzlabben.missilewars.player.MWPlayer;
+import lombok.Getter;
+import org.bukkit.entity.Player;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+public class MapVoting {
+
+    private final Map<MWPlayer, Arena> arenaVotes = new HashMap<>();
+    private Game game;
+    @Getter private VoteState state = VoteState.NULL;
+
+    public MapVoting(Game game) {
+        this.game = game;
+    }
+
+    /**
+     * This method saves the incoming votes of the players, provided that all 
+     * conditions for the voting process are met. If the conditions are not met, 
+     * the player will be notified accordingly.
+     * 
+     * @param player (Player) the voter
+     * @param arenaName (String) the voted arena name
+     */
+    public void addVote(Player player, String arenaName) {
+        
+        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CANT_VOTE));
+            return;
+        }
+        
+        if (state == VoteState.NULL) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CHANGE_TEAM_NOT_NOW));
+            return;
+        } else if (state == VoteState.FINISH) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_CHANGE_TEAM_NO_LONGER_NOW));
+            return;
+        }
+
+        Arena arena = Arenas.getFromName(arenaName);
+        if (arena == null) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.COMMAND_INVALID_MAP));
+            return;
+        }
+        
+        if (!game.getLobby().getArenas().contains(arena)) {
+            player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_MAP_NOT_AVAILABLE));
+            return;
+        }
+
+        MWPlayer mwPlayer = game.getPlayer(player);
+
+        if (arenaVotes.containsKey(mwPlayer)) {
+
+            if (arenaVotes.get(mwPlayer) == arena) {
+                player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_ARENA_ALREADY_SELECTED)
+                        .replace("%map%", arena.getDisplayName()));
+                return;
+            }
+
+            // remove old vote
+            arenaVotes.remove(mwPlayer);
+        }
+
+        // add new vote
+        arenaVotes.put(mwPlayer, arena);
+
+        player.sendMessage(Messages.getMessage(true, Messages.MessageEnum.VOTE_SUCCESS).replace("%map%", arena.getDisplayName()));
+    }
+
+    /**
+     * This method returns the arena that has been voted the most by the players.
+     *
+     * @return (Arena) the winner arena for this vote
+     */
+    private Arena getVotedArena() {
+
+        // If no one voted:
+        if (arenaVotes.size() == 0) return game.getLobby().getArenas().get(0);
+
+        Arena arena = arenaVotes.values().stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
+                .entrySet()
+                .stream()
+                .max(Map.Entry.comparingByValue()).orElseThrow()
+                .getKey();
+
+        return arena;
+    }
+
+    /**
+     * This method unlocks the map voting.
+     */
+    public void startVote() {
+        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING)
+            throw new IllegalStateException("Defined map choose procedure is not \"MAPVOTING\"");
+
+        state = VoteState.RUNNING;
+    }
+
+    /**
+     * This method locks the map voting again.
+     */
+    public void stopVote() {
+        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING)
+            throw new IllegalStateException("Defined map choose procedure is not \"MAPVOTING\"");
+
+        state = VoteState.FINISH;
+    }
+
+    /**
+     * This method checks if there is only one arena map available for this lobby and 
+     * therefore no map vote is necessary.
+     * 
+     * @return (Boolean) true, if only one map exists for this lobby
+     */
+    public boolean onlyOneArenaFound() {
+        return (game.getLobby().getArenas().size() == 1);
+    }
+
+    /**
+     * This method sets the selected arena of map voting for the current game.
+     */
+    public void setVotedArena() {
+        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING)
+            throw new IllegalStateException("Defined map choose procedure is not \"MAPVOTING\"");
+
+        if (onlyOneArenaFound()) return;
+        if (state != VoteState.RUNNING) return;
+
+        stopVote();
+
+        Arena arena = game.getMapVoting().getVotedArena();
+        if (arena == null) throw new IllegalStateException("Voted arena is not present");
+        game.setArena(arena);
+
+        game.broadcast(Messages.getMessage(true, Messages.MessageEnum.VOTE_FINISHED)
+                .replace("%map%", game.getArena().getDisplayName()));
+
+        game.prepareGame();
+    }
+    
+}

+ 30 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/enums/VoteState.java

@@ -0,0 +1,30 @@
+/*
+ * 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.game.enums;
+
+/**
+ * @author Butzlabben
+ * @since 01.01.2018
+ */
+public enum VoteState {
+
+    NULL,
+    RUNNING,
+    FINISH
+}

+ 23 - 10
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/misc/ScoreboardManager.java

@@ -19,21 +19,23 @@
 package de.butzlabben.missilewars.game.misc;
 package de.butzlabben.missilewars.game.misc;
 
 
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Config;
-import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.game.enums.GameState;
 import de.butzlabben.missilewars.player.MWPlayer;
 import de.butzlabben.missilewars.player.MWPlayer;
-import java.util.HashMap;
-import java.util.List;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.RequiredArgsConstructor;
 import lombok.RequiredArgsConstructor;
+import lombok.Setter;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 import org.bukkit.ChatColor;
 import org.bukkit.ChatColor;
 import org.bukkit.scoreboard.DisplaySlot;
 import org.bukkit.scoreboard.DisplaySlot;
 import org.bukkit.scoreboard.Objective;
 import org.bukkit.scoreboard.Objective;
 import org.bukkit.scoreboard.Scoreboard;
 import org.bukkit.scoreboard.Scoreboard;
 
 
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 // Scoreboard Management: https://www.spigotmc.org/wiki/making-scoreboard-with-teams-no-flicker
 // Scoreboard Management: https://www.spigotmc.org/wiki/making-scoreboard-with-teams-no-flicker
 
 
 @RequiredArgsConstructor
 @RequiredArgsConstructor
@@ -43,7 +45,11 @@ public class ScoreboardManager {
 
 
     private Team team1;
     private Team team1;
     private Team team2;
     private Team team2;
-    private Arena arena;
+
+    @Setter
+    private String arenaDisplayName;
+    @Setter
+    private String arenaGameDuration;
 
 
     // get config options
     // get config options
     private static final String SCOREBOARD_TITLE = Config.getScoreboardTitle();
     private static final String SCOREBOARD_TITLE = Config.getScoreboardTitle();
@@ -56,7 +62,7 @@ public class ScoreboardManager {
 
 
     @Getter private Scoreboard board;
     @Getter private Scoreboard board;
     private Objective obj;
     private Objective obj;
-    private HashMap<Integer, org.bukkit.scoreboard.Team> teams = new HashMap<>();
+    private Map<Integer, org.bukkit.scoreboard.Team> teams = new HashMap<>();
     private static final String[] COLOR_CODES = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
     private static final String[] COLOR_CODES = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
 
 
     /**
     /**
@@ -66,7 +72,15 @@ public class ScoreboardManager {
 
 
         team1 = game.getTeam1();
         team1 = game.getTeam1();
         team2 = game.getTeam2();
         team2 = game.getTeam2();
-        arena = game.getArena();
+
+        if (game.getArena() == null) {
+            // using of placeholders until the arena is not set
+            setArenaDisplayName("?");
+            setArenaGameDuration("0");
+        } else {
+            setArenaDisplayName(game.getArena().getDisplayName());
+            setArenaGameDuration(Integer.toString(game.getArena().getGameDuration()));
+        }
 
 
         // register Scoreboard
         // register Scoreboard
         if (board == null) {
         if (board == null) {
@@ -97,7 +111,7 @@ public class ScoreboardManager {
         org.bukkit.scoreboard.Team team;
         org.bukkit.scoreboard.Team team;
 
 
         if (teams.size() < line) {
         if (teams.size() < line) {
-            team = board.registerNewTeam(arena.getName() + "-" + line);
+            team = board.registerNewTeam(arenaDisplayName + "-" + line);
             team.addEntry("§" + COLOR_CODES[line - 1]);
             team.addEntry("§" + COLOR_CODES[line - 1]);
             obj.getScore("§" + COLOR_CODES[line - 1]).setScore(line);
             obj.getScore("§" + COLOR_CODES[line - 1]).setScore(line);
             teams.put(line, team);
             teams.put(line, team);
@@ -233,7 +247,7 @@ public class ScoreboardManager {
         String time = "";
         String time = "";
         if (game.getState() == GameState.LOBBY) {
         if (game.getState() == GameState.LOBBY) {
             // Show the planned duration of the next game:
             // Show the planned duration of the next game:
-            time = Integer.toString(game.getArena().getGameDuration());
+            time = arenaGameDuration;
         } else if (game.getState() == GameState.INGAME) {
         } else if (game.getState() == GameState.INGAME) {
             // Show the remaining duration of the running game:
             // Show the remaining duration of the running game:
             time = Integer.toString(game.getTaskManager().getTimer().getSeconds() / 60);
             time = Integer.toString(game.getTaskManager().getTimer().getSeconds() / 60);
@@ -255,12 +269,11 @@ public class ScoreboardManager {
         text = text.replace("%team2_amount%", Integer.toString(team2.getMembers().size()));
         text = text.replace("%team2_amount%", Integer.toString(team2.getMembers().size()));
 
 
         text = text.replace("%lobby_name%", game.getLobby().getDisplayName());
         text = text.replace("%lobby_name%", game.getLobby().getDisplayName());
-        text = text.replace("%arena_name%", arena.getDisplayName());
+        text = text.replace("%arena_name%", arenaDisplayName);
 
 
         text = text.replace("%time%", time);
         text = text.replace("%time%", time);
 
 
         return text;
         return text;
     }
     }
 
 
-
 }
 }

+ 11 - 26
missilewars-plugin/src/main/java/de/butzlabben/missilewars/game/timer/LobbyTimer.java

@@ -19,13 +19,8 @@
 package de.butzlabben.missilewars.game.timer;
 package de.butzlabben.missilewars.game.timer;
 
 
 import de.butzlabben.missilewars.configuration.Messages;
 import de.butzlabben.missilewars.configuration.Messages;
-import de.butzlabben.missilewars.configuration.arena.Arena;
-import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Game;
-import de.butzlabben.missilewars.game.enums.MapChooseProcedure;
 import de.butzlabben.missilewars.player.MWPlayer;
 import de.butzlabben.missilewars.player.MWPlayer;
-import java.util.Map;
-import java.util.Optional;
 import org.bukkit.Sound;
 import org.bukkit.Sound;
 
 
 /**
 /**
@@ -84,7 +79,7 @@ public class LobbyTimer extends Timer implements Runnable {
                 playPling();
                 playPling();
                 break;
                 break;
             case 10:
             case 10:
-                checkVote();
+                getGame().getMapVoting().setVotedArena();
                 broadcast(Messages.getMessage(true, Messages.MessageEnum.LOBBY_TIMER_GAME_STARTS_IN).replace("%seconds%", Integer.toString(seconds)));
                 broadcast(Messages.getMessage(true, Messages.MessageEnum.LOBBY_TIMER_GAME_STARTS_IN).replace("%seconds%", Integer.toString(seconds)));
                 playPling();
                 playPling();
                 break;
                 break;
@@ -95,9 +90,7 @@ public class LobbyTimer extends Timer implements Runnable {
                     seconds = startTime;
                     seconds = startTime;
                     return;
                     return;
                 }
                 }
-                broadcast(Messages.getMessage(true, Messages.MessageEnum.LOBBY_GAME_STARTS));
-                playPling();
-                getGame().startGame();
+                executeGameStart();
                 return;
                 return;
             default:
             default:
                 break;
                 break;
@@ -112,22 +105,14 @@ public class LobbyTimer extends Timer implements Runnable {
         }
         }
     }
     }
 
 
-    private void checkVote() {
-        Game game = getGame();
-        if (game.getLobby().getMapChooseProcedure() != MapChooseProcedure.MAPVOTING) return;
-        if (game.getArena() != null) return;
-
-        Map.Entry<String, Integer> mostVotes = null;
-        for (Map.Entry<String, Integer> arena : game.getVotes().entrySet()) {
-            if (mostVotes == null) {
-                mostVotes = arena;
-                continue;
-            }
-            if (arena.getValue() > mostVotes.getValue()) mostVotes = arena;
-        }
-        if (mostVotes == null) throw new IllegalStateException("Most votes object was null");
-        Optional<Arena> arena = Arenas.getFromName(mostVotes.getKey());
-        if (arena.isEmpty()) throw new IllegalStateException("Voted arena is not present");
-        game.setArena(arena.get());
+    /**
+     * This method executes the game start. In addition, the participants
+     * are informed about the start.
+     */
+    public void executeGameStart() {
+        broadcast(Messages.getMessage(true, Messages.MessageEnum.LOBBY_GAME_STARTS));
+        playPling();
+        getGame().startGame();
     }
     }
+
 }
 }

+ 6 - 4
missilewars-plugin/src/main/java/de/butzlabben/missilewars/inventory/OrcInventory.java

@@ -19,20 +19,22 @@
 package de.butzlabben.missilewars.inventory;
 package de.butzlabben.missilewars.inventory;
 
 
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.Logger;
-import java.util.HashMap;
-import java.util.Map.Entry;
-import java.util.Objects;
 import lombok.Getter;
 import lombok.Getter;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 import org.bukkit.event.inventory.InventoryType;
 import org.bukkit.event.inventory.InventoryType;
 import org.bukkit.inventory.Inventory;
 import org.bukkit.inventory.Inventory;
 
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+
 @Getter
 @Getter
 public abstract class OrcInventory {
 public abstract class OrcInventory {
 
 
     protected String title;
     protected String title;
-    protected HashMap<Integer, OrcItem> items = new HashMap<>();
+    protected Map<Integer, OrcItem> items = new HashMap<>();
     private int rows;
     private int rows;
     private InventoryType type;
     private InventoryType type;
     private boolean fill = false;
     private boolean fill = false;

+ 5 - 3
missilewars-plugin/src/main/java/de/butzlabben/missilewars/inventory/OrcListener.java

@@ -19,8 +19,6 @@
 package de.butzlabben.missilewars.inventory;
 package de.butzlabben.missilewars.inventory;
 
 
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
-import java.util.HashMap;
-import java.util.UUID;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 import org.bukkit.event.EventHandler;
 import org.bukkit.event.EventHandler;
@@ -28,6 +26,10 @@ import org.bukkit.event.Listener;
 import org.bukkit.event.inventory.InventoryClickEvent;
 import org.bukkit.event.inventory.InventoryClickEvent;
 import org.bukkit.event.inventory.InventoryCloseEvent;
 import org.bukkit.event.inventory.InventoryCloseEvent;
 
 
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
  * @since 10.06.2018
  * @since 10.06.2018
@@ -36,7 +38,7 @@ public class OrcListener implements Listener {
 
 
     private static OrcListener instance;
     private static OrcListener instance;
 
 
-    private final HashMap<UUID, OrcInventory> invs = new HashMap<>();
+    private final Map<UUID, OrcInventory> invs = new HashMap<>();
 
 
     private OrcListener() {
     private OrcListener() {
         Bukkit.getPluginManager().registerEvents(this, MissileWars.getInstance());
         Bukkit.getPluginManager().registerEvents(this, MissileWars.getInstance());

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

@@ -34,12 +34,7 @@ import org.bukkit.event.EventHandler;
 import org.bukkit.event.Listener;
 import org.bukkit.event.Listener;
 import org.bukkit.event.block.BlockPlaceEvent;
 import org.bukkit.event.block.BlockPlaceEvent;
 import org.bukkit.event.entity.FoodLevelChangeEvent;
 import org.bukkit.event.entity.FoodLevelChangeEvent;
-import org.bukkit.event.player.PlayerDropItemEvent;
-import org.bukkit.event.player.PlayerJoinEvent;
-import org.bukkit.event.player.PlayerMoveEvent;
-import org.bukkit.event.player.PlayerPickupItemEvent;
-import org.bukkit.event.player.PlayerQuitEvent;
-import org.bukkit.event.player.PlayerTeleportEvent;
+import org.bukkit.event.player.*;
 import org.bukkit.event.server.ServerListPingEvent;
 import org.bukkit.event.server.ServerListPingEvent;
 import org.bukkit.scheduler.BukkitRunnable;
 import org.bukkit.scheduler.BukkitRunnable;
 
 
@@ -202,9 +197,13 @@ public class PlayerListener implements Listener {
         Logger.DEBUG.log("Location: " + player.getLocation());
         Logger.DEBUG.log("Location: " + player.getLocation());
         Logger.DEBUG.log("Current game amount: " + GameManager.getInstance().getGameAmount());
         Logger.DEBUG.log("Current game amount: " + GameManager.getInstance().getGameAmount());
         Logger.DEBUG.log("Lobby: " + game.getLobby().getDisplayName());
         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 1: " + game.getTeam1());
         Logger.DEBUG.log("Team 2: " + game.getTeam2());
         Logger.DEBUG.log("Team 2: " + game.getTeam2());
 
 
+        if (game.getArena() != null) {
+            Logger.DEBUG.log("Arena: " + game.getArena().getDisplayName());
+        } else {
+            Logger.DEBUG.log("Arena: not yet selected (Map Voting)");
+        }
     }
     }
 }
 }

+ 4 - 2
missilewars-plugin/src/main/java/de/butzlabben/missilewars/listener/game/LobbyListener.java

@@ -83,8 +83,10 @@ public class LobbyListener extends GameBoundListener {
 
 
         } else if (event.getItem().getType() == Material.NETHER_STAR) {
         } else if (event.getItem().getType() == Material.NETHER_STAR) {
             // vote inventory:
             // vote inventory:
-            VoteInventory inventory = new VoteInventory(getGame().getLobby().getArenas());
-            player.openInventory(inventory.getInventory(player));
+            if (player.hasPermission("mw.vote")) {
+                VoteInventory inventory = new VoteInventory(getGame().getLobby().getArenas());
+                player.openInventory(inventory.getInventory(player));
+            }
         }
         }
     }
     }
 
 

+ 8 - 5
missilewars-plugin/src/main/java/de/butzlabben/missilewars/player/MWPlayer.java

@@ -21,14 +21,15 @@ package de.butzlabben.missilewars.player;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Game;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.Team;
 import de.butzlabben.missilewars.game.equipment.PlayerEquipmentRandomizer;
 import de.butzlabben.missilewars.game.equipment.PlayerEquipmentRandomizer;
-import java.util.UUID;
-import java.util.concurrent.atomic.AtomicLong;
 import lombok.EqualsAndHashCode;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.Setter;
 import org.bukkit.Bukkit;
 import org.bukkit.Bukkit;
 import org.bukkit.entity.Player;
 import org.bukkit.entity.Player;
 
 
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
  * @since 01.01.2018
  * @since 01.01.2018
@@ -41,14 +42,16 @@ public class MWPlayer implements Runnable {
     final long id = NEXT_ID.getAndIncrement();
     final long id = NEXT_ID.getAndIncrement();
     private final UUID uuid;
     private final UUID uuid;
     private final Game game;
     private final Game game;
-    @Setter private Team team;
+    @Setter
+    private Team team;
+    @Setter
     private PlayerEquipmentRandomizer randomGameEquipment;
     private PlayerEquipmentRandomizer randomGameEquipment;
-    @Setter private boolean playerInteractEventCancel = false;
+    @Setter
+    private boolean playerInteractEventCancel = false;
 
 
     public MWPlayer(Player player, Game game) {
     public MWPlayer(Player player, Game game) {
         this.uuid = player.getUniqueId();
         this.uuid = player.getUniqueId();
         this.game = game;
         this.game = game;
-        this.randomGameEquipment = new PlayerEquipmentRandomizer(this, game);
     }
     }
 
 
     public Player getPlayer() {
     public Player getPlayer() {

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

@@ -21,16 +21,18 @@ package de.butzlabben.missilewars.util;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.player.PlayerData;
 import de.butzlabben.missilewars.player.PlayerData;
+import org.bukkit.GameMode;
+import org.bukkit.entity.Player;
+
 import java.io.File;
 import java.io.File;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.UUID;
 import java.util.UUID;
-import org.bukkit.GameMode;
-import org.bukkit.entity.Player;
 
 
 public class PlayerDataProvider {
 public class PlayerDataProvider {
 
 
     private static final PlayerDataProvider instance = new PlayerDataProvider();
     private static final PlayerDataProvider instance = new PlayerDataProvider();
-    private final HashMap<UUID, PlayerData> data = new HashMap<>();
+    private final Map<UUID, PlayerData> data = new HashMap<>();
     private final File playerDataDirectory;
     private final File playerDataDirectory;
 
 
     private PlayerDataProvider() {
     private PlayerDataProvider() {

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

@@ -23,14 +23,10 @@ import de.butzlabben.missilewars.MissileWars;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.Config;
 import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.configuration.arena.Arena;
 import de.butzlabben.missilewars.game.Arenas;
 import de.butzlabben.missilewars.game.Arenas;
-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 org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.io.*;
 import java.net.URL;
 import java.net.URL;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Files;
@@ -39,8 +35,6 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.jar.JarFile;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 import java.util.zip.ZipInputStream;
-import org.bukkit.configuration.file.YamlConfiguration;
-import org.bukkit.plugin.java.JavaPlugin;
 
 
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
@@ -106,7 +100,7 @@ public class SetupUtil {
             shieldFolder.mkdir();
             shieldFolder.mkdir();
         }
         }
 
 
-        for (Arena arena : Arenas.getArenas()) {
+        for (Arena arena : Arenas.getARENAS().values()) {
             File shield = new File(shieldFolder, arena.getShieldConfiguration().getSchematic());
             File shield = new File(shieldFolder, arena.getShieldConfiguration().getSchematic());
             if (!shield.isFile()) {
             if (!shield.isFile()) {
                 String resource = "shield.schematic";
                 String resource = "shield.schematic";

+ 9 - 9
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/geometry/GameArea.java

@@ -158,15 +158,6 @@ public class GameArea {
                 position2.getBlockX(), position2.getBlockY(), position2.getBlockZ());
                 position2.getBlockX(), position2.getBlockY(), position2.getBlockZ());
     }
     }
 
 
-    /**
-     * This method returns the arena length along the X coordinate.
-     *
-     * @return (Integer) the X size
-     */
-    public int getXSize() {
-        return maxX - minX;
-    }
-
     /**
     /**
      * This method defines the horizontal direction / rotation of the
      * This method defines the horizontal direction / rotation of the
      * area based on the alignment of the team spawn points.
      * area based on the alignment of the team spawn points.
@@ -179,6 +170,15 @@ public class GameArea {
         EAST_WEST
         EAST_WEST
     }
     }
 
 
+    /**
+     * This method returns the arena length along the X coordinate.
+     *
+     * @return (Integer) the X size
+     */
+    public int getXSize() {
+        return maxX - minX;
+    }
+
     /**
     /**
      * This method returns the arena length along the Y coordinate.
      * This method returns the arena length along the Y coordinate.
      *
      *

+ 6 - 17
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/stats/GameProfileBuilder.java

@@ -18,32 +18,21 @@
 
 
 package de.butzlabben.missilewars.util.stats;
 package de.butzlabben.missilewars.util.stats;
 
 
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
+import com.google.gson.*;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.GameProfile;
 import com.mojang.authlib.properties.Property;
 import com.mojang.authlib.properties.Property;
 import com.mojang.authlib.properties.PropertyMap;
 import com.mojang.authlib.properties.PropertyMap;
 import com.mojang.util.UUIDTypeAdapter;
 import com.mojang.util.UUIDTypeAdapter;
+import lombok.Getter;
+import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+
 import java.io.BufferedReader;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.InputStreamReader;
 import java.lang.reflect.Type;
 import java.lang.reflect.Type;
 import java.net.URL;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import lombok.Getter;
-import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
+import java.util.*;
 
 
 /**
 /**
  * @author Butzlabben
  * @author Butzlabben
@@ -56,7 +45,7 @@ public class GameProfileBuilder {
             .registerTypeAdapter(GameProfile.class, new GameProfileSerializer())
             .registerTypeAdapter(GameProfile.class, new GameProfileSerializer())
             .registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create();
             .registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer()).create();
     @Getter
     @Getter
-    private static final HashMap<UUID, CachedProfile> cache = new HashMap<>();
+    private static final Map<UUID, CachedProfile> cache = new HashMap<>();
     private static final Object sync = new Object();
     private static final Object sync = new Object();
     private static long cacheTime = -1L;
     private static long cacheTime = -1L;