Forráskód Böngészése

Merge branch 'master' into Improvements/AddMissileItemPlaceholder

RedstoneFuture 2 éve
szülő
commit
12166b0d16

+ 2 - 0
1_12/pom.xml

@@ -25,11 +25,13 @@
         <groupId>de.butzlabben</groupId>
         <version>1.0</version>
     </parent>
+
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>1_12</artifactId>
 
     <dependencies>
+        <!-- WorldEdit API -->
         <dependency>
             <groupId>com.sk89q</groupId>
             <artifactId>worldedit</artifactId>

+ 3 - 0
1_13/pom.xml

@@ -25,12 +25,14 @@
         <groupId>de.butzlabben</groupId>
         <version>1.0</version>
     </parent>
+
     <modelVersion>4.0.0</modelVersion>
 
     <artifactId>1_13</artifactId>
 
     <dependencies>
         <dependency>
+            <!-- WorldEdit (Bukkit) API -->
             <groupId>com.sk89q.worldedit</groupId>
             <artifactId>worldedit-bukkit</artifactId>
             <version>7.0.0-SNAPSHOT</version>
@@ -45,6 +47,7 @@
         </dependency>
 
         <dependency>
+            <!-- WorldEdit (Core) API -->
             <groupId>com.sk89q.worldedit</groupId>
             <artifactId>worldedit-core</artifactId>
             <version>7.0.0-SNAPSHOT</version>

+ 2 - 1
1_13_FAWE/pom.xml

@@ -31,11 +31,12 @@
     <artifactId>1_13_FAWE</artifactId>
 
     <dependencies>
+        <!-- FAWE API -->
         <dependency>
             <groupId>com.sk98q.worldedit</groupId>
             <artifactId>FastAsnycWorldEdit</artifactId>
-            <scope>system</scope>
             <version>1.0</version>
+            <scope>system</scope>
             <systemPath>${pom.basedir}/lib/FastAsyncWorldEdit.jar</systemPath>
         </dependency>
     </dependencies>

+ 11 - 6
missilewars-plugin/pom.xml

@@ -32,6 +32,13 @@
 
     <artifactId>missilewars-plugin</artifactId>
 
+    <repositories>
+        <repository>
+            <id>aikar</id>
+            <url>https://repo.aikar.co/content/groups/aikar/</url>
+        </repository>
+    </repositories>
+
     <dependencies>
         <dependency>
             <groupId>de.butzlabben</groupId>
@@ -66,13 +73,11 @@
             <version>2.11.0</version>
         </dependency>
 
-
-        <!-- https://mvnrepository.com/artifact/com.pro-crafting.mc/commandframework -->
+        <!-- https://github.com/aikar/commands -->
         <dependency>
-            <groupId>com.pro-crafting.mc</groupId>
-            <artifactId>commandframework</artifactId>
-            <version>0.2.1</version>
-            <scope>compile</scope>
+            <groupId>co.aikar</groupId>
+            <artifactId>acf-paper</artifactId>
+            <version>0.5.1-SNAPSHOT</version>
         </dependency>
 
         <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->

+ 8 - 36
missilewars-plugin/src/main/java/de/butzlabben/missilewars/Config.java

@@ -19,6 +19,7 @@
 package de.butzlabben.missilewars;
 
 import de.butzlabben.missilewars.game.GameManager;
+import de.butzlabben.missilewars.util.SetupUtil;
 import org.bukkit.Bukkit;
 import org.bukkit.Location;
 import org.bukkit.Material;
@@ -26,8 +27,7 @@ import org.bukkit.World;
 import org.bukkit.configuration.ConfigurationSection;
 import org.bukkit.configuration.file.YamlConfiguration;
 
