├── .gitignore ├── .idea ├── .gitignore ├── artifacts │ └── CubicCountdown.xml ├── compiler.xml ├── discord.xml ├── encodings.xml ├── jarRepositories.xml ├── libraries │ └── PlaceholderAPI_2_11_3.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── vcs.xml ├── .m2 └── settings.xml ├── CubicCountdown.iml ├── README.md ├── pom.xml └── src └── main ├── java └── de │ └── timecoding │ └── cc │ ├── CubicCountdown.java │ ├── api │ ├── AutoUpdater.java │ ├── CubicExpansion.java │ └── Metrics.java │ ├── command │ ├── CubicCommand.java │ ├── completer │ │ └── CubicCompleter.java │ └── setup │ │ └── CubicSetup.java │ ├── event │ ├── CubeCountdownCancelEvent.java │ ├── CubeCountdownEndEvent.java │ └── CubeCountdownStartEvent.java │ ├── file │ ├── ConfigHandler.java │ └── DataHandler.java │ ├── listener │ └── CubicListener.java │ └── util │ ├── CountdownModule.java │ ├── CubicAPI.java │ ├── CubicSettings.java │ └── type │ ├── Cube.java │ └── CubicStateType.java └── resources ├── config.yml ├── datas.yml └── plugin.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Project exclude paths 2 | /target/ -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/artifacts/CubicCountdown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | $USER_HOME$/Desktop/Server/plugins 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/discord.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | -------------------------------------------------------------------------------- /.idea/libraries/PlaceholderAPI_2_11_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.m2/settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ossrh 5 | TimeCode 6 | #KlavierFreak2007 7 | 8 | 9 | -------------------------------------------------------------------------------- /CubicCountdown.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | SPIGOT 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **What's this?**
2 | A simple and easy to use plugin with which it is possible to configure cubes, which in turn can start a countdown after 3 | being filled with blocks (as well as other actions). The countdown as well as the actions can be fully customized and it 4 | even includes an API to customize the plugin perfectly for yourself (Java knowledge required) 5 | 6 | **Compatible Software:** TikFinity
7 | **Compatible Plugins:** DelayedTNT, PlaceholderAPI 8 | 9 | **Installation:**
10 | **1.** Download the newest version from Releases
11 | **2.** Drag and Drop the newest release from the Downloads to the plugins folder of your Minecraft-Server
12 | **3.** Start/Restart your server
13 | **4.** You're ready to go!
14 | 15 | **How to use the plugin?:**
16 | Setup is very easy! Once the plugin is installed, run the **/cc setup** command and follow the plugin's instructions 17 | 18 | **Commands:** 19 |
/cubiccountdown **setup** - Starts the setup process in which 5 simple steps must be followed to add a cube (MAIN 20 | COMMAND) 21 |
/cubiccountdown **fill MAPNAME BLOCKTYPE1,BLOCKTYPE2,... AMOUNT1,AMOUNT2,...** - Fills the cube with an specific 22 | amount of specific blocks 23 |
/cubiccountdown **clear MAPNAME** - Clears the whole area inside the provided cube 24 |
/cubiccountdown **cancel MAPNAME** - Cancels a running countdown of a map 25 |
/cubiccountdown **delete MAPNAME** - Deletes an existing cube (which was created before with /cc setup) 26 |
/cubiccountdown **reload** - Reloads the Config and data files 27 |
/cubiccountdown **cubes** - Shows you all the cubes which you already created 28 |
/cubiccountdown **help** - Opens the help menu 29 |
/cubiccountdown **simulate win/lose/help MAPNAME** - Simulates a win/lose/help 30 |
/cubiccountdown **setEntry CONFIGKEY CONFIGVALUE** - With this command you are able to edit config entries easily 31 | without opening a file so it's easier for me to help people out on my discord. **Important:** Don't use this command if 32 | you don't know what you're doing 33 | 34 | **PlaceholderAPI Support:** 35 |
You want to use the CubicCountdown statistics (like the wins, loses or games played) in another plugin (like a 36 | scoreboard plugin)? Now it is possible! If you're using a plugin which supports the PlaceholderAPI (the PlaceholderAPI 37 | plugin needs to be installed too), you're now able to use these placeholders in it: 38 |
39 |
*Show the number of total wins on a specific cube/map:* **%cc_total_win_counter_MAPNAME%** 40 |
*Show the number of session wins on a specific cube/map:* **%cc_session_win_counter_MAPNAME%** 41 |
*Show the number of total loses on a specific cube/map:* **%cc_total_lose_counter_MAPNAME%** 42 |
*Show the number of session loses on a specific cube/map:* **%cc_session_lose_counter_MAPNAME%** 43 |
*Show the number of total games played on a specific cube/map:* **%cc_total_games_played_MAPNAME%** 44 |
*Show the number of session games played on a specific cube/map:* **%cc_session_games_played_MAPNAME%** 45 |
*Show the number of total helps on a specific cube/map:* **%cc_total_help_counter_MAPNAME%** 46 |
*Show the number of session helps on a specific cube/map:* **%cc_session_help_counter_MAPNAME%** 47 |
*Show the current cube height:* **%cc_current_cube_height_MAPNAME%** 48 |
*Show the cube height:* **%cc_cube_height_MAPNAME%** 49 |
50 |
**By the way:** If you're using version 1.2.0 or higher, you're also able to use placeholders from other plugins in 51 | the titles of the CubicCountdown plugin 52 | 53 | **QAndA:** 54 |
**Q:** *Help! The plugin says that I do not have the permission to use the plugin commands!* 55 |
**A:** To solve this issue just open your server console and type the following command into the console: **op 56 | yourminecraftusername** 57 |
**Q:** *What is the first and second edge?* 58 |
**A:** This means the bottom corner and the diagonal top corner. The order doesn't matter 59 |
**Q:** *How can I change the title formatting, the sound effects,... of the plugin?* 60 |
**A:** This specific settings cannot be changed ingame. To change it, open your minecraft server folder, open the 61 | plugins and after that the CubicCountdown folder. If you open the config.yml you'll see many settings. Change it to your 62 | preferences, save the file, go back ingame and type **/cc reload** into the chat. 63 | 64 | **API:** 65 |
Currently there's no maven or gradle repository out yet. To get access to the api you need to add the JAR file 66 | from the releases tab to the build path of your project (in your IDEA). After that you're able to proceed: 67 |
**COMING SOON** 68 | 69 | **Tutorials:** 70 |
English Tutorial by TikTokLive with Harry: https://www.youtube.com/watch?v=7I4ENO1Km4Q 71 |
Spanish Tutorial by thanitoASMR: https://www.youtube.com/watch?v=CkP_1YHGhW8 72 | 73 | **Support:**
74 | You got any wishes or found any bug? Feel free to join my discord or open an issue on this github 75 | page: https://discord.tikmc.de/ 76 | 77 | **License:**
78 | The source code as well as the JAR file may be used and modified for commercial as well as private 79 | purposes
80 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | de.timecoding 8 | CubicCountdown 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | CubicCountdown 13 | 14 | 15 | 1.8 16 | UTF-8 17 | 18 | 19 | 20 | 21 | 22 | org.apache.maven.plugins 23 | maven-compiler-plugin 24 | 3.8.1 25 | 26 | ${java.version} 27 | ${java.version} 28 | 29 | 30 | 31 | org.apache.maven.plugins 32 | maven-shade-plugin 33 | 3.2.4 34 | 35 | 36 | package 37 | 38 | shade 39 | 40 | 41 | false 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | src/main/resources 50 | true 51 | 52 | 53 | 54 | 55 | 56 | 57 | spigotmc-repo 58 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 59 | 60 | 61 | sonatype 62 | https://oss.sonatype.org/content/groups/public/ 63 | 64 | 65 | placeholderapi 66 | https://repo.extendedclip.com/content/repositories/placeholderapi/ 67 | 68 | 69 | 70 | 71 | 72 | org.spigotmc 73 | spigot-api 74 | 1.19.4-R0.1-SNAPSHOT 75 | provided 76 | 77 | 78 | me.clip 79 | placeholderapi 80 | 2.11.3 81 | provided 82 | 83 | 84 | org.apache.httpcomponents 85 | httpclient 86 | 4.5.14 87 | 88 | 89 | org.json 90 | json 91 | 20231013 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/CubicCountdown.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc; 2 | 3 | import de.timecoding.cc.api.AutoUpdater; 4 | import de.timecoding.cc.api.CubicExpansion; 5 | import de.timecoding.cc.api.Metrics; 6 | import de.timecoding.cc.command.CubicCommand; 7 | import de.timecoding.cc.command.completer.CubicCompleter; 8 | import de.timecoding.cc.command.setup.CubicSetup; 9 | import de.timecoding.cc.file.ConfigHandler; 10 | import de.timecoding.cc.file.DataHandler; 11 | import de.timecoding.cc.listener.CubicListener; 12 | import de.timecoding.cc.util.CountdownModule; 13 | import de.timecoding.cc.util.CubicAPI; 14 | import org.bukkit.Bukkit; 15 | import org.bukkit.command.PluginCommand; 16 | import org.bukkit.entity.Player; 17 | import org.bukkit.plugin.java.JavaPlugin; 18 | 19 | import java.util.HashMap; 20 | import java.util.List; 21 | 22 | public class CubicCountdown extends JavaPlugin { 23 | 24 | private ConfigHandler configHandler; 25 | private DataHandler dataHandler; 26 | private CubicAPI cubicAPI; 27 | 28 | //FOR API USAGE ONLY 29 | private CubicCountdown plugin; 30 | private CubicListener cubicListener; 31 | 32 | private AutoUpdater autoUpdater; 33 | private Metrics metrics = null; 34 | 35 | public void onEnable() { 36 | this.plugin = this; 37 | this.configHandler = new ConfigHandler(this); 38 | this.dataHandler = new DataHandler(this); 39 | this.cubicListener = new CubicListener(this); 40 | this.autoUpdater = new AutoUpdater(this, plugin.getDescription().getVersion()); 41 | this.getServer().getPluginManager().registerEvents(this.cubicListener, this); 42 | cubicAPI = new CubicAPI(this); 43 | PluginCommand pluginCommand = this.getCommand("cubiccountdown"); 44 | pluginCommand.setExecutor(new CubicCommand(this)); 45 | pluginCommand.setTabCompleter(new CubicCompleter(this)); 46 | Bukkit.getConsoleSender().sendMessage("§cCubicCountdown §7(v" + this.getDescription().getVersion() + ") §aby §eTimeCode §awas enabled!"); 47 | Bukkit.getConsoleSender().sendMessage("§cTHIS IS A BETA VERSION OF THE PLUGIN! PLEASE REPORT ALL ISSUES OR WISHES TO OUR DISCORD: https://discord.tikmc.de/"); 48 | if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) { 49 | Bukkit.getConsoleSender().sendMessage("§aSuccessfully registered the CubicCountdown §ePlaceholderAPI Expansion§a!"); 50 | new CubicExpansion(this).register(); 51 | } 52 | if (!configHandler.keyExists("bStats") || configHandler.getBoolean("bStats")) { 53 | this.metrics = new Metrics(this, 19676); 54 | } 55 | cubicAPI.startActionbar(); 56 | for (String key : plugin.getDataHandler().getConfig().getKeys(true)) { 57 | if (key.endsWith("Wins") || key.endsWith("Loses")) { 58 | plugin.getDataHandler().getConfig().set(key, null); 59 | Bukkit.getConsoleSender().sendMessage("§aSuccessfully deleted the §ewin and/or lose §acounter of the map §e" + key.split("\\.")[1]); 60 | plugin.getDataHandler().save(); 61 | } 62 | } 63 | } 64 | 65 | 66 | public ConfigHandler getConfigHandler() { 67 | return configHandler; 68 | } 69 | 70 | public CubicListener getCubicListener() { 71 | return cubicListener; 72 | } 73 | 74 | public List getCountdownList() { 75 | return getCubicAPI().getCountdownList(); 76 | } 77 | 78 | public HashMap getSetupList() { 79 | return getCubicAPI().getSetupList(); 80 | } 81 | 82 | public CubicCountdown getInstance() { 83 | return plugin; 84 | } 85 | 86 | public Metrics getMetrics() { 87 | return metrics; 88 | } 89 | 90 | public DataHandler getDataHandler() { 91 | return dataHandler; 92 | } 93 | 94 | public CubicAPI getCubicAPI() { 95 | return cubicAPI; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/api/AutoUpdater.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.api; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import org.apache.http.HttpResponse; 5 | import org.apache.http.client.methods.HttpGet; 6 | import org.apache.http.impl.client.CloseableHttpClient; 7 | import org.apache.http.impl.client.HttpClientBuilder; 8 | import org.apache.http.util.EntityUtils; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.ChatColor; 11 | import org.json.JSONObject; 12 | 13 | import java.io.*; 14 | import java.net.URL; 15 | 16 | public class AutoUpdater { 17 | 18 | 19 | private final CubicCountdown plugin; 20 | private final String downloadBase = "https://github.com/TimeCodings/CubicCountdown/releases/download/"; 21 | private final String pluginVersion; 22 | private final String newestPluginVersion; 23 | private final boolean sent = false; 24 | private boolean autoUpdaterEnabled = true; 25 | 26 | public AutoUpdater(CubicCountdown plugin, String newestPluginVersion) { 27 | this.plugin = plugin; 28 | this.pluginVersion = plugin.getDescription().getVersion(); 29 | if (this.plugin.getConfigHandler().keyExists("AutoUpdater")) { 30 | this.autoUpdaterEnabled = this.plugin.getConfigHandler().getBoolean("AutoUpdater"); 31 | } 32 | this.newestPluginVersion = newestPluginVersion; 33 | checkForPluginUpdate(); 34 | } 35 | 36 | public String getNewestPluginVersion() { 37 | String url = "https://api.github.com/repos/TimeCodings/CubicCountdown/releases/latest"; 38 | try { 39 | CloseableHttpClient httpClient = HttpClientBuilder.create().build(); 40 | HttpGet request = new HttpGet(url); 41 | request.addHeader("content-type", "application/vnd.github.v3+json"); 42 | HttpResponse result = httpClient.execute(request); 43 | JSONObject json = new JSONObject(EntityUtils.toString(result.getEntity(), "UTF-8")); 44 | if (json != null && json.has("tag_name")) { 45 | return json.get("tag_name").toString(); 46 | } else { 47 | Bukkit.getConsoleSender().sendMessage("§cCould not fetch the newest §eCubicCountdown §crelease! You may be offline!"); 48 | } 49 | } catch (IOException ex) { 50 | Bukkit.getConsoleSender().sendMessage("§cCould not fetch the newest §eCubicCountdown §crelease! You may be offline!"); 51 | } 52 | return pluginVersion; 53 | } 54 | 55 | public boolean pluginUpdateAvailable() { 56 | return !getNewestPluginVersion().replace("v", "").equalsIgnoreCase(pluginVersion); 57 | } 58 | 59 | public void checkForPluginUpdate() { 60 | Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW + "Checking for updates..."); 61 | if (pluginUpdateAvailable()) { 62 | if (autoUpdaterEnabled) { 63 | Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Update found! (" + getNewestPluginVersion() + ") Trying to download the newest update..."); 64 | //Trying to download update 65 | Bukkit.getScheduler().runTaskAsynchronously(this.plugin, new Runnable() { 66 | @Override 67 | public void run() { 68 | downloadUpdate(); 69 | } 70 | }); 71 | } else { 72 | Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Update found (" + getNewestPluginVersion() + ")! You can download it here: " + ChatColor.YELLOW + this.downloadBase + "" + getNewestPluginVersion() + "/CubicCountdown.jar"); 73 | } 74 | } else { 75 | Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "No update found! You're running the latest version (v" + pluginVersion + ")"); 76 | } 77 | } 78 | 79 | private boolean downloadUpdate() { 80 | try { 81 | URL download = new URL(this.downloadBase + getNewestPluginVersion() + "/CubicCountdown.jar?timestamp=" + System.currentTimeMillis()); 82 | BufferedInputStream in = null; 83 | FileOutputStream fout = null; 84 | BufferedOutputStream bout = null; 85 | try { 86 | Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW + "Trying to download the newest CubicCountdown update..."); 87 | in = new BufferedInputStream(download.openStream()); 88 | fout = new FileOutputStream("plugins//" + this.getPluginNameByJar()); 89 | bout = new BufferedOutputStream(fout); 90 | final byte[] data = new byte[1024]; 91 | int count; 92 | while ((count = in.read(data, 0, 1024)) >= 0) { 93 | bout.write(data, 0, count); 94 | } 95 | } catch (Exception e) { 96 | Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Failed to download the newest CubicCountdown update!"); 97 | return false; 98 | } finally { 99 | if (in != null) { 100 | in.close(); 101 | } 102 | if (bout != null) { 103 | bout.close(); 104 | } 105 | } 106 | Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Successfully downloaded the CubicCountdown update!"); 107 | Bukkit.getConsoleSender().sendMessage(ChatColor.AQUA + "The plugin will now try to reload the server..."); 108 | Bukkit.reload(); 109 | return true; 110 | } catch (IOException e) { 111 | Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "Failed to download the newest CubicCountdown update!"); 112 | return false; 113 | } 114 | } 115 | 116 | private String getPluginNameByJar() { 117 | return new File(CubicCountdown.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getName(); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/api/CubicExpansion.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.api; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import me.clip.placeholderapi.expansion.PlaceholderExpansion; 5 | import org.bukkit.OfflinePlayer; 6 | 7 | public class CubicExpansion extends PlaceholderExpansion { 8 | 9 | private CubicCountdown plugin; 10 | 11 | public CubicExpansion(CubicCountdown plugin) { 12 | this.plugin = plugin; 13 | } 14 | 15 | @Override 16 | public String getIdentifier() { 17 | return "cc"; 18 | } 19 | 20 | @Override 21 | public String getAuthor() { 22 | return "TimeCode"; 23 | } 24 | 25 | @Override 26 | public String getVersion() { 27 | return "1.0"; 28 | } 29 | 30 | @Override 31 | public boolean persist() { 32 | return true; 33 | } 34 | 35 | @Override 36 | public String onRequest(OfflinePlayer player, String params) { 37 | if (params.startsWith("total_win_counter_")) { 38 | String cubeName = params.substring(18); 39 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 40 | return plugin.getCubicAPI().getTotalWins(cubeName).toString(); 41 | } else { 42 | return null; 43 | } 44 | } else if (params.startsWith("total_lose_counter_")) { 45 | String cubeName = params.substring(19); 46 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 47 | return plugin.getCubicAPI().getTotalLoses(cubeName).toString(); 48 | } else { 49 | return null; 50 | } 51 | } else if (params.startsWith("total_games_played_")) { 52 | String cubeName = params.substring(19); 53 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 54 | return plugin.getCubicAPI().getTotalGamesPlayed(cubeName).toString(); 55 | } else { 56 | return null; 57 | } 58 | } else if (params.startsWith("total_help_counter_")) { 59 | String cubeName = params.substring(19); 60 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 61 | return plugin.getCubicAPI().getTotalHelps(cubeName).toString(); 62 | } else { 63 | return null; 64 | } 65 | } else if (params.startsWith("session_win_counter_")) { 66 | String cubeName = params.substring(20); 67 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 68 | return plugin.getCubicAPI().getSessionWins(cubeName).toString(); 69 | } else { 70 | return null; 71 | } 72 | } else if (params.startsWith("session_lose_counter_")) { 73 | String cubeName = params.substring(21); 74 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 75 | return plugin.getCubicAPI().getSessionLoses(cubeName).toString(); 76 | } else { 77 | return null; 78 | } 79 | } else if (params.startsWith("session_games_played_")) { 80 | String cubeName = params.substring(21); 81 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 82 | return plugin.getCubicAPI().getSessionGamesPlayed(cubeName).toString(); 83 | } else { 84 | return null; 85 | } 86 | } else if (params.startsWith("session_help_counter_")) { 87 | String cubeName = params.substring(21); 88 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 89 | return plugin.getCubicAPI().getSessionHelps(cubeName).toString(); 90 | } else { 91 | return null; 92 | } 93 | } else if (params.startsWith("cube_height_")) { 94 | String cubeName = params.substring(12); 95 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 96 | return String.valueOf(plugin.getCubicAPI().getCubeByName(cubeName).height()); 97 | } else { 98 | return null; 99 | } 100 | } else if (params.startsWith("cube_current_height_")) { 101 | String cubeName = params.substring(20); 102 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 103 | return String.valueOf(plugin.getCubicAPI().getCubeByName(cubeName).currentHeight()); 104 | } else { 105 | return null; 106 | } 107 | } 108 | return null; 109 | } 110 | 111 | private boolean isInteger(String toTest) { 112 | try { 113 | Integer.parseInt(toTest); 114 | if (Integer.parseInt(toTest) <= 0) { 115 | return false; 116 | } 117 | return true; 118 | } catch (NumberFormatException exception) { 119 | return false; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/api/Metrics.java: -------------------------------------------------------------------------------- 1 | /* 2 | * This Metrics class was auto-generated and can be copied into your project if you are 3 | * not using a build tool like Gradle or Maven for dependency management. 4 | * 5 | * IMPORTANT: You are not allowed to modify this class, except changing the package. 6 | * 7 | * Disallowed modifications include but are not limited to: 8 | * - Remove the option for users to opt-out 9 | * - Change the frequency for data submission 10 | * - Obfuscate the code (every obfuscator should allow you to make an exception for specific files) 11 | * - Reformat the code (if you use a linter, add an exception) 12 | * 13 | * Violations will result in a ban of your plugin and account from bStats. 14 | */ 15 | package de.timecoding.cc.api; 16 | 17 | import org.bukkit.Bukkit; 18 | import org.bukkit.configuration.file.YamlConfiguration; 19 | import org.bukkit.entity.Player; 20 | import org.bukkit.plugin.Plugin; 21 | import org.bukkit.plugin.java.JavaPlugin; 22 | 23 | import javax.net.ssl.HttpsURLConnection; 24 | import java.io.*; 25 | import java.lang.reflect.Method; 26 | import java.net.URL; 27 | import java.nio.charset.StandardCharsets; 28 | import java.util.*; 29 | import java.util.concurrent.Callable; 30 | import java.util.concurrent.ScheduledExecutorService; 31 | import java.util.concurrent.ScheduledThreadPoolExecutor; 32 | import java.util.concurrent.TimeUnit; 33 | import java.util.function.BiConsumer; 34 | import java.util.function.Consumer; 35 | import java.util.function.Supplier; 36 | import java.util.logging.Level; 37 | import java.util.stream.Collectors; 38 | import java.util.zip.GZIPOutputStream; 39 | 40 | public class Metrics { 41 | 42 | private final Plugin plugin; 43 | 44 | private final MetricsBase metricsBase; 45 | 46 | /** 47 | * Creates a new Metrics instance. 48 | * 49 | * @param plugin Your plugin instance. 50 | * @param serviceId The id of the service. It can be found at What is my plugin id? 52 | */ 53 | public Metrics(JavaPlugin plugin, int serviceId) { 54 | this.plugin = plugin; 55 | // Get the config file 56 | File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats"); 57 | File configFile = new File(bStatsFolder, "config.yml"); 58 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 59 | if (!config.isSet("serverUuid")) { 60 | config.addDefault("enabled", true); 61 | config.addDefault("serverUuid", UUID.randomUUID().toString()); 62 | config.addDefault("logFailedRequests", false); 63 | config.addDefault("logSentData", false); 64 | config.addDefault("logResponseStatusText", false); 65 | // Inform the server owners about bStats 66 | config 67 | .options() 68 | .header( 69 | "bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" 70 | + "many people use their plugin and their total player count. It's recommended to keep bStats\n" 71 | + "enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" 72 | + "performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" 73 | + "anonymous.") 74 | .copyDefaults(true); 75 | try { 76 | config.save(configFile); 77 | } catch (IOException ignored) { 78 | } 79 | } 80 | // Load the data 81 | boolean enabled = config.getBoolean("enabled", true); 82 | String serverUUID = config.getString("serverUuid"); 83 | boolean logErrors = config.getBoolean("logFailedRequests", false); 84 | boolean logSentData = config.getBoolean("logSentData", false); 85 | boolean logResponseStatusText = config.getBoolean("logResponseStatusText", false); 86 | metricsBase = 87 | new MetricsBase( 88 | "bukkit", 89 | serverUUID, 90 | serviceId, 91 | enabled, 92 | this::appendPlatformData, 93 | this::appendServiceData, 94 | submitDataTask -> Bukkit.getScheduler().runTask(plugin, submitDataTask), 95 | plugin::isEnabled, 96 | (message, error) -> this.plugin.getLogger().log(Level.WARNING, message, error), 97 | (message) -> this.plugin.getLogger().log(Level.INFO, message), 98 | logErrors, 99 | logSentData, 100 | logResponseStatusText); 101 | } 102 | 103 | /** 104 | * Shuts down the underlying scheduler service. 105 | */ 106 | public void shutdown() { 107 | metricsBase.shutdown(); 108 | } 109 | 110 | /** 111 | * Adds a custom chart. 112 | * 113 | * @param chart The chart to add. 114 | */ 115 | public void addCustomChart(CustomChart chart) { 116 | metricsBase.addCustomChart(chart); 117 | } 118 | 119 | private void appendPlatformData(JsonObjectBuilder builder) { 120 | builder.appendField("playerAmount", getPlayerAmount()); 121 | builder.appendField("onlineMode", Bukkit.getOnlineMode() ? 1 : 0); 122 | builder.appendField("bukkitVersion", Bukkit.getVersion()); 123 | builder.appendField("bukkitName", Bukkit.getName()); 124 | builder.appendField("javaVersion", System.getProperty("java.version")); 125 | builder.appendField("osName", System.getProperty("os.name")); 126 | builder.appendField("osArch", System.getProperty("os.arch")); 127 | builder.appendField("osVersion", System.getProperty("os.version")); 128 | builder.appendField("coreCount", Runtime.getRuntime().availableProcessors()); 129 | } 130 | 131 | private void appendServiceData(JsonObjectBuilder builder) { 132 | builder.appendField("pluginVersion", plugin.getDescription().getVersion()); 133 | } 134 | 135 | private int getPlayerAmount() { 136 | try { 137 | // Around MC 1.8 the return type was changed from an array to a collection, 138 | // This fixes java.lang.NoSuchMethodError: 139 | // org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection; 140 | Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers"); 141 | return onlinePlayersMethod.getReturnType().equals(Collection.class) 142 | ? ((Collection) onlinePlayersMethod.invoke(Bukkit.getServer())).size() 143 | : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length; 144 | } catch (Exception e) { 145 | // Just use the new method if the reflection failed 146 | return Bukkit.getOnlinePlayers().size(); 147 | } 148 | } 149 | 150 | public static class MetricsBase { 151 | 152 | /** 153 | * The version of the Metrics class. 154 | */ 155 | public static final String METRICS_VERSION = "3.0.2"; 156 | 157 | private static final String REPORT_URL = "https://bStats.org/api/v2/data/%s"; 158 | 159 | private final ScheduledExecutorService scheduler; 160 | 161 | private final String platform; 162 | 163 | private final String serverUuid; 164 | 165 | private final int serviceId; 166 | 167 | private final Consumer appendPlatformDataConsumer; 168 | 169 | private final Consumer appendServiceDataConsumer; 170 | 171 | private final Consumer submitTaskConsumer; 172 | 173 | private final Supplier checkServiceEnabledSupplier; 174 | 175 | private final BiConsumer errorLogger; 176 | 177 | private final Consumer infoLogger; 178 | 179 | private final boolean logErrors; 180 | 181 | private final boolean logSentData; 182 | 183 | private final boolean logResponseStatusText; 184 | 185 | private final Set customCharts = new HashSet<>(); 186 | 187 | private final boolean enabled; 188 | 189 | /** 190 | * Creates a new MetricsBase class instance. 191 | * 192 | * @param platform The platform of the service. 193 | * @param serviceId The id of the service. 194 | * @param serverUuid The server uuid. 195 | * @param enabled Whether or not data sending is enabled. 196 | * @param appendPlatformDataConsumer A consumer that receives a {@code JsonObjectBuilder} and 197 | * appends all platform-specific data. 198 | * @param appendServiceDataConsumer A consumer that receives a {@code JsonObjectBuilder} and 199 | * appends all service-specific data. 200 | * @param submitTaskConsumer A consumer that takes a runnable with the submit task. This can be 201 | * used to delegate the data collection to a another thread to prevent errors caused by 202 | * concurrency. Can be {@code null}. 203 | * @param checkServiceEnabledSupplier A supplier to check if the service is still enabled. 204 | * @param errorLogger A consumer that accepts log message and an error. 205 | * @param infoLogger A consumer that accepts info log messages. 206 | * @param logErrors Whether or not errors should be logged. 207 | * @param logSentData Whether or not the sent data should be logged. 208 | * @param logResponseStatusText Whether or not the response status text should be logged. 209 | */ 210 | public MetricsBase( 211 | String platform, 212 | String serverUuid, 213 | int serviceId, 214 | boolean enabled, 215 | Consumer appendPlatformDataConsumer, 216 | Consumer appendServiceDataConsumer, 217 | Consumer submitTaskConsumer, 218 | Supplier checkServiceEnabledSupplier, 219 | BiConsumer errorLogger, 220 | Consumer infoLogger, 221 | boolean logErrors, 222 | boolean logSentData, 223 | boolean logResponseStatusText) { 224 | ScheduledThreadPoolExecutor scheduler = 225 | new ScheduledThreadPoolExecutor(1, task -> new Thread(task, "bStats-Metrics")); 226 | // We want delayed tasks (non-periodic) that will execute in the future to be 227 | // cancelled when the scheduler is shutdown. 228 | // Otherwise, we risk preventing the server from shutting down even when 229 | // MetricsBase#shutdown() is called 230 | scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); 231 | this.scheduler = scheduler; 232 | this.platform = platform; 233 | this.serverUuid = serverUuid; 234 | this.serviceId = serviceId; 235 | this.enabled = enabled; 236 | this.appendPlatformDataConsumer = appendPlatformDataConsumer; 237 | this.appendServiceDataConsumer = appendServiceDataConsumer; 238 | this.submitTaskConsumer = submitTaskConsumer; 239 | this.checkServiceEnabledSupplier = checkServiceEnabledSupplier; 240 | this.errorLogger = errorLogger; 241 | this.infoLogger = infoLogger; 242 | this.logErrors = logErrors; 243 | this.logSentData = logSentData; 244 | this.logResponseStatusText = logResponseStatusText; 245 | checkRelocation(); 246 | if (enabled) { 247 | // WARNING: Removing the option to opt-out will get your plugin banned from 248 | // bStats 249 | startSubmitting(); 250 | } 251 | } 252 | 253 | /** 254 | * Gzips the given string. 255 | * 256 | * @param str The string to gzip. 257 | * @return The gzipped string. 258 | */ 259 | private static byte[] compress(final String str) throws IOException { 260 | if (str == null) { 261 | return null; 262 | } 263 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 264 | try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) { 265 | gzip.write(str.getBytes(StandardCharsets.UTF_8)); 266 | } 267 | return outputStream.toByteArray(); 268 | } 269 | 270 | public void addCustomChart(CustomChart chart) { 271 | this.customCharts.add(chart); 272 | } 273 | 274 | public void shutdown() { 275 | scheduler.shutdown(); 276 | } 277 | 278 | private void startSubmitting() { 279 | final Runnable submitTask = 280 | () -> { 281 | if (!enabled || !checkServiceEnabledSupplier.get()) { 282 | // Submitting data or service is disabled 283 | scheduler.shutdown(); 284 | return; 285 | } 286 | if (submitTaskConsumer != null) { 287 | submitTaskConsumer.accept(this::submitData); 288 | } else { 289 | this.submitData(); 290 | } 291 | }; 292 | // Many servers tend to restart at a fixed time at xx:00 which causes an uneven 293 | // distribution of requests on the 294 | // bStats backend. To circumvent this problem, we introduce some randomness into 295 | // the initial and second delay. 296 | // WARNING: You must not modify and part of this Metrics class, including the 297 | // submit delay or frequency! 298 | // WARNING: Modifying this code will get your plugin banned on bStats. Just 299 | // don't do it! 300 | long initialDelay = (long) (1000 * 60 * (3 + Math.random() * 3)); 301 | long secondDelay = (long) (1000 * 60 * (Math.random() * 30)); 302 | scheduler.schedule(submitTask, initialDelay, TimeUnit.MILLISECONDS); 303 | scheduler.scheduleAtFixedRate( 304 | submitTask, initialDelay + secondDelay, 1000 * 60 * 30, TimeUnit.MILLISECONDS); 305 | } 306 | 307 | private void submitData() { 308 | final JsonObjectBuilder baseJsonBuilder = new JsonObjectBuilder(); 309 | appendPlatformDataConsumer.accept(baseJsonBuilder); 310 | final JsonObjectBuilder serviceJsonBuilder = new JsonObjectBuilder(); 311 | appendServiceDataConsumer.accept(serviceJsonBuilder); 312 | JsonObjectBuilder.JsonObject[] chartData = 313 | customCharts.stream() 314 | .map(customChart -> customChart.getRequestJsonObject(errorLogger, logErrors)) 315 | .filter(Objects::nonNull) 316 | .toArray(JsonObjectBuilder.JsonObject[]::new); 317 | serviceJsonBuilder.appendField("id", serviceId); 318 | serviceJsonBuilder.appendField("customCharts", chartData); 319 | baseJsonBuilder.appendField("service", serviceJsonBuilder.build()); 320 | baseJsonBuilder.appendField("serverUUID", serverUuid); 321 | baseJsonBuilder.appendField("metricsVersion", METRICS_VERSION); 322 | JsonObjectBuilder.JsonObject data = baseJsonBuilder.build(); 323 | scheduler.execute( 324 | () -> { 325 | try { 326 | // Send the data 327 | sendData(data); 328 | } catch (Exception e) { 329 | // Something went wrong! :( 330 | if (logErrors) { 331 | errorLogger.accept("Could not submit bStats metrics data", e); 332 | } 333 | } 334 | }); 335 | } 336 | 337 | private void sendData(JsonObjectBuilder.JsonObject data) throws Exception { 338 | if (logSentData) { 339 | infoLogger.accept("Sent bStats metrics data: " + data.toString()); 340 | } 341 | String url = String.format(REPORT_URL, platform); 342 | HttpsURLConnection connection = (HttpsURLConnection) new URL(url).openConnection(); 343 | // Compress the data to save bandwidth 344 | byte[] compressedData = compress(data.toString()); 345 | connection.setRequestMethod("POST"); 346 | connection.addRequestProperty("Accept", "application/json"); 347 | connection.addRequestProperty("Connection", "close"); 348 | connection.addRequestProperty("Content-Encoding", "gzip"); 349 | connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length)); 350 | connection.setRequestProperty("Content-Type", "application/json"); 351 | connection.setRequestProperty("User-Agent", "Metrics-Service/1"); 352 | connection.setDoOutput(true); 353 | try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) { 354 | outputStream.write(compressedData); 355 | } 356 | StringBuilder builder = new StringBuilder(); 357 | try (BufferedReader bufferedReader = 358 | new BufferedReader(new InputStreamReader(connection.getInputStream()))) { 359 | String line; 360 | while ((line = bufferedReader.readLine()) != null) { 361 | builder.append(line); 362 | } 363 | } 364 | if (logResponseStatusText) { 365 | infoLogger.accept("Sent data to bStats and received response: " + builder); 366 | } 367 | } 368 | 369 | /** 370 | * Checks that the class was properly relocated. 371 | */ 372 | private void checkRelocation() { 373 | // You can use the property to disable the check in your test environment 374 | if (System.getProperty("bstats.relocatecheck") == null 375 | || !System.getProperty("bstats.relocatecheck").equals("false")) { 376 | // Maven's Relocate is clever and changes strings, too. So we have to use this 377 | // little "trick" ... :D 378 | final String defaultPackage = 379 | new String(new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's'}); 380 | final String examplePackage = 381 | new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'}); 382 | // We want to make sure no one just copy & pastes the example and uses the wrong 383 | // package names 384 | if (MetricsBase.class.getPackage().getName().startsWith(defaultPackage) 385 | || MetricsBase.class.getPackage().getName().startsWith(examplePackage)) { 386 | throw new IllegalStateException("bStats Metrics class has not been relocated correctly!"); 387 | } 388 | } 389 | } 390 | } 391 | 392 | public static class SimplePie extends CustomChart { 393 | 394 | private final Callable callable; 395 | 396 | /** 397 | * Class constructor. 398 | * 399 | * @param chartId The id of the chart. 400 | * @param callable The callable which is used to request the chart data. 401 | */ 402 | public SimplePie(String chartId, Callable callable) { 403 | super(chartId); 404 | this.callable = callable; 405 | } 406 | 407 | @Override 408 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 409 | String value = callable.call(); 410 | if (value == null || value.isEmpty()) { 411 | // Null = skip the chart 412 | return null; 413 | } 414 | return new JsonObjectBuilder().appendField("value", value).build(); 415 | } 416 | } 417 | 418 | public static class MultiLineChart extends CustomChart { 419 | 420 | private final Callable> callable; 421 | 422 | /** 423 | * Class constructor. 424 | * 425 | * @param chartId The id of the chart. 426 | * @param callable The callable which is used to request the chart data. 427 | */ 428 | public MultiLineChart(String chartId, Callable> callable) { 429 | super(chartId); 430 | this.callable = callable; 431 | } 432 | 433 | @Override 434 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 435 | JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); 436 | Map map = callable.call(); 437 | if (map == null || map.isEmpty()) { 438 | // Null = skip the chart 439 | return null; 440 | } 441 | boolean allSkipped = true; 442 | for (Map.Entry entry : map.entrySet()) { 443 | if (entry.getValue() == 0) { 444 | // Skip this invalid 445 | continue; 446 | } 447 | allSkipped = false; 448 | valuesBuilder.appendField(entry.getKey(), entry.getValue()); 449 | } 450 | if (allSkipped) { 451 | // Null = skip the chart 452 | return null; 453 | } 454 | return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); 455 | } 456 | } 457 | 458 | public static class AdvancedPie extends CustomChart { 459 | 460 | private final Callable> callable; 461 | 462 | /** 463 | * Class constructor. 464 | * 465 | * @param chartId The id of the chart. 466 | * @param callable The callable which is used to request the chart data. 467 | */ 468 | public AdvancedPie(String chartId, Callable> callable) { 469 | super(chartId); 470 | this.callable = callable; 471 | } 472 | 473 | @Override 474 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 475 | JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); 476 | Map map = callable.call(); 477 | if (map == null || map.isEmpty()) { 478 | // Null = skip the chart 479 | return null; 480 | } 481 | boolean allSkipped = true; 482 | for (Map.Entry entry : map.entrySet()) { 483 | if (entry.getValue() == 0) { 484 | // Skip this invalid 485 | continue; 486 | } 487 | allSkipped = false; 488 | valuesBuilder.appendField(entry.getKey(), entry.getValue()); 489 | } 490 | if (allSkipped) { 491 | // Null = skip the chart 492 | return null; 493 | } 494 | return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); 495 | } 496 | } 497 | 498 | public static class SimpleBarChart extends CustomChart { 499 | 500 | private final Callable> callable; 501 | 502 | /** 503 | * Class constructor. 504 | * 505 | * @param chartId The id of the chart. 506 | * @param callable The callable which is used to request the chart data. 507 | */ 508 | public SimpleBarChart(String chartId, Callable> callable) { 509 | super(chartId); 510 | this.callable = callable; 511 | } 512 | 513 | @Override 514 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 515 | JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); 516 | Map map = callable.call(); 517 | if (map == null || map.isEmpty()) { 518 | // Null = skip the chart 519 | return null; 520 | } 521 | for (Map.Entry entry : map.entrySet()) { 522 | valuesBuilder.appendField(entry.getKey(), new int[]{entry.getValue()}); 523 | } 524 | return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); 525 | } 526 | } 527 | 528 | public static class AdvancedBarChart extends CustomChart { 529 | 530 | private final Callable> callable; 531 | 532 | /** 533 | * Class constructor. 534 | * 535 | * @param chartId The id of the chart. 536 | * @param callable The callable which is used to request the chart data. 537 | */ 538 | public AdvancedBarChart(String chartId, Callable> callable) { 539 | super(chartId); 540 | this.callable = callable; 541 | } 542 | 543 | @Override 544 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 545 | JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); 546 | Map map = callable.call(); 547 | if (map == null || map.isEmpty()) { 548 | // Null = skip the chart 549 | return null; 550 | } 551 | boolean allSkipped = true; 552 | for (Map.Entry entry : map.entrySet()) { 553 | if (entry.getValue().length == 0) { 554 | // Skip this invalid 555 | continue; 556 | } 557 | allSkipped = false; 558 | valuesBuilder.appendField(entry.getKey(), entry.getValue()); 559 | } 560 | if (allSkipped) { 561 | // Null = skip the chart 562 | return null; 563 | } 564 | return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); 565 | } 566 | } 567 | 568 | public static class DrilldownPie extends CustomChart { 569 | 570 | private final Callable>> callable; 571 | 572 | /** 573 | * Class constructor. 574 | * 575 | * @param chartId The id of the chart. 576 | * @param callable The callable which is used to request the chart data. 577 | */ 578 | public DrilldownPie(String chartId, Callable>> callable) { 579 | super(chartId); 580 | this.callable = callable; 581 | } 582 | 583 | @Override 584 | public JsonObjectBuilder.JsonObject getChartData() throws Exception { 585 | JsonObjectBuilder valuesBuilder = new JsonObjectBuilder(); 586 | Map> map = callable.call(); 587 | if (map == null || map.isEmpty()) { 588 | // Null = skip the chart 589 | return null; 590 | } 591 | boolean reallyAllSkipped = true; 592 | for (Map.Entry> entryValues : map.entrySet()) { 593 | JsonObjectBuilder valueBuilder = new JsonObjectBuilder(); 594 | boolean allSkipped = true; 595 | for (Map.Entry valueEntry : map.get(entryValues.getKey()).entrySet()) { 596 | valueBuilder.appendField(valueEntry.getKey(), valueEntry.getValue()); 597 | allSkipped = false; 598 | } 599 | if (!allSkipped) { 600 | reallyAllSkipped = false; 601 | valuesBuilder.appendField(entryValues.getKey(), valueBuilder.build()); 602 | } 603 | } 604 | if (reallyAllSkipped) { 605 | // Null = skip the chart 606 | return null; 607 | } 608 | return new JsonObjectBuilder().appendField("values", valuesBuilder.build()).build(); 609 | } 610 | } 611 | 612 | public abstract static class CustomChart { 613 | 614 | private final String chartId; 615 | 616 | protected CustomChart(String chartId) { 617 | if (chartId == null) { 618 | throw new IllegalArgumentException("chartId must not be null"); 619 | } 620 | this.chartId = chartId; 621 | } 622 | 623 | public JsonObjectBuilder.JsonObject getRequestJsonObject( 624 | BiConsumer errorLogger, boolean logErrors) { 625 | JsonObjectBuilder builder = new JsonObjectBuilder(); 626 | builder.appendField("chartId", chartId); 627 | try { 628 | JsonObjectBuilder.JsonObject data = getChartData(); 629 | if (data == null) { 630 | // If the data is null we don't send the chart. 631 | return null; 632 | } 633 | builder.appendField("data", data); 634 | } catch (Throwable t) { 635 | if (logErrors) { 636 | errorLogger.accept("Failed to get data for custom chart with id " + chartId, t); 637 | } 638 | return null; 639 | } 640 | return builder.build(); 641 | } 642 | 643 | protected abstract JsonObjectBuilder.JsonObject getChartData() throws Exception; 644 | } 645 | 646 | public static class SingleLineChart extends CustomChart { 647 | 648 | private final Callable callable; 649 | 650 | /** 651 | * Class constructor. 652 | * 653 | * @param chartId The id of the chart. 654 | * @param callable The callable which is used to request the chart data. 655 | */ 656 | public SingleLineChart(String chartId, Callable callable) { 657 | super(chartId); 658 | this.callable = callable; 659 | } 660 | 661 | @Override 662 | protected JsonObjectBuilder.JsonObject getChartData() throws Exception { 663 | int value = callable.call(); 664 | if (value == 0) { 665 | // Null = skip the chart 666 | return null; 667 | } 668 | return new JsonObjectBuilder().appendField("value", value).build(); 669 | } 670 | } 671 | 672 | /** 673 | * An extremely simple JSON builder. 674 | * 675 | *

