├── .github └── workflows │ └── maven-publish.yml ├── .gitignore ├── .travis.yml ├── LICENCE.MD ├── README.md ├── plugin ├── pom.xml └── src │ └── main │ ├── java │ └── net │ │ └── mcjukebox │ │ └── plugin │ │ └── bukkit │ │ ├── MCJukebox.java │ │ ├── api │ │ ├── JukeboxAPI.java │ │ ├── ResourceType.java │ │ └── models │ │ │ └── Media.java │ │ ├── commands │ │ ├── ImportCommand.java │ │ ├── JukeboxCommand.java │ │ ├── JukeboxCommandExecutor.java │ │ ├── PlayCommand.java │ │ ├── RegionCommand.java │ │ ├── SetKeyCommand.java │ │ ├── ShowCommand.java │ │ ├── StopCommand.java │ │ └── TabArgument.java │ │ ├── events │ │ ├── ClientConnectEvent.java │ │ └── ClientDisconnectEvent.java │ │ ├── listeners │ │ └── RegionListener.java │ │ ├── managers │ │ ├── LangManager.java │ │ ├── RegionManager.java │ │ ├── shows │ │ │ ├── Show.java │ │ │ ├── ShowManager.java │ │ │ └── ShowSyncTask.java │ │ └── skript │ │ │ ├── SkriptManager.java │ │ │ ├── effects │ │ │ └── EffRequestToken.java │ │ │ └── expressions │ │ │ └── ExprGetToken.java │ │ ├── sockets │ │ ├── DripTask.java │ │ ├── KeyHandler.java │ │ ├── ReconnectTask.java │ │ ├── SocketHandler.java │ │ └── listeners │ │ │ ├── ClientConnectListener.java │ │ │ ├── ClientDisconnectListener.java │ │ │ ├── ConnectionListener.java │ │ │ ├── DropListener.java │ │ │ ├── LangListener.java │ │ │ └── TokenListener.java │ │ └── utils │ │ ├── DataUtils.java │ │ ├── MessageUtils.java │ │ ├── SpigotUtils.java │ │ ├── TimeUtils.java │ │ └── UrlUtils.java │ └── resources │ └── plugin.yml ├── pom.xml ├── region-providers ├── api │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── mcjukebox │ │ └── shared │ │ └── api │ │ ├── Region.java │ │ └── RegionProvider.java ├── pom.xml ├── wg6 │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ └── net │ │ └── mcjukebox │ │ └── shared │ │ └── wg6 │ │ └── RegionProvider.java └── wg7 │ ├── pom.xml │ └── src │ └── main │ └── java │ └── net │ └── mcjukebox │ └── shared │ └── wg7 │ └── RegionProvider.java ├── scripts └── installSkript.sh └── shared ├── pom.xml └── src └── main └── java └── net └── mcjukebox └── shared └── utils └── RegionUtils.java /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package 5 | 6 | on: 7 | push: 8 | tags: 9 | - 'v*' 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up JDK 21 19 | uses: actions/setup-java@v1 20 | with: 21 | java-version: 21 22 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 23 | settings-path: ${{ github.workspace }} # location for the settings.xml file 24 | 25 | - name: Add Skript Dependency 26 | run: sh ./scripts/installSkript.sh 27 | 28 | - name: Clean & Install 29 | run: mvn clean install --file pom.xml 30 | 31 | - name: Build with Maven 32 | run: mvn -B package --file pom.xml 33 | 34 | - name: Publish to GitHub Packages Apache Maven 35 | # Only run mvn deploy when on Oliver's repo (aka. not forks) 36 | # This is because the distribution repo is hardcoded in the pom file 37 | if: github.repository == 'oliverdunk/JukeboxAPI' 38 | run: mvn deploy -Dregistry=https://maven.pkg.github.com/${{ github.repository_owner }} -s $GITHUB_WORKSPACE/settings.xml 39 | env: 40 | GITHUB_TOKEN: ${{ github.token }} 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .idea/ 3 | dependency-reduced-pom.xml 4 | *.iml 5 | target/ 6 | Skript.jar 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | dist: trusty 3 | jdk: 4 | - openjdk8 5 | before_install: 6 | - ./scripts/installSkript.sh 7 | deploy: 8 | provider: releases 9 | skip_cleanup: true 10 | api_key: 11 | secure: LbOEmph3JxVwgs7Y0gJYU1Hl8Lidn+fcvnUYBIAHFvofog3QpF0RgxZQrnhrAlpS8wBGhTPITclxQtOfsXIcWpM5CQWyqqkyX4umNWOmFDSMAR1ZAdOZlpYTlc0m1Jd0slCOoPBX8ucMCbraFT0TDO6mNtgciozTDGWSUZz1YToS59L1GghTY+Zg2e8FW0MBGSqbWfuqELeJxeniBg+ShikecKUK2LG8LRUNcvASZxMFbraPEcXbDlr6cdIWIQhPkaUxx9Eekj8oEMKXwZqnb26AzThlwSoThl2bfK7XbMXCXAgHkr9ABFmqSnwI31tY01TqoSJ/sldaYtSDdUANw3WHak26SpxWKma7P9vgfOpv9nKNYYGJj9dzSAVIYRpjNpCo7FsIP/qUY4DlFBPG1K7ma/jhRwSAT6nCfjodQ57VwnQwQXtOA965p5oYHmbtToXe+aIkMd3Rw4WZHr3bEJqS/8GGQIVmZWjnZLBVVIEHTBZxKKaeAQnEaPjvonn670xytWAnMoBaOn0n5BMryzgGUb54j60TK8ole72furX2H+gpOlpEim6m23mXbjiAJ6Cdy2PKcXp/bS5m5K0QXW1QNts4KJpByMK7DgpgtTJ090Z/MADNnXmruCtfmERsKdgy5MuWeoIJmzwaXTkprzsLs0hrAAd79j7Iaud2s3M= 12 | file: plugin/target/MCJukebox.jar 13 | on: 14 | tags: true 15 | -------------------------------------------------------------------------------- /LICENCE.MD: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Oliver Dunk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JukeboxAPI [![Build Status](https://travis-ci.org/oliverdunk/JukeboxAPI.svg?branch=master)](https://travis-ci.org/oliverdunk/JukeboxAPI) 2 | Java API and plugin for [Jukebox](https://mcjukebox.net), my web based music player. 3 | 4 | # Usage 5 | Please see the [SpigotMC](https://www.spigotmc.org/resources/mcjukebox.16024/) resource for more information. 6 | 7 | # Support 8 | If you need any assistance please email [support@mcjukebox.net](mailto:support@mcjukebox.net) or join our discord: [discord.gg/N3TMTCH](https://discord.gg/N3TMTCH). 9 | 10 | # Maven Repository 11 | If you use JukeboxAPI as a dependency for your plugin you can add it directly with the latest Jar file from the [releases page](https://github.com/oliverdunk/JukeboxAPI/releases) or as a Maven dependency. 12 | 13 | First you need to add the GitHub Packages repository: 14 | 15 | ``` 16 | 17 | 18 | github 19 | https://maven.pkg.github.com/oliverdunk/JukeboxAPI 20 | 21 | 22 | ``` 23 | 24 | Then you need to add the dependency, with the latest version from the [packages listing](https://github.com/oliverdunk/JukeboxAPI/packages?q=com.oliverdunk.MCJukebox-plugin). 25 | 26 | ``` 27 | 28 | com.oliverdunk.MCJukebox-plugin 29 | mcjukebox-plugin 30 | VERSION 31 | 32 | ``` 33 | -------------------------------------------------------------------------------- /plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox 10 | 2.7.0-RELEASE 11 | 12 | 13 | mcjukebox-plugin 14 | 2.7.0-RELEASE 15 | 16 | 17 | 18 | paper 19 | https://repo.destroystokyo.com/repository/maven-public/ 20 | 21 | 22 | sk89-repo 23 | https://maven.enginehub.org/repo/ 24 | 25 | 26 | skript 27 | https://repo.skriptlang.org/releases 28 | 29 | 30 | 31 | 32 | 33 | com.oliverdunk 34 | mcjukebox-shared 35 | 2.7.0-RELEASE 36 | 37 | 38 | com.github.SkriptLang 39 | Skript 40 | 2.9.2 41 | provided 42 | 43 | 44 | io.socket 45 | socket.io-client 46 | 1.0.0 47 | compile 48 | 49 | 50 | 51 | com.squareup.okhttp3 52 | okhttp 53 | 4.7.2 54 | compile 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-compiler-plugin 63 | 3.3 64 | 65 | 1.8 66 | 1.8 67 | 68 | 69 | 70 | org.projectlombok 71 | lombok-maven-plugin 72 | 1.18.6.0 73 | 74 | 75 | generate-sources 76 | 77 | delombok 78 | 79 | 80 | 81 | 82 | /plugin/src 83 | true 84 | lombok.encoding 85 | 86 | 87 | 88 | org.apache.maven.plugins 89 | maven-jar-plugin 90 | 2.4 91 | 92 | MCJukebox 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-shade-plugin 98 | 2.4.3 99 | 100 | 101 | package 102 | 103 | shade 104 | 105 | 106 | 107 | 108 | io.socket 109 | net.mcjukebox.plugin.bukkit.libs.socket 110 | 111 | 112 | okhttp3 113 | net.mcjukebox.plugin.bukkit.libs.okhttp3 114 | 115 | 116 | okio 117 | net.mcjukebox.plugin.bukkit.libs.okio 118 | 119 | 120 | org.json 121 | net.mcjukebox.plugin.bukkit.libs.json 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/MCJukebox.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit; 2 | 3 | import net.mcjukebox.plugin.bukkit.managers.skript.SkriptManager; 4 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowSyncTask; 5 | import net.mcjukebox.plugin.bukkit.sockets.SocketHandler; 6 | import net.mcjukebox.plugin.bukkit.commands.JukeboxCommandExecutor; 7 | import net.mcjukebox.plugin.bukkit.listeners.RegionListener; 8 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 9 | import net.mcjukebox.plugin.bukkit.utils.DataUtils; 10 | import net.mcjukebox.plugin.bukkit.managers.LangManager; 11 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 12 | import net.mcjukebox.plugin.bukkit.managers.RegionManager; 13 | import lombok.Getter; 14 | import net.mcjukebox.plugin.bukkit.utils.TimeUtils; 15 | import org.bukkit.Bukkit; 16 | import org.bukkit.plugin.java.JavaPlugin; 17 | 18 | public class MCJukebox extends JavaPlugin { 19 | 20 | @Getter private static final boolean DEBUG = true; 21 | 22 | //Stores the main plugin instance for access from other classes. 23 | @Getter private static MCJukebox instance; 24 | @Getter private SocketHandler socketHandler; 25 | @Getter private RegionManager regionManager; 26 | @Getter private SkriptManager skriptManager; 27 | @Getter private RegionListener regionListener; 28 | @Getter private LangManager langManager; 29 | @Getter private ShowManager showManager; 30 | @Getter private TimeUtils timeUtils; 31 | 32 | /** 33 | * Called when the plugin is first loaded by Spigot. 34 | */ 35 | public void onEnable(){ 36 | this.instance = this; 37 | 38 | langManager = new LangManager(); 39 | MessageUtils.setLangManager(langManager); 40 | 41 | socketHandler = new SocketHandler(); 42 | regionManager = new RegionManager(); 43 | 44 | showManager = new ShowManager(); 45 | // This needs to be run synchronously so it can identify when the main thread is busy 46 | Bukkit.getScheduler().runTaskTimer(this, new ShowSyncTask(), 1, 1); 47 | 48 | timeUtils = new TimeUtils(); 49 | 50 | // Only register region events if WorldGuard is installed 51 | if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { 52 | regionListener = new RegionListener(regionManager); 53 | Bukkit.getPluginManager().registerEvents(regionListener, this); 54 | } 55 | 56 | if (Bukkit.getPluginManager().getPlugin("Skript") != null) { 57 | skriptManager = new SkriptManager(); 58 | } 59 | 60 | Bukkit.getPluginCommand("jukebox").setExecutor(new JukeboxCommandExecutor(regionManager)); 61 | this.getLogger().info(this.getName() + " has been loaded!"); 62 | } 63 | 64 | public String getAPIKey() { 65 | return (String) DataUtils.loadObjectFromPath(getDataFolder() + "/api.key"); 66 | } 67 | 68 | /** 69 | * Called when the server is restarted or stopped. 70 | */ 71 | public void onDisable(){ 72 | socketHandler.disconnect(); 73 | regionManager.save(); 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/api/JukeboxAPI.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.api; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import net.mcjukebox.plugin.bukkit.api.models.Media; 5 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 6 | import net.mcjukebox.plugin.bukkit.sockets.listeners.TokenListener; 7 | import org.bukkit.Bukkit; 8 | import org.bukkit.entity.Player; 9 | import org.json.JSONObject; 10 | 11 | public class JukeboxAPI { 12 | 13 | /** 14 | * Requests that an audio file is played to the player. 15 | * 16 | * @param player The player for which the song should be played 17 | * @param media The file which should be played 18 | */ 19 | public static void play(Player player, final Media media){ 20 | final JSONObject params = new JSONObject(); 21 | params.put("username", player.getName()); 22 | params.put("url", media.getURL()); 23 | params.put("volume", media.getVolume()); 24 | params.put("looping", media.isLooping()); 25 | params.put("channel", media.getChannel()); 26 | if(media.getFadeDuration() != -1) params.put("fadeDuration", media.getFadeDuration()); 27 | if(media.getStartTime() != -1) params.put("startTime", media.getStartTime()); 28 | Bukkit.getScheduler().runTaskAsynchronously(MCJukebox.getInstance(), new Runnable() { 29 | public void run() { 30 | String channel = media.getType() == ResourceType.MUSIC ? "playMusic" : "playSound"; 31 | MCJukebox.getInstance().getSocketHandler().emit("command/" + channel, params); 32 | } 33 | }); 34 | } 35 | 36 | /** 37 | * Stops the current music track. 38 | * 39 | * @param player The player to stop the music for. 40 | */ 41 | public static void stopMusic(Player player){ 42 | stopMusic(player, "default", -1); 43 | } 44 | 45 | /** 46 | * Stops the current music track. 47 | * 48 | * @param player The player to stop the music for. 49 | * @param channel Channel to play music in, default is "default" 50 | * @param fadeDuration Length of fade, use 0 to disable and -1 for default 51 | */ 52 | public static void stopMusic(Player player, String channel, int fadeDuration){ 53 | final JSONObject params = new JSONObject(); 54 | params.put("username", player.getName()); 55 | params.put("channel", channel); 56 | if(fadeDuration != -1) params.put("fadeDuration", fadeDuration); 57 | Bukkit.getScheduler().runTaskAsynchronously(MCJukebox.getInstance(), new Runnable() { 58 | public void run() { 59 | MCJukebox.getInstance().getSocketHandler().emit("command/stopMusic", params); 60 | } 61 | }); 62 | } 63 | 64 | /** 65 | * Stops all music and sounds in a channel. 66 | * 67 | * @param player The player to stop the music for. 68 | * @param channel Channel to play music in, default is "default" 69 | * @param fadeDuration Length of fade, use 0 to disable and -1 for default 70 | */ 71 | public static void stopAll(Player player, String channel, int fadeDuration) { 72 | final JSONObject params = new JSONObject(); 73 | params.put("username", player.getName()); 74 | params.put("channel", channel); 75 | if(fadeDuration != -1) params.put("fadeDuration", fadeDuration); 76 | ; Bukkit.getScheduler().runTaskAsynchronously(MCJukebox.getInstance(), new Runnable() { 77 | public void run() { 78 | MCJukebox.getInstance().getSocketHandler().emit("command/stopAll", params); 79 | } 80 | }); 81 | } 82 | 83 | /** 84 | * Gets the authentication token needed for a player to open the client. Since tokens are requested from the server, 85 | * this method blocks the thread until a token is received. It should always be run asynchronously. 86 | * 87 | * @param player The player to get the token for 88 | * @return Token to be used in the query parameters of the client URL 89 | */ 90 | public static String getToken(Player player) { 91 | // Construct and send request for token 92 | JSONObject params = new JSONObject(); 93 | params.put("username", player.getName()); 94 | MCJukebox.getInstance().getSocketHandler().emit("command/getToken", params); 95 | 96 | // Create a new lock that can be notified when a token is received 97 | Object lock = new Object(); 98 | 99 | synchronized (lock) { 100 | try { 101 | TokenListener tokenListener = MCJukebox.getInstance().getSocketHandler().getTokenListener(); 102 | // Register the lock so that we are notified when a new token is received 103 | tokenListener.addLock(player.getName(), lock); 104 | lock.wait(); 105 | return tokenListener.getToken(player.getName()); 106 | } catch (InterruptedException exception) { 107 | // This should never happen, so someone has gone out of their way to cancel this call 108 | return null; 109 | } 110 | } 111 | } 112 | 113 | /** 114 | * Gets the ShowManager instance currently in use. Note that show are a client side feature, implemented 115 | * as as a higher level version of our internal channels system which allows multiple audio tracks 116 | * to be played simultaneously. 117 | * 118 | * @return ShowManager being used by MCJukebox 119 | */ 120 | public static ShowManager getShowManager() { 121 | return MCJukebox.getInstance().getShowManager(); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/api/ResourceType.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.api; 2 | 3 | public enum ResourceType { 4 | SOUND_EFFECT, 5 | MUSIC 6 | } 7 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/api/models/Media.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.api.models; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import net.mcjukebox.plugin.bukkit.api.ResourceType; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | import org.json.JSONObject; 8 | 9 | public class Media { 10 | 11 | @Getter @Setter private ResourceType type = ResourceType.SOUND_EFFECT; 12 | @Getter @Setter private String URL = ""; 13 | 14 | //The following options are only supported for the MUSIC resource type 15 | @Getter @Setter private int volume = 100; 16 | @Getter @Setter private boolean looping = true; 17 | @Getter @Setter private int fadeDuration = -1; 18 | @Getter @Setter private long startTime = -1; 19 | @Getter @Setter private String channel = "default"; 20 | 21 | public Media(ResourceType type, String URL) { 22 | setType(type); 23 | setURL(URL); 24 | } 25 | 26 | public Media(ResourceType type, String URL, JSONObject options){ 27 | setType(type); 28 | setURL(URL); 29 | 30 | loadOptions(options); 31 | } 32 | 33 | public void loadOptions(JSONObject options) { 34 | if(options.has("volume")) { 35 | if(options.get("volume") instanceof Integer) { 36 | if (options.getInt("volume") <= 100 && options.getInt("volume") >= 0) { 37 | volume = options.getInt("volume"); 38 | } 39 | } 40 | } 41 | 42 | if(options.has("looping")) { 43 | if(options.get("looping") instanceof Boolean) { 44 | looping = options.getBoolean("looping"); 45 | } 46 | } 47 | 48 | if(options.has("fadeDuration")) { 49 | if(options.get("fadeDuration") instanceof Integer) { 50 | if (options.getInt("fadeDuration") <= 30 && options.getInt("fadeDuration") >= 0) { 51 | fadeDuration = options.getInt("fadeDuration"); 52 | } 53 | } 54 | } 55 | 56 | if(options.has("startTime")) { 57 | if(options.get("startTime") instanceof String && options.getString("startTime").equalsIgnoreCase("now")) { 58 | startTime = MCJukebox.getInstance().getTimeUtils().currentTimeMillis(); 59 | } else if(options.get("startTime") instanceof Long || options.get("startTime") instanceof Integer) { 60 | startTime = options.getLong("startTime"); 61 | } 62 | } 63 | 64 | if(options.has("channel")) { 65 | if(options.get("channel") instanceof String) { 66 | this.channel = options.getString("channel"); 67 | } 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/ImportCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.managers.RegionManager; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class ImportCommand extends JukeboxCommand { 8 | 9 | private RegionManager regionManager; 10 | 11 | public ImportCommand(RegionManager regionManager) { 12 | this.regionManager = regionManager; 13 | } 14 | 15 | @Override 16 | public boolean execute(CommandSender dispatcher, String[] args) { 17 | if (args.length != 1) return false; 18 | 19 | int imported = -1; 20 | 21 | // Import using the correct importer 22 | switch (args[0]) { 23 | case "oa": 24 | imported = regionManager.importFromOA(); 25 | break; 26 | } 27 | 28 | if (imported >= 0) { 29 | // At the very least, we found an importer to run 30 | dispatcher.sendMessage("" + ChatColor.GREEN + imported + " region(s) imported."); 31 | } else { 32 | // We never found a compatible importer 33 | dispatcher.sendMessage(ChatColor.RED + "Unknown import source."); 34 | } 35 | 36 | return true; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/JukeboxCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import org.bukkit.Bukkit; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.Location; 6 | import org.bukkit.command.BlockCommandSender; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Entity; 9 | import org.bukkit.entity.Player; 10 | import org.json.JSONObject; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | import java.util.regex.Matcher; 17 | import java.util.regex.Pattern; 18 | 19 | public abstract class JukeboxCommand { 20 | 21 | private final String SELECTOR_REGEX = "@[ap]\\[r=([0-9]{1,4})\\]"; 22 | protected HashMap suggestions = new HashMap<>(); 23 | 24 | public abstract boolean execute(CommandSender dispatcher, String[] args); 25 | 26 | public boolean executeWithSelectors(CommandSender dispatcher, String[] args) { 27 | int targetIndex = -1; 28 | 29 | for (int i = 0; i < args.length; i++) { 30 | if (args[i].equalsIgnoreCase("@a")) targetIndex = i; 31 | if (args[i].equalsIgnoreCase("@p")) targetIndex = i; 32 | if (args[i].matches(SELECTOR_REGEX)) targetIndex = i; 33 | } 34 | 35 | // If all selectors in this command have been replaced 36 | if (targetIndex == -1) { 37 | return execute(dispatcher, args); 38 | } 39 | 40 | // We still have selectors to parse 41 | String selector = args[targetIndex]; 42 | String[] newArgs = Arrays.copyOf(args, args.length); 43 | Location dispatchLocation = null; 44 | 45 | if (dispatcher instanceof BlockCommandSender) { 46 | dispatchLocation = ((BlockCommandSender) dispatcher).getBlock().getLocation(); 47 | } else if (dispatcher instanceof Entity) { 48 | dispatchLocation = ((Entity) dispatcher).getLocation(); 49 | } 50 | 51 | List targets = new ArrayList(); 52 | 53 | if (selector.toLowerCase().startsWith("@a")) { 54 | for (Player player : Bukkit.getOnlinePlayers()) { 55 | targets.add(player); 56 | } 57 | } 58 | 59 | if (selector.toLowerCase().startsWith("@p")) { 60 | if (dispatchLocation == null) { 61 | dispatcher.sendMessage(ChatColor.RED + "@p cannot be used here."); 62 | return true; 63 | } 64 | Player nearestPlayer = getNearestPlayer(dispatchLocation); 65 | if (nearestPlayer != null) { 66 | targets.add(nearestPlayer); 67 | } 68 | } 69 | 70 | boolean failed = false; 71 | 72 | for (Player player : targets) { 73 | if (isInRange(dispatchLocation, player, selector)) { 74 | newArgs[targetIndex] = player.getName(); 75 | boolean result = execute(dispatcher, newArgs); 76 | if (!result) failed = true; 77 | } 78 | } 79 | 80 | return !failed; 81 | } 82 | 83 | private Player getNearestPlayer(Location location) { 84 | double nearestDistance = Double.MAX_VALUE; 85 | Player nearestPlayer = null; 86 | 87 | for (Entity entity : location.getWorld().getNearbyEntities(location, 1000, 1000, 1000)) { 88 | if (entity instanceof Player) { 89 | Player candidate = (Player) entity; 90 | if (candidate.getLocation().distanceSquared(location) < nearestDistance) { 91 | nearestDistance = candidate.getLocation().distance(location); 92 | nearestPlayer = candidate; 93 | } 94 | } 95 | } 96 | 97 | return nearestPlayer; 98 | } 99 | 100 | private boolean isInRange(Location from, Player target, String selector) { 101 | if (selector.length() == 2) return true; 102 | if (from.getWorld() != target.getWorld()) return false; 103 | 104 | Matcher matcher = Pattern.compile(SELECTOR_REGEX, Pattern.CASE_INSENSITIVE).matcher(selector); 105 | matcher.find(); 106 | int radius = Integer.parseInt(matcher.group(1)); 107 | 108 | return from.distance(target.getLocation()) <= radius; 109 | } 110 | 111 | protected JSONObject jsonFromArgs(String[] args, int startPoint) { 112 | StringBuilder json = new StringBuilder(); 113 | for(int i = startPoint; i < args.length; i++) json.append(args[i]); 114 | 115 | try { 116 | return new JSONObject(json.toString()); 117 | }catch(Exception ex) { 118 | return null; 119 | } 120 | } 121 | 122 | public HashMap getSuggestions() { 123 | return this.suggestions; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/JukeboxCommandExecutor.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 5 | import net.mcjukebox.plugin.bukkit.api.ResourceType; 6 | import net.mcjukebox.plugin.bukkit.managers.RegionManager; 7 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 8 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 9 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.ChatColor; 12 | import org.bukkit.command.Command; 13 | import org.bukkit.command.CommandSender; 14 | import org.bukkit.command.TabExecutor; 15 | import org.bukkit.entity.Player; 16 | import org.bukkit.util.StringUtil; 17 | 18 | import java.util.*; 19 | 20 | public class JukeboxCommandExecutor implements TabExecutor { 21 | 22 | private HashMap commands = new HashMap<>(); 23 | 24 | public JukeboxCommandExecutor(RegionManager regionManager) { 25 | commands.put("music", new PlayCommand(ResourceType.MUSIC)); 26 | commands.put("sound", new PlayCommand(ResourceType.SOUND_EFFECT)); 27 | commands.put("stop", new StopCommand()); 28 | commands.put("setkey", new SetKeyCommand()); 29 | commands.put("region", new RegionCommand(regionManager)); 30 | commands.put("show", new ShowCommand()); 31 | commands.put("import", new ImportCommand(regionManager)); 32 | } 33 | 34 | public boolean onCommand(CommandSender commandSender, Command command, String label, String[] args) { 35 | // Warn user an API key is needed, unless they have one or are attempting to add one 36 | boolean requireApiKey = args.length == 0 || !args[0].equalsIgnoreCase("setkey"); 37 | if (MCJukebox.getInstance().getAPIKey() == null && requireApiKey) { 38 | commandSender.sendMessage(ChatColor.RED + "No API Key set. Type /jukebox setkey ."); 39 | commandSender.sendMessage(ChatColor.DARK_RED + "You can get this key from https://www.mcjukebox.net/admin"); 40 | return true; 41 | } 42 | 43 | // Generate a client URL for users who don't specify an argument, or lack other permissions 44 | if (args.length == 0 || !commandSender.hasPermission("mcjukebox." + args[0].toLowerCase())) { 45 | // TODO: Warn user if we skipped the command due to permissions 46 | return URL(commandSender); 47 | } 48 | 49 | // Run the command if it exists, or show the help menu otherwise 50 | if (commands.containsKey(args[0])) { 51 | JukeboxCommand commandToExecute = commands.get(args[0]); 52 | args = Arrays.copyOfRange(args, 1, args.length); 53 | if(commandToExecute.executeWithSelectors(commandSender, args)) return true; 54 | } 55 | 56 | return help(commandSender); 57 | } 58 | 59 | private boolean URL(final CommandSender sender) { 60 | MessageUtils.sendMessage(sender, "user.openLoading"); 61 | Bukkit.getScheduler().runTaskAsynchronously(MCJukebox.getInstance(), new Runnable() { 62 | @Override 63 | public void run() { 64 | if (!(sender instanceof Player)) return; 65 | String token = JukeboxAPI.getToken((Player) sender); 66 | MessageUtils.sendURL((Player) sender, token); 67 | } 68 | }); 69 | return true; 70 | } 71 | 72 | private boolean help(CommandSender sender){ 73 | sender.sendMessage(ChatColor.GREEN + "Jukebox Commands:"); 74 | sender.sendMessage("/jukebox music {options}"); 75 | sender.sendMessage("/jukebox sound {options}"); 76 | sender.sendMessage("/jukebox stop "); 77 | sender.sendMessage("/jukebox stop {options}"); 78 | sender.sendMessage("/jukebox region add "); 79 | sender.sendMessage("/jukebox region remove "); 80 | sender.sendMessage("/jukebox region list "); 81 | sender.sendMessage("/jukebox show add/remove <@show>"); 82 | sender.sendMessage("/jukebox setkey "); 83 | sender.sendMessage("/jukebox import "); 84 | return true; 85 | } 86 | 87 | public List onTabComplete(CommandSender sender, Command command, String label, String[] args) { 88 | // Create an empty suggestion list. If no suggestions are added, return empty list. 89 | List suggestions = new ArrayList<>(); 90 | 91 | // Suggest the main sub commands 92 | if (args.length == 1) { 93 | suggestions.add("help"); 94 | suggestions.addAll(commands.keySet()); 95 | return StringUtil.copyPartialMatches(args[0], suggestions, new ArrayList()); 96 | } 97 | 98 | // Suggest values for sub commands 99 | else if (args.length > 1) { 100 | Integer argumentIndex = args.length - 2; 101 | if (commands.get(args[0]) != null) { 102 | JukeboxCommand cmd = commands.get(args[0]); 103 | if (cmd.getSuggestions().get(argumentIndex) != null) { 104 | suggestions.addAll(cmd.getSuggestions().get(argumentIndex).getSuggestions()); 105 | } 106 | } 107 | return StringUtil.copyPartialMatches(args[args.length - 1], suggestions, new ArrayList()); 108 | } 109 | 110 | return suggestions; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/PlayCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 4 | import net.mcjukebox.plugin.bukkit.api.ResourceType; 5 | import net.mcjukebox.plugin.bukkit.api.models.Media; 6 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 7 | import net.mcjukebox.plugin.bukkit.utils.UrlUtils; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.ChatColor; 10 | import org.bukkit.command.CommandSender; 11 | import org.bukkit.entity.Player; 12 | import org.json.JSONObject; 13 | 14 | import java.util.HashMap; 15 | 16 | public class PlayCommand extends JukeboxCommand { 17 | 18 | private ResourceType type; 19 | 20 | public PlayCommand(ResourceType type) { 21 | this.type = type; 22 | suggestions.put(0, new PlayerOrShowTabArgument(new String[] {})); 23 | } 24 | 25 | @Override 26 | public boolean execute(CommandSender dispatcher, String[] args) { 27 | if (args.length < 2) return false; 28 | 29 | String url = args[1]; 30 | 31 | if(!UrlUtils.isValidURI(url)) { 32 | MessageUtils.sendMessage(dispatcher, "command.invalidUrl"); 33 | return true; 34 | } 35 | if(!UrlUtils.isDirectMediaFile(url)) { 36 | MessageUtils.sendMessage(dispatcher, "command.unexpectedUrl"); 37 | } 38 | 39 | Media toPlay = new Media(type, url); 40 | 41 | if (args.length >= 3) { 42 | JSONObject options = jsonFromArgs(args, 2); 43 | 44 | if (options == null) { 45 | dispatcher.sendMessage(ChatColor.RED + "Unable to parse options as JSON."); 46 | return true; 47 | } 48 | 49 | toPlay.loadOptions(options); 50 | } 51 | 52 | if (args[0].startsWith("@")) { 53 | JukeboxAPI.getShowManager().getShow(args[0]).play(toPlay); 54 | } else { 55 | Player player = Bukkit.getPlayer(args[0]); 56 | if (player != null) { 57 | JukeboxAPI.play(player, toPlay); 58 | } else { 59 | HashMap findAndReplace = new HashMap(); 60 | findAndReplace.put("user", args[1]); 61 | MessageUtils.sendMessage(dispatcher, "command.notOnline", findAndReplace); 62 | } 63 | } 64 | 65 | return true; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/RegionCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import lombok.AllArgsConstructor; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.managers.RegionManager; 6 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 7 | import net.mcjukebox.plugin.bukkit.utils.UrlUtils; 8 | import org.bukkit.ChatColor; 9 | import org.bukkit.command.CommandSender; 10 | import java.util.ArrayList; 11 | import java.util.HashMap; 12 | 13 | public class RegionCommand extends JukeboxCommand { 14 | 15 | private static final int REGIONS_PER_PAGE = 5; 16 | private RegionManager regionManager; 17 | 18 | public RegionCommand(RegionManager regionManager) { 19 | this.regionManager = regionManager; 20 | suggestions.put(0, new StringTabArgument(new String[] {"add", "remove", "list"})); 21 | suggestions.put(1, new RegionTabArgument()); 22 | suggestions.put(2, new ShowTabArgument()); 23 | } 24 | 25 | @Override 26 | public boolean execute(CommandSender dispatcher, String[] args) { 27 | // region add 28 | if (args.length == 3 && args[0].equalsIgnoreCase("add")){ 29 | String url = args[2]; 30 | 31 | if (!url.startsWith("@")) { 32 | if (!UrlUtils.isValidURI(url)) { 33 | MessageUtils.sendMessage(dispatcher, "command.invalidUrl"); 34 | return true; 35 | } 36 | 37 | if (!UrlUtils.isDirectMediaFile(url)) { 38 | MessageUtils.sendMessage(dispatcher, "command.unexpectedUrl"); 39 | } 40 | } 41 | 42 | MCJukebox.getInstance().getRegionManager().addRegion(args[1], url); 43 | MessageUtils.sendMessage(dispatcher, "region.registered"); 44 | return true; 45 | } 46 | 47 | // region remove 48 | if (args.length == 2 && args[0].equalsIgnoreCase("remove")){ 49 | if (MCJukebox.getInstance().getRegionManager().hasRegion(args[1])){ 50 | MCJukebox.getInstance().getRegionManager().removeRegion(args[1]); 51 | MessageUtils.sendMessage(dispatcher, "region.unregistered"); 52 | } else { 53 | MessageUtils.sendMessage(dispatcher, "region.notregistered"); 54 | } 55 | return true; 56 | } 57 | 58 | // region list 59 | if ((args.length == 1 || args.length == 2) && args[0].equalsIgnoreCase("list")) { 60 | ArrayList regions = new ArrayList(regionManager.getRegions().keySet()); 61 | 62 | int pageCount = (regions.size() - 1) / REGIONS_PER_PAGE + 1; 63 | 64 | int page = 1; 65 | if (args.length == 2) { 66 | try { 67 | page = Integer.parseInt(args[1]); 68 | } catch (NumberFormatException e) {} 69 | 70 | if (page > pageCount) { 71 | return false; 72 | } 73 | } 74 | 75 | dispatcher.sendMessage(ChatColor.GREEN + "Registered Regions (Page " + page + "/" + pageCount + "):"); 76 | dispatcher.sendMessage(""); 77 | 78 | for (int i = (page-1) * REGIONS_PER_PAGE; i < page * REGIONS_PER_PAGE && i < regions.size(); i++) { 79 | String region = regions.get(i); 80 | dispatcher.sendMessage(ChatColor.GOLD + "Name: " + ChatColor.WHITE + region); 81 | dispatcher.sendMessage(ChatColor.GOLD + "URL/Show: " + ChatColor.WHITE + regionManager.getRegions().get(region)); 82 | 83 | if (i != regions.size() - 1) { 84 | dispatcher.sendMessage(""); 85 | } 86 | } 87 | 88 | if (page < pageCount) { 89 | dispatcher.sendMessage(ChatColor.GRAY + "Type '/jukebox region list " + (page + 1) + "' to see more..."); 90 | } 91 | 92 | return true; 93 | } 94 | 95 | return false; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/SetKeyCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import org.bukkit.ChatColor; 5 | import org.bukkit.command.CommandSender; 6 | 7 | public class SetKeyCommand extends JukeboxCommand { 8 | 9 | @Override 10 | public boolean execute(CommandSender dispatcher, String[] args) { 11 | // We only need a single argument, which is the key to try 12 | if (args.length != 1) return false; 13 | 14 | MCJukebox.getInstance().getSocketHandler().getKeyHandler().tryKey(dispatcher, args[0]); 15 | dispatcher.sendMessage(ChatColor.GREEN + "Trying key..."); 16 | return true; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/ShowCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 5 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | 10 | import java.util.HashMap; 11 | 12 | public class ShowCommand extends JukeboxCommand { 13 | 14 | public ShowCommand() { 15 | suggestions.put(0, new StringTabArgument(new String[] {"add", "remove"})); 16 | suggestions.put(1, new PlayerTabArgument()); 17 | suggestions.put(2, new ShowTabArgument()); 18 | } 19 | 20 | @Override 21 | public boolean execute(CommandSender dispatcher, String[] args) { 22 | if (args.length != 3) return false; 23 | 24 | if (Bukkit.getPlayer(args[1]) == null) { 25 | HashMap findAndReplace = new HashMap(); 26 | findAndReplace.put("user", args[1]); 27 | MessageUtils.sendMessage(dispatcher, "command.notOnline", findAndReplace); 28 | return true; 29 | } 30 | 31 | Show show = MCJukebox.getInstance().getShowManager().getShow(args[2]); 32 | Player target = Bukkit.getPlayer(args[1]); 33 | 34 | switch (args[0]) { 35 | case "add": 36 | show.addMember(target, false); 37 | return true; 38 | case "remove": 39 | show.removeMember(target); 40 | return true; 41 | default: 42 | return false; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/StopCommand.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 4 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.ChatColor; 7 | import org.bukkit.command.CommandSender; 8 | import org.bukkit.entity.Player; 9 | import org.json.JSONObject; 10 | 11 | import java.util.HashMap; 12 | 13 | public class StopCommand extends JukeboxCommand { 14 | 15 | public StopCommand() { 16 | suggestions.put(0, new PlayerOrShowTabArgument(new String[] {"music", "all"})); 17 | suggestions.put(1, new PlayerOrShowTabArgument(new String[] {})); 18 | } 19 | 20 | @Override 21 | public boolean execute(CommandSender dispatcher, String[] args) { 22 | if (args.length == 0) return false; 23 | JSONObject options = new JSONObject(); 24 | 25 | if (args.length >= 3) { 26 | options = jsonFromArgs(args, 2); 27 | 28 | if (options == null) { 29 | dispatcher.sendMessage(ChatColor.RED + "Unable to parse options as JSON."); 30 | return true; 31 | } 32 | } 33 | 34 | int fadeDuration = options.has("fadeDuration") ? options.getInt("fadeDuration") : -1; 35 | String channel = options.has("channel") ? options.getString("channel") : "default"; 36 | String selection = args.length >= 2 ? args[0] : "music"; 37 | int targetIndex = args.length == 1 ? 0 : 1; 38 | 39 | // Stop music in a show 40 | if (args[targetIndex].startsWith("@") && selection.equalsIgnoreCase("all")) { 41 | JukeboxAPI.getShowManager().getShow(args[targetIndex]).stopAll(fadeDuration); 42 | return true; 43 | } 44 | 45 | // Stop everything in a show 46 | if (args[targetIndex].startsWith("@") && selection.equalsIgnoreCase("music")) { 47 | JukeboxAPI.getShowManager().getShow(args[targetIndex]).stopMusic(fadeDuration); 48 | return true; 49 | } 50 | 51 | // We haven't encountered either show case, so assume a player is the target 52 | 53 | if (args.length == 1) { 54 | // We weren't provided a player name 55 | return false; 56 | } 57 | 58 | Player target = Bukkit.getPlayer(args[targetIndex]); 59 | 60 | if (target == null) { 61 | HashMap findAndReplace = new HashMap(); 62 | findAndReplace.put("user", args[1]); 63 | MessageUtils.sendMessage(dispatcher, "command.notOnline", findAndReplace); 64 | return true; 65 | } 66 | 67 | // Stop music for a particular player 68 | if (selection.equalsIgnoreCase("all")) { 69 | JukeboxAPI.stopAll(target, channel, fadeDuration); 70 | return true; 71 | } 72 | 73 | // Stop everything for a particular player 74 | if (selection.equalsIgnoreCase("music")) { 75 | JukeboxAPI.stopMusic(target, channel, fadeDuration); 76 | return true; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/commands/TabArgument.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.commands; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 5 | import org.bukkit.Bukkit; 6 | import org.bukkit.entity.Player; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.HashMap; 11 | import java.util.List; 12 | 13 | abstract class TabArgument { 14 | 15 | protected List suggestions = new ArrayList<>(); 16 | 17 | public List getSuggestions() { 18 | return this.suggestions; 19 | } 20 | } 21 | 22 | class StringTabArgument extends TabArgument { 23 | 24 | public StringTabArgument(String[] customSuggestions) { 25 | suggestions.addAll(Arrays.asList(customSuggestions)); 26 | } 27 | } 28 | 29 | class RegionTabArgument extends TabArgument { 30 | 31 | @Override 32 | public List getSuggestions() { 33 | HashMap regions = MCJukebox.getInstance().getRegionManager().getRegions(); 34 | suggestions.addAll(regions.keySet()); 35 | return this.suggestions; 36 | } 37 | } 38 | 39 | class PlayerTabArgument extends TabArgument { 40 | 41 | @Override 42 | public List getSuggestions() { 43 | for (Player player : Bukkit.getOnlinePlayers()) { 44 | suggestions.add(player.getName()); 45 | } 46 | return this.suggestions; 47 | } 48 | } 49 | 50 | class ShowTabArgument extends TabArgument { 51 | 52 | @Override 53 | public List getSuggestions() { 54 | HashMap shows = MCJukebox.getInstance().getShowManager().getShows(); 55 | for (String show : shows.keySet()) { 56 | suggestions.add("@" + show); 57 | } 58 | return this.suggestions; 59 | } 60 | } 61 | 62 | class PlayerOrShowTabArgument extends TabArgument { 63 | 64 | public PlayerOrShowTabArgument(String[] customSuggestions) { 65 | suggestions.addAll(Arrays.asList(customSuggestions)); 66 | } 67 | 68 | @Override 69 | public List getSuggestions() { 70 | HashMap shows = MCJukebox.getInstance().getShowManager().getShows(); 71 | for (String show : shows.keySet()) { 72 | suggestions.add("@" + show); 73 | } 74 | for (Player player : Bukkit.getOnlinePlayers()) { 75 | suggestions.add(player.getName()); 76 | } 77 | return this.suggestions; 78 | } 79 | } -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/events/ClientConnectEvent.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.events; 2 | 3 | import lombok.Getter; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.HandlerList; 6 | 7 | public class ClientConnectEvent extends Event { 8 | 9 | private static final HandlerList handlers = new HandlerList(); 10 | 11 | @Getter private String username; 12 | @Getter private long timestamp; 13 | 14 | public ClientConnectEvent(String username, long timestamp) { 15 | super(true); 16 | this.username = username; 17 | this.timestamp = timestamp; 18 | } 19 | 20 | public HandlerList getHandlers() { 21 | return handlers; 22 | } 23 | 24 | public static HandlerList getHandlerList() { 25 | return handlers; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/events/ClientDisconnectEvent.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.events; 2 | 3 | import lombok.Getter; 4 | import org.bukkit.event.Event; 5 | import org.bukkit.event.HandlerList; 6 | 7 | public class ClientDisconnectEvent extends Event { 8 | 9 | private static final HandlerList handlers = new HandlerList(); 10 | 11 | @Getter private String username; 12 | @Getter private long timestamp; 13 | 14 | public ClientDisconnectEvent(String username, long timestamp) { 15 | super(true); 16 | this.username = username; 17 | this.timestamp = timestamp; 18 | } 19 | 20 | public HandlerList getHandlers() { 21 | return handlers; 22 | } 23 | 24 | public static HandlerList getHandlerList() { 25 | return handlers; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/listeners/RegionListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.listeners; 2 | 3 | import lombok.Getter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 6 | import net.mcjukebox.plugin.bukkit.api.ResourceType; 7 | import net.mcjukebox.plugin.bukkit.api.models.Media; 8 | import net.mcjukebox.plugin.bukkit.managers.RegionManager; 9 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 10 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 11 | import net.mcjukebox.shared.utils.RegionUtils; 12 | import net.mcjukebox.shared.api.Region; 13 | import org.bukkit.entity.Player; 14 | import org.bukkit.event.EventHandler; 15 | import org.bukkit.event.Listener; 16 | import org.bukkit.event.player.PlayerJoinEvent; 17 | import org.bukkit.event.player.PlayerMoveEvent; 18 | import org.bukkit.event.player.PlayerQuitEvent; 19 | import org.bukkit.event.player.PlayerTeleportEvent; 20 | import org.bukkit.event.vehicle.VehicleMoveEvent; 21 | 22 | import java.util.HashMap; 23 | import java.util.UUID; 24 | 25 | public class RegionListener implements Listener{ 26 | 27 | private RegionManager utils; 28 | @Getter private HashMap playerInRegion = new HashMap(); 29 | 30 | public RegionListener(RegionManager utils){ 31 | this.utils = utils; 32 | } 33 | 34 | @EventHandler 35 | public void onJoin(PlayerJoinEvent event) { 36 | //The from location has to be offset else the event will not be run 37 | onMove(new PlayerMoveEvent(event.getPlayer(), event.getPlayer().getLocation().add(1, 0, 0), event.getPlayer().getLocation())); 38 | } 39 | 40 | @EventHandler 41 | public void onTeleport(PlayerTeleportEvent event){ 42 | onMove(new PlayerMoveEvent(event.getPlayer(), event.getFrom(), event.getTo())); 43 | } 44 | 45 | @EventHandler 46 | public void onMinecartMove(VehicleMoveEvent event) { 47 | if (event.getVehicle().getPassenger() != null && event.getVehicle().getPassenger() instanceof Player) { 48 | onMove(new PlayerMoveEvent((Player) event.getVehicle().getPassenger(), event.getFrom(), event.getTo())); 49 | } 50 | } 51 | 52 | @EventHandler 53 | public void onMove(PlayerMoveEvent e){ 54 | //Only execute if the player moves an entire block 55 | if(e.getFrom().getBlockX() == e.getTo().getBlockX() && e.getFrom().getBlockZ() == e.getTo().getBlockZ()) return; 56 | 57 | int highestPriority = -1; 58 | String highestRegion = null; 59 | for (Region region : RegionUtils.getInstance().getProvider().getApplicableRegions(e.getTo())) { 60 | if (region.getPriority() > highestPriority && utils.hasRegion(region.getId())) { 61 | highestPriority = region.getPriority(); 62 | highestRegion = region.getId(); 63 | } 64 | } 65 | 66 | ShowManager showManager = MCJukebox.getInstance().getShowManager(); 67 | 68 | if(highestRegion == null && utils.hasRegion("__global__")) { 69 | highestRegion = "__global__"; 70 | } 71 | 72 | //In this case, there are no applicable shared so we need go no further 73 | if(highestRegion == null){ 74 | if(playerInRegion.containsKey(e.getPlayer().getUniqueId())){ 75 | String lastShow = utils.getURL(playerInRegion.get(e.getPlayer().getUniqueId())); 76 | playerInRegion.remove(e.getPlayer().getUniqueId()); 77 | 78 | if (lastShow == null || lastShow.toCharArray()[0] != '@') { 79 | //Region no longer exists, stop the music. 80 | JukeboxAPI.stopMusic(e.getPlayer()); 81 | return; 82 | } else if(lastShow.toCharArray()[0] == '@') { 83 | showManager.getShow(lastShow).removeMember(e.getPlayer()); 84 | return; 85 | } 86 | } 87 | return; 88 | } 89 | 90 | if(playerInRegion.containsKey(e.getPlayer().getUniqueId()) && 91 | playerInRegion.get(e.getPlayer().getUniqueId()).equals(highestRegion)) return; 92 | 93 | if(playerInRegion.containsKey(e.getPlayer().getUniqueId()) && 94 | utils.getURL(playerInRegion.get(e.getPlayer().getUniqueId())).equals( 95 | utils.getURL(highestRegion))) { 96 | // No need to restart the track, or re-add them to a show, but still update our records 97 | playerInRegion.put(e.getPlayer().getUniqueId(), highestRegion); 98 | return; 99 | } 100 | 101 | if(playerInRegion.containsKey(e.getPlayer().getUniqueId())) { 102 | String lastShow = utils.getURL(playerInRegion.get(e.getPlayer().getUniqueId())); 103 | if(lastShow.toCharArray()[0] == '@') { 104 | showManager.getShow(lastShow).removeMember(e.getPlayer()); 105 | } 106 | } 107 | 108 | if(utils.getURL(highestRegion).toCharArray()[0] == '@') { 109 | if(playerInRegion.containsKey(e.getPlayer().getUniqueId())) JukeboxAPI.stopMusic(e.getPlayer()); 110 | showManager.getShow(utils.getURL(highestRegion)).addMember(e.getPlayer(), true); 111 | playerInRegion.put(e.getPlayer().getUniqueId(), highestRegion); 112 | return; 113 | } 114 | 115 | Media media = new Media(ResourceType.MUSIC, utils.getURL(highestRegion)); 116 | JukeboxAPI.play(e.getPlayer(), media); 117 | playerInRegion.put(e.getPlayer().getUniqueId(), highestRegion); 118 | } 119 | 120 | @EventHandler 121 | public void onLeave(PlayerQuitEvent event){ 122 | if(playerInRegion.containsKey(event.getPlayer().getUniqueId())) { 123 | String lastAudio = utils.getURL(playerInRegion.get(event.getPlayer().getUniqueId())); 124 | 125 | if(lastAudio == null || lastAudio.toCharArray()[0] != '@') { 126 | JukeboxAPI.stopMusic(event.getPlayer()); 127 | } 128 | 129 | playerInRegion.remove(event.getPlayer().getUniqueId()); 130 | } 131 | 132 | ShowManager showManager = MCJukebox.getInstance().getShowManager(); 133 | if(showManager.inInShow(event.getPlayer().getUniqueId())) { 134 | for(Show show : showManager.getShowsByPlayer(event.getPlayer().getUniqueId())) { 135 | //Only run if they were added by a region 136 | if (!show.getMembers().get(event.getPlayer().getUniqueId())) return; 137 | show.removeMember(event.getPlayer()); 138 | } 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/LangManager.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers; 2 | 3 | import org.bukkit.ChatColor; 4 | import org.bukkit.plugin.Plugin; 5 | import org.json.JSONObject; 6 | 7 | import java.util.Iterator; 8 | 9 | public class LangManager { 10 | 11 | private JSONObject keyValues = new JSONObject(); 12 | 13 | public LangManager(){ 14 | addDefaults(); 15 | } 16 | 17 | /** 18 | * Loads the language file from the data folder, if it exists. 19 | */ 20 | public void loadLang(JSONObject langObject){ 21 | keyValues = langObject; 22 | addDefaults(); 23 | } 24 | 25 | /** 26 | * Returns the value associated with a particular key. 27 | * 28 | * @param key Key to find 29 | * @return Value from the config 30 | */ 31 | public String get(String key){ 32 | String[] elements = key.split("\\."); 33 | JSONObject finalParent = keyValues; 34 | 35 | for(int i = 0; i < elements.length - 1; i++){ 36 | if(!finalParent.has(elements[i])) finalParent.put(elements[i], new JSONObject()); 37 | finalParent = finalParent.getJSONObject(elements[i]); 38 | } 39 | 40 | String value = null; 41 | if(elements.length == 0 && finalParent.has(key)) value = finalParent.getString(key); 42 | else if(finalParent.has(elements[elements.length - 1])) value = finalParent.getString(elements[elements.length - 1]); 43 | 44 | if(value != null) return ChatColor.translateAlternateColorCodes('&', value); 45 | return ChatColor.RED + "Missing Key: " + key; 46 | } 47 | 48 | /** 49 | * Adds all default configuration values to the JSON Object. 50 | */ 51 | private void addDefaults(){ 52 | addDefault("region.registered", "&aRegion registered!"); 53 | addDefault("region.unregistered", "&aRegion unregistered!"); 54 | addDefault("region.notregistered", "&cThat region is not registered!"); 55 | 56 | addDefault("user.openLoading", "&aGenerating link..."); 57 | addDefault("user.openClient", "Click here to launch our custom music client."); 58 | addDefault("user.openHover", "Launch client"); 59 | addDefault("user.openDomain", "https://client.mcjukebox.net"); 60 | 61 | addDefault("event.clientConnect", "&aYou connected to our audio server!"); 62 | addDefault("event.clientDisconnect", "&cYou disconnected from our audio server."); 63 | 64 | addDefault("command.notOnline", "&c[user] is not currently online."); 65 | addDefault("command.invalidUrl", "&cThe URL you entered is not valid. Please make sure that you entered the complete URL."); 66 | addDefault("command.unexpectedUrl", "&6Warning: The URL you provided does not have a recognised file extension and may fail to play. Links to files hosted on services like Spotify, YouTube and SoundCloud are unsupported."); 67 | } 68 | 69 | /** 70 | * Adds a particular property to the JSON Object if it is not already present. 71 | * 72 | * @param key Key to associate the value with 73 | * @param value Value to associate the key with 74 | */ 75 | private void addDefault(String key, String value){ 76 | String[] elements = key.split("\\."); 77 | JSONObject finalParent = keyValues; 78 | 79 | for(int i = 0; i < elements.length - 1; i++){ 80 | if(!finalParent.has(elements[i])) finalParent.put(elements[i], new JSONObject()); 81 | finalParent = finalParent.getJSONObject(elements[i]); 82 | } 83 | 84 | if(!finalParent.has(key) && elements.length == 0) finalParent.put(key, value); 85 | else if(!finalParent.has(elements[elements.length - 1])) finalParent.put(elements[elements.length - 1], value); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/RegionManager.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers; 2 | 3 | import lombok.Getter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 6 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 7 | import net.mcjukebox.plugin.bukkit.utils.DataUtils; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.configuration.ConfigurationSection; 10 | import org.bukkit.configuration.file.YamlConfiguration; 11 | import org.bukkit.entity.Player; 12 | import org.bukkit.event.Listener; 13 | 14 | import java.io.File; 15 | import java.util.HashMap; 16 | import java.util.Iterator; 17 | import java.util.UUID; 18 | 19 | public class RegionManager implements Listener { 20 | 21 | @Getter 22 | private HashMap regions; 23 | private String folder; 24 | 25 | public RegionManager(){ 26 | folder = MCJukebox.getInstance().getDataFolder() + ""; 27 | load(); 28 | } 29 | 30 | private void load(){ 31 | regions = DataUtils.loadObjectFromPath(folder + "/regions.data"); 32 | if(regions == null) regions = new HashMap<>(); 33 | 34 | // Import from the "shared.data" file we accidentally created 35 | HashMap sharedFile = DataUtils.loadObjectFromPath(folder + "/shared.data"); 36 | if (sharedFile != null) { 37 | MCJukebox.getInstance().getLogger().info("Running migration of shared.data regions..."); 38 | for (String key : sharedFile.keySet()) regions.put(key, sharedFile.get(key)); 39 | new File(folder + "/shared.data").delete(); 40 | MCJukebox.getInstance().getLogger().info("Migration complete."); 41 | } 42 | } 43 | 44 | public int importFromOA() { 45 | try { 46 | File configFile = new File("plugins/OpenAudioMc/config.yml"); 47 | YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile); 48 | ConfigurationSection regionConfig = config.getConfigurationSection("storage.regions"); 49 | int added = 0; 50 | for (String region : regionConfig.getKeys(false)) { 51 | String url = regionConfig.getString(region + ".src"); 52 | if (url.length() > 0 && !url.contains(" ")) { 53 | regions.put(region.toLowerCase(), url); 54 | added++; 55 | } 56 | } 57 | return added; 58 | } catch (Exception ex) { 59 | return 0; 60 | } 61 | } 62 | 63 | public void save(){ 64 | DataUtils.saveObjectToPath(regions, folder + "/regions.data"); 65 | } 66 | 67 | public void addRegion(String ID, String URL){ 68 | regions.put(ID.toLowerCase(), URL); 69 | } 70 | 71 | public void removeRegion(String ID){ 72 | ShowManager showManager = MCJukebox.getInstance().getShowManager(); 73 | HashMap playersInRegion = MCJukebox.getInstance().getRegionListener().getPlayerInRegion(); 74 | 75 | Iterator keys = playersInRegion.keySet().iterator(); 76 | 77 | while (keys.hasNext()) { 78 | UUID uuid = keys.next(); 79 | String regionID = playersInRegion.get(uuid); 80 | 81 | if (regionID.equals(ID)) { 82 | Player player = Bukkit.getPlayer(uuid); 83 | if (player == null) continue; 84 | 85 | if (regions.get(ID).charAt(0) == '@') { 86 | showManager.getShow(regions.get(ID)).removeMember(player); 87 | } else { 88 | JukeboxAPI.stopMusic(player); 89 | keys.remove(); 90 | } 91 | } 92 | } 93 | 94 | regions.remove(ID); 95 | } 96 | 97 | public HashMap getRegions() { return this.regions; } 98 | 99 | public boolean hasRegion(String ID){ 100 | return regions.containsKey(ID); 101 | } 102 | 103 | public String getURL(String ID){ 104 | return regions.get(ID); 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/shows/Show.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.shows; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import net.mcjukebox.plugin.bukkit.MCJukebox; 6 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 7 | import net.mcjukebox.plugin.bukkit.api.ResourceType; 8 | import net.mcjukebox.plugin.bukkit.api.models.Media; 9 | import org.bukkit.Bukkit; 10 | import org.bukkit.entity.Player; 11 | 12 | import java.util.HashMap; 13 | import java.util.UUID; 14 | 15 | public class Show { 16 | 17 | @Getter private HashMap members = new HashMap(); 18 | @Getter private Media currentTrack; 19 | @Getter @Setter private String channel = "default"; 20 | 21 | public void addMember(Player player, boolean addedByRegion) { 22 | if(members.containsKey(player.getUniqueId())) return; 23 | members.put(player.getUniqueId(), addedByRegion); 24 | 25 | if(currentTrack != null) JukeboxAPI.play(player, currentTrack); 26 | } 27 | 28 | public void removeMember(Player player) { 29 | if(!members.containsKey(player.getUniqueId())) return; 30 | members.remove(player.getUniqueId()); 31 | JukeboxAPI.stopAll(player, channel, -1); 32 | } 33 | 34 | public void play(Media media) { 35 | media.setStartTime(MCJukebox.getInstance().getTimeUtils().currentTimeMillis()); 36 | media.setChannel(channel); 37 | 38 | if(media.getType() == ResourceType.MUSIC) { 39 | currentTrack = media; 40 | } 41 | 42 | for(UUID UUID : members.keySet()) { 43 | if(Bukkit.getPlayer(UUID) == null) continue; 44 | JukeboxAPI.play(Bukkit.getPlayer(UUID), media); 45 | } 46 | } 47 | 48 | public void stopMusic() { 49 | stopMusic(-1); 50 | } 51 | 52 | public void stopMusic(int fadeDuration) { 53 | if(currentTrack == null) return; 54 | for(UUID UUID : members.keySet()) { 55 | if(Bukkit.getPlayer(UUID) == null) continue; 56 | JukeboxAPI.stopMusic(Bukkit.getPlayer(UUID), channel, fadeDuration); 57 | } 58 | currentTrack = null; 59 | } 60 | 61 | public void stopAll() { 62 | stopAll(-1); 63 | } 64 | 65 | public void stopAll(int fadeDuration) { 66 | for(UUID UUID : members.keySet()) { 67 | if(Bukkit.getPlayer(UUID) == null) continue; 68 | JukeboxAPI.stopAll(Bukkit.getPlayer(UUID), channel, fadeDuration); 69 | } 70 | this.currentTrack = null; 71 | } 72 | 73 | protected void jumpBack(long offset) { 74 | if (currentTrack == null) return; 75 | currentTrack.setStartTime(currentTrack.getStartTime() + offset); 76 | 77 | // Send updated start time to all connected players 78 | for(UUID UUID : members.keySet()) { 79 | if(Bukkit.getPlayer(UUID) == null) continue; 80 | JukeboxAPI.play(Bukkit.getPlayer(UUID), currentTrack); 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/shows/ShowManager.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.shows; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.ArrayList; 6 | import java.util.HashMap; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | public class ShowManager { 11 | 12 | @Getter private HashMap shows = new HashMap(); 13 | 14 | public Show getShow(String name) { 15 | name = name.replace("@", ""); 16 | name = name.toLowerCase(); 17 | if(shows.containsKey(name)) return shows.get(name); 18 | 19 | Show show = new Show(); 20 | show.setChannel(name); 21 | shows.put(name, show); 22 | return show; 23 | } 24 | 25 | public List getShowsByPlayer(UUID UUID) { 26 | ArrayList inShows = new ArrayList(); 27 | for(Show show : shows.values()) { 28 | if(show.getMembers().containsKey(UUID)) inShows.add(show); 29 | } 30 | return inShows; 31 | } 32 | 33 | public boolean inInShow(UUID UUID) { 34 | for(Show show : shows.values()) { 35 | if(show.getMembers().containsKey(UUID)) return true; 36 | } 37 | return false; 38 | } 39 | 40 | public boolean showExists(String name) { 41 | name = name.replace("@", ""); 42 | return shows.containsKey(name); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/shows/ShowSyncTask.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.shows; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | 5 | /** 6 | * This task identifies when the server freezes and jumps back in show music tracks to compensate. The task is run 7 | * every tick but only affects shows with exclamation marks at the start of their name. 8 | */ 9 | public class ShowSyncTask implements Runnable { 10 | 11 | private long lastSync; 12 | private long expectedTime; 13 | 14 | public ShowSyncTask() { 15 | expectedTime = System.currentTimeMillis(); 16 | lastSync = 0; 17 | } 18 | 19 | @Override 20 | public void run() { 21 | // Update expected time as a tick has passed 22 | expectedTime = expectedTime + (1000 / 20); 23 | 24 | // If the server froze for more than a second 25 | if (System.currentTimeMillis() >= expectedTime + 1000) { 26 | // and we haven't jumped back in the track for the last three seconds 27 | if (System.currentTimeMillis() - 3000 > lastSync) { 28 | jumpBack(System.currentTimeMillis() - expectedTime); 29 | lastSync = System.currentTimeMillis(); 30 | expectedTime = System.currentTimeMillis(); 31 | } 32 | } 33 | } 34 | 35 | private void jumpBack(long offset) { 36 | ShowManager showManager = MCJukebox.getInstance().getShowManager(); 37 | for (String showName : showManager.getShows().keySet()) { 38 | if (showName.startsWith("!")) { 39 | Show show = showManager.getShow(showName); 40 | show.jumpBack(offset); 41 | } 42 | } 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/skript/SkriptManager.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.skript; 2 | 3 | import ch.njol.skript.Skript; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.managers.skript.effects.EffRequestToken; 6 | import net.mcjukebox.plugin.bukkit.managers.skript.expressions.ExprGetToken; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | import java.util.UUID; 11 | 12 | public class SkriptManager { 13 | 14 | public static Map tokens = new HashMap(); 15 | 16 | public SkriptManager() { 17 | Skript.registerEffect(EffRequestToken.class, EffRequestToken.FORMAT); 18 | ExprGetToken.register(ExprGetToken.class, String.class, "mcjukebox_token", "player"); 19 | MCJukebox.getInstance().getLogger().info("Skript integration enabled!"); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/skript/effects/EffRequestToken.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.skript.effects; 2 | 3 | import ch.njol.skript.lang.Effect; 4 | import ch.njol.skript.lang.Expression; 5 | import ch.njol.skript.lang.SkriptParser; 6 | import ch.njol.skript.lang.TriggerItem; 7 | import ch.njol.util.Kleenean; 8 | import net.mcjukebox.plugin.bukkit.MCJukebox; 9 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 10 | import net.mcjukebox.plugin.bukkit.managers.skript.SkriptManager; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.entity.Player; 13 | import org.bukkit.event.Event; 14 | 15 | public class EffRequestToken extends Effect { 16 | 17 | public static String FORMAT = "mcjukebox requestToken %player%"; 18 | 19 | private Expression playerExpression; 20 | 21 | @Override 22 | public boolean init(Expression[] exprs, int matchedPattern, Kleenean isDelayed, SkriptParser.ParseResult parseResult) { 23 | playerExpression = (Expression) exprs[0]; 24 | return true; 25 | } 26 | 27 | @Override 28 | public String toString(Event e, boolean debug) { 29 | return "mcjukebox gettoken " + playerExpression.getSingle(e); 30 | } 31 | 32 | @Override 33 | protected TriggerItem walk(Event e) { 34 | debug(e, true); 35 | execute(e); 36 | return null; 37 | } 38 | 39 | @Override 40 | protected void execute(final Event e) { 41 | final Player player = playerExpression.getSingle(e); 42 | Bukkit.getScheduler().runTaskAsynchronously(MCJukebox.getInstance(), new Runnable() { 43 | @Override 44 | public void run() { 45 | String token = JukeboxAPI.getToken(player); 46 | SkriptManager.tokens.put(player.getUniqueId(), token); 47 | Bukkit.getScheduler().runTask(MCJukebox.getInstance(), new Runnable() { 48 | @Override 49 | public void run() { 50 | if (getNext() != null) { 51 | TriggerItem.walk(getNext(), e); 52 | } 53 | } 54 | }); 55 | } 56 | }); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/managers/skript/expressions/ExprGetToken.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.managers.skript.expressions; 2 | 3 | import ch.njol.skript.expressions.base.SimplePropertyExpression; 4 | import net.mcjukebox.plugin.bukkit.managers.skript.SkriptManager; 5 | import org.bukkit.entity.Player; 6 | 7 | public class ExprGetToken extends SimplePropertyExpression { 8 | 9 | @Override 10 | protected String getPropertyName() { 11 | return "mcjukebox_token"; 12 | } 13 | 14 | @Override 15 | public String convert(Player player) { 16 | return SkriptManager.tokens.get(player.getUniqueId()); 17 | } 18 | 19 | @Override 20 | public Class getReturnType() { 21 | return String.class; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/DripTask.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets; 2 | 3 | public class DripTask implements Runnable { 4 | 5 | private SocketHandler socketHandler; 6 | 7 | public DripTask(SocketHandler socketHandler) { 8 | this.socketHandler = socketHandler; 9 | } 10 | 11 | @Override 12 | public void run() { 13 | if (socketHandler.getServer() != null && socketHandler.getServer().connected()) { 14 | socketHandler.getDropListener().setLastDripSent(System.currentTimeMillis()); 15 | socketHandler.emit("drip", null); 16 | } 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/KeyHandler.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets; 2 | 3 | import lombok.Getter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.utils.DataUtils; 6 | import org.bukkit.Bukkit; 7 | import org.bukkit.ChatColor; 8 | import org.bukkit.command.CommandSender; 9 | 10 | import java.io.File; 11 | 12 | public class KeyHandler { 13 | 14 | private SocketHandler socketHandler; 15 | @Getter private CommandSender currentlyTryingKey; 16 | 17 | public KeyHandler(SocketHandler socketHandler) { 18 | this.socketHandler = socketHandler; 19 | } 20 | 21 | public void onKeyRejected(String reason) { 22 | CommandSender toNotify = currentlyTryingKey != null ? currentlyTryingKey : Bukkit.getConsoleSender(); 23 | toNotify.sendMessage(ChatColor.RED + "API key rejected with message: " + reason); 24 | deleteKey(); 25 | currentlyTryingKey = null; 26 | } 27 | 28 | public void tryKey(CommandSender sender, String key) { 29 | currentlyTryingKey = sender; 30 | DataUtils.saveObjectToPath(key, MCJukebox.getInstance().getDataFolder() + "/api.key"); 31 | socketHandler.disconnect(); 32 | socketHandler.attemptConnection(); 33 | } 34 | 35 | private void deleteKey() { 36 | new File(MCJukebox.getInstance().getDataFolder() + "/api.key").delete(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/ReconnectTask.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets; 2 | 3 | import lombok.Setter; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.ChatColor; 6 | 7 | import java.util.Random; 8 | 9 | public class ReconnectTask implements Runnable { 10 | 11 | private SocketHandler socketHandler; 12 | private long lastReconnectionAttempt = 0; 13 | private int reconnectionFailures = 0; 14 | @Setter private boolean reconnecting = true; 15 | 16 | public ReconnectTask(SocketHandler socketHandler) { 17 | this.socketHandler = socketHandler; 18 | } 19 | 20 | @Override 21 | public void run() { 22 | if(reconnecting || socketHandler.getServer().connected()) return; 23 | long timeSinceRun = System.currentTimeMillis() - lastReconnectionAttempt; 24 | if(timeSinceRun < getCurrentReconnectionDelay(reconnectionFailures)) return; 25 | 26 | //Add some randomness to reconnect interval to prevent server overload 27 | if(new Random().nextInt(15) != 7) return; 28 | 29 | reconnecting = true; 30 | reconnectionFailures = reconnectionFailures + 1; 31 | lastReconnectionAttempt = System.currentTimeMillis(); 32 | 33 | socketHandler.attemptConnection(); 34 | } 35 | 36 | public void reset() { 37 | lastReconnectionAttempt = 0; 38 | reconnectionFailures = 0; 39 | reconnecting = false; 40 | } 41 | 42 | private int getCurrentReconnectionDelay(int reconnectionFailures) { 43 | if(reconnectionFailures <= 3) return 3 * 1000; 44 | if(reconnectionFailures <= 6) return 15 * 1000; 45 | if(reconnectionFailures <= 8) return 30 * 1000; 46 | return 2 * 60 * 1000; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/SocketHandler.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets; 2 | 3 | import lombok.Getter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.sockets.listeners.*; 6 | import io.socket.client.IO; 7 | import io.socket.client.Socket; 8 | import org.bukkit.Bukkit; 9 | import org.json.JSONObject; 10 | 11 | public class SocketHandler { 12 | 13 | @Getter private Socket server; 14 | @Getter private ReconnectTask reconnectTask; 15 | @Getter private DripTask dripTask; 16 | @Getter private DropListener dropListener = new DropListener(); 17 | @Getter private TokenListener tokenListener; 18 | @Getter private KeyHandler keyHandler = new KeyHandler(this); 19 | @Getter private ConnectionListener connectionListener = new ConnectionListener(this); 20 | 21 | public SocketHandler() { 22 | reconnectTask = new ReconnectTask(this); 23 | dripTask = new DripTask(this); 24 | tokenListener = new TokenListener(); 25 | Bukkit.getScheduler().runTaskTimerAsynchronously(MCJukebox.getInstance(), reconnectTask, 0, 1 * 20); 26 | Bukkit.getScheduler().runTaskTimerAsynchronously(MCJukebox.getInstance(), dripTask, 0, 30 * 20); 27 | attemptConnection(); 28 | } 29 | 30 | public void attemptConnection() { 31 | try { 32 | if(MCJukebox.getInstance().getAPIKey() == null) { 33 | MCJukebox.getInstance().getLogger().warning("No API key set - ignoring attempt to connect."); 34 | return; 35 | } 36 | 37 | IO.Options opts = new IO.Options(); 38 | opts.secure = true; 39 | opts.reconnection = false; 40 | opts.query = "APIKey=" + MCJukebox.getInstance().getAPIKey(); 41 | 42 | String url = "https://secure.ws.mcjukebox.net"; 43 | 44 | server = IO.socket(url, opts); 45 | server.connect(); 46 | 47 | registerEventListeners(); 48 | }catch(Exception ex){ 49 | MCJukebox.getInstance().getLogger().warning("An unknown error occurred, disabling plugin..."); 50 | ex.printStackTrace(); 51 | //Disable plugin due to an error 52 | Bukkit.getPluginManager().disablePlugin(MCJukebox.getInstance()); 53 | } 54 | } 55 | 56 | private void registerEventListeners() { 57 | //Connection issue listener 58 | server.on(Socket.EVENT_ERROR, connectionListener.getConnectionFailedListener()); 59 | server.on(Socket.EVENT_CONNECT_ERROR, connectionListener.getConnectionFailedListener()); 60 | server.on(Socket.EVENT_CONNECT_TIMEOUT, connectionListener.getConnectionFailedListener()); 61 | server.on(Socket.EVENT_CONNECT, connectionListener.getConnectionSuccessListener()); 62 | 63 | //Event and data listeners 64 | server.on("drop", dropListener); 65 | server.on("event/clientConnect", new ClientConnectListener()); 66 | server.on("event/clientDisconnect", new ClientDisconnectListener()); 67 | server.on("data/lang", new LangListener()); 68 | server.on("data/token", tokenListener); 69 | } 70 | 71 | public void emit(String channel, JSONObject params) { 72 | if(server == null || !server.connected()) connectionListener.addToQueue(channel, params); 73 | else server.emit(channel, params); 74 | } 75 | 76 | public void disconnect() { 77 | if(server == null || !server.connected()) return; 78 | server.close(); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/ClientConnectListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 6 | import net.mcjukebox.plugin.bukkit.events.ClientConnectEvent; 7 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 8 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 9 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 10 | import org.bukkit.Bukkit; 11 | import org.bukkit.entity.Player; 12 | import org.json.JSONObject; 13 | 14 | public class ClientConnectListener implements Emitter.Listener { 15 | 16 | @Override 17 | public void call(Object... objects) { 18 | JSONObject data = (JSONObject) objects[0]; 19 | ClientConnectEvent event = new ClientConnectEvent( 20 | data.getString("username"), data.getLong("timestamp")); 21 | MCJukebox.getInstance().getServer().getPluginManager().callEvent(event); 22 | 23 | if(Bukkit.getPlayer(data.getString("username")) == null) return; 24 | Player player = Bukkit.getPlayer(data.getString("username")); 25 | MessageUtils.sendMessage(player, "event.clientConnect"); 26 | 27 | ShowManager showManager = MCJukebox.getInstance().getShowManager(); 28 | if(!showManager.inInShow(player.getUniqueId())) return; 29 | 30 | for(Show show : showManager.getShowsByPlayer(player.getUniqueId())) { 31 | if(show.getCurrentTrack() != null) 32 | JukeboxAPI.play(player, show.getCurrentTrack()); 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/ClientDisconnectListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import net.mcjukebox.plugin.bukkit.api.JukeboxAPI; 6 | import net.mcjukebox.plugin.bukkit.events.ClientConnectEvent; 7 | import net.mcjukebox.plugin.bukkit.events.ClientDisconnectEvent; 8 | import net.mcjukebox.plugin.bukkit.managers.shows.Show; 9 | import net.mcjukebox.plugin.bukkit.managers.shows.ShowManager; 10 | import net.mcjukebox.plugin.bukkit.utils.MessageUtils; 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.entity.Player; 13 | import org.json.JSONObject; 14 | 15 | public class ClientDisconnectListener implements Emitter.Listener { 16 | 17 | @Override 18 | public void call(Object... objects) { 19 | JSONObject data = (JSONObject) objects[0]; 20 | ClientDisconnectEvent event = new ClientDisconnectEvent( 21 | data.getString("username"), data.getLong("timestamp")); 22 | MCJukebox.getInstance().getServer().getPluginManager().callEvent(event); 23 | 24 | if(Bukkit.getPlayer(data.getString("username")) == null) return; 25 | MessageUtils.sendMessage(Bukkit.getPlayer(data.getString("username")), "event.clientDisconnect"); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/ConnectionListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | import net.mcjukebox.plugin.bukkit.MCJukebox; 7 | import net.mcjukebox.plugin.bukkit.sockets.SocketHandler; 8 | import org.bukkit.Bukkit; 9 | import org.bukkit.ChatColor; 10 | import org.bukkit.command.CommandSender; 11 | import org.json.JSONObject; 12 | 13 | import java.util.ArrayList; 14 | import java.util.HashMap; 15 | import java.util.List; 16 | 17 | public class ConnectionListener { 18 | 19 | @Getter private ConnectionFailedListener connectionFailedListener; 20 | @Getter private ConnectionSuccessListener connectionSuccessListener; 21 | 22 | private SocketHandler socketHandler; 23 | private HashMap> queue = new HashMap>(); 24 | private boolean noConnectionWarned = false; 25 | 26 | public ConnectionListener(SocketHandler socketHandler) { 27 | this.socketHandler = socketHandler; 28 | connectionFailedListener = new ConnectionFailedListener(); 29 | connectionSuccessListener = new ConnectionSuccessListener(socketHandler.getDropListener()); 30 | } 31 | 32 | public class ConnectionFailedListener implements Emitter.Listener { 33 | 34 | @Override 35 | public void call(Object... objects) { 36 | String reason = objects.length > 0 && objects[0] instanceof String ? (String) objects[0] : null; 37 | 38 | if(reason != null) { 39 | socketHandler.getKeyHandler().onKeyRejected(reason); 40 | return; 41 | } 42 | 43 | socketHandler.getReconnectTask().setReconnecting(false); 44 | if(noConnectionWarned) return; 45 | 46 | CommandSender recipient = socketHandler.getKeyHandler().getCurrentlyTryingKey(); 47 | if(recipient == null) recipient = Bukkit.getConsoleSender(); 48 | recipient.sendMessage(ChatColor.RED + "Unable to connect to MCJukebox."); 49 | recipient.sendMessage(ChatColor.RED + "This could be caused by a period of server maintenance."); 50 | recipient.sendMessage(ChatColor.GOLD + "If the problem persists, please email support@mcjukebox.net"); 51 | noConnectionWarned = true; 52 | } 53 | 54 | } 55 | 56 | @AllArgsConstructor 57 | public class ConnectionSuccessListener implements Emitter.Listener { 58 | 59 | private DropListener dropListener; 60 | 61 | @Override 62 | public void call(Object... objects) { 63 | CommandSender recipient = socketHandler.getKeyHandler().getCurrentlyTryingKey(); 64 | String message = "Key accepted and connection to MCJukebox established."; 65 | 66 | if(recipient != null) recipient.sendMessage(ChatColor.GREEN + message); 67 | else MCJukebox.getInstance().getLogger().info(message); 68 | 69 | dropListener.setLastDripSent(System.currentTimeMillis()); 70 | socketHandler.emit("drip", null); 71 | 72 | for(String channel : queue.keySet()) { 73 | for(JSONObject params : queue.get(channel)) { 74 | socketHandler.emit(channel, params); 75 | } 76 | } 77 | 78 | queue.clear(); 79 | noConnectionWarned = false; 80 | socketHandler.getReconnectTask().reset(); 81 | } 82 | 83 | } 84 | 85 | public void addToQueue(String channel, JSONObject params) { 86 | List toRun = queue.containsKey(channel) ? queue.get(channel) : new ArrayList(); 87 | toRun.add(params); 88 | queue.put(channel, toRun); 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/DropListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import lombok.Setter; 5 | import net.mcjukebox.plugin.bukkit.MCJukebox; 6 | 7 | public class DropListener implements Emitter.Listener { 8 | 9 | @Setter private long lastDripSent; 10 | 11 | @Override 12 | public void call(Object... objects) { 13 | long roundTripTime = System.currentTimeMillis() - lastDripSent; 14 | long serverTime = (long) objects[0]; 15 | MCJukebox.getInstance().getTimeUtils().updateOffset(roundTripTime, serverTime); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/LangListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import net.mcjukebox.plugin.bukkit.MCJukebox; 5 | import org.json.JSONObject; 6 | 7 | public class LangListener implements Emitter.Listener { 8 | 9 | @Override 10 | public void call(Object... objects) { 11 | JSONObject data = (JSONObject) (objects.length == 2 ? objects[1] : objects[0]); 12 | MCJukebox.getInstance().getLangManager().loadLang(data); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/sockets/listeners/TokenListener.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.sockets.listeners; 2 | 3 | import io.socket.emitter.Emitter; 4 | import org.json.JSONObject; 5 | 6 | import java.util.*; 7 | 8 | public class TokenListener implements Emitter.Listener { 9 | 10 | private Map> tokenLocks; 11 | private Map tokens; 12 | 13 | public TokenListener() { 14 | tokenLocks = new HashMap<>(); 15 | tokens = new HashMap<>(); 16 | } 17 | 18 | @Override 19 | public void call(Object... objects) { 20 | JSONObject data = (JSONObject) objects[0]; 21 | String username = data.getString("username"); 22 | String token = data.getString("token"); 23 | tokens.put(username, token); 24 | 25 | if(!tokenLocks.containsKey(username)) return; 26 | 27 | for (Object lock : tokenLocks.get(username)) { 28 | synchronized (lock) { 29 | lock.notify(); 30 | } 31 | } 32 | 33 | tokenLocks.remove(username); 34 | return; 35 | } 36 | 37 | public void addLock(String username, Object lock) { 38 | if (tokenLocks.containsKey(username)) { 39 | tokenLocks.get(username).add(lock); 40 | } else { 41 | ArrayList locks = new ArrayList(); 42 | locks.add(lock); 43 | tokenLocks.put(username, locks); 44 | } 45 | } 46 | 47 | public String getToken(String username) { 48 | return tokens.get(username); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/utils/DataUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.utils; 2 | 3 | import java.io.*; 4 | 5 | public class DataUtils { 6 | 7 | /** 8 | * Attempts to write an object to the provided path, creating the file if it does not already 9 | * exist. Uses the default serialization methods, and as result, it is recommended that in 10 | * Bukkit applications, it be used in conjunction with the Serialization package 11 | * 12 | * @param objectToSave The object which should be saved 13 | * @param pathToSaveTo The path in which to save the object 14 | */ 15 | public static void saveObjectToPath(T objectToSave, String pathToSaveTo) { 16 | try { 17 | File file = new File(pathToSaveTo); 18 | if(!file.getParentFile().exists()) file.getParentFile().mkdirs(); 19 | if(!file.exists()) file.createNewFile(); 20 | ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); 21 | oos.writeObject(objectToSave); 22 | oos.close(); 23 | } catch (IOException e) { 24 | e.printStackTrace(); 25 | } 26 | } 27 | 28 | /** 29 | * Loads an object saved using the saveObjectToPath method. Returns an object which can be 30 | * casted, after checking to the expected Type. 31 | * 32 | * @param pathToLoadFrom The path in which the object file is located 33 | * @return An object which has been loaded from the file, or null if the file does not exist 34 | */ 35 | public static T loadObjectFromPath(String pathToLoadFrom) { 36 | try { 37 | ObjectInputStream ois = new ObjectInputStream(new FileInputStream(pathToLoadFrom)); 38 | T result = (T) ois.readObject(); 39 | ois.close(); 40 | return result; 41 | } catch (IOException | ClassNotFoundException ex) { 42 | return null; 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/utils/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.utils; 2 | 3 | import lombok.Setter; 4 | import net.mcjukebox.plugin.bukkit.managers.LangManager; 5 | import org.bukkit.ChatColor; 6 | import org.bukkit.command.CommandSender; 7 | import org.bukkit.entity.Player; 8 | 9 | import java.util.HashMap; 10 | 11 | public class MessageUtils { 12 | 13 | @Setter private static LangManager langManager; 14 | 15 | /** 16 | * Sends a message to the given player. 17 | * 18 | * @param player Player who the message should be sent to 19 | * @param key Key which should be used to lookup the message 20 | */ 21 | public static void sendMessage(CommandSender player, String key){ 22 | sendMessage(player, key, null); 23 | } 24 | 25 | /** 26 | * Sends a message to the given player, replacing particular keys. 27 | * 28 | * @param player Player who the message should be sent to 29 | * @param key Key which should be used to lookup the message 30 | * @param findAndReplace Optional list of keys which should be replaced with the corresponding values 31 | */ 32 | public static void sendMessage(CommandSender player, String key, HashMap findAndReplace){ 33 | String message = langManager.get(key); 34 | 35 | //Don't send message if the localisation is blank 36 | if(message.trim().equalsIgnoreCase("")) return; 37 | 38 | //Replace any values in the find and replace HashMap, if it is present 39 | message = ChatColor.translateAlternateColorCodes('&', message); 40 | if (findAndReplace != null) { 41 | for (String find : findAndReplace.keySet()) message = message.replace("[" + find + "]", findAndReplace.get(find)); 42 | } 43 | 44 | player.sendMessage(message); 45 | } 46 | 47 | public static void sendURL(Player player, String token){ 48 | if(isSpigot()){ 49 | new SpigotUtils().URL(player, langManager, token); 50 | }else{ 51 | String URL = langManager.get("user.openDomain") + "?token=" + token; 52 | player.sendMessage(ChatColor.GOLD + langManager.get("user.openClient")); 53 | player.sendMessage(ChatColor.GOLD + URL); 54 | } 55 | } 56 | 57 | private static boolean isSpigot(){ 58 | try { 59 | Class.forName("org.spigotmc.SpigotConfig"); 60 | return true; 61 | } catch(Exception e) { 62 | return false; 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/utils/SpigotUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.utils; 2 | 3 | import net.mcjukebox.plugin.bukkit.managers.LangManager; 4 | import net.md_5.bungee.api.ChatColor; 5 | import net.md_5.bungee.api.chat.*; 6 | import org.bukkit.entity.Player; 7 | 8 | public class SpigotUtils { 9 | 10 | public static void URL(Player player, LangManager langManager, String token){ 11 | String URL = langManager.get("user.openDomain") + "?token=" + token; 12 | 13 | TextComponent message = new TextComponent(TextComponent.fromLegacyText(langManager.get("user.openClient"))); 14 | message.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, URL)); 15 | message.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, TextComponent.fromLegacyText(langManager.get("user.openHover")))); 16 | player.spigot().sendMessage(message); 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/utils/TimeUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.utils; 2 | 3 | import net.mcjukebox.plugin.bukkit.MCJukebox; 4 | 5 | public class TimeUtils { 6 | 7 | private static boolean hasUpdated = false; 8 | private static long offset = 0; 9 | 10 | public boolean hasUpdated() { 11 | return hasUpdated; 12 | } 13 | 14 | public void updateOffset(long roundTripTime, long serverTime) { 15 | long offsetFromServer = roundTripTime / 2; 16 | long estimatedTime = serverTime + offsetFromServer; 17 | 18 | offset = estimatedTime - System.currentTimeMillis(); 19 | 20 | if (!hasUpdated()) { 21 | // This is our first offset prediction 22 | MCJukebox.getInstance().getLogger().info("Predicted time offset: " + offset + "ms."); 23 | MCJukebox.getInstance().getLogger().info("This will be silently updated every 30 seconds."); 24 | hasUpdated = true; 25 | } 26 | } 27 | 28 | public long currentTimeMillis() { 29 | return System.currentTimeMillis() + offset; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /plugin/src/main/java/net/mcjukebox/plugin/bukkit/utils/UrlUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.plugin.bukkit.utils; 2 | 3 | import java.net.URI; 4 | import java.net.URISyntaxException; 5 | 6 | public class UrlUtils { 7 | private static String[] supportedFiles = { 8 | "mp3", 9 | "ogg", 10 | "wav", 11 | "webm", 12 | "flac", 13 | "adts" 14 | }; 15 | 16 | public static boolean isValidURI(String url) 17 | { 18 | URI uri; 19 | try { 20 | uri = new URI(url); 21 | } catch (URISyntaxException e) { 22 | return false; 23 | } 24 | 25 | if(!uri.isAbsolute() || (!uri.getScheme().equals("http") && !uri.getScheme().equals("https"))) { 26 | return false; 27 | } 28 | 29 | return true; 30 | } 31 | 32 | public static boolean isDirectMediaFile(String url) { 33 | URI uri; 34 | try { 35 | uri = new URI(url); 36 | } catch (URISyntaxException e) { 37 | return false; 38 | } 39 | 40 | if (uri.getPath() == null) { 41 | return false; 42 | } 43 | 44 | String path = uri.getPath(); 45 | for (String fileType: supportedFiles) { 46 | if (path.endsWith("."+fileType)) { 47 | return true; 48 | } 49 | } 50 | return false; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /plugin/src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: MCJukebox 2 | version: 2.7.0 3 | api-version: 1.13 4 | main: net.mcjukebox.plugin.bukkit.MCJukebox 5 | softdepend: [WorldGuard, Skript] 6 | 7 | commands: 8 | jukebox: 9 | usage: /jukebox {options} 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | pom 8 | 9 | com.oliverdunk 10 | mcjukebox 11 | 2.7.0-RELEASE 12 | 13 | 14 | region-providers 15 | shared 16 | plugin 17 | 18 | 19 | 20 | 21 | github 22 | GitHub oliverdunk Apache Maven Packages 23 | https://maven.pkg.github.com/oliverdunk/JukeboxAPI 24 | 25 | 26 | 27 | 28 | 29 | spigot 30 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 31 | 32 | 33 | sonatype 34 | https://oss.sonatype.org/content/repositories/snapshots 35 | 36 | 37 | 38 | 39 | 40 | org.spigotmc 41 | spigot-api 42 | 1.14.3-R0.1-SNAPSHOT 43 | provided 44 | 45 | 46 | org.json 47 | json 48 | 20231013 49 | compile 50 | 51 | 52 | org.projectlombok 53 | lombok 54 | 1.18.30 55 | provided 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-compiler-plugin 64 | 3.3 65 | 66 | 1.8 67 | 1.8 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /region-providers/api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox-region-providers 10 | 2.7.0-RELEASE 11 | 12 | 13 | mcjukebox-region-providers-api 14 | 15 | 16 | 17 | com.sk89q 18 | worldguard 19 | 6.1.1-SNAPSHOT 20 | provided 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /region-providers/api/src/main/java/net/mcjukebox/shared/api/Region.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.shared.api; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | @AllArgsConstructor 7 | @Data 8 | public class Region { 9 | 10 | private String id; 11 | private int priority; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /region-providers/api/src/main/java/net/mcjukebox/shared/api/RegionProvider.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.shared.api; 2 | 3 | import org.bukkit.Location; 4 | 5 | import java.util.List; 6 | 7 | public interface RegionProvider { 8 | 9 | public String getName(); 10 | public List getApplicableRegions(Location location); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /region-providers/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox 10 | 2.7.0-RELEASE 11 | 12 | 13 | 14 | 1.8 15 | 1.8 16 | 17 | 18 | mcjukebox-region-providers 19 | pom 20 | 2.7.0-RELEASE 21 | 22 | 23 | api 24 | wg6 25 | wg7 26 | 27 | 28 | 29 | 30 | sk89-repo 31 | https://maven.enginehub.org/repo/ 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /region-providers/wg6/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox-region-providers 10 | 2.7.0-RELEASE 11 | 12 | 13 | mcjukebox-region-providers-wg6 14 | 15 | 16 | 17 | com.oliverdunk 18 | mcjukebox-region-providers-api 19 | 2.7.0-RELEASE 20 | 21 | 22 | com.sk89q 23 | worldguard 24 | 6.1.1-SNAPSHOT 25 | provided 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /region-providers/wg6/src/main/java/net/mcjukebox/shared/wg6/RegionProvider.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.shared.wg6; 2 | 3 | import com.sk89q.worldguard.bukkit.WGBukkit; 4 | import com.sk89q.worldguard.protection.ApplicableRegionSet; 5 | import com.sk89q.worldguard.protection.managers.RegionManager; 6 | import com.sk89q.worldguard.protection.regions.ProtectedRegion; 7 | import net.mcjukebox.shared.api.Region; 8 | import org.bukkit.Location; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class RegionProvider implements net.mcjukebox.shared.api.RegionProvider { 14 | 15 | public String getName() { 16 | return "wg6"; 17 | } 18 | 19 | public List getApplicableRegions(Location location) { 20 | ArrayList regionList = new ArrayList(); 21 | 22 | RegionManager regionManager = WGBukkit.getRegionManager(location.getWorld()); 23 | ApplicableRegionSet regions = regionManager.getApplicableRegions(location); 24 | 25 | for (ProtectedRegion region : regions) { 26 | regionList.add(new Region(region.getId(), region.getPriority())); 27 | } 28 | 29 | return regionList; 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /region-providers/wg7/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox-region-providers 10 | 2.7.0-RELEASE 11 | 12 | 13 | mcjukebox-region-providers-wg7 14 | 15 | 16 | 17 | ${project.groupId} 18 | mcjukebox-region-providers-api 19 | 2.7.0-RELEASE 20 | 21 | 22 | com.sk89q.worldguard 23 | worldguard-legacy 24 | 7.0.0-SNAPSHOT 25 | provided 26 | 27 | 28 | org.bukkit 29 | bukkit 30 | 31 | 32 | org.bstats 33 | bstats-bukkit 34 | 35 | 36 | com.sk89q.intake 37 | intake 38 | 39 | 40 | com.sk89q 41 | squirrelid 42 | 43 | 44 | org.flywaydb 45 | flyway-core 46 | 47 | 48 | org.khelekore 49 | prtree 50 | 51 | 52 | com.googlecode.json-simple 53 | json-simple 54 | 55 | 56 | net.sf.opencsv 57 | opencsv 58 | 59 | 60 | com.sk89q 61 | commandbook 62 | 63 | 64 | de.schlichtherle 65 | truezip 66 | 67 | 68 | rhino 69 | js 70 | 71 | 72 | org.yaml 73 | snakeyaml 74 | 75 | 76 | com.google.guava 77 | guava 78 | 79 | 80 | com.sk89q 81 | jchronic 82 | 83 | 84 | com.google.code.findbugs 85 | jsr305 86 | 87 | 88 | com.thoughtworks.paranamer 89 | paranamer 90 | 91 | 92 | com.google.code.gson 93 | gson 94 | 95 | 96 | com.sk89q.lib 97 | jlibnoise 98 | 99 | 100 | com.sk89q 101 | dummypermscompat 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /region-providers/wg7/src/main/java/net/mcjukebox/shared/wg7/RegionProvider.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.shared.wg7; 2 | 3 | import com.sk89q.worldedit.math.BlockVector3; 4 | import com.sk89q.worldedit.world.World; 5 | import com.sk89q.worldguard.WorldGuard; 6 | import com.sk89q.worldguard.internal.platform.WorldGuardPlatform; 7 | import com.sk89q.worldguard.protection.managers.RegionManager; 8 | import com.sk89q.worldguard.protection.regions.ProtectedRegion; 9 | import net.mcjukebox.shared.api.Region; 10 | import org.bukkit.Location; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | import java.util.Set; 15 | 16 | public class RegionProvider implements net.mcjukebox.shared.api.RegionProvider { 17 | 18 | public String getName() { 19 | return "wg7"; 20 | } 21 | 22 | public List getApplicableRegions(Location location) { 23 | ArrayList regionList = new ArrayList(); 24 | 25 | WorldGuardPlatform platform = WorldGuard.getInstance().getPlatform(); 26 | 27 | World world = platform.getMatcher().getWorldByName(location.getWorld().getName()); 28 | RegionManager regionManager = platform.getRegionContainer().get(world); 29 | org.bukkit.util.Vector bukkitVector = location.toVector(); 30 | BlockVector3 vector = BlockVector3.at(bukkitVector.getX(), bukkitVector.getY(), bukkitVector.getZ()); 31 | Set regions = regionManager.getApplicableRegions(vector).getRegions(); 32 | 33 | for (ProtectedRegion region : regions) { 34 | regionList.add(new Region(region.getId(), region.getPriority())); 35 | } 36 | 37 | return regionList; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /scripts/installSkript.sh: -------------------------------------------------------------------------------- 1 | curl -O -L https://github.com/SkriptLang/Skript/releases/download/2.3-beta1/Skript.jar 2 | mvn install:install-file -Dfile=Skript.jar -DgroupId=com.github.SkriptLang -DartifactId=Skript -Dversion=2.3-beta1 -Dpackaging=jar -------------------------------------------------------------------------------- /shared/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | com.oliverdunk 9 | mcjukebox 10 | 2.7.0-RELEASE 11 | 12 | 13 | mcjukebox-shared 14 | 2.7.0-RELEASE 15 | 16 | 17 | 18 | sk89-repo 19 | https://maven.sk89q.com/repo/ 20 | 21 | 22 | 23 | 24 | 25 | com.oliverdunk 26 | mcjukebox-region-providers-wg6 27 | 2.7.0-RELEASE 28 | 29 | 30 | com.oliverdunk 31 | mcjukebox-region-providers-wg7 32 | 2.7.0-RELEASE 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /shared/src/main/java/net/mcjukebox/shared/utils/RegionUtils.java: -------------------------------------------------------------------------------- 1 | package net.mcjukebox.shared.utils; 2 | 3 | import net.mcjukebox.shared.api.Region; 4 | import net.mcjukebox.shared.api.RegionProvider; 5 | import org.bukkit.Location; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class RegionUtils { 11 | 12 | private static RegionUtils instance; 13 | 14 | public static RegionUtils getInstance() { 15 | if (instance == null) { 16 | instance = new RegionUtils(); 17 | } 18 | return instance; 19 | } 20 | 21 | public net.mcjukebox.shared.api.RegionProvider getProvider() { 22 | if (classExists("com.sk89q.worldguard.bukkit.WGBukkit")) { 23 | return new net.mcjukebox.shared.wg6.RegionProvider(); 24 | } else if (classExists("com.sk89q.worldguard.bukkit.WorldGuardPlugin")) { 25 | return new net.mcjukebox.shared.wg7.RegionProvider(); 26 | } else { 27 | return new RegionProvider() { 28 | public String getName() { 29 | return null; 30 | } 31 | 32 | public List getApplicableRegions(Location location) { 33 | return new ArrayList(); 34 | } 35 | }; 36 | } 37 | } 38 | 39 | private boolean classExists(String name) { 40 | try { 41 | Class.forName(name); 42 | return true; 43 | } catch (ClassNotFoundException ex) { 44 | return false; 45 | } 46 | } 47 | 48 | } 49 | --------------------------------------------------------------------------------