-import java.io.*;
-import java.nio.charset.StandardCharsets;
+import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,34 +43,15 @@ public class Config {
     private static final File DIR = MissileWars.getInstance().getDataFolder();
     private static final File FILE = new File(DIR, "config.yml");
     private static YamlConfiguration cfg;
-    private static boolean configNew = false;
+    private static boolean isNewConfig = false;
 
     public static void load() {
 
-        // check if the directory "/MissileWars" exists
-        if (!DIR.exists()) {
-            DIR.mkdirs();
-        }
-
-        // check if the config file exists
-        if (!FILE.exists()) {
-            try {
-                FILE.createNewFile();
-            } catch (IOException e) {
-                Logger.ERROR.log("Could not create config.yml!");
-                e.printStackTrace();
-            }
-            configNew = true;
-        }
+        // check if the directory and the file exists or create it new
+        isNewConfig = SetupUtil.isNewConfig(DIR, FILE);
 
         // try to load the config
-        try {
-            cfg = YamlConfiguration.loadConfiguration(new InputStreamReader(new FileInputStream(FILE), StandardCharsets.UTF_8));
-        } catch (FileNotFoundException e) {
-            Logger.ERROR.log("Couldn't load config.yml");
-            e.printStackTrace();
-            return;
-        }
+        cfg = SetupUtil.getLoadedConfig(FILE);
 
         // copy the config input
         cfg.options().copyDefaults(true);
@@ -79,7 +60,7 @@ public class Config {
         addDefaults();
 
         // re-save the config with only validated options
-        saveConfig();
+        SetupUtil.safeFile(FILE, cfg);
     }
 
     private static void addDefaults() {
@@ -133,7 +114,7 @@ public class Config {
         cfg.addDefault("sidebar.member_list_style", "%team_color%%playername%");
         cfg.addDefault("sidebar.member_list_max", "4");
 
-        if (configNew) {
+        if (isNewConfig) {
             List<String> sidebarList = new ArrayList<>();
 
             sidebarList.add("§7Time left:");
@@ -147,15 +128,6 @@ public class Config {
         }
     }
 
-    private static void saveConfig() {
-        try {
-            cfg.save(FILE);
-        } catch (IOException e) {
-            Logger.ERROR.log("Could not save config.yml!");
-            e.printStackTrace();
-        }
-    }
-
     public static List<String> getScoreboardEntries() {
         return cfg.getStringList("sidebar.entries");
     }

+ 7 - 33
missilewars-plugin/src/main/java/de/butzlabben/missilewars/MessageConfig.java

@@ -18,11 +18,11 @@
 
 package de.butzlabben.missilewars;
 
+import de.butzlabben.missilewars.util.SetupUtil;
 import org.bukkit.ChatColor;
 import org.bukkit.configuration.file.YamlConfiguration;
 
-import java.io.*;
-import java.nio.charset.StandardCharsets;
+import java.io.File;
 
 
 /**
@@ -34,32 +34,15 @@ public class MessageConfig {
     private static final File DIR = MissileWars.getInstance().getDataFolder();
     private static final File FILE = new File(DIR, "messages.yml");
     private static YamlConfiguration cfg;
+    private static boolean isNewConfig = false;
 
     public static void load() {
 
-        // check if the directory "/MissileWars" exists
-        if (!DIR.exists()) {
-            DIR.mkdirs();
-        }
-
-        // check if the config file exists
-        if (!FILE.exists()) {
-            try {
-                FILE.createNewFile();
-            } catch (IOException e) {
-                Logger.ERROR.log("Could not create messages.yml!");
-                e.printStackTrace();
-            }
-        }
+        // check if the directory and the file exists or create it new
+        isNewConfig = SetupUtil.isNewConfig(DIR, FILE);
 
         // try to load the config
-        try {
-            cfg = YamlConfiguration.loadConfiguration(new InputStreamReader(new FileInputStream(FILE), StandardCharsets.UTF_8));
-        } catch (FileNotFoundException e) {
-            Logger.ERROR.log("Couldn't load messages.yml");
-            e.printStackTrace();
-            return;
-        }
+        cfg = SetupUtil.getLoadedConfig(FILE);
 
         // copy the config input
         cfg.options().copyDefaults(true);
@@ -68,7 +51,7 @@ public class MessageConfig {
         addDefaults();
 
         // re-save the config with only validated options
-        saveConfig();
+        SetupUtil.safeFile(FILE, cfg);
     }
 
     private static void addDefaults() {
@@ -141,15 +124,6 @@ public class MessageConfig {
 
     }
 
-    private static void saveConfig() {
-        try {
-            cfg.save(FILE);
-        } catch (IOException e) {
-            Logger.ERROR.log("Could not save messages.yml!");
-            e.printStackTrace();
-        }
-    }
-
     public static String getMessage(String path) {
         return getPrefix() + getNativeMessage(path);
     }

+ 28 - 13
missilewars-plugin/src/main/java/de/butzlabben/missilewars/MissileWars.java

@@ -18,7 +18,7 @@
 
 package de.butzlabben.missilewars;
 
-import com.pro_crafting.mc.commandframework.CommandFramework;
+import co.aikar.commands.PaperCommandManager;
 import de.butzlabben.missilewars.cmd.MWCommands;
 import de.butzlabben.missilewars.cmd.StatsCommands;
 import de.butzlabben.missilewars.cmd.UserCommands;
@@ -54,7 +54,6 @@ public class MissileWars extends JavaPlugin {
 
     private static MissileWars instance;
     public final String version = getDescription().getVersion();
-    private CommandFramework framework;
     private SignRepository signRepository;
 
     private boolean foundFAWE;
@@ -93,17 +92,8 @@ public class MissileWars extends JavaPlugin {
         SignRepository repository = SignRepository.load();
         this.signRepository = repository;
 
-        Bukkit.getPluginManager().registerEvents(new PlayerListener(), this);
-        Bukkit.getPluginManager().registerEvents(new ClickListener(), this);
-        Bukkit.getPluginManager().registerEvents(new ManageListener(), this);
-
-        Logger.BOOT.log("Registering commands");
-        framework = new CommandFramework(this);
-        framework.registerCommands(new MWCommands());
-        framework.registerCommands(new StatsCommands());
-        framework.registerCommands(new UserCommands());
-        // TODO make more admin commands usable with console by adding more optional arguments like game name etc.
-        framework.setInGameOnlyMessage(MessageConfig.getPrefix() + "§cYou are not a player");
+        registerEvents();
+        registerCommands();
 
         Arenas.load();
         SetupUtil.checkShields();
@@ -142,6 +132,31 @@ public class MissileWars extends JavaPlugin {
         ConnectionHolder.close();
     }
 
+    /**
+     * This method registers all events of the missilewars event listener.
+     */
+    private void registerEvents() {
+        Bukkit.getPluginManager().registerEvents(new PlayerListener(), this);
+        Bukkit.getPluginManager().registerEvents(new ClickListener(), this);
+        Bukkit.getPluginManager().registerEvents(new ManageListener(), this);
+    }
+
+    /**
+     * This method loads the command manager and registers the missilewars commands.
+     */
+    private void registerCommands() {
+        Logger.BOOT.log("Registering commands");
+
+        // Using the Paper Command Manager does not mean the plugin requires Paper.
+        // It simply lets it take advantage of Paper specific features if available,
+        // such as Asynchronous Tab Completions.
+        PaperCommandManager manager = new PaperCommandManager(this);
+
+        manager.registerCommand(new MWCommands());
+        manager.registerCommand(new StatsCommands());
+        manager.registerCommand(new UserCommands());
+    }
+
     /**
      * This method checks if FAWE (FastAsyncWorldEdit) is installed.
      *

+ 85 - 63
missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/MWCommands.java

@@ -18,8 +18,8 @@
 
 package de.butzlabben.missilewars.cmd;
 
-import com.pro_crafting.mc.commandframework.Command;
-import com.pro_crafting.mc.commandframework.CommandArgs;
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.*;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.Logger;
 import de.butzlabben.missilewars.MessageConfig;
@@ -42,12 +42,47 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
-public class MWCommands {
+@CommandAlias("mw|missilewars")
+public class MWCommands extends BaseCommand {
 
-    @Command(name = "mw.paste", usage = "/mw paste <missile>", permission = "mw.paste", description = "Pastes a missile", inGameOnly = true)
-    public void pasteCommand(CommandArgs args) {
+    @Default
+    @Description("Shows information about the MissileWars Plugin.")
+    public void mwCommand(CommandSender sender) {
+
+        sender.sendMessage(MessageConfig.getPrefix() + "MissileWars v" + MissileWars.getInstance().version + " by Butzlabben");
+
+        if (sender.hasPermission("mw.quit"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw quit -  Quit a game");
+        if (sender.hasPermission("mw.start"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw start - Starts the game");
+        if (sender.hasPermission("mw.stop"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw stop - Stops the game");
+        if (sender.hasPermission("mw.restart"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw start - Restarts the game");
+        if (sender.hasPermission("mw.appendrestart"))
+            sender.sendMessage(MessageConfig.getPrefix()
+                    + "/mw appendrestart - Appends a restart after the next game ends");
+        if (sender.hasPermission("mw.paste"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw paste - Pastes a missile");
+        if (sender.hasPermission("mw.reload"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw reload - Reloads configurations");
+        if (sender.hasPermission("mw.stats"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats - Shows stats");
+        if (sender.hasPermission("mw.stats.recommendations"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats recommendations - Shows recommendations");
+        if (sender.hasPermission("mw.stats.players"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats players - Shows player list");
+        if (sender.hasPermission("mw.stats.list"))
+            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats list - Lists history of games");
+    }
+
+    @Subcommand("paste")
+    @Description("Pastes a missile.")
+    @Syntax("/mw paste <missile>")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.paste")
+    public void pasteCommand(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -67,10 +102,13 @@ public class MWCommands {
         m.paste(player, mf, game);
     }
 
-    @Command(name = "mw.start", usage = "/mw start", permission = "mw.start", description = "Starts the game", inGameOnly = true)
-    public void startCommand(CommandArgs args) {
+    @Subcommand("start")
+    @Description("Starts the game.")
+    @Syntax("/mw start")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.start")
+    public void startCommand(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -107,10 +145,13 @@ public class MWCommands {
         }
     }
 
-    @Command(name = "mw.stop", usage = "/mw stop", permission = "mw.stop", description = "Stops the game", inGameOnly = true)
-    public void stopCommand(CommandArgs args) {
+    @Subcommand("stop")
+    @Description("Stops the game.")
+    @Syntax("/mw stop")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.stop")
+    public void stopCommand(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -123,11 +164,13 @@ public class MWCommands {
         Bukkit.getScheduler().runTask(MissileWars.getInstance(), game::stopGame);
     }
 
+    @Subcommand("restart")
+    @Description("Restarts the game.")
+    @Syntax("/mw restart")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.restart")
+    public void restartCommand(CommandSender sender, String[] args) {
 
-    @Command(name = "mw.restart", usage = "/mw restart", permission = "mw.restart", description = "Restarts the game", inGameOnly = true)
-    public void restartCommand(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -145,48 +188,23 @@ public class MWCommands {
         });
     }
 
-    @Command(name = "mw.appendrestart", usage = "/mw appendrestart", permission = "mw.appendrestart", description = "Appends a restart after the next game ends")
-    public void appendRestartCommand(CommandArgs args) {
-        GameManager.getInstance().getGames().values().forEach(Game::appendRestart);
-        args.getSender().sendMessage(MessageConfig.getMessage("restart_after_game"));
-    }
-
-    @Command(name = "mw", aliases = "missilewars", usage = "/mw", description = "Shows information about the MissileWars Plugin")
-    public void mwCommand(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
-
-        sender.sendMessage(MessageConfig.getPrefix() + "MissileWars v" + MissileWars.getInstance().version + " by Butzlabben");
+    @Subcommand("appendrestart")
+    @Description("Appends a restart after the next game ends.")
+    @Syntax("/mw appendrestart")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.appendrestart")
+    public void appendRestartCommand(CommandSender sender, String[] args) {
 
-        if (sender.hasPermission("mw.quit"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw quit -  Quit a game");
-        if (sender.hasPermission("mw.start"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw start - Starts the game");
-        if (sender.hasPermission("mw.stop"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw stop - Stops the game");
-        if (sender.hasPermission("mw.restart"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw start - Restarts the game");
-        if (sender.hasPermission("mw.appendrestart"))
-            sender.sendMessage(MessageConfig.getPrefix()
-                    + "/mw appendrestart - Appends a restart after the next game ends");
-        if (sender.hasPermission("mw.paste"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw paste - Pastes a missile");
-        if (sender.hasPermission("mw.reload"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw reload - Reloads configurations");
-        if (sender.hasPermission("mw.stats"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats - Shows stats");
-        if (sender.hasPermission("mw.stats.recommendations"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats recommendations - Shows recommendations");
-        if (sender.hasPermission("mw.stats.players"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats players - Shows player list");
-        if (sender.hasPermission("mw.stats.list"))
-            sender.sendMessage(MessageConfig.getPrefix() + "/mw stats list - Lists history of games");
+        GameManager.getInstance().getGames().values().forEach(Game::appendRestart);
+        sender.sendMessage(MessageConfig.getMessage("restart_after_game"));
     }
 
-    @Command(name = "mw.reload", permission = "mw.reload", usage = "/mw reload")
-    public void onReload(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
+    @Subcommand("reload")
+    @Description("Reload the plugin.")
+    @Syntax("/mw reload")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.reload")
+    public void onReload(CommandSender sender, String[] args) {
 
         Config.load();
         MessageConfig.load();
@@ -194,10 +212,12 @@ public class MWCommands {
         sender.sendMessage(MessageConfig.getPrefix() + "Reloaded configs");
     }
 
-    @Command(name = "mw.debug", permission = "mw.debug", usage = "/mw debug")
-    public void onDebug(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
+    @Subcommand("debug")
+    @Description("Show debug info.")
+    @Syntax("/mw debug")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.debug")
+    public void onDebug(CommandSender sender, String[] args) {
 
         int i = 0;
         Logger.NORMAL.log("Starting to print debug information for MissileWars v" + MissileWars.getInstance().version);
@@ -208,10 +228,12 @@ public class MWCommands {
         sender.sendMessage(MessageConfig.getPrefix() + "Printed debug message into the log file");
     }
 
-    @Command(name = "mw.restartall", permission = "mw.reload", usage = "/mw restartall")
-    public void onRestartAll(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
+    @Subcommand("restartall")
+    @Description("Restart all games.")
+    @Syntax("/mw restartall")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.reload")
+    public void onRestartAll(CommandSender sender, String[] args) {
 
         sender.sendMessage(MessageConfig.getPrefix() + "§cWarning - Restarting all games. This may take a while");
         List<Lobby> arenaPropertiesList = GameManager.getInstance().getGames().values()

+ 65 - 55
missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/StatsCommands.java

@@ -18,8 +18,8 @@
 
 package de.butzlabben.missilewars.cmd;
 
-import com.pro_crafting.mc.commandframework.Command;
-import com.pro_crafting.mc.commandframework.CommandArgs;
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.*;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.MessageConfig;
 import de.butzlabben.missilewars.inventory.CustomInv;
@@ -42,7 +42,9 @@ import java.time.Duration;
 import java.util.*;
 import java.util.stream.Collectors;
 
-public class StatsCommands {
+@CommandAlias("mw|missilewars")
+@Subcommand("stats")
+public class StatsCommands extends BaseCommand {
 
     private final static int MAX_FIGHT_DRAW_PERCENTAGE = 15;
     private final static int MIN_FIGHT_DURATION = 5;
@@ -50,48 +52,12 @@ public class StatsCommands {
     private final SimpleDateFormat format = new SimpleDateFormat("dd.MM.yyyy");
     private final SimpleDateFormat preciseFormat = new SimpleDateFormat("hh:mm dd.MM.yyyy");
 
-    @Command(name = "mw.stats.recommendations", permission = "mw.stats.recommendations", inGameOnly = true, usage = "/mw stats recommendations [from] [arena]")
-    public void onRecommendations(CommandArgs args) {
+    @Default
+    @Description("Shows stats.")
+    @Syntax("/mw stats [from] [arena]")
+    @CommandPermission("mw.stats")
+    public void onStats(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
-        if (!senderIsPlayer(sender)) return;
-        Player player = (Player) sender;
-
-        StatsFetcher fetcher = getFetcher(player, args);
-        if (fetcher == null) return;
-        SavedStats avgStatsWithDraws = fetcher.getAverageSavedStats(false);
-        SavedStats avgStatsWithoutDraws = fetcher.getAverageSavedStats(true);
-        List<String> recommendations = new ArrayList<>();
-        int gameCount = fetcher.getGameCount();
-
-        double avgWins = avgStatsWithoutDraws.getTeamWon();
-        if (Math.abs(avgWins - 1) > MAX_AVIATION_WIN) {
-            recommendations.add("It could be, that your map is biased to one team, as wins are not equally distributed");
-        }
-
-        int draws = fetcher.getDrawFights();
-        if ((((double) draws / (double) gameCount) * 100) > MAX_FIGHT_DRAW_PERCENTAGE) {
-            recommendations.add("Increase the game_length option. More than 15% of your games are draws");
-        }
-
-        Duration duration = Duration.ofMillis(avgStatsWithoutDraws.getTimeElapsed());
-        if (((double) duration.getSeconds() / 60.0) <= MIN_FIGHT_DURATION) {
-            recommendations.add("Remove some overpowered features. The average game length at won games is under 5 minutes");
-        }
-        // TODO implement more features
-
-        if (recommendations.size() == 0) {
-            player.sendMessage(MessageConfig.getPrefix() + "§aThere are currently no recommendations, everything seems fine :)");
-        } else {
-            player.sendMessage(MessageConfig.getPrefix() + "§7=====[ §eMissileWars recommendations §7]=====");
-            recommendations.forEach(str -> player.sendMessage(MessageConfig.getPrefix() + str));
-        }
-    }
-
-    @Command(name = "mw.stats", permission = "mw.stats", inGameOnly = true, usage = "/mw stats [from] [arena]")
-    public void onStats(CommandArgs args) {
-
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -139,10 +105,52 @@ public class StatsCommands {
         player.openInventory(inv.getInventory(player));
     }
 
-    @Command(name = "mw.stats.players", permission = "mw.stats.players", inGameOnly = true, usage = "/mw stats players [from] [arena]")
-    public void onPlayers(CommandArgs args) {
+    @Subcommand("recommendations")
+    @Description("Shows recommendations.")
+    @Syntax("/mw stats recommendations [from] [arena]")
+    @CommandPermission("mw.stats.recommendations")
+    public void onRecommendations(CommandSender sender, String[] args) {
+
+        if (!senderIsPlayer(sender)) return;
+        Player player = (Player) sender;
+
+        StatsFetcher fetcher = getFetcher(player, args);
+        if (fetcher == null) return;
+        SavedStats avgStatsWithDraws = fetcher.getAverageSavedStats(false);
+        SavedStats avgStatsWithoutDraws = fetcher.getAverageSavedStats(true);
+        List<String> recommendations = new ArrayList<>();
+        int gameCount = fetcher.getGameCount();
+
+        double avgWins = avgStatsWithoutDraws.getTeamWon();
+        if (Math.abs(avgWins - 1) > MAX_AVIATION_WIN) {
+            recommendations.add("It could be, that your map is biased to one team, as wins are not equally distributed");
+        }
+
+        int draws = fetcher.getDrawFights();
+        if ((((double) draws / (double) gameCount) * 100) > MAX_FIGHT_DRAW_PERCENTAGE) {
+            recommendations.add("Increase the game_length option. More than 15% of your games are draws");
+        }
+
+        Duration duration = Duration.ofMillis(avgStatsWithoutDraws.getTimeElapsed());
+        if (((double) duration.getSeconds() / 60.0) <= MIN_FIGHT_DURATION) {
+            recommendations.add("Remove some overpowered features. The average game length at won games is under 5 minutes");
+        }
+        // TODO implement more features
+
+        if (recommendations.size() == 0) {
+            player.sendMessage(MessageConfig.getPrefix() + "§aThere are currently no recommendations, everything seems fine :)");
+        } else {
+            player.sendMessage(MessageConfig.getPrefix() + "§7=====[ §eMissileWars recommendations §7]=====");
+            recommendations.forEach(str -> player.sendMessage(MessageConfig.getPrefix() + str));
+        }
+    }
+
+    @Subcommand("players")
+    @Description("Shows player list.")
+    @Syntax("/mw stats players [from] [arena]")
+    @CommandPermission("mw.stats.players")
+    public void onPlayers(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -155,10 +163,12 @@ public class StatsCommands {
         playerGuiFactory.openWhenReady(player);
     }
 
-    @Command(name = "mw.stats.list", permission = "mw.stats.list", inGameOnly = true, usage = "/mw stats list [from] [arena]")
-    public void onList(CommandArgs args) {
+    @Subcommand("list")
+    @Description("Lists history of games.")
+    @Syntax("/mw stats list [from] [arena]")
+    @CommandPermission("mw.stats.list")
+    public void onList(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -176,22 +186,22 @@ public class StatsCommands {
         creator.show(player);
     }
 
-    private StatsFetcher getFetcher(Player player, CommandArgs args) {
+    private StatsFetcher getFetcher(Player player, String[] args) {
         if (!Config.isFightStatsEnabled()) {
             player.sendMessage(MessageConfig.getPrefix() + "§cFightStats are not enabled!");
             return null;
         }
         Date from = new Date(0);
         String arena = "";
-        if (args.length() > 0) {
+        if (args.length > 0) {
             try {
-                from = format.parse(args.getArgs(0));
+                from = format.parse(args[0]);
             } catch (ParseException e) {
                 player.sendMessage(MessageConfig.getPrefix() + "§cPlease use the date format dd.MM.yyyy");
                 return null;
             }
-            if (args.length() > 1) {
-                arena = args.getArgs(1);
+            if (args.length > 1) {
+                arena = args[1];
             }
         }
 

+ 26 - 18
missilewars-plugin/src/main/java/de/butzlabben/missilewars/cmd/UserCommands.java

@@ -18,8 +18,8 @@
 
 package de.butzlabben.missilewars.cmd;
 
-import com.pro_crafting.mc.commandframework.Command;
-import com.pro_crafting.mc.commandframework.CommandArgs;
+import co.aikar.commands.BaseCommand;
+import co.aikar.commands.annotation.*;
 import de.butzlabben.missilewars.Config;
 import de.butzlabben.missilewars.MessageConfig;
 import de.butzlabben.missilewars.game.Arenas;
@@ -36,12 +36,16 @@ import org.bukkit.entity.Player;
 
 import java.util.Optional;
 
-public class UserCommands {
+@CommandAlias("mw|missilewars")
+public class UserCommands extends BaseCommand {
 
-    @Command(name = "mw.change", usage = "/mw change <1|2>", permission = "mw.change", description = "Changes your team", inGameOnly = true)
-    public void changeCommand(CommandArgs args) {
+    @Subcommand("change")
+    @Description("Changes your team.")
+    @Syntax("/mw change <1|2>")
+    @CommandCompletion("@range:1-2")
+    @CommandPermission("mw.change")
+    public void changeCommand(CommandSender sender, String[] args) {
 
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -56,18 +60,18 @@ public class UserCommands {
             return;
         }
 
-        if (args.length() != 1) {
+        if (args.length != 1) {
             player.sendMessage(MessageConfig.getPrefix() + "§c/mw vote <arena>");
             return;
         }
 
-        if (args.length() != 1) {
+        if (args.length != 1) {
             player.sendMessage(MessageConfig.getPrefix() + "§c/mw change <1|2>");
             return;
         }
         try {
             MWPlayer mwPlayer = game.getPlayer(player);
-            int teamNumber = Integer.parseInt(args.getArgs(0));
+            int teamNumber = Integer.parseInt(args[0]);
             Team to = teamNumber == 1 ? game.getTeam1() : game.getTeam2();
             int otherCount = to.getEnemyTeam().getMembers().size() - 1;
             int toCount = to.getMembers().size() + 1;
@@ -86,12 +90,13 @@ public class UserCommands {
         }
     }
 
-
-    @Command(name = "mw.vote", usage = "/mw vote <arena>", description = "Stops the game", inGameOnly = true)
-    public void voteCommand(CommandArgs args) {
+    @Subcommand("vote")
+    @Description("Stops the game.")
+    @Syntax("/mw vote <arena>")
+    @CommandPermission("mw.vote")
+    public void voteCommand(CommandSender sender, String[] args) {
 
         // TODO more messageconfig
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 
@@ -116,12 +121,12 @@ public class UserCommands {
             return;
         }
 
-        if (args.length() != 1) {
+        if (args.length != 1) {
             player.sendMessage(MessageConfig.getPrefix() + "§c/mw vote <arena>");
             return;
         }
 
-        String arenaName = args.getArgs(0);
+        String arenaName = args[0];
         Optional<Arena> arena = Arenas.getFromName(arenaName);
         if (!game.getVotes().containsKey(arenaName) || !arena.isPresent()) {
             player.sendMessage(MessageConfig.getPrefix() + "§cNo map with this title was found");
@@ -132,11 +137,14 @@ public class UserCommands {
         player.sendMessage(MessageConfig.getMessage("vote.success").replace("%map%", arena.get().getDisplayName()));
     }
 
-    @Command(name = "mw.quit", inGameOnly = true, usage = "/mw quit", permission = "mw.quit", description = "Quit a game")
-    public void onQuit(CommandArgs args) {
+    @Subcommand("quit|leave")
+    @Description("Quit a game.")
+    @Syntax("/mw quit")
+    @CommandCompletion("@nothing")
+    @CommandPermission("mw.quit")
+    public void onQuit(CommandSender sender, String[] args) {
 
         // TODO message config
-        CommandSender sender = args.getSender();
         if (!senderIsPlayer(sender)) return;
         Player player = (Player) sender;
 

+ 49 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/SetupUtil.java

@@ -23,10 +23,12 @@ 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.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.util.Enumeration;
 import java.util.jar.JarEntry;
@@ -45,6 +47,53 @@ public class SetupUtil {
     private SetupUtil() {
     }
 
+    public static boolean isNewConfig(File dir, File file) {
+        String fileName = file.getName();
+
+        // check if the directory exists
+        if (!dir.exists()) {
+            dir.mkdirs();
+        }
+
+        // check if the config file exists
+        if (!file.exists()) {
+            try {
+                file.createNewFile();
+            } catch (IOException e) {
+                Logger.ERROR.log("Could not create " + fileName + "!");
+                e.printStackTrace();
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    public static YamlConfiguration getLoadedConfig(File file) {
+        String fileName = file.getName();
+        YamlConfiguration cfg;
+
+        try {
+            cfg = YamlConfiguration.loadConfiguration(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8));
+        } catch (FileNotFoundException e) {
+            Logger.ERROR.log("Couldn't load " + fileName + "!");
+            e.printStackTrace();
+            return null;
+        }
+        return cfg;
+    }
+
+    public static void safeFile(File file, YamlConfiguration cfg) {
+        String fileName = file.getName();
+
+        try {
+            cfg.save(file);
+        } catch (IOException e) {
+            Logger.ERROR.log("Could not save " + fileName + "!");
+            e.printStackTrace();
+        }
+    }
+
     public static void checkShields() {
         for (Arena arena : Arenas.getArenas()) {
             File file = new File(MissileWars.getInstance().getDataFolder(), arena.getShieldConfiguration().getSchematic());

+ 81 - 97
missilewars-plugin/src/main/java/de/butzlabben/missilewars/util/version/VersionUtil.java

@@ -43,14 +43,51 @@ public class VersionUtil {
     private VersionUtil() {
     }
 
-    public static void setUnbreakable(ItemStack is) {
-        ItemMeta im = is.getItemMeta();
-        if (getVersion() >= 11) {
-            im.setUnbreakable(true);
+    public static int getVersion() {
+
+        if (version == 0) {
+            // Detect version
+            String v = Bukkit.getVersion();
+            if (v.contains("1.20")) version = 20;
+            else if (v.contains("1.19")) version = 19;
+            else if (v.contains("1.18")) version = 18;
+            else if (v.contains("1.17")) version = 17;
+            else if (v.contains("1.16")) version = 16;
+            else if (v.contains("1.15")) version = 15;
+            else if (v.contains("1.14")) version = 14;
+            else if (v.contains("1.13")) version = 13;
+            else if (v.contains("1.12")) version = 12;
+            else if (v.contains("1.11")) version = 11;
+            else if (v.contains("1.10")) version = 10;
+            else if (v.contains("1.9")) version = 9;
+            else if (v.contains("1.8")) version = 8;
+            else if (v.contains("1.7")) version = 7;
+            else if (v.contains("1.6")) version = 6;
+            else if (v.contains("1.5")) version = 5;
+            else if (v.contains("1.4")) version = 4;
+            else if (v.contains("1.3")) version = 3;
+        }
+
+        if (version == 0) {
+            Logger.WARN.log("Unknown version: " + Bukkit.getVersion());
+            Logger.WARN.log("Choosing version 1.12.2");
+            version = 12;
+        }
+        return version;
+    }
+
+    public static void sendTitle(Player p, String title, String subtitle) {
+        if (getVersion() > 8) {
+            try {
+                Method m = p.getClass().getMethod("sendTitle", String.class, String.class);
+                m.invoke(p, title, subtitle);
+            } catch (Exception e) {
+                Logger.ERROR.log("Couldn't send title to player");
+                e.printStackTrace();
+            }
         } else {
-//            im.spigot().setUnbreakable(true);
+            p.sendMessage(MessageConfig.getPrefix() + title + " " + subtitle);
         }
-        is.setItemMeta(im);
     }
 
     public static void playFireball(Player p, Location loc) {
@@ -88,22 +125,14 @@ public class VersionUtil {
             p.playSound(p.getLocation(), sound("ENTITY_WITHER_DEATH"), 100, 0);
     }
 
-    public static void restart() {
-        Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
-    }
-
-    public static void sendTitle(Player p, String title, String subtitle) {
-        if (getVersion() > 8) {
-            try {
-                Method m = p.getClass().getMethod("sendTitle", String.class, String.class);
-                m.invoke(p, title, subtitle);
-            } catch (Exception e) {
-                Logger.ERROR.log("Couldn't send title to player");
-                e.printStackTrace();
-            }
-        } else {
-            p.sendMessage(MessageConfig.getPrefix() + title + " " + subtitle);
+    private static Sound sound(String s) {
+        Sound sound = null;
+        try {
+            sound = Sound.valueOf(s);
+        } catch (Exception e) {
+            Logger.ERROR.log("Couldn't find sound " + s);
         }
+        return sound;
     }
 
     public static Material getFireball() {
@@ -133,6 +162,17 @@ public class VersionUtil {
         }
     }
 
+    public static boolean isMonsterEgg(Material material) {
+        if (material == null)
+            return false;
+        String name = material.name();
+        if (name.equals("EGG"))
+            return false;
+        if (name.contains("SPAWN_EGG"))
+            return true;
+        return name.equals("MONSTER_EGG");
+    }
+
     public static Material getPortal() {
         if (getVersion() < 13)
             return Material.valueOf("PORTAL");
@@ -147,82 +187,6 @@ public class VersionUtil {
             return Material.valueOf("DOUBLE_PLANT");
     }
 
-    public static int getVersion() {
-        if (version == 0) {
-            // Detect version
-            String v = Bukkit.getVersion();
-            if (v.contains("1.20"))
-                version = 20;
-            else if (v.contains("1.19"))
-                version = 19;
-            else if (v.contains("1.18"))
-                version = 18;
-            else if (v.contains("1.17"))
-                version = 17;
-            else if (v.contains("1.16"))
-                version = 16;
-            else if (v.contains("1.15"))
-                version = 15;
-            else if (v.contains("1.14"))
-                version = 14;
-            else if (v.contains("1.13"))
-                version = 13;
-            else if (v.contains("1.12"))
-                version = 12;
-            else if (v.contains("1.11"))
-                version = 11;
-            else if (v.contains("1.10"))
-                version = 10;
-            else if (v.contains("1.9"))
-                version = 9;
-            else if (v.contains("1.8"))
-                version = 8;
-            else if (v.contains("1.7"))
-                version = 7;
-            else if (v.contains("1.6"))
-                version = 6;
-            else if (v.contains("1.5"))
-                version = 5;
-            else if (v.contains("1.4"))
-                version = 4;
-            else if (v.contains("1.3"))
-                version = 3;
-        }
-        if (version == 0) {
-            Logger.WARN.log("Unknown version: " + Bukkit.getVersion());
-            Logger.WARN.log("Choosing version 1.12.2");
-            version = 12;
-        }
-        return version;
-    }
-
-    private static Sound sound(String s) {
-        Sound sound = null;
-        try {
-            sound = Sound.valueOf(s);
-        } catch (Exception e) {
-            Logger.ERROR.log("Couldn't find sound " + s);
-        }
-        return sound;
-    }
-
-    public static boolean isStainedGlassPane(Material material) {
-        if (material == null)
-            return false;
-        return material.name().contains("STAINED_GLASS_PANE");
-    }
-
-    public static boolean isMonsterEgg(Material material) {
-        if (material == null)
-            return false;
-        String name = material.name();
-        if (name.equals("EGG"))
-            return false;
-        if (name.contains("SPAWN_EGG"))
-            return true;
-        return name.equals("MONSTER_EGG");
-    }
-
     @SuppressWarnings("deprecation")
     public static ItemStack getGlassPlane(Team team) {
         String colorCode = team.getColorCode();
@@ -239,6 +203,12 @@ public class VersionUtil {
         return is;
     }
 
+    public static boolean isStainedGlassPane(Material material) {
+        if (material == null)
+            return false;
+        return material.name().contains("STAINED_GLASS_PANE");
+    }
+
     @SuppressWarnings("deprecation")
     public static ItemStack getGlassPlane(String colorCode) {
         ItemStack is;
@@ -261,4 +231,18 @@ public class VersionUtil {
     public static boolean isWallSignMaterial(Material material) {
         return material.name().contains("_SIGN");
     }
+
+    public static void setUnbreakable(ItemStack is) {
+        ItemMeta im = is.getItemMeta();
+        if (getVersion() >= 11) {
+            im.setUnbreakable(true);
+        } else {
+//            im.spigot().setUnbreakable(true);
+        }
+        is.setItemMeta(im);
+    }
+
+    public static void restart() {
+        Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "restart");
+    }
 }

+ 1 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/Arena.java

@@ -43,6 +43,7 @@ public class Arena {
     @SerializedName("game_spawn") private GameSpawnConfiguration spawn = new GameSpawnConfiguration();
     @SerializedName("game_respawn") private GameRespawnConfiguration respawn = new GameRespawnConfiguration();
     @SerializedName("do_tile_drops") private boolean doTileDrops = false;
+    @SerializedName("keep_inventory") private boolean keepInventory = false;
     @SerializedName("max_height") private int maxHeight = 170;
     @SerializedName("death_height") private int deathHeight = 65;
     @SerializedName("max_spectators") private int maxSpectators = -1;

+ 1 - 0
missilewars-plugin/src/main/java/de/butzlabben/missilewars/wrapper/abstracts/GameWorld.java

@@ -135,6 +135,7 @@ public class GameWorld {
             World world = Bukkit.createWorld(new WorldCreator(worldName));
             Bukkit.getWorlds().add(world);
             world.setGameRuleValue("doTileDrops", String.valueOf(game.getArena().isDoTileDrops()));
+            world.setGameRuleValue("keepInventory", String.valueOf(game.getArena().isKeepInventory()));
         }
     }