While this class is neither feature-rich nor the most performant one, it's sufficient enough 676 | * for its use-case. 677 | */ 678 | public static class JsonObjectBuilder { 679 | 680 | private StringBuilder builder = new StringBuilder(); 681 | 682 | private boolean hasAtLeastOneField = false; 683 | 684 | public JsonObjectBuilder() { 685 | builder.append("{"); 686 | } 687 | 688 | /** 689 | * Escapes the given string like stated in https://www.ietf.org/rfc/rfc4627.txt. 690 | * 691 | *

This method escapes only the necessary characters '"', '\'. and '\u0000' - '\u001F'. 692 | * Compact escapes are not used (e.g., '\n' is escaped as "\u000a" and not as "\n"). 693 | * 694 | * @param value The value to escape. 695 | * @return The escaped value. 696 | */ 697 | private static String escape(String value) { 698 | final StringBuilder builder = new StringBuilder(); 699 | for (int i = 0; i < value.length(); i++) { 700 | char c = value.charAt(i); 701 | if (c == '"') { 702 | builder.append("\\\""); 703 | } else if (c == '\\') { 704 | builder.append("\\\\"); 705 | } else if (c <= '\u000F') { 706 | builder.append("\\u000").append(Integer.toHexString(c)); 707 | } else if (c <= '\u001F') { 708 | builder.append("\\u00").append(Integer.toHexString(c)); 709 | } else { 710 | builder.append(c); 711 | } 712 | } 713 | return builder.toString(); 714 | } 715 | 716 | /** 717 | * Appends a null field to the JSON. 718 | * 719 | * @param key The key of the field. 720 | * @return A reference to this object. 721 | */ 722 | public JsonObjectBuilder appendNull(String key) { 723 | appendFieldUnescaped(key, "null"); 724 | return this; 725 | } 726 | 727 | /** 728 | * Appends a string field to the JSON. 729 | * 730 | * @param key The key of the field. 731 | * @param value The value of the field. 732 | * @return A reference to this object. 733 | */ 734 | public JsonObjectBuilder appendField(String key, String value) { 735 | if (value == null) { 736 | throw new IllegalArgumentException("JSON value must not be null"); 737 | } 738 | appendFieldUnescaped(key, "\"" + escape(value) + "\""); 739 | return this; 740 | } 741 | 742 | /** 743 | * Appends an integer field to the JSON. 744 | * 745 | * @param key The key of the field. 746 | * @param value The value of the field. 747 | * @return A reference to this object. 748 | */ 749 | public JsonObjectBuilder appendField(String key, int value) { 750 | appendFieldUnescaped(key, String.valueOf(value)); 751 | return this; 752 | } 753 | 754 | /** 755 | * Appends an object to the JSON. 756 | * 757 | * @param key The key of the field. 758 | * @param object The object. 759 | * @return A reference to this object. 760 | */ 761 | public JsonObjectBuilder appendField(String key, JsonObject object) { 762 | if (object == null) { 763 | throw new IllegalArgumentException("JSON object must not be null"); 764 | } 765 | appendFieldUnescaped(key, object.toString()); 766 | return this; 767 | } 768 | 769 | /** 770 | * Appends a string array to the JSON. 771 | * 772 | * @param key The key of the field. 773 | * @param values The string array. 774 | * @return A reference to this object. 775 | */ 776 | public JsonObjectBuilder appendField(String key, String[] values) { 777 | if (values == null) { 778 | throw new IllegalArgumentException("JSON values must not be null"); 779 | } 780 | String escapedValues = 781 | Arrays.stream(values) 782 | .map(value -> "\"" + escape(value) + "\"") 783 | .collect(Collectors.joining(",")); 784 | appendFieldUnescaped(key, "[" + escapedValues + "]"); 785 | return this; 786 | } 787 | 788 | /** 789 | * Appends an integer array to the JSON. 790 | * 791 | * @param key The key of the field. 792 | * @param values The integer array. 793 | * @return A reference to this object. 794 | */ 795 | public JsonObjectBuilder appendField(String key, int[] values) { 796 | if (values == null) { 797 | throw new IllegalArgumentException("JSON values must not be null"); 798 | } 799 | String escapedValues = 800 | Arrays.stream(values).mapToObj(String::valueOf).collect(Collectors.joining(",")); 801 | appendFieldUnescaped(key, "[" + escapedValues + "]"); 802 | return this; 803 | } 804 | 805 | /** 806 | * Appends an object array to the JSON. 807 | * 808 | * @param key The key of the field. 809 | * @param values The integer array. 810 | * @return A reference to this object. 811 | */ 812 | public JsonObjectBuilder appendField(String key, JsonObject[] values) { 813 | if (values == null) { 814 | throw new IllegalArgumentException("JSON values must not be null"); 815 | } 816 | String escapedValues = 817 | Arrays.stream(values).map(JsonObject::toString).collect(Collectors.joining(",")); 818 | appendFieldUnescaped(key, "[" + escapedValues + "]"); 819 | return this; 820 | } 821 | 822 | /** 823 | * Appends a field to the object. 824 | * 825 | * @param key The key of the field. 826 | * @param escapedValue The escaped value of the field. 827 | */ 828 | private void appendFieldUnescaped(String key, String escapedValue) { 829 | if (builder == null) { 830 | throw new IllegalStateException("JSON has already been built"); 831 | } 832 | if (key == null) { 833 | throw new IllegalArgumentException("JSON key must not be null"); 834 | } 835 | if (hasAtLeastOneField) { 836 | builder.append(","); 837 | } 838 | builder.append("\"").append(escape(key)).append("\":").append(escapedValue); 839 | hasAtLeastOneField = true; 840 | } 841 | 842 | /** 843 | * Builds the JSON string and invalidates this builder. 844 | * 845 | * @return The built JSON string. 846 | */ 847 | public JsonObject build() { 848 | if (builder == null) { 849 | throw new IllegalStateException("JSON has already been built"); 850 | } 851 | JsonObject object = new JsonObject(builder.append("}").toString()); 852 | builder = null; 853 | return object; 854 | } 855 | 856 | /** 857 | * A super simple representation of a JSON object. 858 | * 859 | *

This class only exists to make methods of the {@link JsonObjectBuilder} type-safe and not 860 | * allow a raw string inputs for methods like {@link JsonObjectBuilder#appendField(String, 861 | * JsonObject)}. 862 | */ 863 | public static class JsonObject { 864 | 865 | private final String value; 866 | 867 | private JsonObject(String value) { 868 | this.value = value; 869 | } 870 | 871 | @Override 872 | public String toString() { 873 | return value; 874 | } 875 | } 876 | } 877 | } 878 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/command/CubicCommand.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.command; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import de.timecoding.cc.command.setup.CubicSetup; 5 | import de.timecoding.cc.util.type.Cube; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.Effect; 8 | import org.bukkit.Material; 9 | import org.bukkit.Sound; 10 | import org.bukkit.block.Block; 11 | import org.bukkit.command.Command; 12 | import org.bukkit.command.CommandExecutor; 13 | import org.bukkit.command.CommandSender; 14 | import org.bukkit.entity.Player; 15 | 16 | import java.util.*; 17 | import java.util.concurrent.atomic.AtomicInteger; 18 | import java.util.stream.Collectors; 19 | 20 | public class CubicCommand implements CommandExecutor { 21 | 22 | private CubicCountdown plugin; 23 | 24 | private HashMap, List>> queue = new HashMap<>(); 25 | private HashMap fillTaskList = new HashMap<>(); 26 | private HashMap clearTaskList = new HashMap<>(); 27 | private String help = " \n §cCommands: \n §e/cc setup §7- §eStarts a setup to create a cubic-map \n §e/cc delete MAPNAME §7- §eDeletes the provided map \n §e/cc fill MAPNAME BLOCKTYPE1,BLOCKTYPE2,... AMOUNT1,AMOUNT2,... §7- §eFills the cube with an specific amount of specific blocks \n §e/cc clear MAPNAME §7- §eClears the whole area inside the cube \n §e/cc cancel MAPNAME §7- §eStops a running countdown of a map \n §e/cc reload §7- §eReloads all the plugin files \n §e/cc cubes §7- §eDisplays all available cubes \n §e/cc help §7- §eOpens the help menu \n §e/cc simulate MAPNAME §7- §eSimulates a win/lose or help \n §e/cc setEntry KEY VALUE §7- §eEdits a specific config/data entry \n "; 28 | 29 | public CubicCommand(CubicCountdown plugin) { 30 | this.plugin = plugin; 31 | this.fillTaskList = plugin.getCubicAPI().getFillAnimationList(); 32 | this.clearTaskList = plugin.getCubicAPI().getClearAnimationList(); 33 | } 34 | 35 | @Override 36 | public boolean onCommand(CommandSender commandSender, Command command, String s, String[] strings) { 37 | if (commandSender.isOp()) { 38 | if (strings.length == 1) { 39 | if (strings[0].equalsIgnoreCase("setup")) { 40 | if (commandSender instanceof Player) { 41 | Player player = (Player) commandSender; 42 | if (!plugin.getSetupList().containsKey(player)) { 43 | new CubicSetup((Player) commandSender, plugin); 44 | } else { 45 | player.playSound(player.getLocation(), Sound.BLOCK_SWEET_BERRY_BUSH_PICK_BERRIES, 2, 2); 46 | player.sendMessage("§cYou already started another setup! You need to cancel it to start a new one! §7(type §cCANCEL §7into the chat)"); 47 | } 48 | } else { 49 | commandSender.sendMessage("§cSorry, but only players are able to use this command!"); 50 | } 51 | } else if (strings[0].equalsIgnoreCase("reload")) { 52 | this.plugin.getDataHandler().reload(); 53 | this.plugin.getConfigHandler().reload(); 54 | commandSender.sendMessage("§aThe §econfig.yml §aand §edata.yml §awere successfully reloaded!"); 55 | plugin.getCubicAPI().startActionbar(); 56 | } else if (strings[0].equalsIgnoreCase("cubes")) { 57 | String maps = ""; 58 | for (Cube cube : plugin.getCubicAPI().getCubes()) { 59 | maps = maps + "§e" + cube.getName() + "§7, "; 60 | } 61 | if (maps.length() > 1) { 62 | maps = maps.substring(0, (maps.length() - 2)); 63 | commandSender.sendMessage(" \n §aAvailable Cubes/Maps: \n " + maps + " \n "); 64 | } else { 65 | commandSender.sendMessage("§cYou haven't created any cube yet! Use §e/cc setup §cto create one!"); 66 | } 67 | } else if (strings[0].equalsIgnoreCase("help")) { 68 | commandSender.sendMessage(this.help); 69 | } else { 70 | commandSender.sendMessage("§cUnknown command! Type §e/cc help §cto look up all commands!"); 71 | } 72 | } else if (strings.length == 2) { 73 | if (strings[0].equalsIgnoreCase("cancel")) { 74 | String map = strings[1].toUpperCase(); 75 | AtomicInteger i = new AtomicInteger(); 76 | plugin.getCubicAPI().getCubes().forEach(cube -> { 77 | if (cube.getName().equals(map)) { 78 | i.getAndIncrement(); 79 | if (plugin.getCubicAPI().getCountdownModuleFromCube(cube) != null) { 80 | plugin.getCubicAPI().getCountdownModuleFromCube(cube).cancel(); 81 | commandSender.sendMessage("§aThe Countdown was cancelled for map §e" + map); 82 | } else { 83 | commandSender.sendMessage("§cThere's no countdown running for the map §e" + map); 84 | } 85 | } 86 | }); 87 | if (i.get() <= 0) { 88 | commandSender.sendMessage("§cThe map §e" + map + " §cdoes not exist! You can create it with §e/cc setup"); 89 | } 90 | } else if (strings[0].equalsIgnoreCase("delete")) { 91 | String map = strings[1].toUpperCase(); 92 | AtomicInteger i = new AtomicInteger(); 93 | plugin.getCubicAPI().getCubes().forEach(cube -> { 94 | if (cube.getName().equals(map)) { 95 | i.getAndIncrement(); 96 | if (plugin.getCubicAPI().getCountdownModuleFromCube(cube) != null) { 97 | plugin.getCubicAPI().getCountdownModuleFromCube(cube).cancel(); 98 | } 99 | plugin.getDataHandler().getConfig().set("Cube." + map, null); 100 | plugin.getDataHandler().save(); 101 | commandSender.sendMessage("§aThe map §e" + map + " §awas deleted successfully!"); 102 | } 103 | }); 104 | if (i.get() <= 0) { 105 | commandSender.sendMessage("§cThe map §e" + map + " §cdoes not exist! You can create it with §e/cc setup"); 106 | } 107 | } else if (strings[0].equalsIgnoreCase("clear")) { 108 | String cubeName = strings[1].toUpperCase(); 109 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 110 | Cube cube = plugin.getCubicAPI().getCubeByName(cubeName); 111 | if (plugin.getConfigHandler().getBoolean("ClearAction.Animation")) { 112 | if (!clearTaskList.containsKey(cubeName)) { 113 | int taskID; 114 | int animationTicks = plugin.getConfigHandler().getInteger("ClearAction.AnimationTicks"); 115 | taskID = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { 116 | List removedBlocks = new ArrayList<>(); 117 | int i = 0; 118 | 119 | @Override 120 | public void run() { 121 | List blockList = cube.blockList(false); 122 | blockList.removeAll(removedBlocks); 123 | if (!blockList.isEmpty()) { 124 | if (!plugin.getConfigHandler().getStringList("ClearAction.DisabledBlocks").contains(blockList.get(0).getType().toString().toUpperCase())) { 125 | blockList.get(0).setType(Material.AIR); 126 | if (plugin.getConfigHandler().getBoolean("ClearAction.Effect.Enabled")) { 127 | for (int i = 0; i <= (plugin.getConfigHandler().getInteger("ClearAction.Effect.BlocksAhead")); i++) { 128 | if (blockList.size() > (this.i + i)) { 129 | blockList.get((this.i + i)).getLocation().getWorld().playEffect(blockList.get((this.i + i)).getLocation(), Effect.valueOf(plugin.getConfigHandler().getString("ClearAction.Effect.Type")), plugin.getConfigHandler().getConfig().getInt("ClearAction.Effect.Amount")); 130 | } 131 | } 132 | } 133 | if (plugin.getConfigHandler().getBoolean("ClearAction.ProceedSound.Enabled") && blockList.size() > 0) { 134 | if (plugin.getConfigHandler().getString("ClearAction.ProceedSound.Custom").equalsIgnoreCase("")) { 135 | blockList.get(0).getWorld().playSound(blockList.get(0).getLocation(), Sound.valueOf(plugin.getConfigHandler().getString("ClearAction.ProceedSound.Sound")), (float) plugin.getConfigHandler().getConfig().getDouble("ClearAction.ProceedSound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("ClearAction.ProceedSound.Pitch")); 136 | } else { 137 | blockList.get(0).getWorld().playSound(blockList.get(0).getLocation(), plugin.getConfigHandler().getString("ClearAction.ProceedSound.Custom"), (float) plugin.getConfigHandler().getConfig().getDouble("ClearAction.ProceedSound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("ClearAction.ProceedSound.Pitch")); 138 | } 139 | } 140 | } 141 | removedBlocks.add(blockList.get(0)); 142 | } else { 143 | Bukkit.getScheduler().cancelTask(clearTaskList.get(cubeName)); 144 | Player player = null; 145 | if (commandSender instanceof Player) { 146 | player = (Player) commandSender; 147 | } 148 | plugin.getCubicListener().proof(player, cube.getPos1(), true); 149 | } 150 | } 151 | }, animationTicks, animationTicks); 152 | clearTaskList.put(cubeName, taskID); 153 | commandSender.sendMessage("§aSuccessfully cleared the whole area in the cube §e" + cubeName); 154 | 155 | } else { 156 | commandSender.sendMessage("§cPlease wait until §e" + cubeName + "'s §crunning §eClear §canimation is finished!"); 157 | } 158 | } else { 159 | clearTaskList.put(cubeName, 00000); 160 | cube.blockList(false).forEach(block -> { 161 | if (!plugin.getConfigHandler().getStringList("ClearAction.DisabledBlocks").contains(block.getType().toString().toUpperCase())) { 162 | block.setType(Material.AIR); 163 | } 164 | }); 165 | Player player = null; 166 | if (commandSender instanceof Player) { 167 | player = (Player) commandSender; 168 | } 169 | plugin.getCubicListener().proof(player, cube.getPos1(), true); 170 | commandSender.sendMessage("§aSuccessfully cleared the whole area in the cube §e" + cubeName); 171 | } 172 | } else { 173 | commandSender.sendMessage("§cThe map §e" + cubeName + " §cdoes not exist! You can create it with §e/cc setup"); 174 | } 175 | } else { 176 | commandSender.sendMessage("§cUnknown command! Type §e/cc help §cto look up all commands!"); 177 | } 178 | } else if (strings.length >= 3 && strings[0].equalsIgnoreCase("setEntry") || strings.length >= 3 && strings[0].equalsIgnoreCase("set")) { 179 | String entry = strings[2]; 180 | for (int i = 3; i < strings.length; i++) { 181 | entry = entry + " " + strings[i]; 182 | } 183 | if (entry.endsWith(" ")) { 184 | entry = entry.substring(0, (entry.length() - 1)); 185 | } 186 | if (entry.startsWith("/")) { 187 | entry.substring(1, entry.length()); 188 | } 189 | if (plugin.getConfigHandler().keyExists(strings[1])) { 190 | if (isBoolean(entry)) { 191 | plugin.getConfigHandler().getConfig().set(strings[1], Boolean.parseBoolean(entry)); 192 | } else if (isInteger(entry)) { 193 | plugin.getConfigHandler().getConfig().set(strings[1], Integer.parseInt(entry)); 194 | } else if (isStringList(entry)) { 195 | plugin.getConfigHandler().getConfig().set(strings[1], toStringList(entry)); 196 | } else { 197 | plugin.getConfigHandler().getConfig().set(strings[1], entry); 198 | } 199 | plugin.getConfigHandler().save(); 200 | plugin.getConfigHandler().reload(); 201 | plugin.getCubicAPI().startActionbar(); 202 | commandSender.sendMessage("§aSuccessfully set §e" + strings[1] + "'s §avalue to §e" + entry + "§a! §7(config.yml)"); 203 | } else if (plugin.getDataHandler().keyExists(strings[1])) { 204 | if (isBoolean(entry)) { 205 | plugin.getDataHandler().getConfig().set(strings[1], Boolean.parseBoolean(entry)); 206 | } else if (isInteger(entry)) { 207 | plugin.getDataHandler().getConfig().set(strings[1], Integer.parseInt(entry)); 208 | } else if (isStringList(entry)) { 209 | plugin.getDataHandler().getConfig().set(strings[1], toStringList(entry)); 210 | } else { 211 | plugin.getDataHandler().getConfig().set(strings[1], entry); 212 | } 213 | plugin.getDataHandler().save(); 214 | plugin.getDataHandler().reload(); 215 | plugin.getCubicAPI().startActionbar(); 216 | commandSender.sendMessage("§aSuccessfully set §e" + strings[1] + "'s §avalue to §e" + entry + "§a! §7(datas.yml)"); 217 | } else { 218 | commandSender.sendMessage("§cThe key §e" + strings[1] + " §cdoes not exist!"); 219 | } 220 | } else if (strings.length == 3 && strings[0].equalsIgnoreCase("simulate")) { 221 | String map = strings[2].toUpperCase(); 222 | if (plugin.getCubicAPI().cubeNameExists(map)) { 223 | Cube cube = plugin.getCubicAPI().getCubeByName(map); 224 | if (strings[1].equalsIgnoreCase("win")) { 225 | plugin.getCubicAPI().increaseWins(cube); 226 | commandSender.sendMessage("§aA §ewin §awas successfully simulated!"); 227 | } else if (strings[1].equalsIgnoreCase("lose")) { 228 | plugin.getCubicAPI().increaseLoses(cube); 229 | commandSender.sendMessage("§aA §close §awas successfully simulated!"); 230 | } else if (strings[1].equalsIgnoreCase("help")) { 231 | plugin.getCubicAPI().increaseHelpCounter(cube); 232 | commandSender.sendMessage("§aA §fhelp §awas successfully simulated!"); 233 | } else { 234 | commandSender.sendMessage("§cThe variable §e" + strings[1] + " §cdoes not exist!"); 235 | } 236 | } else { 237 | commandSender.sendMessage("§cThe map §e" + map + " §cdoes not exist! You can create it with §e/cc setup"); 238 | } 239 | } else if (strings.length == 4) { 240 | if (strings[0].equalsIgnoreCase("fill")) { 241 | String cubeName = strings[1].toUpperCase(); 242 | if (plugin.getCubicAPI().cubeNameExists(cubeName)) { 243 | List typeList = Arrays.stream(strings[2].split(",")).collect(Collectors.toList()); 244 | final List[] amountList = new List[]{new ArrayList<>()}; 245 | final String[] amountErrors = {""}; 246 | Arrays.stream(strings[3].split(",")).forEach(string -> { 247 | if (isInteger(string)) { 248 | amountList[0].add(Integer.parseInt(string)); 249 | } else { 250 | amountErrors[0] = "§e" + amountErrors[0] + string + ", "; 251 | } 252 | }); 253 | if (amountErrors[0].length() > 0) { 254 | commandSender.sendMessage("§cThe following parameters aren't numbers: §e" + amountErrors[0].substring(0, (amountErrors[0].length() - 2))); 255 | return false; 256 | } 257 | if (typeList.size() == 0) { 258 | typeList.add(strings[2]); 259 | } 260 | if (amountList[0].size() == 0) { 261 | if (isInteger(strings[3])) { 262 | amountList[0].add(Integer.parseInt(strings[3])); 263 | } else { 264 | commandSender.sendMessage("§e" + strings[3] + " §cis not a valid number!"); 265 | return false; 266 | } 267 | } 268 | if (typeList.size() == amountList[0].size()) { 269 | String notExist = ""; 270 | final List[] materialList = new List[]{new ArrayList<>()}; 271 | int i = 0; 272 | for (String type : typeList) { 273 | if (isMaterial(type.toUpperCase())) { 274 | materialList[0].add(Material.valueOf(type.toUpperCase())); 275 | } else { 276 | notExist = notExist + type + ", "; 277 | } 278 | } 279 | if (!fillTaskList.containsKey(cubeName)) { 280 | if (notExist.length() > 0) { 281 | commandSender.sendMessage("§cThe following material-type(s) does not exist: §e" + notExist.substring(0, notExist.length() - 2)); 282 | } else { 283 | final AtomicInteger[] random = {new AtomicInteger(new Random().nextInt(materialList[0].size()))}; 284 | Cube cube = plugin.getCubicAPI().getCubeByName(cubeName); 285 | int task = 0; 286 | int animationTicks = plugin.getConfigHandler().getInteger("FillAction.AnimationTicks"); 287 | final List[] blockList = new List[]{cube.blockList(true)}; 288 | if (plugin.getConfigHandler().getBoolean("FillAction.OnlyAir")) { 289 | blockList[0] = cube.airBlockList(); 290 | } 291 | commandSender.sendMessage("§aSuccessfully performed §efill-action §afor map §e" + cubeName + "§a!"); 292 | final List[] finalBlockList = new List[]{blockList[0]}; 293 | List playerList = new ArrayList<>(); 294 | if (plugin.getConfigHandler().getBoolean("Viewer.AllPlayers")) { 295 | Bukkit.getOnlinePlayers().forEach(onlinePlayer -> playerList.add(onlinePlayer)); 296 | } else if (plugin.getConfigHandler().getBoolean("Viewer.AllPlayersInCubeRadius.Enabled")) { 297 | cube.blockList(true).forEach(block -> { 298 | Bukkit.getOnlinePlayers().forEach(onlinePlayer -> { 299 | if (block.getLocation().distance(onlinePlayer.getLocation()) < plugin.getConfigHandler().getInteger("Viewer.AllPlayersInCubeRadius.RadiusInBlocks")) { 300 | playerList.add(onlinePlayer); 301 | } 302 | }); 303 | }); 304 | } else if (commandSender instanceof Player) { 305 | playerList.add((Player) commandSender); 306 | } 307 | if (plugin.getConfigHandler().getBoolean("FillAction.Animation")) { 308 | List finalBlockList1 = blockList[0]; 309 | task = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { 310 | int i = 0; 311 | List removedBlocks = new ArrayList<>(); 312 | 313 | @Override 314 | public void run() { 315 | finalBlockList[0] = new ArrayList<>(); 316 | if (plugin.getConfigHandler().getBoolean("FillAction.OnlyAir")) { 317 | finalBlockList[0] = cube.airBlockList(); 318 | } else { 319 | finalBlockList[0] = cube.blockList(true); 320 | } 321 | removedBlocks.forEach(block -> { 322 | if (block != null && !block.getType().isAir()) { 323 | finalBlockList[0].remove(block); 324 | } 325 | }); 326 | if (amountList[0].size() > random[0].get() && amountList[0].get(random[0].get()) > 0 && i < finalBlockList[0].size()) { 327 | if (!plugin.getConfigHandler().getStringList("FillAction.DisabledBlocks").contains(finalBlockList[0].get(i).getType().toString().toUpperCase())) { 328 | removedBlocks.add(finalBlockList[0].get(i)); 329 | fillCube(amountList, materialList, i, random, finalBlockList[0]); 330 | if (plugin.getConfigHandler().getBoolean("FillAction.Effect.Enabled")) { 331 | for (int i = 0; i <= (plugin.getConfigHandler().getInteger("FillAction.Effect.BlocksAhead")); i++) { 332 | if (finalBlockList1.size() > (this.i + i)) { 333 | finalBlockList1.get((this.i + i)).getLocation().getWorld().playEffect(finalBlockList1.get((this.i + i)).getLocation(), Effect.valueOf(plugin.getConfigHandler().getString("FillAction.Effect.Type")), plugin.getConfigHandler().getConfig().getInt("FillAction.Effect.Amount")); 334 | } 335 | } 336 | } 337 | if (plugin.getConfigHandler().getBoolean("FillAction.ProceedSound.Enabled") && finalBlockList[0].size() > 0) { 338 | if (plugin.getConfigHandler().getString("FillAction.ProceedSound.Custom").equalsIgnoreCase("")) { 339 | finalBlockList[0].get(0).getWorld().playSound(finalBlockList[0].get(0).getLocation(), Sound.valueOf(plugin.getConfigHandler().getString("FillAction.ProceedSound.Sound")), (float) plugin.getConfigHandler().getConfig().getDouble("FillAction.ProceedSound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("FillAction.ProceedSound.Pitch")); 340 | } else { 341 | finalBlockList[0].get(0).getWorld().playSound(finalBlockList[0].get(0).getLocation(), plugin.getConfigHandler().getString("FillAction.ProceedSound.Custom"), (float) plugin.getConfigHandler().getConfig().getDouble("FillAction.ProceedSound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("FillAction.ProceedSound.Pitch")); 342 | } 343 | } 344 | finalBlockList[0].get(i).getWorld().getPlayers().forEach(player -> { 345 | if (finalBlockList[0].get(i).getLocation().distance(player.getLocation()) <= 1.0 && plugin.getConfigHandler().getBoolean("FillAction.Teleport")) { 346 | player.teleport(player.getLocation().add(0, 1, 0)); 347 | } 348 | }); 349 | } 350 | } else { 351 | boolean progress = true; 352 | for (Integer i : amountList[0]) { 353 | if (i > 0) { 354 | progress = false; 355 | } 356 | } 357 | if (progress && !queue.containsKey(cubeName.toUpperCase())) { 358 | Bukkit.getScheduler().cancelTask(fillTaskList.get(cubeName)); 359 | Player player = null; 360 | if (commandSender instanceof Player) { 361 | player = (Player) commandSender; 362 | } 363 | plugin.getCubicListener().proof(player, cube.getPos1(), true); 364 | } else if (progress && queue.containsKey(cubeName.toUpperCase())) { 365 | HashMap, List> mixedMap = queue.get(cubeName.toUpperCase()); 366 | materialList[0] = mixedMap.keySet().stream().collect(Collectors.toList()).get(0); 367 | amountList[0] = mixedMap.values().stream().collect(Collectors.toList()).get(0); 368 | random[0] = new AtomicInteger(new Random().nextInt(materialList[0].size())); 369 | queue.get(cubeName.toUpperCase()).remove(materialList[0], amountList[0]); 370 | if (mixedMap.size() < 1) { 371 | queue.remove(cubeName.toUpperCase()); 372 | } 373 | } else if (!progress) { 374 | if (materialList[0].size() > random[0].get()) { 375 | materialList[0].remove(materialList[0].get(random[0].get())); 376 | amountList[0].remove(amountList[0].get(random[0].get())); 377 | if (materialList[0].size() > 0) { 378 | random[0].set(new Random().nextInt(materialList[0].size())); 379 | } 380 | } 381 | } 382 | } 383 | } 384 | }, animationTicks, animationTicks); 385 | fillTaskList.put(cubeName, task); 386 | } else { 387 | fillTaskList.put(cubeName, 00000); 388 | while (amountList[0].size() > random[0].get() && amountList[0].get(random[0].get()) > 0 && i < finalBlockList[0].size()) { 389 | if (!plugin.getConfigHandler().getStringList("FillAction.DisabledBlocks").contains(finalBlockList[0].get(i).getType().toString().toUpperCase())) { 390 | fillCube(amountList, materialList, i, random, finalBlockList[0]); 391 | int finalI = i; 392 | finalBlockList[0].get(i).getWorld().getPlayers().forEach(player -> { 393 | if (finalBlockList[0].get(finalI).getLocation().distance(player.getLocation()) <= 1.0 && plugin.getConfigHandler().getBoolean("FillAction.Teleport")) { 394 | player.teleport(player.getLocation().add(0, 1, 0)); 395 | } 396 | }); 397 | } 398 | i++; 399 | } 400 | Player player = null; 401 | if (commandSender instanceof Player) { 402 | player = (Player) commandSender; 403 | } 404 | plugin.getCubicListener().proof(player, cube.getPos1(), true); 405 | } 406 | } 407 | } else { 408 | if (plugin.getConfigHandler().getBoolean("FillAction.Queue")) { 409 | if (queue.containsKey(cubeName.toUpperCase())) { 410 | queue.get(cubeName.toUpperCase()).put(materialList[0], amountList[0]); 411 | } else { 412 | HashMap hashMap = new HashMap(); 413 | hashMap.put(materialList[0], amountList[0]); 414 | queue.put(cubeName.toUpperCase(), hashMap); 415 | } 416 | commandSender.sendMessage("§aSuccessfully added your §efill-action §ato the §equeue§a!"); 417 | } else { 418 | commandSender.sendMessage("§cPlease wait until §e" + cubeName + "'s §crunning §eFill animation is finished!"); 419 | } 420 | } 421 | } else { 422 | commandSender.sendMessage("§cPlease configurate an amount for every block-type you've added to the command!"); 423 | } 424 | } else { 425 | commandSender.sendMessage("§cThe map §e" + cubeName + " §cdoes not exist! You can create it with §e/cc setup"); 426 | } 427 | } else { 428 | commandSender.sendMessage("§cUnknown command! Type §e/cc help §cto look up all commands!"); 429 | } 430 | } else { 431 | commandSender.sendMessage("§cUnknown command! Type §e/cc help §cto look up all commands!"); 432 | } 433 | } else { 434 | commandSender.sendMessage("§cYou do not have the permission to use that command! §cType §eop " + commandSender.getName() + " §cinto the server-console to get permission!"); 435 | } 436 | return false; 437 | } 438 | 439 | private void fillCube(List[] amountList, List[] materialList, int i, AtomicInteger[] random, List finalBlockList) { 440 | try { 441 | finalBlockList.get(i).setType(materialList[0].get(random[0].get())); 442 | } catch (IllegalArgumentException exception) { 443 | } 444 | amountList[0].set(random[0].get(), (amountList[0].get(random[0].get()) - 1)); 445 | random[0].set(new Random().nextInt(materialList[0].size())); 446 | } 447 | 448 | private boolean isInteger(String toTest) { 449 | try { 450 | Integer.parseInt(toTest); 451 | if (Integer.parseInt(toTest) <= 0) { 452 | return false; 453 | } 454 | return true; 455 | } catch (NumberFormatException exception) { 456 | return false; 457 | } 458 | } 459 | 460 | private boolean isBoolean(String toTest) { 461 | return (toTest.equals("true") || toTest.equals("false")); 462 | } 463 | 464 | private boolean isStringList(String toTest) { 465 | return toTest.split(",").length > 1; 466 | } 467 | 468 | private List toStringList(String to) { 469 | String[] split = to.split(","); 470 | if (split[0].startsWith("[")) { 471 | split[0] = split[0].substring(1, split[0].length()); 472 | } 473 | if (split[split.length - 1].endsWith("]")) { 474 | split[split.length - 1] = split[split.length - 1].substring(0, split[split.length - 1].length() - 1); 475 | } 476 | return Arrays.stream(split).collect(Collectors.toList()); 477 | } 478 | 479 | private boolean isMaterial(String toTest) { 480 | try { 481 | Material.valueOf(toTest); 482 | return true; 483 | } catch (IllegalArgumentException exception) { 484 | return false; 485 | } 486 | } 487 | } 488 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/command/completer/CubicCompleter.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.command.completer; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import org.bukkit.Material; 5 | import org.bukkit.command.Command; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.command.TabCompleter; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.concurrent.atomic.AtomicReference; 13 | 14 | public class CubicCompleter implements TabCompleter { 15 | 16 | private CubicCountdown plugin; 17 | 18 | public CubicCompleter(CubicCountdown plugin) { 19 | this.plugin = plugin; 20 | } 21 | 22 | @Override 23 | public List onTabComplete(CommandSender commandSender, Command command, String s, String[] strings) { 24 | if (command.getName().equalsIgnoreCase("cubiccountdown")) { 25 | List complete = new ArrayList<>(); 26 | List completer = new ArrayList<>(); 27 | if (strings.length == 1) { 28 | complete.add("setup"); 29 | complete.add("cancel"); 30 | complete.add("delete"); 31 | complete.add("reload"); 32 | complete.add("fill"); 33 | complete.add("clear"); 34 | complete.add("cubes"); 35 | complete.add("simulate"); 36 | complete.add("setEntry"); 37 | complete.add("help"); 38 | if (strings[0].length() > 0) { 39 | complete.forEach(s1 -> { 40 | if (s1.contains(strings[0])) { 41 | completer.add(s1); 42 | } 43 | }); 44 | } else { 45 | completer.addAll(complete); 46 | } 47 | } else if (strings.length == 2) { 48 | if (strings[0].equalsIgnoreCase("cancel")) { 49 | plugin.getCountdownList().forEach(countdownModule -> completer.add(countdownModule.getCubicSettings().getCube().getName())); 50 | } else if (strings[0].equalsIgnoreCase("delete") || strings[0].equalsIgnoreCase("fill") || strings[0].equalsIgnoreCase("clear")) { 51 | plugin.getCubicAPI().getCubes().forEach(cube -> completer.add(cube.getName())); 52 | } else if (strings[0].equalsIgnoreCase("setEntry") || strings[0].equalsIgnoreCase("set")) { 53 | plugin.getConfigHandler().getConfig().getKeys(true).forEach(string -> { 54 | if (string.length() > 0 && string.contains(strings[1]) || strings[1].length() == 0) { 55 | completer.add(string); 56 | } 57 | }); 58 | plugin.getDataHandler().getConfig().getKeys(true).forEach(string -> { 59 | if (string.length() > 0 && string.contains(strings[1]) || strings[1].length() == 0) { 60 | completer.add(string); 61 | } 62 | }); 63 | } else if (strings[0].equalsIgnoreCase("simulate")) { 64 | complete.clear(); 65 | complete.add("win"); 66 | complete.add("lose"); 67 | complete.add("help"); 68 | if (strings[1].length() > 0) { 69 | complete.forEach(s1 -> { 70 | if (s1.contains(strings[0])) { 71 | completer.add(s1); 72 | } 73 | }); 74 | } else { 75 | completer.addAll(complete); 76 | } 77 | } 78 | } else if (strings.length == 3) { 79 | if (strings[0].equalsIgnoreCase("fill")) { 80 | String value = strings[2]; 81 | if (value.contains(",")) { 82 | String[] split = value.split(","); 83 | final String[] matList = {""}; 84 | Arrays.stream(split).forEach(string -> { 85 | if (isMaterial(string.toUpperCase())) { 86 | matList[0] = matList[0] + string + ","; 87 | } 88 | }); 89 | if (matList[0].equalsIgnoreCase("")) { 90 | matList[0] = value; 91 | } 92 | Arrays.stream(Material.values()).forEach(material -> completer.add(matList[0] + material.toString().toLowerCase())); 93 | } else { 94 | Arrays.stream(Material.values()).forEach(material -> completer.add(material.toString().toLowerCase())); 95 | } 96 | } else if (strings[0].equalsIgnoreCase("setEntry") || strings[0].equalsIgnoreCase("set")) { 97 | if (plugin.getConfigHandler().keyExists(strings[1])) { 98 | completer.add(plugin.getConfigHandler().getConfig().get(strings[1]).toString()); 99 | } 100 | } else if (strings[0].equalsIgnoreCase("simulate")) { 101 | if (strings[1].equalsIgnoreCase("win") || strings[1].equalsIgnoreCase("lose") || strings[1].equalsIgnoreCase("help")) { 102 | plugin.getCubicAPI().getCubes().forEach(cube -> completer.add(cube.getName())); 103 | } 104 | } 105 | } else if (strings.length == 4) { 106 | if (strings[0].equalsIgnoreCase("fill")) { 107 | AtomicReference after = new AtomicReference<>(""); 108 | String value = strings[2]; 109 | if (value.contains(",")) { 110 | String[] split = value.split(","); 111 | Arrays.stream(split).forEach(string -> { 112 | after.set(after + "PLACE, "); 113 | }); 114 | } else { 115 | after.set("PLACE, "); 116 | } 117 | for (int i = 1; i < 50; i++) { 118 | completer.add(after.get().substring(0, after.get().length() - 2).replaceAll("PLACE", String.valueOf(i))); 119 | } 120 | } 121 | } 122 | return completer; 123 | } 124 | return null; 125 | } 126 | 127 | private boolean isMaterial(String toTest) { 128 | try { 129 | Material.valueOf(toTest); 130 | return true; 131 | } catch (IllegalArgumentException exception) { 132 | return false; 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/command/setup/CubicSetup.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.command.setup; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import org.bukkit.Location; 5 | import org.bukkit.Sound; 6 | import org.bukkit.entity.Player; 7 | import org.bukkit.event.EventHandler; 8 | import org.bukkit.event.Listener; 9 | import org.bukkit.event.block.Action; 10 | import org.bukkit.event.player.PlayerChatEvent; 11 | import org.bukkit.event.player.PlayerInteractEvent; 12 | import org.bukkit.inventory.EquipmentSlot; 13 | 14 | public class CubicSetup implements Listener { 15 | 16 | private Player player; 17 | private CubicCountdown plugin; 18 | private int step = 0; 19 | 20 | private Location pos1 = null; 21 | private Location pos2 = null; 22 | private Integer countdown = 10; 23 | private String name = ""; 24 | 25 | public CubicSetup(Player player, CubicCountdown plugin) { 26 | this.player = player; 27 | this.plugin = plugin; 28 | this.triggerNextStep(); 29 | plugin.getServer().getPluginManager().registerEvents(this, plugin); 30 | } 31 | 32 | public Player getPlayer() { 33 | return player; 34 | } 35 | 36 | public int getStep() { 37 | return step; 38 | } 39 | 40 | private void triggerNextStep() { 41 | step++; 42 | if (!plugin.getSetupList().containsKey(player)) { 43 | plugin.getSetupList().put(player, this); 44 | } 45 | switch (step) { 46 | case 1: 47 | player.sendMessage("§7Hello and welcome to the CubicCountdown setup! If you want to start and create your first countdown please follow the following steps:"); 48 | player.sendMessage("§cStep 1: §7Please §cright-click §7the §afirst §7edge of the (bedrock-)cube"); 49 | player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 2, 2); 50 | break; 51 | case 2: 52 | player.sendMessage("§cStep 2: §7Please §cright-click §7the §esecond §7edge of the (bedrock-)cube"); 53 | player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 2, 2); 54 | break; 55 | case 3: 56 | player.sendMessage("§cStep 3: §7Please write into the chat how long the countdown should go (in seconds) (press T to open the chat)"); 57 | player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 2, 2); 58 | break; 59 | case 4: 60 | player.sendMessage("§cStep 4: §7Please write the name of the cube into the chat (press T to open the chat)"); 61 | player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS, 2, 2); 62 | break; 63 | case 5: 64 | player.sendMessage("§aSetup completed! §7Next time, when the created cube is filled out with blocks, a countdown will start!"); 65 | plugin.getSetupList().remove(player); 66 | player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 2, 2); 67 | plugin.getDataHandler().setLocation("Cube." + name.toUpperCase() + ".Pos1", pos1); 68 | plugin.getDataHandler().setLocation("Cube." + name.toUpperCase() + ".Pos2", pos2); 69 | plugin.getConfigHandler().getConfig().set("Countdown", countdown); 70 | plugin.getDataHandler().getConfig().set("Cube." + name + ".Countdown", countdown); 71 | plugin.getDataHandler().save(); 72 | plugin.getConfigHandler().save(); 73 | break; 74 | } 75 | } 76 | 77 | @EventHandler 78 | public void onPlayerInteract(PlayerInteractEvent event) { 79 | if (event.getPlayer().equals(player)) { 80 | if (event.getAction() == Action.RIGHT_CLICK_BLOCK || event.getAction() == Action.RIGHT_CLICK_AIR) { 81 | if (event.getHand() == EquipmentSlot.HAND) { 82 | event.setCancelled(true); 83 | if (step == 1) { 84 | player.sendMessage("§aEdge 1 was set! §7You made a mistake? Just type §cCANCEL §7into the chat"); 85 | this.pos1 = event.getClickedBlock().getLocation(); 86 | triggerNextStep(); 87 | } else if (step == 2) { 88 | player.sendMessage("§aEdge 2 was set! §7You made a mistake? Just type §cCANCEL §7into the chat"); 89 | this.pos2 = event.getClickedBlock().getLocation(); 90 | triggerNextStep(); 91 | } else { 92 | event.setCancelled(false); 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | @EventHandler 100 | public void onPlayerChat(PlayerChatEvent event) { 101 | if (event.getPlayer().equals(player)) { 102 | event.setCancelled(true); 103 | String msg = event.getMessage().toUpperCase(); 104 | if (step > 0 && msg.equals("CANCEL")) { 105 | player.playSound(player.getLocation(), Sound.ITEM_GOAT_HORN_SOUND_0, 2, 2); 106 | player.sendMessage("§cThe setup was cancelled successfully! To restart the setup type §e/cc setup"); 107 | step = 0; 108 | plugin.getSetupList().remove(player); 109 | } else if (step == 3) { 110 | if (isInteger(msg)) { 111 | player.sendMessage("§aThe countdown was set to §e" + msg + " seconds§a! §7You made a mistake? Just type §cCANCEL §7into the chat"); 112 | this.countdown = Integer.parseInt(msg); 113 | triggerNextStep(); 114 | } else { 115 | player.sendMessage("§cThe message can only contain numbers!"); 116 | } 117 | } else if (step == 4) { 118 | player.sendMessage("§aThe cube-name was set to §e" + msg + "§a!"); 119 | this.name = msg; 120 | triggerNextStep(); 121 | } else { 122 | event.setCancelled(false); 123 | } 124 | } 125 | } 126 | 127 | private boolean isInteger(String string) { 128 | try { 129 | Integer.parseInt(string); 130 | return true; 131 | } catch (NumberFormatException exception) { 132 | return false; 133 | } 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/event/CubeCountdownCancelEvent.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.event; 2 | 3 | import de.timecoding.cc.util.CountdownModule; 4 | import de.timecoding.cc.util.CubicSettings; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.Cancellable; 7 | import org.bukkit.event.Event; 8 | import org.bukkit.event.HandlerList; 9 | 10 | public class CubeCountdownCancelEvent extends Event implements Cancellable { 11 | private static final HandlerList handlers = new HandlerList(); 12 | private boolean cancelled = false; 13 | private Player player; 14 | private CubicSettings cubicSettings; 15 | private CountdownModule countdownModule; 16 | 17 | public CubeCountdownCancelEvent(Player player, CountdownModule countdownModule) { 18 | this.player = player; 19 | this.countdownModule = countdownModule; 20 | this.cubicSettings = this.countdownModule.getCubicSettings(); 21 | } 22 | 23 | public static HandlerList getHandlerList() { 24 | return handlers; 25 | } 26 | 27 | public boolean whileFillAnimation() { 28 | if (cubicSettings.getCube() != null) { 29 | return countdownModule.getPlugin().getCubicAPI().getFillAnimationList().containsKey(cubicSettings.getCube().getName()); 30 | } 31 | return false; 32 | } 33 | 34 | public boolean whileClearAnimation() { 35 | if (cubicSettings.getCube() != null) { 36 | return countdownModule.getPlugin().getCubicAPI().getClearAnimationList().containsKey(cubicSettings.getCube().getName()); 37 | } 38 | return false; 39 | } 40 | 41 | public Player getPlayer() { 42 | return player; 43 | } 44 | 45 | public CountdownModule getCountdownModule() { 46 | return countdownModule; 47 | } 48 | 49 | public CubicSettings getCubicSettings() { 50 | return cubicSettings; 51 | } 52 | 53 | public void setCubicSettings(CubicSettings cubicSettings) { 54 | this.cubicSettings = cubicSettings; 55 | } 56 | 57 | @Override 58 | public boolean isCancelled() { 59 | return this.cancelled; 60 | } 61 | 62 | @Override 63 | public void setCancelled(boolean cancelled) { 64 | this.cancelled = cancelled; 65 | } 66 | 67 | @Override 68 | public HandlerList getHandlers() { 69 | return handlers; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/event/CubeCountdownEndEvent.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.event; 2 | 3 | import de.timecoding.cc.util.CountdownModule; 4 | import de.timecoding.cc.util.CubicSettings; 5 | import org.bukkit.event.Cancellable; 6 | import org.bukkit.event.Event; 7 | import org.bukkit.event.HandlerList; 8 | 9 | public class CubeCountdownEndEvent extends Event implements Cancellable { 10 | private static final HandlerList handlers = new HandlerList(); 11 | private boolean cancelled = false; 12 | private CubicSettings cubicSettings; 13 | private CountdownModule countdownModule; 14 | 15 | public CubeCountdownEndEvent(CountdownModule countdownModule) { 16 | this.countdownModule = countdownModule; 17 | this.cubicSettings = this.countdownModule.getCubicSettings(); 18 | } 19 | 20 | public static HandlerList getHandlerList() { 21 | return handlers; 22 | } 23 | 24 | public CountdownModule getCountdownModule() { 25 | return countdownModule; 26 | } 27 | 28 | public CubicSettings getCubicSettings() { 29 | return cubicSettings; 30 | } 31 | 32 | @Override 33 | public boolean isCancelled() { 34 | return this.cancelled; 35 | } 36 | 37 | @Override 38 | public void setCancelled(boolean cancelled) { 39 | this.cancelled = cancelled; 40 | } 41 | 42 | @Override 43 | public HandlerList getHandlers() { 44 | return handlers; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/event/CubeCountdownStartEvent.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.event; 2 | 3 | import de.timecoding.cc.util.CountdownModule; 4 | import de.timecoding.cc.util.CubicSettings; 5 | import org.bukkit.entity.Player; 6 | import org.bukkit.event.Cancellable; 7 | import org.bukkit.event.Event; 8 | import org.bukkit.event.HandlerList; 9 | 10 | public class CubeCountdownStartEvent extends Event implements Cancellable { 11 | private static final HandlerList handlers = new HandlerList(); 12 | private boolean cancelled = false; 13 | private Player player; 14 | private CubicSettings cubicSettings; 15 | private CountdownModule countdownModule; 16 | 17 | public CubeCountdownStartEvent(Player player, CountdownModule countdownModule) { 18 | this.player = player; 19 | this.countdownModule = countdownModule; 20 | this.cubicSettings = this.countdownModule.getCubicSettings(); 21 | } 22 | 23 | public static HandlerList getHandlerList() { 24 | return handlers; 25 | } 26 | 27 | public boolean whileFillAnimation() { 28 | if (cubicSettings.getCube() != null) { 29 | return countdownModule.getPlugin().getCubicAPI().getFillAnimationList().containsKey(cubicSettings.getCube().getName()); 30 | } 31 | return false; 32 | } 33 | 34 | public boolean whileClearAnimation() { 35 | if (cubicSettings.getCube() != null) { 36 | return countdownModule.getPlugin().getCubicAPI().getClearAnimationList().containsKey(cubicSettings.getCube().getName()); 37 | } 38 | return false; 39 | } 40 | 41 | public Player getPlayer() { 42 | return player; 43 | } 44 | 45 | public CountdownModule getCountdownModule() { 46 | return countdownModule; 47 | } 48 | 49 | public CubicSettings getCubicSettings() { 50 | return cubicSettings; 51 | } 52 | 53 | public void setCubicSettings(CubicSettings cubicSettings) { 54 | this.cubicSettings = cubicSettings; 55 | } 56 | 57 | 58 | @Override 59 | public boolean isCancelled() { 60 | return this.cancelled; 61 | } 62 | 63 | @Override 64 | public void setCancelled(boolean cancelled) { 65 | this.cancelled = cancelled; 66 | } 67 | 68 | @Override 69 | public HandlerList getHandlers() { 70 | return handlers; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/file/ConfigHandler.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.file; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import me.clip.placeholderapi.PlaceholderAPI; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.Material; 8 | import org.bukkit.configuration.file.YamlConfiguration; 9 | import org.bukkit.enchantments.Enchantment; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.nio.file.Files; 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | import java.util.Map; 17 | 18 | public class ConfigHandler { 19 | 20 | private final CubicCountdown plugin; 21 | private final String newconfigversion = "1.2.2"; 22 | private final boolean retry = false; 23 | public YamlConfiguration cfg = null; 24 | private File f = null; 25 | 26 | public ConfigHandler(CubicCountdown plugin) { 27 | this.plugin = plugin; 28 | init(); 29 | } 30 | 31 | public void init() { 32 | plugin.saveDefaultConfig(); 33 | f = new File(plugin.getDataFolder(), "config.yml"); 34 | cfg = YamlConfiguration.loadConfiguration(f); 35 | cfg.options().copyDefaults(true); 36 | checkForConfigUpdate(); 37 | } 38 | 39 | public String getPluginVersion() { 40 | return plugin.getDescription().getVersion(); 41 | } 42 | 43 | public void save() { 44 | try { 45 | cfg.save(f); 46 | } catch (IOException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | 51 | public void reload() { 52 | cfg = YamlConfiguration.loadConfiguration(f); 53 | } 54 | 55 | public YamlConfiguration getConfig() { 56 | return cfg; 57 | } 58 | 59 | public void setString(String key, String value) { 60 | cfg.set(key, value); 61 | save(); 62 | } 63 | 64 | public Integer getInteger(String key) { 65 | if (keyExists(key)) { 66 | return cfg.getInt(key); 67 | } 68 | return 0; 69 | } 70 | 71 | public String getString(String key) { 72 | if (keyExists(key)) { 73 | if (plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { 74 | return PlaceholderAPI.setPlaceholders(null, ChatColor.translateAlternateColorCodes('&', cfg.getString(key))); 75 | } 76 | return ChatColor.translateAlternateColorCodes('&', cfg.getString(key)); 77 | } 78 | return ""; 79 | } 80 | 81 | public Boolean getBoolean(String key) { 82 | if (keyExists(key)) { 83 | return cfg.getBoolean(key); 84 | } 85 | return false; 86 | } 87 | 88 | public List getStringList(String key) { 89 | if (keyExists(key)) { 90 | List list = getConfig().getStringList(key); 91 | list.replaceAll(msg -> msg.replace("&", "§")); 92 | if (plugin.getServer().getPluginManager().isPluginEnabled("PlaceholderAPI")) { 93 | list.replaceAll(msg -> PlaceholderAPI.setPlaceholders(null, msg)); 94 | } 95 | return list; 96 | } 97 | return new ArrayList<>(); 98 | } 99 | 100 | public Material getMaterialByString(String material) { 101 | for (Material mats : Material.values()) { 102 | if (mats.name().equalsIgnoreCase(material)) { 103 | return Material.valueOf(material); 104 | } 105 | } 106 | return Material.GRASS_BLOCK; 107 | } 108 | 109 | public Enchantment getEnchantmentByString(String enchant) { 110 | for (Enchantment ench : Enchantment.values()) { 111 | if (ench.toString().equalsIgnoreCase(enchant)) { 112 | return Enchantment.getByName(enchant); 113 | } 114 | } 115 | return Enchantment.ARROW_DAMAGE; 116 | } 117 | 118 | public String getNewestConfigVersion() { 119 | return this.newconfigversion; 120 | } 121 | 122 | public boolean configUpdateAvailable() { 123 | return !getNewestConfigVersion().equalsIgnoreCase(getString("config-version")); 124 | } 125 | 126 | public void checkForConfigUpdate() { 127 | Bukkit.getConsoleSender().sendMessage(ChatColor.YELLOW + "Checking for config updates..."); 128 | if (configUpdateAvailable()) { 129 | final Map quicksave = getConfig().getValues(true); 130 | File file = new File("plugins//CubicCountdown", "config.yml"); 131 | if (file.exists()) { 132 | try { 133 | Files.delete(file.toPath()); 134 | } catch (IOException e) { 135 | e.printStackTrace(); 136 | } 137 | Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Config Update found! (" + getNewestConfigVersion() + ") Updating config..."); 138 | Bukkit.getScheduler().runTaskLaterAsynchronously(this.plugin, new Runnable() { 139 | 140 | public void run() { 141 | plugin.saveResource("config.yml", true); 142 | reload(); 143 | save(); 144 | Bukkit.getConsoleSender().sendMessage(ChatColor.GREEN + "Config got updated!"); 145 | } 146 | }, 50); 147 | } else { 148 | Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "No Config found! Creating a new one..."); 149 | this.plugin.saveResource("config.yml", false); 150 | } 151 | } else { 152 | Bukkit.getConsoleSender().sendMessage(ChatColor.RED + "No config update found!"); 153 | } 154 | } 155 | 156 | 157 | public Integer getItemSlot(String key) { 158 | return getInteger("Item." + key + ".Slot"); 159 | } 160 | 161 | public Integer readItemSlot(String key) { 162 | return getInteger(key + ".Slot"); 163 | } 164 | 165 | public boolean keyExists(String key) { 166 | return cfg.get(key) != null; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/file/DataHandler.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.file; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.Location; 6 | import org.bukkit.configuration.file.YamlConfiguration; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | public class DataHandler { 12 | 13 | private final CubicCountdown plugin; 14 | private final ConfigHandler configHandler; 15 | public YamlConfiguration cfg = null; 16 | private File file = null; 17 | 18 | public DataHandler(CubicCountdown plugin) { 19 | this.plugin = plugin; 20 | this.configHandler = this.plugin.getConfigHandler(); 21 | init(); 22 | } 23 | 24 | public void init() { 25 | file = new File(plugin.getDataFolder(), "datas.yml"); 26 | if (!file.exists()) { 27 | plugin.saveResource("datas.yml", false); 28 | } 29 | cfg = YamlConfiguration.loadConfiguration(file); 30 | cfg.options().copyDefaults(true); 31 | } 32 | 33 | public String getPluginVersion() { 34 | return plugin.getDescription().getVersion(); 35 | } 36 | 37 | public void save() { 38 | try { 39 | cfg.save(file); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | public void reload() { 46 | cfg = YamlConfiguration.loadConfiguration(file); 47 | } 48 | 49 | public YamlConfiguration getConfig() { 50 | return cfg; 51 | } 52 | 53 | 54 | public void setString(String key, String value) { 55 | cfg.set(key, value); 56 | save(); 57 | } 58 | 59 | public void setLocation(String key, Location location) { 60 | cfg.set(key, location); 61 | save(); 62 | } 63 | 64 | public Integer getInteger(String key) { 65 | if (keyExists(key)) { 66 | return cfg.getInt(key); 67 | } 68 | return 0; 69 | } 70 | 71 | public Location getLocation(String key) { 72 | if (keyExists(key)) { 73 | return cfg.getLocation(key); 74 | } 75 | return null; 76 | } 77 | 78 | public String getString(String key) { 79 | if (keyExists(key)) { 80 | return ChatColor.translateAlternateColorCodes('&', cfg.getString(key)); 81 | } 82 | return ""; 83 | } 84 | 85 | public Boolean getBoolean(String key) { 86 | if (keyExists(key)) { 87 | return cfg.getBoolean(key); 88 | } 89 | return false; 90 | } 91 | 92 | public boolean keyExists(String key) { 93 | return cfg.get(key) != null; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/listener/CubicListener.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.listener; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import de.timecoding.cc.event.CubeCountdownCancelEvent; 5 | import de.timecoding.cc.event.CubeCountdownEndEvent; 6 | import de.timecoding.cc.event.CubeCountdownStartEvent; 7 | import de.timecoding.cc.file.DataHandler; 8 | import de.timecoding.cc.util.CountdownModule; 9 | import de.timecoding.cc.util.CubicSettings; 10 | import de.timecoding.cc.util.type.Cube; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.Location; 13 | import org.bukkit.entity.Player; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.Listener; 16 | import org.bukkit.event.block.BlockBreakEvent; 17 | import org.bukkit.event.block.BlockPlaceEvent; 18 | 19 | import java.util.ArrayList; 20 | import java.util.ConcurrentModificationException; 21 | import java.util.HashMap; 22 | import java.util.List; 23 | import java.util.concurrent.atomic.AtomicBoolean; 24 | import java.util.concurrent.atomic.AtomicReference; 25 | 26 | public class CubicListener implements Listener { 27 | private CubicCountdown plugin; 28 | private int checkID = -1; 29 | 30 | //WIN & LOSE & HELP COUNTER + CommandExecuter 31 | private HashMap> lastCauses = new HashMap<>(); 32 | 33 | public CubicListener(CubicCountdown plugin) { 34 | this.plugin = plugin; 35 | this.startChecker(); 36 | } 37 | 38 | @EventHandler 39 | public void onCubeCountdownEnd(CubeCountdownEndEvent event) { 40 | if (event.getCubicSettings().getCube() != null) { 41 | plugin.getCubicAPI().increaseWins(event.getCubicSettings().getCube()); 42 | } 43 | } 44 | 45 | @EventHandler 46 | public void onCubeCountdownCancel(CubeCountdownCancelEvent event) { 47 | if (event.getCubicSettings().getCube() != null) { 48 | executeCommands("OnCountdownCancel", event.getCubicSettings().getCube().getName()); 49 | plugin.getCubicAPI().increaseLoses(event.getCubicSettings().getCube()); 50 | if (!plugin.getConfigHandler().getBoolean("Reverse") && event.whileFillAnimation() || plugin.getConfigHandler().getBoolean("Reverse") && event.whileClearAnimation()) { 51 | plugin.getCubicAPI().increaseHelpCounter(event.getCubicSettings().getCube()); 52 | } 53 | } 54 | } 55 | 56 | // 57 | 58 | //CAUSE CHECKER 59 | 60 | @EventHandler 61 | public void onCubeCountdownStart(CubeCountdownStartEvent event) { 62 | if (event.getCubicSettings().getCube() != null) { 63 | executeCommands("OnCountdownStart", event.getCubicSettings().getCube().getName()); 64 | if (!plugin.getConfigHandler().getBoolean("Reverse") && event.whileFillAnimation() || plugin.getConfigHandler().getBoolean("Reverse") && event.whileClearAnimation()) { 65 | plugin.getCubicAPI().increaseHelpCounter(event.getCubicSettings().getCube()); 66 | } 67 | } 68 | } 69 | 70 | public void executeCommands(String key, String map) { 71 | plugin.getConfigHandler().getStringList("Commands." + key + "").forEach(command -> { 72 | if (command.length() > 0) { 73 | if (command.startsWith(" ") || command.startsWith("/")) { 74 | command = command.substring(0, command.length() - 1); 75 | } 76 | Cube cube = plugin.getCubicAPI().getCubeByName(map); 77 | if (cube != null) { 78 | Bukkit.dispatchCommand(Bukkit.getConsoleSender(), plugin.getCubicAPI().replaceWithPlaceholders(cube, command)); 79 | } 80 | } 81 | }); 82 | } 83 | 84 | // 85 | 86 | @EventHandler 87 | public void onBlockPlace(BlockPlaceEvent event) { 88 | addCause(plugin.getCubicAPI().getCubeAtLocation(event.getBlock().getLocation()), event.getPlayer()); 89 | } 90 | 91 | @EventHandler 92 | public void onBlockBreak(BlockBreakEvent event) { 93 | addCause(plugin.getCubicAPI().getCubeAtLocation(event.getBlock().getLocation()), event.getPlayer()); 94 | } 95 | 96 | private boolean checkerRunning() { 97 | return (checkID != -1); 98 | } 99 | 100 | private void startChecker() { 101 | if (!checkerRunning()) { 102 | Integer checker = 40; 103 | if (plugin.getConfigHandler().keyExists("CheckerTicks")) { 104 | checker = plugin.getConfigHandler().getInteger("CheckerTicks"); 105 | } 106 | Integer finalChecker = checker; 107 | this.checkID = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { 108 | @Override 109 | public void run() { 110 | if (plugin.getConfigHandler().keyExists("CheckerTicks") && plugin.getConfigHandler().getInteger("CheckerTicks") != finalChecker) { 111 | stopChecker(); 112 | startChecker(); 113 | } 114 | plugin.getCubicAPI().getCubes().forEach(cube -> { 115 | if (proofForAll(lastCauses.get(cube.getName()), cube.getPos1())) { 116 | clearCauses(cube); 117 | } 118 | }); 119 | ; 120 | } 121 | }, checker, checker); 122 | } 123 | } 124 | 125 | private void addCause(Cube cube, Player player) { 126 | if (cube != null) { 127 | List last = new ArrayList<>(); 128 | if (lastCauses.containsKey(cube.getName())) { 129 | last = lastCauses.get(cube.getName()); 130 | lastCauses.remove(cube.getName()); 131 | } 132 | if (!last.contains(player)) { 133 | last.add(player); 134 | } 135 | lastCauses.put(cube.getName(), last); 136 | } 137 | } 138 | 139 | private void clearCauses(Cube cube) { 140 | if (lastCauses.containsKey(cube.getName())) { 141 | lastCauses.remove(cube.getName()); 142 | } 143 | } 144 | 145 | private void stopChecker() { 146 | if (checkerRunning()) { 147 | Bukkit.getScheduler().cancelTask(checkID); 148 | checkID = -1; 149 | } 150 | } 151 | 152 | public boolean proofForAll(List playerList, Location origin) { 153 | AtomicBoolean proofed = new AtomicBoolean(false); 154 | if (playerList != null && playerList.size() > 0) { 155 | playerList.forEach(player -> { 156 | if (!proofed.get() && proof(player, origin, false)) { 157 | proofed.set(true); 158 | } 159 | }); 160 | } else { 161 | proof(null, origin, false); 162 | } 163 | return proofed.get(); 164 | } 165 | 166 | public boolean proof(Player player, Location origin, boolean commandOrigin) { 167 | AtomicBoolean r = new AtomicBoolean(false); 168 | Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { 169 | @Override 170 | public void run() { 171 | DataHandler dataHandler = plugin.getDataHandler(); 172 | AtomicReference atomicCube = new AtomicReference<>(); 173 | plugin.getCubicAPI().getCubes().forEach(searchedCube -> { 174 | if (searchedCube.inCube(origin)) { 175 | atomicCube.set(searchedCube); 176 | boolean reverse = plugin.getConfigHandler().getBoolean("Reverse"); 177 | if (!reverse && atomicCube.get() != null && atomicCube.get().filledOut() || reverse && atomicCube.get() != null && atomicCube.get().empty()) { 178 | CubicSettings cubicSettings = new CubicSettings(plugin, true); 179 | cubicSettings.setCube(atomicCube.get()); 180 | if (plugin.getConfigHandler().getBoolean("Viewer.AllPlayers")) { 181 | Bukkit.getOnlinePlayers().forEach(onlinePlayer -> cubicSettings.addPlayer(onlinePlayer)); 182 | } else if (plugin.getConfigHandler().getBoolean("Viewer.AllPlayersInCubeRadius.Enabled")) { 183 | atomicCube.get().blockList(true).forEach(block -> { 184 | Bukkit.getOnlinePlayers().forEach(onlinePlayer -> { 185 | if (block.getLocation().distance(onlinePlayer.getLocation()) < plugin.getConfigHandler().getInteger("Viewer.AllPlayersInCubeRadius.RadiusInBlocks")) { 186 | cubicSettings.addPlayer(onlinePlayer); 187 | } 188 | }); 189 | }); 190 | } 191 | if (player != null && !cubicSettings.playerList().contains(player)) { 192 | cubicSettings.addPlayer(player); 193 | } 194 | if (atomicCube.get() != null && plugin.getCubicAPI().getCountdownModuleFromCube(atomicCube.get()) == null) { 195 | CountdownModule countdownModule = new CountdownModule(cubicSettings); 196 | CubeCountdownStartEvent event = new CubeCountdownStartEvent(player, countdownModule); 197 | Bukkit.getPluginManager().callEvent(event); 198 | if (!event.isCancelled()) { 199 | countdownModule.start(); 200 | r.set(true); 201 | } 202 | if (plugin.getConfigHandler().getBoolean("Reverse") && plugin.getCubicAPI().getClearAnimationList().containsKey(atomicCube.get().getName())) { 203 | Bukkit.getScheduler().cancelTask(plugin.getCubicAPI().getClearAnimationList().get(atomicCube.get().getName())); 204 | plugin.getCubicAPI().getClearAnimationList().remove(atomicCube.get().getName()); 205 | } else if (plugin.getCubicAPI().getFillAnimationList().containsKey(atomicCube.get().getName())) { 206 | Bukkit.getScheduler().cancelTask(plugin.getCubicAPI().getFillAnimationList().get(atomicCube.get().getName())); 207 | plugin.getCubicAPI().getFillAnimationList().remove(atomicCube.get().getName()); 208 | } 209 | } 210 | } else if (!reverse && atomicCube.get() != null && !atomicCube.get().filledOut() || reverse && atomicCube.get() != null && !atomicCube.get().empty()) { 211 | try { 212 | plugin.getCountdownList().forEach(countdownModule -> { 213 | if (countdownModule.getCubicSettings().getCube() != null && countdownModule.getCubicSettings().getCube().isSimilar(atomicCube.get())) { 214 | if (atomicCube.get() != null && plugin.getCubicAPI().getCountdownModuleFromCube(atomicCube.get()) != null) { 215 | CubeCountdownCancelEvent event = new CubeCountdownCancelEvent(player, countdownModule); 216 | Bukkit.getPluginManager().callEvent(event); 217 | if (!event.isCancelled()) { 218 | countdownModule.cancel(); 219 | r.set(true); 220 | } 221 | if (!plugin.getConfigHandler().getBoolean("Reverse") && plugin.getCubicAPI().getClearAnimationList().containsKey(atomicCube.get().getName())) { 222 | Bukkit.getScheduler().cancelTask(plugin.getCubicAPI().getClearAnimationList().get(atomicCube.get().getName())); 223 | plugin.getCubicAPI().getClearAnimationList().remove(atomicCube.get().getName()); 224 | } else if (plugin.getCubicAPI().getFillAnimationList().containsKey(atomicCube.get().getName())) { 225 | Bukkit.getScheduler().cancelTask(plugin.getCubicAPI().getFillAnimationList().get(atomicCube.get().getName())); 226 | plugin.getCubicAPI().getFillAnimationList().remove(atomicCube.get().getName()); 227 | } 228 | } 229 | } 230 | }); 231 | //TODO 232 | } catch (ConcurrentModificationException exception) { 233 | } 234 | } 235 | if (commandOrigin) { 236 | plugin.getCubicAPI().getClearAnimationList().remove(atomicCube.get().getName()); 237 | plugin.getCubicAPI().getFillAnimationList().remove(atomicCube.get().getName()); 238 | } 239 | } 240 | }); 241 | } 242 | }, 1); 243 | return r.get(); 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/util/CountdownModule.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.util; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import de.timecoding.cc.event.CubeCountdownEndEvent; 5 | import de.timecoding.cc.util.type.CubicStateType; 6 | import org.bukkit.*; 7 | import org.bukkit.block.Block; 8 | import org.bukkit.entity.EntityType; 9 | import org.bukkit.entity.Firework; 10 | import org.bukkit.entity.Player; 11 | import org.bukkit.inventory.meta.FireworkMeta; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | public class CountdownModule { 17 | 18 | private CubicCountdown plugin; 19 | private CubicSettings cubicSettings; 20 | 21 | private CountdownModule countdownModule = this; 22 | 23 | private int countdownId = -1; 24 | private int seconds = -1; 25 | private int fallbackId = -1; 26 | 27 | public CountdownModule(CubicSettings settings) { 28 | this.cubicSettings = settings; 29 | this.plugin = this.cubicSettings.getPlugin(); 30 | } 31 | 32 | public void start() { 33 | start(false); 34 | } 35 | 36 | public void start(boolean ignoreStartTitle) { 37 | if (!isRunning() || ignoreStartTitle) { 38 | boolean next = true; 39 | if (!ignoreStartTitle) { 40 | for (CountdownModule countdownModule : plugin.getCountdownList()) { 41 | if (getCubicSettings().getCube() != null && countdownModule.getCubicSettings().getCube() != null && countdownModule.getCubicSettings().getCube().getName().equalsIgnoreCase(getCubicSettings().getCube().getName())) { 42 | next = false; 43 | } 44 | } 45 | } 46 | if (next) { 47 | if (!ignoreStartTitle) { 48 | this.seconds = this.cubicSettings.getCountdownSeconds(); 49 | plugin.getCountdownList().add(this); 50 | if (cubicSettings.getStartDelay() >= 20 && cubicSettings.hasTitle(CubicStateType.START)) { 51 | sendTitle(CubicStateType.START); 52 | } 53 | } 54 | Integer startDelay = 0; 55 | if (!ignoreStartTitle) { 56 | startDelay = cubicSettings.getStartDelay(); 57 | } 58 | countdownModule = this; 59 | this.countdownId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { 60 | @Override 61 | public void run() { 62 | if (plugin.getConfigHandler().keyExists("Settings.CUSTOM." + seconds) && seconds > 0) { 63 | String base = "Settings.CUSTOM." + seconds + "."; 64 | if (plugin.getConfigHandler().keyExists(base + "Title")) { 65 | String subtitle = ""; 66 | if (plugin.getConfigHandler().keyExists(base + "Subtitle")) { 67 | subtitle = plugin.getConfigHandler().getString(base + "Subtitle"); 68 | } 69 | sendTitle(plugin.getConfigHandler().getString(base + "Title").replace("%seconds%", String.valueOf(seconds)), subtitle); 70 | } 71 | cubicSettings.playerList().forEach(player -> { 72 | if (plugin.getConfigHandler().getString(base + "Sound.Custom").equalsIgnoreCase("")) { 73 | player.playSound(player.getLocation(), Sound.valueOf(plugin.getConfigHandler().getString(base + "Sound.Sound")), (float) plugin.getConfigHandler().getConfig().getDouble(base + ".Sound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble(base + ".Sound.Pitch")); 74 | } else { 75 | player.playSound(player.getLocation(), plugin.getConfigHandler().getString(base + "Sound.Custom"), (float) plugin.getConfigHandler().getConfig().getDouble(base + ".Sound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble(base + ".Sound.Pitch")); 76 | } 77 | }); 78 | if (plugin.getConfigHandler().keyExists(base + "Ticks")) { 79 | extraTicks(base); 80 | } 81 | } else { 82 | sendTitle(cubicSettings.getTitle(CubicStateType.PROCEED).replace("%seconds%", String.valueOf(seconds)), cubicSettings.getSubtitle(CubicStateType.PROCEED)); 83 | playSound(CubicStateType.PROCEED); 84 | } 85 | if (seconds <= 0) { 86 | CubeCountdownEndEvent event = new CubeCountdownEndEvent(countdownModule); 87 | Bukkit.getPluginManager().callEvent(event); 88 | if (!event.isCancelled()) { 89 | detonateFirework(); 90 | sendTitle(CubicStateType.END); 91 | cubicSettings.fireworkLocations().forEach(location -> { 92 | Firework firework = (Firework) location.getWorld().spawnEntity(location, EntityType.FIREWORK); 93 | firework.setFireworkMeta(cubicSettings.getFireworkMeta()); 94 | firework.detonate(); 95 | }); 96 | clearCube(); 97 | plugin.getCubicListener().executeCommands("OnCountdownEnd", event.getCubicSettings().getCube().getName()); 98 | stop(); 99 | } 100 | } 101 | seconds--; 102 | } 103 | }, startDelay, 20); 104 | } 105 | } 106 | } 107 | 108 | private void sendTitle(CubicStateType cubicStateType) { 109 | cubicSettings.playerList().forEach(player -> { 110 | if (cubicSettings.hasTitle(cubicStateType)) { 111 | player.sendTitle(cubicSettings.getTitle(cubicStateType), cubicSettings.getSubtitle(cubicStateType)); 112 | } 113 | }); 114 | //TRIGGERS AT THE SAME TIME 115 | this.playSound(cubicStateType); 116 | } 117 | 118 | private void playSound(CubicStateType cubicStateType) { 119 | cubicSettings.playerList().forEach(player -> { 120 | if (cubicSettings.hasSound(cubicStateType)) { 121 | if (plugin.getConfigHandler().getString("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Custom").equalsIgnoreCase("")) { 122 | player.playSound(player.getLocation(), cubicSettings.getSound(cubicStateType), (float) plugin.getConfigHandler().getConfig().getDouble("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Pitch")); 123 | } else { 124 | player.playSound(player.getLocation(), plugin.getConfigHandler().getString("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Custom"), (float) plugin.getConfigHandler().getConfig().getDouble("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Volume"), (float) plugin.getConfigHandler().getConfig().getDouble("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Pitch")); 125 | } 126 | } 127 | }); 128 | } 129 | 130 | private void playSound(Sound sound) { 131 | cubicSettings.playerList().forEach(player -> { 132 | player.playSound(player.getLocation(), sound, 2, 2); 133 | }); 134 | } 135 | 136 | public void clearCube() { 137 | if (plugin.getConfigHandler().getBoolean("ClearCube.Enabled") && getCubicSettings().getCube() != null) { 138 | getCubicSettings().getCube().blockList(false).forEach(block -> { 139 | List stringList = plugin.getConfigHandler().getStringList("ClearCube.DisabledBlocks"); 140 | if (!stringList.contains(block.getType().toString())) { 141 | block.setType(Material.AIR); 142 | } 143 | }); 144 | } 145 | } 146 | 147 | private void sendTitle(String title, String subtitle) { 148 | cubicSettings.playerList().forEach(player -> player.sendTitle(title, subtitle)); 149 | } 150 | 151 | public void cancel() { 152 | if (isRunning()) { 153 | sendTitle(CubicStateType.CANCELLED); 154 | if (fallbackId > -1) { 155 | Bukkit.getScheduler().cancelTask(fallbackId); 156 | fallbackId = -1; 157 | } 158 | this.stop(); 159 | } 160 | } 161 | 162 | public void stop() { 163 | if (isRunning()) { 164 | Bukkit.getScheduler().cancelTask(this.countdownId); 165 | plugin.getCountdownList().remove(this); 166 | this.countdownId = -1; 167 | } 168 | } 169 | 170 | private void extraTicks(String base) { 171 | if (isRunning()) { 172 | Bukkit.getScheduler().cancelTask(this.countdownId); 173 | this.countdownId = -1; 174 | this.fallbackId = Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() { 175 | @Override 176 | public void run() { 177 | fallbackId = -1; 178 | start(true); 179 | } 180 | }, plugin.getConfigHandler().getInteger(base + "Ticks")); 181 | } 182 | } 183 | 184 | public void detonateFirework() { 185 | if (plugin.getConfigHandler().getBoolean("Firework.ForPlayer") && getCubicSettings().playerList().size() > 0) { 186 | for (Player player : getCubicSettings().playerList()) { 187 | Firework firework = (Firework) player.getWorld().spawnEntity(player.getLocation(), EntityType.FIREWORK); 188 | FireworkMeta fireworkMeta = firework.getFireworkMeta(); 189 | fireworkMeta.setPower(plugin.getConfigHandler().getInteger("Firework.Power")); 190 | this.getFireworkEffects().forEach(fireworkEffect -> { 191 | fireworkMeta.addEffect(fireworkEffect); 192 | }); 193 | firework.setFireworkMeta(fireworkMeta); 194 | firework.detonate(); 195 | } 196 | } 197 | if (plugin.getConfigHandler().getBoolean("Firework.AtEachBlock")) { 198 | for (Block block : getCubicSettings().getCube().blockList(true)) { 199 | Firework firework = (Firework) block.getLocation().getWorld().spawnEntity(block.getLocation(), EntityType.FIREWORK); 200 | FireworkMeta fireworkMeta = firework.getFireworkMeta(); 201 | fireworkMeta.setPower(plugin.getConfigHandler().getInteger("Firework.Power")); 202 | this.getFireworkEffects().forEach(fireworkEffect -> { 203 | fireworkMeta.addEffect(fireworkEffect); 204 | }); 205 | firework.setFireworkMeta(fireworkMeta); 206 | firework.detonate(); 207 | } 208 | } 209 | } 210 | 211 | private List getFireworkEffects() { 212 | List list = new ArrayList<>(); 213 | List stringList = plugin.getConfigHandler().getStringList("Firework.Colors"); 214 | List fireworkEffects = new ArrayList<>(); 215 | if (stringList != null) { 216 | stringList.forEach(s -> { 217 | fireworkEffects.add(FireworkEffect.builder().withColor(getColor(s)).build()); 218 | }); 219 | } 220 | return fireworkEffects; 221 | } 222 | 223 | private Color getColor(String s) { 224 | List list = new ArrayList<>(); 225 | switch (s) { 226 | case "RED": 227 | list.add(Color.RED); 228 | break; 229 | case "AQUA": 230 | list.add(Color.AQUA); 231 | break; 232 | case "BLUE": 233 | list.add(Color.BLUE); 234 | break; 235 | case "LIME": 236 | list.add(Color.LIME); 237 | break; 238 | case "OLIVE": 239 | list.add(Color.OLIVE); 240 | break; 241 | case "ORANGE": 242 | list.add(Color.ORANGE); 243 | break; 244 | case "PURPLE": 245 | list.add(Color.PURPLE); 246 | break; 247 | case "WHITE": 248 | list.add(Color.WHITE); 249 | break; 250 | case "BLACK": 251 | list.add(Color.BLACK); 252 | break; 253 | case "FUCHSIA": 254 | list.add(Color.FUCHSIA); 255 | break; 256 | case "GREY": 257 | list.add(Color.GRAY); 258 | break; 259 | case "GREEN": 260 | list.add(Color.GREEN); 261 | break; 262 | case "MAROON": 263 | list.add(Color.MAROON); 264 | break; 265 | case "NAVY": 266 | list.add(Color.NAVY); 267 | break; 268 | case "SILVER": 269 | list.add(Color.SILVER); 270 | break; 271 | case "YELLOW": 272 | list.add(Color.YELLOW); 273 | break; 274 | case "TEAL": 275 | list.add(Color.TEAL); 276 | break; 277 | } 278 | if (list.size() == 0) { 279 | return Color.GRAY; 280 | } else { 281 | return list.get(0); 282 | } 283 | } 284 | 285 | public boolean isRunning() { 286 | return (seconds != -1); 287 | } 288 | 289 | public int getSecondsLeft() { 290 | return seconds; 291 | } 292 | 293 | public int getCountdownId() { 294 | return countdownId; 295 | } 296 | 297 | public CubicSettings getCubicSettings() { 298 | return cubicSettings; 299 | } 300 | 301 | public CubicCountdown getPlugin() { 302 | return plugin; 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/util/CubicAPI.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.util; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import de.timecoding.cc.command.setup.CubicSetup; 5 | import de.timecoding.cc.util.type.Cube; 6 | import net.md_5.bungee.api.ChatMessageType; 7 | import net.md_5.bungee.api.chat.TextComponent; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.Location; 10 | import org.bukkit.block.Block; 11 | import org.bukkit.entity.Player; 12 | 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.concurrent.atomic.AtomicBoolean; 17 | import java.util.concurrent.atomic.AtomicReference; 18 | 19 | public class CubicAPI { 20 | 21 | private CubicCountdown plugin; 22 | private List countdownList = new ArrayList<>(); 23 | private HashMap setupList = new HashMap<>(); 24 | 25 | private HashMap fillAnimationList = new HashMap<>(); 26 | private HashMap clearAnimationList = new HashMap<>(); 27 | private int actionRunnable = -1; 28 | private HashMap session_wins = new HashMap<>(); 29 | private HashMap session_loses = new HashMap<>(); 30 | private HashMap session_helps = new HashMap<>(); 31 | 32 | public CubicAPI(CubicCountdown plugin) { 33 | this.plugin = plugin; 34 | } 35 | 36 | public List getCountdownList() { 37 | return countdownList; 38 | } 39 | 40 | public HashMap getSetupList() { 41 | return setupList; 42 | } 43 | 44 | public void startActionbar() { 45 | if (!actionbarRunning()) { 46 | actionRunnable = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable() { 47 | @Override 48 | public void run() { 49 | if (!plugin.getConfigHandler().getBoolean("Actionbar.Enabled")) { 50 | cancelActionbar(); 51 | } else { 52 | Bukkit.getOnlinePlayers().forEach(player -> { 53 | Cube cube = getNearestCube(player); 54 | if (cube != null) { 55 | player.spigot().sendMessage(ChatMessageType.ACTION_BAR, TextComponent.fromLegacyText(plugin.getCubicAPI().replaceWithPlaceholders(cube, plugin.getConfigHandler().getString("Actionbar.Format")))); 56 | } 57 | }); 58 | } 59 | } 60 | }, 0, 20); 61 | } 62 | } 63 | 64 | public void cancelActionbar() { 65 | if (actionbarRunning()) { 66 | Bukkit.getScheduler().cancelTask(actionRunnable); 67 | actionRunnable = -1; 68 | } 69 | } 70 | 71 | private boolean actionbarRunning() { 72 | return (actionRunnable != -1); 73 | } 74 | 75 | public HashMap getFillAnimationList() { 76 | return fillAnimationList; 77 | } 78 | 79 | public HashMap getClearAnimationList() { 80 | return clearAnimationList; 81 | } 82 | 83 | public String replaceWithPlaceholders(Cube cube, String title) { 84 | return title.replaceAll("%total_win_counter%", plugin.getCubicAPI().getTotalWins(cube).toString()) 85 | .replaceAll("%total_lose_counter%", plugin.getCubicAPI().getTotalLoses(cube).toString()) 86 | .replaceAll("%total_games_played%", plugin.getCubicAPI().getTotalGamesPlayed(cube).toString()) 87 | .replaceAll("%total_help_counter%", plugin.getCubicAPI().getTotalHelps(cube).toString()) 88 | .replaceAll("%session_win_counter%", plugin.getCubicAPI().getSessionWins(cube).toString()) 89 | .replaceAll("%session_lose_counter%", plugin.getCubicAPI().getSessionLoses(cube).toString()) 90 | .replaceAll("%session_games_played%", plugin.getCubicAPI().getSessionGamesPlayed(cube).toString()) 91 | .replaceAll("%session_help_counter%", plugin.getCubicAPI().getSessionHelps(cube).toString()) 92 | .replaceAll("%cube_height%", String.valueOf(cube.height())) 93 | .replaceAll("%cube_current_height%", String.valueOf(cube.currentHeight())) 94 | .replaceAll("%map%", cube.getName()); 95 | } 96 | 97 | public List getCubes() { 98 | List cubeStringList = new ArrayList<>(); 99 | for (String key : plugin.getDataHandler().getConfig().getValues(true).keySet()) { 100 | String[] keys = key.split("\\."); 101 | if (keys.length == 3 && !cubeStringList.contains(keys[1])) { 102 | cubeStringList.add(keys[1]); 103 | } 104 | } 105 | List cubeList = new ArrayList<>(); 106 | cubeStringList.forEach(string -> cubeList.add(new Cube(string, plugin.getDataHandler().getLocation("Cube." + string + ".Pos1"), plugin.getDataHandler().getLocation("Cube." + string + ".Pos2"), plugin))); 107 | return cubeList; 108 | } 109 | 110 | public Cube getCubeAtLocation(Location location) { 111 | for (Cube cube : getCubes()) { 112 | for (Block block : cube.blockList(true)) { 113 | if (location.equals(block.getLocation())) { 114 | return cube; 115 | } 116 | } 117 | } 118 | return null; 119 | } 120 | 121 | public Cube getCubeByName(String cubeName) { 122 | for (Cube cube : getCubes()) { 123 | if (cube.getName().equalsIgnoreCase(cubeName)) { 124 | return cube; 125 | } 126 | } 127 | return null; 128 | } 129 | 130 | public Cube getNearestCube(Player player) { 131 | for (Cube cube : getCubes()) { 132 | for (Block block : cube.blockList(true)) { 133 | if (block.getLocation().distance(player.getLocation()) < plugin.getConfigHandler().getInteger("CubeRadius")) { 134 | return cube; 135 | } 136 | } 137 | } 138 | return null; 139 | } 140 | 141 | public boolean viewingCountdown(Player player) { 142 | AtomicBoolean viewing = new AtomicBoolean(false); 143 | countdownList.forEach(countdownModule -> { 144 | countdownModule.getCubicSettings().playerList().forEach(player1 -> { 145 | if (player1.getUniqueId().toString().equals(player.getUniqueId().toString())) { 146 | viewing.set(true); 147 | } 148 | }); 149 | }); 150 | return viewing.get(); 151 | } 152 | 153 | public void removeCountdown(CountdownModule countdownModule) { 154 | countdownList.forEach(countdownModule1 -> { 155 | if (countdownModule1.getCountdownId() == countdownModule.getCountdownId()) { 156 | countdownList.remove(countdownModule1); 157 | } 158 | }); 159 | } 160 | 161 | public CountdownModule getCountdownModuleFromCube(Cube cube) { 162 | AtomicReference finalCountdownModule = new AtomicReference<>(); 163 | countdownList.forEach(countdownModule -> { 164 | if (countdownModule.getCubicSettings().getCube().isSimilar(cube)) { 165 | finalCountdownModule.set(countdownModule); 166 | } 167 | }); 168 | return finalCountdownModule.get(); 169 | } 170 | 171 | public Integer getTotalWins(Cube cube) { 172 | return plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".WinCounter"); 173 | } 174 | 175 | public Integer getTotalWins(String cube) { 176 | return plugin.getDataHandler().getInteger("Cube." + cube.toUpperCase() + ".WinCounter"); 177 | } 178 | 179 | public void increaseTotalWins(Cube cube) { 180 | Integer integer = 1; 181 | if (plugin.getDataHandler().keyExists("Cube." + cube.getName() + ".WinCounter")) { 182 | integer = (plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".WinCounter") + 1); 183 | } 184 | plugin.getDataHandler().getConfig().set("Cube." + cube.getName() + ".WinCounter", integer); 185 | plugin.getDataHandler().save(); 186 | } 187 | 188 | public Integer getTotalHelps(Cube cube) { 189 | return plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".HelpCounter"); 190 | } 191 | 192 | public Integer getTotalHelps(String cube) { 193 | return plugin.getDataHandler().getInteger("Cube." + cube.toUpperCase() + ".HelpCounter"); 194 | } 195 | 196 | public void increaseTotalHelps(Cube cube) { 197 | Integer integer = 1; 198 | if (plugin.getDataHandler().keyExists("Cube." + cube.getName() + ".HelpCounter")) { 199 | integer = (plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".HelpCounter") + 1); 200 | } 201 | plugin.getDataHandler().getConfig().set("Cube." + cube.getName() + ".HelpCounter", integer); 202 | plugin.getDataHandler().save(); 203 | } 204 | 205 | public Integer getTotalLoses(Cube cube) { 206 | return plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".LoseCounter"); 207 | } 208 | 209 | public Integer getTotalLoses(String cube) { 210 | return plugin.getDataHandler().getInteger("Cube." + cube.toUpperCase() + ".LoseCounter"); 211 | } 212 | 213 | public Integer getTotalGamesPlayed(Cube cube) { 214 | return getTotalGamesPlayed(cube.getName()); 215 | } 216 | 217 | public Integer getTotalGamesPlayed(String cube) { 218 | return (getTotalWins(cube) + getTotalLoses(cube)); 219 | } 220 | 221 | public void increaseTotalLoses(Cube cube) { 222 | Integer integer = 1; 223 | if (plugin.getDataHandler().keyExists("Cube." + cube.getName() + ".LoseCounter")) { 224 | integer = (plugin.getDataHandler().getInteger("Cube." + cube.getName() + ".LoseCounter") + 1); 225 | } 226 | plugin.getDataHandler().getConfig().set("Cube." + cube.getName() + ".LoseCounter", integer); 227 | plugin.getDataHandler().save(); 228 | } 229 | 230 | public Integer getSessionWins(Cube cube) { 231 | return getSessionWins(cube.getName()); 232 | } 233 | 234 | public Integer getSessionWins(String cube) { 235 | if (session_wins.containsKey(cube)) { 236 | return session_wins.get(cube); 237 | } 238 | return 0; 239 | } 240 | 241 | public void increaseSessionWins(Cube cube) { 242 | Integer integer = 1; 243 | if (session_wins.containsKey(cube.getName())) { 244 | integer = session_wins.get(cube.getName()) + 1; 245 | } 246 | session_wins.put(cube.getName(), (integer)); 247 | } 248 | 249 | public Integer getSessionHelps(Cube cube) { 250 | return getSessionHelps(cube.getName()); 251 | } 252 | 253 | public Integer getSessionHelps(String cube) { 254 | if (session_helps.containsKey(cube)) { 255 | return session_helps.get(cube); 256 | } 257 | return 0; 258 | } 259 | 260 | public void increaseSessionHelps(Cube cube) { 261 | Integer integer = 1; 262 | if (session_helps.containsKey(cube.getName())) { 263 | integer = session_helps.get(cube.getName()) + 1; 264 | } 265 | session_helps.put(cube.getName(), (integer)); 266 | } 267 | 268 | public Integer getSessionLoses(Cube cube) { 269 | return getSessionLoses(cube.getName()); 270 | } 271 | 272 | public Integer getSessionLoses(String cube) { 273 | if (session_loses.containsKey(cube)) { 274 | return session_loses.get(cube); 275 | } 276 | return 0; 277 | } 278 | 279 | public void increaseSessionLoses(Cube cube) { 280 | Integer integer = 1; 281 | if (session_loses.containsKey(cube.getName())) { 282 | integer = session_loses.get(cube.getName()) + 1; 283 | } 284 | session_loses.put(cube.getName(), (integer)); 285 | } 286 | 287 | public Integer getSessionGamesPlayed(Cube cube) { 288 | return getSessionGamesPlayed(cube.getName()); 289 | } 290 | 291 | public Integer getSessionGamesPlayed(String cube) { 292 | return (getSessionWins(cube) + getSessionLoses(cube)); 293 | } 294 | 295 | public void increaseWins(Cube cube) { 296 | increaseTotalWins(cube); 297 | increaseSessionWins(cube); 298 | } 299 | 300 | public void increaseLoses(Cube cube) { 301 | increaseTotalLoses(cube); 302 | increaseSessionLoses(cube); 303 | } 304 | 305 | public void increaseHelpCounter(Cube cube) { 306 | increaseTotalHelps(cube); 307 | increaseSessionHelps(cube); 308 | } 309 | 310 | public boolean cubeNameExists(String name) { 311 | AtomicBoolean exists = new AtomicBoolean(false); 312 | getCubes().forEach(cube -> { 313 | if (cube.getName().equalsIgnoreCase(name)) { 314 | exists.set(true); 315 | } 316 | }); 317 | return exists.get(); 318 | } 319 | 320 | } 321 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/util/CubicSettings.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.util; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import de.timecoding.cc.file.ConfigHandler; 5 | import de.timecoding.cc.util.type.Cube; 6 | import de.timecoding.cc.util.type.CubicStateType; 7 | import org.bukkit.Location; 8 | import org.bukkit.Sound; 9 | import org.bukkit.entity.Player; 10 | import org.bukkit.inventory.meta.FireworkMeta; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | 17 | public class CubicSettings { 18 | 19 | //PLUGIN, PLAYER, SOUNDS, SECONDS, START, FORMAT, CANCELLED, END, FIREWORK 20 | 21 | private CubicCountdown plugin = null; 22 | private List playerList = new ArrayList<>(); 23 | private HashMap soundMap = new HashMap<>(); 24 | private HashMap titleMap = new HashMap<>(); 25 | private HashMap subTitleMap = new HashMap<>(); 26 | private List fireworkLocations = new ArrayList<>(); 27 | 28 | private Integer countdownSeconds = 10; 29 | private FireworkMeta firework = null; 30 | 31 | private Cube cube = null; 32 | 33 | private Integer startDelay = 0; 34 | 35 | public CubicSettings(CubicCountdown plugin, boolean fromConfig) { 36 | this.plugin = plugin; 37 | if (fromConfig) { 38 | ConfigHandler configHandler = this.plugin.getConfigHandler(); 39 | setCountdownSeconds(configHandler.getInteger("Countdown")); 40 | setStartDelay(configHandler.getInteger("StartDelayInTicks")); 41 | Arrays.stream(CubicStateType.values()).forEach(cubicStateType -> { 42 | setTitle(cubicStateType, configHandler.getString("Settings." + cubicStateType.toString().toUpperCase() + ".Title")); 43 | setSubtitle(cubicStateType, configHandler.getString("Settings." + cubicStateType.toString().toUpperCase() + ".Subtitle")); 44 | String soundString = configHandler.getString("Settings." + cubicStateType.toString().toUpperCase() + ".Sound.Sound"); 45 | Sound sound = getSoundFromString(soundString); 46 | if (sound != null) { 47 | setSound(cubicStateType, sound); 48 | } 49 | }); 50 | } 51 | } 52 | 53 | public Cube getCube() { 54 | return cube; 55 | } 56 | 57 | public void setCube(Cube cube) { 58 | this.cube = cube; 59 | } 60 | 61 | private Sound getSoundFromString(String soundString) { 62 | if (Sound.valueOf(soundString) != null) { 63 | return Sound.valueOf(soundString); 64 | } 65 | return null; 66 | } 67 | 68 | public Integer getCountdownSeconds() { 69 | if (cube != null && plugin.getDataHandler().keyExists(cube.getDataPath() + "Countdown")) { 70 | return plugin.getDataHandler().getInteger(cube.getDataPath() + "Countdown"); 71 | } 72 | return countdownSeconds; 73 | } 74 | 75 | public void setCountdownSeconds(Integer countdownSeconds) { 76 | this.countdownSeconds = countdownSeconds; 77 | } 78 | 79 | public Integer getStartDelay() { 80 | return startDelay; 81 | } 82 | 83 | public void setStartDelay(Integer startDelay) { 84 | this.startDelay = startDelay; 85 | } 86 | 87 | public CubicCountdown getPlugin() { 88 | return plugin; 89 | } 90 | 91 | public List playerList() { 92 | return playerList; 93 | } 94 | 95 | public void addPlayer(Player player) { 96 | if (!plugin.getCubicAPI().viewingCountdown(player) && !playerList.contains(player)) { 97 | playerList.add(player); 98 | } 99 | } 100 | 101 | public void removePlayer(Player player) { 102 | playerList.remove(player); 103 | } 104 | 105 | public FireworkMeta getFireworkMeta() { 106 | return firework; 107 | } 108 | 109 | private HashMap getSoundMap() { 110 | return soundMap; 111 | } 112 | 113 | public Sound getSound(CubicStateType cubicStateType) { 114 | return getSoundMap().get(cubicStateType); 115 | } 116 | 117 | public void setSound(CubicStateType cubicStateType, Sound sound) { 118 | getSoundMap().put(cubicStateType, sound); 119 | } 120 | 121 | public void removeSound(CubicStateType cubicStateType) { 122 | getSoundMap().remove(cubicStateType); 123 | } 124 | 125 | public boolean hasSound(CubicStateType cubicStateType) { 126 | return (getSoundMap().containsKey(cubicStateType)); 127 | } 128 | 129 | private HashMap getTitleMap() { 130 | return titleMap; 131 | } 132 | 133 | public boolean hasTitle(CubicStateType cubicStateType) { 134 | return (getTitleMap().containsKey(cubicStateType)); 135 | } 136 | 137 | public String getTitle(CubicStateType cubicStateType) { 138 | if (hasTitle(cubicStateType)) { 139 | return plugin.getCubicAPI().replaceWithPlaceholders(cube, getTitleMap().get(cubicStateType)); 140 | } else { 141 | return ""; 142 | } 143 | } 144 | 145 | public void setTitle(CubicStateType cubicStateType, String string) { 146 | getTitleMap().put(cubicStateType, string); 147 | } 148 | 149 | public void removeTitle(CubicStateType cubicStateType) { 150 | getTitleMap().remove(cubicStateType); 151 | } 152 | 153 | public boolean hasSubtitle(CubicStateType cubicStateType) { 154 | return (getSubTitleMap().containsKey(cubicStateType)); 155 | } 156 | 157 | public String getSubtitle(CubicStateType cubicStateType) { 158 | if (hasSubtitle(cubicStateType)) { 159 | return plugin.getCubicAPI().replaceWithPlaceholders(cube, getSubTitleMap().get(cubicStateType)); 160 | } else { 161 | return ""; 162 | } 163 | } 164 | 165 | public void setSubtitle(CubicStateType cubicStateType, String string) { 166 | getSubTitleMap().put(cubicStateType, string); 167 | } 168 | 169 | public void removeSubtitle(CubicStateType cubicStateType) { 170 | getSubTitleMap().remove(cubicStateType); 171 | } 172 | 173 | private HashMap getSubTitleMap() { 174 | return subTitleMap; 175 | } 176 | 177 | public List fireworkLocations() { 178 | return fireworkLocations; 179 | } 180 | 181 | public void addFireworkLocation(Location location) { 182 | fireworkLocations.add(location); 183 | } 184 | 185 | public void removeFireworkLocation(Location location) { 186 | fireworkLocations.remove(location); 187 | } 188 | 189 | public void setFirework(FireworkMeta firework) { 190 | this.firework = firework; 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/util/type/Cube.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.util.type; 2 | 3 | import de.timecoding.cc.CubicCountdown; 4 | import org.bukkit.Location; 5 | import org.bukkit.Material; 6 | import org.bukkit.block.Block; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | import java.util.concurrent.atomic.AtomicInteger; 12 | 13 | public class Cube { 14 | 15 | private String name = "UNNAMED"; 16 | private Location pos1; 17 | private Location pos2; 18 | 19 | private CubicCountdown plugin; 20 | 21 | public Cube(String name, Location pos1, Location pos2, CubicCountdown plugin) { 22 | this.name = name; 23 | this.pos1 = pos1; 24 | this.pos2 = pos2; 25 | this.plugin = plugin; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public Location getPos1() { 33 | return pos1; 34 | } 35 | 36 | public Location getPos2() { 37 | return pos2; 38 | } 39 | 40 | public List blockList(boolean includeAir) { 41 | List blockList = new ArrayList<>(); 42 | if (pos1 != null && pos2 != null) { 43 | int topBlockX = (pos1.getBlockX() < pos2.getBlockX() ? pos2.getBlockX() : pos1.getBlockX()); 44 | int bottomBlockX = (pos1.getBlockX() > pos2.getBlockX() ? pos2.getBlockX() : pos1.getBlockX()); 45 | 46 | int topBlockY = (pos1.getBlockY() < pos2.getBlockY() ? pos2.getBlockY() : pos1.getBlockY()); 47 | int bottomBlockY = (pos1.getBlockY() > pos2.getBlockY() ? pos2.getBlockY() : pos1.getBlockY()); 48 | 49 | int topBlockZ = (pos1.getBlockZ() < pos2.getBlockZ() ? pos2.getBlockZ() : pos1.getBlockZ()); 50 | int bottomBlockZ = (pos1.getBlockZ() > pos2.getBlockZ() ? pos2.getBlockZ() : pos1.getBlockZ()); 51 | 52 | for (int y = bottomBlockY; y <= topBlockY; y++) { 53 | for (int z = bottomBlockZ; z <= topBlockZ; z++) { 54 | for (int x = bottomBlockX; x <= topBlockX; x++) { 55 | Block block = pos1.getWorld().getBlockAt(x, y, z); 56 | if (!includeAir && block.getType() != null && block.getType() != Material.AIR || includeAir) { 57 | blockList.add(block); 58 | } 59 | } 60 | } 61 | } 62 | } 63 | return blockList; 64 | } 65 | 66 | public List airBlockList() { 67 | List airBlockList = new ArrayList<>(); 68 | for (Block block : blockList(true)) { 69 | if (block == null || block.getType() == Material.AIR) { 70 | airBlockList.add(block); 71 | } 72 | } 73 | return airBlockList; 74 | } 75 | 76 | public boolean filledOut() { 77 | boolean startWhenFullFirstLayer = plugin.getConfigHandler().getBoolean("StartWhenFullFirstLayer"); 78 | 79 | if (startWhenFullFirstLayer) { 80 | AtomicBoolean filledOut = new AtomicBoolean(true); 81 | int topBlockX = Math.max(pos1.getBlockX(), pos2.getBlockX()); 82 | int bottomBlockX = Math.min(pos1.getBlockX(), pos2.getBlockX()); 83 | int topBlockZ = Math.max(pos1.getBlockZ(), pos2.getBlockZ()); 84 | int bottomBlockZ = Math.min(pos1.getBlockZ(), pos2.getBlockZ()); 85 | int topBlockY = Math.max(pos1.getBlockY(), pos2.getBlockY()); 86 | 87 | for (int x = bottomBlockX; x <= topBlockX; x++) { 88 | for (int z = bottomBlockZ; z <= topBlockZ; z++) { 89 | Block block = pos1.getWorld().getBlockAt(x, topBlockY, z); 90 | if (block == null || block.getType() == Material.AIR) { 91 | filledOut.set(false); 92 | break; 93 | } 94 | } 95 | if (!filledOut.get()) { 96 | break; 97 | } 98 | } 99 | return filledOut.get(); 100 | } else { 101 | return blockList(true).stream().noneMatch(block -> block == null || block.getType() == Material.AIR); 102 | } 103 | } 104 | 105 | public int getLowestY() { 106 | AtomicInteger lowest = new AtomicInteger(100000000); 107 | blockList(true).forEach(block -> { 108 | if (block.getY() < lowest.get()) { 109 | lowest.set(block.getY()); 110 | } 111 | }); 112 | return lowest.get(); 113 | } 114 | 115 | public int height() { 116 | List blockList = blockList(true); 117 | List yValues = new ArrayList<>(); 118 | blockList.forEach(block -> { 119 | if (!yValues.contains(block.getLocation().getBlockY())) { 120 | yValues.add(block.getLocation().getBlockY()); 121 | } 122 | }); 123 | return yValues.size(); 124 | } 125 | 126 | public int currentHeight() { 127 | List blockList = blockList(false); 128 | List yValues = new ArrayList<>(); 129 | blockList.forEach(block -> { 130 | if (!yValues.contains(block.getLocation().getBlockY())) { 131 | yValues.add(block.getLocation().getBlockY()); 132 | } 133 | }); 134 | return yValues.size(); 135 | } 136 | 137 | public boolean empty() { 138 | List blockList = blockList(false); 139 | List toRemove = new ArrayList<>(); 140 | if (blockList != null) { 141 | for (Block block : blockList) { 142 | if (plugin.getConfigHandler().getStringList("ClearCube.DisabledBlocks").contains(block.getType().toString().toUpperCase())) { 143 | toRemove.add(block); 144 | } 145 | } 146 | } 147 | blockList.removeAll(toRemove); 148 | return (blockList.size() == 0); 149 | } 150 | 151 | public boolean inCube(Location location) { 152 | AtomicBoolean inCube = new AtomicBoolean(false); 153 | blockList(true).forEach(block -> { 154 | if (block.getLocation().getWorld().getName().equals(location.getWorld().getName()) && block.getLocation().getBlockX() == location.getBlockX() && block.getLocation().getBlockY() == location.getBlockY() && block.getLocation().getBlockZ() == location.getBlockZ()) { 155 | inCube.set(true); 156 | } 157 | }); 158 | return inCube.get(); 159 | } 160 | 161 | public boolean isSimilar(Cube cube) { 162 | return (cube.getName().equals(name)); 163 | } 164 | 165 | public String getDataPath() { 166 | return "Cube." + name + "."; 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /src/main/java/de/timecoding/cc/util/type/CubicStateType.java: -------------------------------------------------------------------------------- 1 | package de.timecoding.cc.util.type; 2 | 3 | public enum CubicStateType { 4 | 5 | START, PROCEED, CANCELLED, END; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/config.yml: -------------------------------------------------------------------------------- 1 | ############################### 2 | # CubicCountdown v1.2.3 # 3 | # made by TimeCoding # 4 | ###################################################### 5 | # Support: https://discord.tikmc.de/ # 6 | # Donate: https://donate.tikmc.de/ # 7 | ###################################################### 8 | 9 | #The DEFAULT countdown in seconds 10 | Countdown: 10 11 | #Set this to true if the countdown should start when a cube is empty (instead of a cube is full of blocks) 12 | #THIS DISABLES THE CUBE CLEAR OPTION 13 | Reverse: false 14 | #The general sound and title settings 15 | #Placeholders which can be used in the titles and the actionbar: 16 | # %total_win_counter%, %total_lose_counter%, %total_games_played%, %total_help_counter% 17 | # %session_win_counter%, %session_lose_counter%, %session_games_played%, %session_help_counter% 18 | # %map% 19 | Settings: 20 | #Title and sound settings for the first second of the countdown 21 | START: 22 | Title: "&a&lThe game ends" 23 | Subtitle: "" 24 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 25 | Sound: 26 | Enabled: true 27 | Sound: ENTITY_FIREWORK_ROCKET_SHOOT 28 | Volume: 2 29 | Pitch: 2 30 | #If you want to use a custom sound out of your ressourcepack, type the name in here 31 | Custom: "" 32 | #Title format and sound settings for the countdown in general 33 | PROCEED: 34 | Title: "&e&l%seconds%" 35 | Subtitle: "" 36 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 37 | Sound: 38 | Enabled: true 39 | Sound: BLOCK_NOTE_BLOCK_BASS 40 | Volume: 2 41 | Pitch: 2 42 | #If you want to use a custom sound out of your ressourcepack, type the name in here 43 | Custom: "" 44 | #Title and sound settings which will be applied when the cube state turns from "FULL CUBE" to "PARTLY FULL CUBE" 45 | CANCELLED: 46 | Title: "&c&lCountdown cancelled!" 47 | Subtitle: "" 48 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 49 | Sound: 50 | Enabled: true 51 | Sound: BLOCK_NOTE_BLOCK_BASS 52 | Volume: 2 53 | Pitch: 2 54 | #If you want to use a custom sound out of your ressourcepack, type the name in here 55 | Custom: "" 56 | #Title and sound settings for the countdown ending 57 | END: 58 | Title: "&a&lG&b&la&c&lm&d&le &f&le&4&ln&2&ld&d&le&b&ld&f&l!" 59 | Subtitle: "&cWins: %total_win_counter%" 60 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 61 | Sound: 62 | Enabled: true 63 | Sound: BLOCK_NOTE_BLOCK_BASS 64 | Volume: 2 65 | Pitch: 2 66 | #If you want to use a custom sound out of your ressourcepack, type the name in here 67 | Custom: "" 68 | CUSTOM: 69 | #Set custom settings for second 3 70 | 3: 71 | #Set the title 72 | Title: "&a&l3" 73 | #Set the subtitle 74 | Subtitle: "" 75 | #Set the sound 76 | Sound: 77 | Enabled: true 78 | Sound: BLOCK_NOTE_BLOCK_BASS 79 | Volume: 2 80 | Pitch: 2 81 | #If you want to use a custom sound out of your ressourcepack, type the name in here 82 | Custom: "" 83 | #Set ticks, so the countdown seems slower or faster (20 ticks = 1 second) 84 | Ticks: 20 85 | 2: 86 | Title: "&c&l2" 87 | Subtitle: "" 88 | Sound: 89 | Enabled: true 90 | Sound: BLOCK_NOTE_BLOCK_BASS 91 | Volume: 2 92 | Pitch: 2 93 | Custom: "" 94 | Ticks: 20 95 | 1: 96 | Title: "&f&l1" 97 | Subtitle: "" 98 | Sound: 99 | Enabled: true 100 | Sound: BLOCK_NOTE_BLOCK_BASS 101 | Volume: 2 102 | Pitch: 2 103 | Custom: "" 104 | Ticks: 20 105 | #Disable this if the countdown should only start if the whole cube is full of blocks 106 | #WISH BY THE TIKFINITY COMMUNITY 107 | StartWhenFullFirstLayer: true 108 | #Decide if all blocks in the cube, after the countdown reached 0, should be deleted/cleared 109 | ClearCube: 110 | Enabled: true 111 | #Decide if some block-"types" should be disabled/ignored 112 | #You can find all block-names here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html 113 | DisabledBlocks: 114 | - BEDROCK 115 | #The delay between the countdown and the start title (the start title will only be shown if the delay is => 20) IN TICKS 116 | # INFORMATION: 20 ticks = 1 second # 117 | StartDelayInTicks: 20 118 | #Normally only the player who break the block can see the countdown, but you can change it here 119 | Viewer: 120 | #Include all online players 121 | AllPlayers: false 122 | #Include players which are in a specific radius of a cube 123 | AllPlayersInCubeRadius: 124 | Enabled: true 125 | RadiusInBlocks: 10 126 | #Configurate the firework effect on the end of the countdown 127 | Firework: 128 | #Enable this to spawn a firework at the player location 129 | ForPlayer: true 130 | #Set this to true if the firework should also spawn at each block after the countdown ended (wish by lanzyy) 131 | AtEachBlock: false 132 | #Set the power of the effect (1-Unlimited) 133 | Power: 1 134 | #Set the colors of the firework 135 | #You can find all color-names here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Color.html 136 | Colors: 137 | - "RED" 138 | - "ORANGE" 139 | - "YELLOW" 140 | #NEW IN v1.2: In this version you're now able to fill out a cube with blocks 141 | FillAction: 142 | #Disable the animation by setting this to "false" 143 | Animation: true 144 | #Set the ticks (20 ticks = 1 second) how fast the animation should be played 145 | AnimationTicks: 2 146 | #Set this to true if all players which would "bug" into a block after the fill action is performed get teleported over the added blocks 147 | Teleport: true 148 | #Disable this if every block should be replaced 149 | OnlyAir: true 150 | #Disable this if it shouldn't be possible to execute the fill action/animation while another is running 151 | Queue: true 152 | #Display any effect when a new block is going to spawn 153 | Effect: 154 | Enabled: false 155 | #You can find all effect types here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Effect.html 156 | Type: MOBSPAWNER_FLAMES 157 | #The particle amount 158 | Amount: 0 159 | #Set how many blocks the effect can be away 160 | BlocksAhead: 0 161 | #A sound which will be played every time an animation block was set 162 | ProceedSound: 163 | #Set this to true if you want to enable it 164 | Enabled: false 165 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 166 | Sound: BLOCK_NOTE_BLOCK_BELL 167 | Volume: 1 168 | Pitch: 2 169 | Custom: "" 170 | #Decide if some block-"types" should be disabled/ignored 171 | #You can find all block-names here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html 172 | DisabledBlocks: 173 | - BEDROCK 174 | #NEW IN v1.2: In this version you're now able to clear a cube 175 | ClearAction: 176 | #Enable the animation by setting this to "true" 177 | Animation: false 178 | #Set the ticks (20 ticks = 1 second) how fast the animation should be played 179 | AnimationTicks: 2 180 | #Display any effect when a new block is going to spawn 181 | Effect: 182 | Enabled: false 183 | #You can find all effect types here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Effect.html 184 | Type: MOBSPAWNER_FLAMES 185 | #The particle amount 186 | Amount: 0 187 | #Set how many blocks the effect can be away 188 | BlocksAhead: 0 189 | #A sound which will be played every time an animation block was set 190 | ProceedSound: 191 | #Set this to true if you want to enable it 192 | Enabled: false 193 | #You can find all sounds here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Sound.html 194 | Sound: BLOCK_NOTE_BLOCK_BELL 195 | Volume: 1 196 | Pitch: 2 197 | Custom: "" 198 | #Decide if some block-"types" should be disabled/ignored 199 | #You can find all block-names here: https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Material.html 200 | DisabledBlocks: 201 | - BEDROCK 202 | #NEW in v1.2.2: Select commands which should be executed IN THE CONSOLE after a countdown ended, started or was cancelled 203 | #Placeholders: %map% 204 | Commands: 205 | #This commands will be executed after the countdown was cancelled 206 | OnCountdownCancel: 207 | - "" 208 | #This commands will be executed after the countdown was started 209 | OnCountdownStart: 210 | - "" 211 | #This commands will be executed after the countdown ended 212 | OnCountdownEnd: 213 | - "" 214 | #NEW IN v1.2: Create an actionbar, which can show the win, games played and the loose counter 215 | #when a player is near a cube 216 | Actionbar: 217 | Enabled: false 218 | Format: "&aWins: §7%total_win_counter% &f| §cLoses: &7%total_lose_counter% &f| §eGames played: §7%total_games_played%" 219 | #The max distance in blocks of the player to the next cube that it shows the actionbar (and maybe soon the scoreboard) 220 | CubeRadius: 5 221 | #The checker every X seconds whether a cube is full in all worlds (NEW in v1.2.1). In this option you can set how often it does this (make this number higher to prevent lag). Please note that the information is in TICKS. 20 ticks = 1 second 222 | CheckerTicks: 1 223 | #bStats is sending data to their platform, so everyone can look up how many users are using this plugin 224 | #To disable the sharing mode, set this option to false 225 | bStats: true 226 | #Set this to false if you don't want that the plugin gets updated automatically 227 | AutoUpdater: true 228 | #DO NOT CHANGE THIS 229 | config-version: 1.2.2 230 | 231 | ####################################################################################### 232 | # A SETTING IS MISSING FOR YOU? NO PROBLEM, JOIN OUR DISCORD AND WE'LL ADD IT FOR YOU # 233 | ####################################################################################### -------------------------------------------------------------------------------- /src/main/resources/datas.yml: -------------------------------------------------------------------------------- 1 | ############################################# 2 | # PLEASE KEEP THIS FILE UNTOUCHED # 3 | # IF YOU DON'T KNOW WHAT YOU'RE DOING # 4 | ############################################# 5 | -------------------------------------------------------------------------------- /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: CubicCountdown 2 | version: 1.2.3_BETA 3 | main: de.timecoding.cc.CubicCountdown 4 | api-version: 1.13 5 | softdepend: [ PlaceholderAPI ] 6 | description: "A plugin which is able to start a countdown when a cube is full/empty (of blocks)" 7 | 8 | commands: 9 | cubiccountdown: 10 | aliases: 11 | - cc 12 | description: "A command which allows players to do the CubicCountdown setup" 13 | --------------------------------------------------------------------------------