├── .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 [](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 extends String> 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