diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6806a3a --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +# Created by .ignore support plugin (hsz.mobi) +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### Example user template template +### Example user template + +# IntelliJ project files +.idea +*.iml +out +gen diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..9c8f3ea --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..db917ec --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +# SimpleQueue - Queues player when the server is full + +# [Download here](https://github.com/0blu/SimpleQueue/releases) + +SimpleQueue is a plugin that will automatically queue players when the server is full. + +There are also really neat features like a prioritized player list, reserved slots and allowing players to join the server even if there are no slots left. + +You are also not even required to have a permission system to add prioritized players, see [Config](#config). + +### Content +- [Screenshot](#screenshot) +- [Commands](#commands) +- [Permissions](#permissions) +- [Config](#config) + +### Issue tracker: https://github.com/0blu/SimpleQueue/issues + +## Screenshot +![QueuePreview](https://i.imgur.com/UQXZg5U.png) + + +## Commands +Everthing can also be read in the [plugin.yml](./src/main/java/resources/plugin.yml) +- [/sqversion](#sqversion) +- [/sqqueue](#sqqueue) +- [/sqlist](#sqlist) +- [/sqadd](#sqadd) +- [/sqremove](#sqremove) + +### ``/sqversion`` + +**Usage**: ``/sqversion``
+**Permission**: [simplequeue.sqversion](#simplequeuesqversion)
+Displays version of SimpleQueue Plugin + +### ``/sqqueue`` + +**Usage**: ``/sqqueue``
+**Permission**: [simplequeue.sqqueue](#simplequeuesqqueue)
+Lists current players in queue + +### ``/sqlist`` + +**Usage**: ``/sqlis``
+**Permission**: [simplequeue.sqlist](#simplequeuesqlist)
+Lists all players in the prioritizedPlayers.yml file + +### ``/sqadd`` + +**Usage**: ``/sqadd [player]``
+**Permission**: [simplequeue.sqadd](#simplequeuesqadd)
+Adds a player to the prioritizedPlayers.yml file + +### ``/sqremove`` + +**Usage**: ``/sqremove [player]``
+**Permission**: [simplequeue.sqremove](#simplequeuesqremove)
+Removes a player from the prioritizedPlayers.yml file + + + +## Permissions +- [simplequeue.ignoreslotlimit](#simplequeueignoreslotlimit) +- [simplequeue.prioritized](#simplequeueprioritized) +- [simplequeue.sqversion](#simplequeuesqversion) +- [simplequeue.sqqueue](#simplequeuesqqueue) +- [simplequeue.sqlist](#simplequeuesqlist) +- [simplequeue.sqadd](#simplequeuesqadd) +- [simplequeue.sqremove](#simplequeuesqremove) + +### ``simplequeue.ignoreslotlimit`` +Allows the user to connect to the server even if the slot limit is reached + +### ``simplequeue.prioritized`` +Puts the user in front of normal users in the queue and uses reserved slots + +### ``simplequeue.sqversion`` +Grants access to the [/sqversion](#sqversion) command - Displays version of SimpleQueue Plugin + +### ``simplequeue.sqqueue`` +Grants access to the [/sqqueue](#sqqueue) command - Lists current players in queue + +### ``simplequeue.sqlist`` +Grants access to the [/sqlist](#sqlist) command - Lists all players in the prioritizedPlayers.yml file + +### ``simplequeue.sqadd`` +Grants access to the [/sqadd](#sqadd) command - Adds a player to the prioritizedPlayers.yml file + +### ``simplequeue.sqremove`` +Grants access to the [/sqremove](#sqremove) command - Removes a player from the prioritizedPlayers.yml file + + +## Config + +### ``config.yml`` + +Can also be read in the [config.yml](./src/main/java/resources/defaultConfig/config.yml) + +#### ``reservedSlots`` +```yaml +reservedSlots: 1 +``` + +**Number**: How many slots should be saved for prioritized players. + +#### ``allowPrioritizedPlayersViaConfig`` +```yaml +allowPrioritizedPlayersViaConfig: true +``` +**Boolean**: If prioritized players can be defined by the ``prioritizedPlayers.yml`` + + + +#### ``mustReconnectWithinSec`` +```yaml +mustReconnectWithinSec: 120 +``` +**Number**: How long (in seconds) the place in the queue will be guaranteed. + + +#### ``kickMessageQueued`` +```yaml +kickMessageQueued: |2- + + &cThe server is currently full!&r + + You are in the queue. + Position &a%d/%d&r. + &7Please reconnect within the next &6%d sec&7 to keep your position.&r + +``` +**String(Multiline)**: The message if a player connects but is still in the queue. + +'``&``' can be used as an alternate color code + +Meaning of the ``%d``: +1. players current queue position +2. the number of all users in the queue +3. ``mustReconnectWithinSec`` + +### ``prioritizedPlayers.yml`` + +Can also be read in the [config.yml](./src/main/java/resources/defaultConfig/prioritizedPlayers.yml) + +#### ``prioritizedPlayers`` +``` +prioritizedPlayers: [] +``` + +**List of OfflinePlayers**: List of offline players. Shouldn't be changed by hand! + +--- + +License: [Apache License 2.0](./LICENSE.txt) + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..01edbe3 --- /dev/null +++ b/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + wtf.blu + simpleQueue + 1.0.0 + + + SimpleQueue + https://github.com/0blu/SimpleQueue + + + 1.8 + 1.8 + UTF-8 + + + + + + md5-repo + https://hub.spigotmc.org/nexus/content/repositories/snapshots/ + + + + + + + org.spigotmc + spigot-api + 1.12-R0.1-SNAPSHOT + provided + + + + org.bukkit + bukkit + 1.12-R0.1-SNAPSHOT + provided + + + + + + + src/main/resources + true + + + + \ No newline at end of file diff --git a/src/main/java/wtf/blu/simpleQueue/OnPlayerLoginListener.java b/src/main/java/wtf/blu/simpleQueue/OnPlayerLoginListener.java new file mode 100644 index 0000000..112f7b8 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/OnPlayerLoginListener.java @@ -0,0 +1,88 @@ +package wtf.blu.simpleQueue; + +import org.bukkit.ChatColor; +import org.bukkit.Server; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerLoginEvent; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +import java.util.logging.Logger; + +class OnPlayerLoginListener implements Listener { + private final Server server; + private final Logger logger; + private final PlayerQueue playerQueue; + + private final int maxPlayerCount; + + /* Config */ + private final String kickMessage; + private final long mustReconnectWithinSec; + private final int reservedSlots; + + OnPlayerLoginListener(Server server, Logger logger, PlayerQueue playerQueue, WtfYamlConfiguration configYaml) { + this.server = server; + this.logger = logger; + this.playerQueue = playerQueue; + + maxPlayerCount = server.getMaxPlayers(); + + kickMessage = configYaml.getString("kickMessageQueued"); + mustReconnectWithinSec = configYaml.getLong("mustReconnectWithinSec"); + reservedSlots = configYaml.getInt("reservedSlots"); + } + + @EventHandler(priority = EventPriority.HIGH) + void onPlayerLogin(PlayerLoginEvent e) { + Player player = e.getPlayer(); + + if(e.getResult() != PlayerLoginEvent.Result.ALLOWED && e.getResult() != PlayerLoginEvent.Result.KICK_FULL) { + logger.warning("[SimpleQueue]: A player logged in but had an unexpected login result. Please report this if you this is an error."); + playerQueue.removePlayer(player); + return; + } + + int currentPlayerCount = server.getOnlinePlayers().size(); + + //Checks if the player is able to ignore the slot limit + if(SimpleQueuePermission.IGNORE_SLOT_LIMIT.hasPermission(player)) { + playerQueue.removePlayer(player); + e.allow(); + return; + } + + int indexInQueue = playerQueue.getCurrentIndexOrInsert(player); + + if ( + //Is first in queue + (indexInQueue == 0) && ( + //server is not full(incl reserved slots) + (maxPlayerCount > (currentPlayerCount + reservedSlots)) || + //server is not full and if prioritized player + ((maxPlayerCount > currentPlayerCount) && SimpleQueuePermission.PRIORITIZED_PLAYER.hasPermission(player)) + )) { + playerQueue.removePlayer(player); + //Intentionally no e.allow(); + return; + } + + //Still in queue + kickedQueued(e, indexInQueue); + } + + private void kickedQueued(PlayerLoginEvent e, int index) { + e.setResult(PlayerLoginEvent.Result.KICK_FULL); + + String kickMessageFormatted = String.format( + ChatColor.translateAlternateColorCodes('&', kickMessage), + (index+1), /* Current pos */ + (playerQueue.getQueuedPlayers().size()), /* Out of pos */ + (mustReconnectWithinSec) /* Reconnect within sec */ + ); + + e.disallow(e.getResult(), kickMessageFormatted); + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/PlayerQueue.java b/src/main/java/wtf/blu/simpleQueue/PlayerQueue.java new file mode 100644 index 0000000..ce95f11 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/PlayerQueue.java @@ -0,0 +1,147 @@ +package wtf.blu.simpleQueue; + +import org.bukkit.entity.Player; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +import java.util.*; + +public class PlayerQueue { + private final long mustReconnectWithinSec; + + private LinkedList playerQueue = new LinkedList<>(); + private Map lastSeenMap = new HashMap<>(); + private Map prioritizedPlayerMap = new HashMap<>(); + private PrioritizedPlayerConfigHandler prioritizedPlayerHandler; + + public PlayerQueue(PrioritizedPlayerConfigHandler prioritizedPlayerHandler, WtfYamlConfiguration configYaml) { + this.prioritizedPlayerHandler = prioritizedPlayerHandler; + mustReconnectWithinSec = configYaml.getLong("mustReconnectWithinSec"); + } + + /** + * Will return the current index in the queue of a player
+ * If the player is not in the queue the player will be added
+ * The permissions will respected + * @param player The player + * @return The index of the player in the queue + */ + public int getCurrentIndexOrInsert(Player player) { + removeExceededPlayers(); + + //Update timestamp + updateLastSeen(player); + + int queueIndex = getInQueueIndex(player); + + if(queueIndex == -1) { + return addPlayer(player); + } + + return queueIndex; + } + + /** + * @return A unmodifiableList of all queued players + */ + public List getQueuedPlayers() { + removeExceededPlayers(); + return Collections.unmodifiableList(playerQueue); + } + + /** + * @param player The player + * @return The current index in the queue or -1 + */ + public int getInQueueIndex(Player player) { + return playerQueue.indexOf(player.getUniqueId()); + } + + /** + * Updates the last seen timestamp in the {@link #lastSeenMap} + * @param player The player which should be updated + */ + private void updateLastSeen(Player player) { + lastSeenMap.put(player.getUniqueId(), System.currentTimeMillis()); + } + + /** + * Checks if the player is prioritized + * @param player The player + * @return true if the player is prioritized + */ + private boolean isPrioritized(Player player) { + return isPrioritized(player.getUniqueId()); + } + + /** + * Checks if the player is prioritized + * @param playerUUID The UUID of the player + * @return true if the player is prioritized + */ + private boolean isPrioritized(UUID playerUUID) { + return prioritizedPlayerMap.getOrDefault(playerUUID, false) || + prioritizedPlayerHandler.isInConfig(playerUUID); + } + + /** + * Adds the player to the {@link #prioritizedPlayerMap} whenever the player is prioritized + * @param player The player + */ + private void addToMapIfPrioritizedByPermission(Player player) { + if(SimpleQueuePermission.PRIORITIZED_PLAYER.hasPermission(player)) + prioritizedPlayerMap.put(player.getUniqueId(), true); + } + + /** + * Adds a player to the queue.
+ * If prioritized({@link #isPrioritized(UUID)}): The player will be added before the normal players.
+ * If putFirst: The player will be added to the first pos of the queue + * @param player The player + * @return The queue INDEX (You have to add 1 to get the "position") + */ + private int addPlayer(Player player) { + UUID playerUUID = player.getUniqueId(); + addToMapIfPrioritizedByPermission(player); + + boolean isThisPrioritized = isPrioritized(player); + + if(isThisPrioritized) { + //Insert prioritized player before normal players + for(int i = 0; i < playerQueue.size(); i++) { + if(!isPrioritized(playerQueue.get(i))) { + playerQueue.add(i, playerUUID); + return i; + } + } + } + playerQueue.addLast(playerUUID); + return playerQueue.size() - 1; + } + + /** + * Removes a given player from the queue + * @param player + */ + void removePlayer(Player player) { + removePlayer(player.getUniqueId()); + } + + + /** + * Removes a given player from the queue + * @param playerUUID + */ + void removePlayer(UUID playerUUID) { + playerQueue.remove(playerUUID); + lastSeenMap.remove(playerUUID); + } + + /*** + * Removes players that are longer in the queue then {@link #mustReconnectWithinSec} + */ + private void removeExceededPlayers() { + long timestampMustBeMore = System.currentTimeMillis() - (mustReconnectWithinSec*1000); + + playerQueue.removeIf(uuid -> lastSeenMap.get(uuid) < timestampMustBeMore); + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/PrioritizedPlayerConfigHandler.java b/src/main/java/wtf/blu/simpleQueue/PrioritizedPlayerConfigHandler.java new file mode 100644 index 0000000..14c3c63 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/PrioritizedPlayerConfigHandler.java @@ -0,0 +1,72 @@ +package wtf.blu.simpleQueue; + +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +import java.util.Collections; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +public class PrioritizedPlayerConfigHandler { + + private final Server server; + private final Logger logger; + private final WtfYamlConfiguration prioritizedPlayerYaml; + private final List prioritizedPlayerList; + + public PrioritizedPlayerConfigHandler(Server server, Logger logger, WtfYamlConfiguration prioritizedPlayerYaml) { + this.server = server; + this.logger = logger; + this.prioritizedPlayerYaml = prioritizedPlayerYaml; + prioritizedPlayerList = (List)(prioritizedPlayerYaml.getList("prioritizedPlayers")); + } + + /** + * @return Returns a unmodifiable list view of prioritizedPlayerList + */ + public List getPrioritizedPlayerList() { + return Collections.unmodifiableList(prioritizedPlayerList); + } + + /** + * Checks if player is in the prioritizedPlayer config + * @param playerUUID The UUID of an player to check + * @return true if the player is prioritized + */ + public boolean isInConfig(UUID playerUUID) { + return isInConfig(server.getOfflinePlayer(playerUUID)); + } + + /** + * Checks if player is in the prioritizedPlayer config + * @param player The player to check + * @return true if the player is prioritized + */ + public boolean isInConfig(OfflinePlayer player) { + return prioritizedPlayerList.contains(player); + } + + /** + * Adds a player to the prioritizedPlayer config and saves it + * @param offlinePlayer The player to add + */ + public void addPlayer(OfflinePlayer offlinePlayer) { + if(isInConfig(offlinePlayer)) + return; + prioritizedPlayerList.add(offlinePlayer); + prioritizedPlayerYaml.save(); + } + + /** + * Removes a player from the prioritizedPlayer config and saves it + * @param offlinePlayer The player to remove + */ + public void removePlayer(OfflinePlayer offlinePlayer) { + if(!isInConfig(offlinePlayer)) + return; + prioritizedPlayerList.remove(offlinePlayer); + prioritizedPlayerYaml.save(); + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/SimpleQueuePermission.java b/src/main/java/wtf/blu/simpleQueue/SimpleQueuePermission.java new file mode 100644 index 0000000..47690ee --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/SimpleQueuePermission.java @@ -0,0 +1,39 @@ +package wtf.blu.simpleQueue; + +import org.bukkit.permissions.Permissible; + +public enum SimpleQueuePermission { + + WILDCARD("*"), + IGNORE_SLOT_LIMIT("ignoreslotlimit"), + PRIORITIZED_PLAYER("prioritized"), + COMMAND_SQVERSION("sqversion"), + COMMAND_SQQUEUE("sqqueue"), + COMMAND_SQLIST("sqlist"), + COMMAND_SQADD("sqadd"), + COMMAND_SQREMOVE("sqremove"), + ; + + private final String permission; + private static final String PREFIX = "simplequeue."; + + SimpleQueuePermission(String permission) { + this.permission = permission; + } + + /** + * @return Returns the name of the permission + */ + public String getPermission() { + return PREFIX + permission; + } + + /** + * Checks if a Permissible has access to a given right + * @param permissible + * @return true if able to use this permission + */ + public boolean hasPermission(Permissible permissible) { + return permissible.hasPermission(WILDCARD.getPermission()) || permissible.hasPermission(getPermission()); + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/SimpleQueuePlugin.java b/src/main/java/wtf/blu/simpleQueue/SimpleQueuePlugin.java new file mode 100644 index 0000000..b51bf66 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/SimpleQueuePlugin.java @@ -0,0 +1,65 @@ +package wtf.blu.simpleQueue; + +import org.bukkit.plugin.PluginDescriptionFile; +import org.bukkit.plugin.PluginManager; +import org.bukkit.plugin.java.JavaPlugin; +import wtf.blu.simpleQueue.commands.*; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +import java.io.File; +import java.util.logging.Logger; + +public class SimpleQueuePlugin extends JavaPlugin { + + private Logger logger; + private WtfYamlConfiguration configYaml; + private WtfYamlConfiguration prioritizedPlayersYaml; + + private PrioritizedPlayerConfigHandler prioritizedPlayerHandler; + private PlayerQueue playerQueue; + + @Override + public void onEnable() { + logger = Logger.getLogger("Minecraft"); + + setupConfigs(); + + prioritizedPlayerHandler = new PrioritizedPlayerConfigHandler(getServer(), logger, prioritizedPlayersYaml); + playerQueue = new PlayerQueue(prioritizedPlayerHandler, configYaml); + + registerEvents(); + setCommandExecutors(); + + PluginDescriptionFile pdFile = getDescription(); + logger.info(String.format( + "%s %s has been enabled!", + pdFile.getName(), + pdFile.getVersion() + )); + } + + @Override + public void onDisable() { + + } + + private void setupConfigs() { + configYaml = new WtfYamlConfiguration(logger, new File(getDataFolder(), "config.yml"), + "/defaultConfig/config.yml"); + prioritizedPlayersYaml = new WtfYamlConfiguration(logger, new File(getDataFolder(), "prioritizedPlayers.yml"), + "/defaultConfig/prioritizedPlayers.yml"); + } + + private void registerEvents() { + PluginManager pm = getServer().getPluginManager(); + pm.registerEvents(new OnPlayerLoginListener(getServer(), logger, playerQueue, configYaml), this); + } + + private void setCommandExecutors() { + getCommand("sqversion").setExecutor(new CommandSqVersion(this)); + getCommand("sqlist").setExecutor(new CommandSqList(configYaml, prioritizedPlayerHandler)); + getCommand("sqqueue").setExecutor(new CommandSqQueue(playerQueue)); + getCommand("sqadd").setExecutor(new CommandSqAdd(configYaml, prioritizedPlayerHandler)); + getCommand("sqremove").setExecutor(new CommandSqRemove(configYaml, prioritizedPlayerHandler)); + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/commands/CommandSqAdd.java b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqAdd.java new file mode 100644 index 0000000..3d85ed7 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqAdd.java @@ -0,0 +1,44 @@ +package wtf.blu.simpleQueue.commands; + +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import wtf.blu.simpleQueue.PrioritizedPlayerConfigHandler; +import wtf.blu.simpleQueue.SimpleQueuePermission; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +public class CommandSqAdd implements CommandExecutor { + + private final WtfYamlConfiguration configYaml; + private final PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler; + + public CommandSqAdd(WtfYamlConfiguration configYaml, PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler) { + this.configYaml = configYaml; + this.prioritizedPlayerConfigHandler = prioritizedPlayerConfigHandler; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if(!command.getLabel().equals("sqadd") || !SimpleQueuePermission.COMMAND_SQADD.hasPermission(sender)) + return false; + + if(!configYaml.getBoolean("allowPrioritizedPlayersViaConfig")) { + sender.sendMessage("[SimpleQueue]: Please enable 'allowPrioritizedPlayersViaConfig' in the config.yml"); + return true; + } + + if(args.length != 1) + return false; + + Server server = sender.getServer(); + OfflinePlayer foundPlayer = server.getOfflinePlayer(args[0]); + + prioritizedPlayerConfigHandler.addPlayer(foundPlayer); + + sender.sendMessage(String.format("[SimpleQueue]: %s was successfully added to the prioritizedPlayers.yml.", foundPlayer.getName())); + + return true; + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/commands/CommandSqList.java b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqList.java new file mode 100644 index 0000000..cfaef4e --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqList.java @@ -0,0 +1,50 @@ +package wtf.blu.simpleQueue.commands; + +import org.bukkit.OfflinePlayer; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import wtf.blu.simpleQueue.PrioritizedPlayerConfigHandler; +import wtf.blu.simpleQueue.SimpleQueuePermission; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +import java.util.List; + +public class CommandSqList implements CommandExecutor { + + private final WtfYamlConfiguration configYaml; + private final PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler; + + public CommandSqList(WtfYamlConfiguration configYaml, PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler) { + this.configYaml = configYaml; + this.prioritizedPlayerConfigHandler = prioritizedPlayerConfigHandler; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if(!command.getLabel().equals("sqlist") || !SimpleQueuePermission.COMMAND_SQLIST.hasPermission(sender)) + return false; + + if(!configYaml.getBoolean("allowPrioritizedPlayersViaConfig")) { + sender.sendMessage("[SimpleQueue]: Please enable 'allowPrioritizedPlayersViaConfig' in the config.yml"); + return true; + } + + StringBuilder sb = new StringBuilder(); + List prioritizedPlayerList = prioritizedPlayerConfigHandler.getPrioritizedPlayerList(); + int listSize = prioritizedPlayerList.size(); + int index = 0; + + sb.append(String.format("[SimpleQueue]: There are %d in the prioritizedPlayers.yml:\n", listSize)); + + for(OfflinePlayer player : prioritizedPlayerList) { + sb.append(player.getName()); + if(++index < listSize) + sb.append(", "); + } + + sender.sendMessage(sb.toString()); + + return true; + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/commands/CommandSqQueue.java b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqQueue.java new file mode 100644 index 0000000..8d0089a --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqQueue.java @@ -0,0 +1,47 @@ +package wtf.blu.simpleQueue.commands; + +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import wtf.blu.simpleQueue.PlayerQueue; +import wtf.blu.simpleQueue.SimpleQueuePermission; + +import java.util.List; +import java.util.UUID; + +public class CommandSqQueue implements CommandExecutor { + + private final PlayerQueue playerQueue; + + public CommandSqQueue(PlayerQueue playerQueue) { + this.playerQueue = playerQueue; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if(!command.getLabel().equals("sqqueue") || !SimpleQueuePermission.COMMAND_SQQUEUE.hasPermission(sender)) + return false; + Server server = sender.getServer(); + + StringBuilder sb = new StringBuilder(); + List uuidList = playerQueue.getQueuedPlayers(); + int listSize = uuidList.size(); + int index = 0; + + if(listSize == 1) + sb.append(String.format("[SimpleQueue]: There is currently 1 player in the queue: ")); + else + sb.append(String.format("[SimpleQueue]: There are currently %d players in the queue:\n", listSize)); + + for(UUID playerUUID : uuidList) { + sb.append(server.getOfflinePlayer(playerUUID).getName()); + if(++index < listSize) + sb.append(", "); + } + + sender.sendMessage(sb.toString()); + + return true; + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/commands/CommandSqRemove.java b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqRemove.java new file mode 100644 index 0000000..cede9c7 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqRemove.java @@ -0,0 +1,44 @@ +package wtf.blu.simpleQueue.commands; + +import org.bukkit.OfflinePlayer; +import org.bukkit.Server; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import wtf.blu.simpleQueue.PrioritizedPlayerConfigHandler; +import wtf.blu.simpleQueue.SimpleQueuePermission; +import wtf.blu.simpleQueue.util.WtfYamlConfiguration; + +public class CommandSqRemove implements CommandExecutor { + + private final WtfYamlConfiguration configYaml; + private final PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler; + + public CommandSqRemove(WtfYamlConfiguration configYaml, PrioritizedPlayerConfigHandler prioritizedPlayerConfigHandler) { + this.configYaml = configYaml; + this.prioritizedPlayerConfigHandler = prioritizedPlayerConfigHandler; + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if(!command.getLabel().equals("sqremove") || !SimpleQueuePermission.COMMAND_SQREMOVE.hasPermission(sender)) + return false; + + if(!configYaml.getBoolean("allowPrioritizedPlayersViaConfig")) { + sender.sendMessage("[SimpleQueue]: Please enable 'allowPrioritizedPlayersViaConfig' in the config.yml"); + return true; + } + + if(args.length != 1) + return false; + + Server server = sender.getServer(); + OfflinePlayer foundPlayer = server.getOfflinePlayer(args[0]); + + prioritizedPlayerConfigHandler.removePlayer(foundPlayer); + + sender.sendMessage(String.format("[SimpleQueue]: %s was successfully removed from the prioritizedPlayers.yml.", foundPlayer.getPlayer())); + + return true; + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/commands/CommandSqVersion.java b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqVersion.java new file mode 100644 index 0000000..a81d974 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/commands/CommandSqVersion.java @@ -0,0 +1,28 @@ +package wtf.blu.simpleQueue.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.plugin.PluginDescriptionFile; +import wtf.blu.simpleQueue.SimpleQueuePermission; +import wtf.blu.simpleQueue.SimpleQueuePlugin; + +public class CommandSqVersion implements CommandExecutor { + + private final PluginDescriptionFile pdf; + + public CommandSqVersion(SimpleQueuePlugin plugin) { + pdf = plugin.getDescription(); + } + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if(!command.getLabel().equals("sqversion") || !SimpleQueuePermission.COMMAND_SQVERSION.hasPermission(sender)) + return false; + + sender.sendMessage(String.format("[SimpleQueue]: You are running %s version %s", pdf.getName(), pdf.getVersion())); + + + return true; + } +} diff --git a/src/main/java/wtf/blu/simpleQueue/util/WtfYamlConfiguration.java b/src/main/java/wtf/blu/simpleQueue/util/WtfYamlConfiguration.java new file mode 100644 index 0000000..e87cdc8 --- /dev/null +++ b/src/main/java/wtf/blu/simpleQueue/util/WtfYamlConfiguration.java @@ -0,0 +1,64 @@ +package wtf.blu.simpleQueue.util; + +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.InputStreamReader; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class WtfYamlConfiguration extends YamlConfiguration { + + private final File file; + private final Logger logger; + + public WtfYamlConfiguration(Logger logger, File file, String defaultConfigResourcePath) { + this(logger, file); + + try (InputStreamReader reader = new InputStreamReader(getClass().getResourceAsStream(defaultConfigResourcePath))) { + YamlConfiguration defaultConfig = YamlConfiguration.loadConfiguration(reader); + setDefaults(defaultConfig); + options().copyDefaults(true); + save(); + } + catch (Exception e) { + logException(logger, "An error occurred while setting the defaults in the config.", e); + } + } + + public WtfYamlConfiguration(Logger logger, File file) { + this.file = file; + this.logger = logger; + + file.getParentFile().mkdirs(); + if(!file.exists()) { + try { + file.createNewFile(); + } + catch (Exception e) { + logException(logger, "An error occurred while creating the config.", e); + } + } + reload(); + } + + public void reload() { + try { + load(file); + } catch (Exception e) { + logException(logger, "An error occurred while reloading the config.", e); + } + } + + public void save() { + try { + save(file); + } catch (Exception e) { + logException(logger, "An error occurred while saving the config.", e); + } + } + + private static void logException(Logger logger, String message, Exception e) { + logger.log(Level.WARNING, message, e); + } +} diff --git a/src/main/resources/defaultConfig/config.yml b/src/main/resources/defaultConfig/config.yml new file mode 100644 index 0000000..1217bfe --- /dev/null +++ b/src/main/resources/defaultConfig/config.yml @@ -0,0 +1,10 @@ +reservedSlots: 1 +allowPrioritizedPlayersViaConfig: true +mustReconnectWithinSec: 120 +kickMessageQueued: |2- + + &cThe server is currently full!&r + + You are in the queue. + Position &a%d/%d&r. + &7Please reconnect within the next &6%d sec&7 to keep your position.&r diff --git a/src/main/resources/defaultConfig/prioritizedPlayers.yml b/src/main/resources/defaultConfig/prioritizedPlayers.yml new file mode 100644 index 0000000..8652cf8 --- /dev/null +++ b/src/main/resources/defaultConfig/prioritizedPlayers.yml @@ -0,0 +1 @@ +prioritizedPlayers: [] \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..6c161db --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,53 @@ +name: ${project.name} +version: ${project.version} +description: Queues player when the server is full +author: _BLU https://github.com/0blu +main: wtf.blu.simpleQueue.SimpleQueuePlugin + +commands: + sqversion: + description: Displays version of SimpleQueue Plugin + permission: simplequeue.sqversion + usage: /sqversion + sqqueue: + description: Lists current players in queue + permission: simplequeue.sqqueue + usage: /sqqueue + sqlist: + description: Lists all players in the prioritizedPlayers.yml file + permission: simplequeue.sqlist + usage: /sqlist + sqadd: + description: Adds a player to the prioritizedPlayers.yml file + permission: simplequeue.sqadd + usage: /sqadd [player] + sqremove: + description: Removes a player from the prioritizedPlayers.yml file + permission: simplequeue.sqremove + usage: /sqremove [player] + +permissions: + simplequeue.*: + description: Grants access to all SimpleQueue commands and enables the player to connect to the server even if the slot limit is reached + children: + simplequeue.ignoreslotlimit: true + simplequeue.prioritized: true + simplequeue.sqversion: true + simplequeue.sqqueue: true + simplequeue.sqlist: true + simplequeue.sqadd: true + simplequeue.sqremove: true + simplequeue.ignoreslotlimit: + description: Allows the user to connect to the server even if the slot limit is reached + simplequeue.prioritized: + description: Puts the user in front of normal users in the queue and uses reserved slots + simplequeue.sqversion: + description: Grants access to the /sqversion command - Displays version of SimpleQueue Plugin + simplequeue.sqqueue: + description: Grants access to the /sqqueue command - Lists current players in queue + simplequeue.sqlist: + description: Grants access to the /sqlist command - Lists all players in the prioritizedPlayers.yml file + simplequeue.sqadd: + description: Grants access to the /sqadd command - Adds a player to the prioritizedPlayers.yml file + simplequeue.sqremove: + description: Grants access to the /sqremove command - Removes a player from the prioritizedPlayers.yml file