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