diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm
index ed76f6fd3ff..ac0972850ea 100644
--- a/code/__DEFINES/subsystems.dm
+++ b/code/__DEFINES/subsystems.dm
@@ -45,7 +45,8 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_PROFILER 101
-#define INIT_ORDER_TITLE 100 // Load this quickly so people dont see a blank lobby screen
+#define INIT_ORDER_QUEUE 100 // Load this quickly so people cant queue skip
+#define INIT_ORDER_TITLE 99 // Load this quickly so people dont see a blank lobby screen
#define INIT_ORDER_GARBAGE 21
#define INIT_ORDER_DBCORE 20
#define INIT_ORDER_BLACKBOX 19
diff --git a/code/_globalvars/misc.dm b/code/_globalvars/misc.dm
index 40dfb374170..49bef9c90cd 100644
--- a/code/_globalvars/misc.dm
+++ b/code/_globalvars/misc.dm
@@ -50,8 +50,6 @@ GLOBAL_VAR_INIT(copier_items_printed_logged, FALSE)
GLOBAL_DATUM_INIT(data_core, /datum/datacore, new) // Station datacore, manifest, etc
-GLOBAL_VAR_INIT(panic_bunker_enabled, FALSE) // Is the panic bunker enabled
-
GLOBAL_LIST_EMPTY(ability_verbs) // Create-level abilities
GLOBAL_LIST_INIT(pipe_colors, list("grey" = PIPE_COLOR_GREY, "red" = PIPE_COLOR_RED, "blue" = PIPE_COLOR_BLUE, "cyan" = PIPE_COLOR_CYAN, "green" = PIPE_COLOR_GREEN, "yellow" = PIPE_COLOR_YELLOW, "purple" = PIPE_COLOR_PURPLE))
diff --git a/code/controllers/configuration/sections/general_configuration.dm b/code/controllers/configuration/sections/general_configuration.dm
index 296304c28b7..816775fa6d9 100644
--- a/code/controllers/configuration/sections/general_configuration.dm
+++ b/code/controllers/configuration/sections/general_configuration.dm
@@ -14,8 +14,6 @@
var/lobby_time = 240
/// Ban all Guest BYOND accounts
var/guest_ban = TRUE
- /// Player threshold to automatically enable panic bunker
- var/panic_bunker_threshold = 150
/// Allow players to use AntagHUD?
var/allow_antag_hud = TRUE
/// Forbid players from rejoining if they use AntagHUD?
@@ -121,7 +119,6 @@
// Numbers
CONFIG_LOAD_NUM(lobby_time, data["lobby_time"])
- CONFIG_LOAD_NUM(panic_bunker_threshold, data["panic_bunker_threshold"])
CONFIG_LOAD_NUM(base_loadout_points, data["base_loadout_points"])
CONFIG_LOAD_NUM(cryo_penalty_period, data["cryo_penalty_period"])
CONFIG_LOAD_NUM(minimum_client_build, data["minimum_client_build"])
diff --git a/code/controllers/subsystem/server_queue.dm b/code/controllers/subsystem/server_queue.dm
new file mode 100644
index 00000000000..7855c870918
--- /dev/null
+++ b/code/controllers/subsystem/server_queue.dm
@@ -0,0 +1,45 @@
+#define QUEUE_DATA_FILE "data/queue_data.json"
+// These are defines for the sake of making sure we get the right keys
+#define QUEUE_DATA_FILE_THRESHOLD_KEY "threshold"
+#define QUEUE_DATA_FILE_ENABLED_KEY "enabled"
+#define QUEUE_DATA_FILE_PERSISTENT_KEY "persistent"
+
+SUBSYSTEM_DEF(queue)
+ name = "Server Queue"
+ init_order = INIT_ORDER_QUEUE // 100
+ flags = SS_NO_FIRE
+ /// Threshold of players to queue new people
+ var/queue_threshold = 0
+ /// Whether the queue is enabled or not
+ var/queue_enabled = FALSE
+ /// Whether to persist these settings over multiple rounds
+ var/persist_queue = FALSE
+ /// List of ckeys allowed to bypass queue this round
+ var/list/queue_bypass_list = list()
+ /// Last world.time we let a ckey in. 3 second delay between each letin to avoid a mass bubble
+ var/last_letin_time = 0
+
+/datum/controller/subsystem/queue/Initialize(start_timeofday)
+ if(fexists(QUEUE_DATA_FILE))
+ try
+ var/F = file2text(QUEUE_DATA_FILE)
+ var/list/data = json_decode(F)
+ queue_threshold = data[QUEUE_DATA_FILE_THRESHOLD_KEY]
+ queue_enabled = data[QUEUE_DATA_FILE_ENABLED_KEY]
+ persist_queue = data[QUEUE_DATA_FILE_PERSISTENT_KEY]
+ catch
+ stack_trace("Failed to load [QUEUE_DATA_FILE] from disk due to malformed JSON. You may need to setup the queue again.")
+
+ return ..()
+
+/datum/controller/subsystem/queue/Shutdown()
+ // Save if persistent
+ if(persist_queue)
+ if(fexists(QUEUE_DATA_FILE))
+ fdel(QUEUE_DATA_FILE)
+ var/data = list()
+ data[QUEUE_DATA_FILE_THRESHOLD_KEY] = queue_threshold
+ data[QUEUE_DATA_FILE_ENABLED_KEY] = queue_enabled
+ data[QUEUE_DATA_FILE_PERSISTENT_KEY] = persist_queue
+ var/json_data = json_encode(data)
+ text2file(json_data, QUEUE_DATA_FILE)
diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm
index 15decbb6848..428241409e4 100644
--- a/code/modules/admin/IsBanned.dm
+++ b/code/modules/admin/IsBanned.dm
@@ -97,10 +97,9 @@
qdel(exist_query)
else
if(!exist_query.NextRow()) // If there isnt a row, they aint been seen before
- if(GLOB.panic_bunker_enabled)
+ if(SSqueue && SSqueue.queue_enabled && (length(GLOB.clients) > SSqueue.queue_threshold) && !(ckey in SSqueue.queue_bypass_list)) // To the person who tells me in code review "Oh you should use ?. here". No. That logic isnt appropriate here. It needs a dedicated null check due to the length comparison.
qdel(exist_query)
- var/threshold = GLOB.configuration.general.panic_bunker_threshold
- return list("reason" = "panic bunker", "desc" = "Server is not accepting connections from never-before-seen players until player count is less than [threshold]. Please try again later.")
+ return list("reason" = "server queue", "desc" = "You seem to have managed to skip the server queue, possibly due to connecting during a restart. Please reconnect in 10 minutes. If you still cant connect, please inform the server host.")
qdel(exist_query)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 598705668ae..485b0f22cc3 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -137,7 +137,9 @@ GLOBAL_LIST_INIT(admin_verbs_server, list(
/client/proc/toggle_antagHUD_restrictions,
/client/proc/set_ooc,
/client/proc/reset_ooc,
- /client/proc/set_next_map
+ /client/proc/set_next_map,
+ /client/proc/manage_queue,
+ /client/proc/add_queue_server_bypass
))
GLOBAL_LIST_INIT(admin_verbs_debug, list(
/client/proc/cmd_admin_list_open_jobs,
diff --git a/code/modules/admin/verbs/manage_queue.dm b/code/modules/admin/verbs/manage_queue.dm
new file mode 100644
index 00000000000..cc0f690a12b
--- /dev/null
+++ b/code/modules/admin/verbs/manage_queue.dm
@@ -0,0 +1,58 @@
+/client/proc/manage_queue()
+ set name = "Manage Queue Server"
+ set desc = "Manage the queue server and its settings"
+ set category = "Server"
+
+ if(!check_rights(R_SERVER))
+ return
+
+ var/list/choices = list("Show Status", "Toggle Queue Server", "Set Threshold", "Toggle Setting Persistence")
+ var/choice = input(usr, "Please, select an option", "Queue Server Manipulation") as null|anything in choices
+
+ switch(choice)
+ if("Show Status")
+ to_chat(usr, "Queue Server Status")
+ to_chat(usr, "Enabled: [SSqueue.queue_enabled ? "Yes" : "No"]")
+ to_chat(usr, "Queue Threshold: [SSqueue.queue_threshold]")
+ to_chat(usr, "Setting Persistence: [SSqueue.persist_queue ? "Yes" : "No"]")
+ if("Toggle Queue Server")
+ SSqueue.queue_enabled = !SSqueue.queue_enabled
+ to_chat(usr, "Queue server is now [SSqueue.queue_enabled ? "Enabled" : "Disabled"]")
+ message_admins("[key_name_admin(usr)] has [SSqueue.queue_enabled ? "enabled" : "disabled"] the server queue.")
+ log_admin("[key_name(usr)] has [SSqueue.queue_enabled ? "enabled" : "disabled"] the server queue.")
+ if("Set Threshold")
+ var/new_threshold = input(usr, "Enter new threshold", "Queue Server Manipulation", SSqueue.queue_threshold) as num|null
+ if(!new_threshold)
+ return
+ SSqueue.queue_threshold = new_threshold
+ to_chat(usr, "Queue threshold is now [SSqueue.queue_threshold]")
+ message_admins("[key_name_admin(usr)] has set the queue threshold to [SSqueue.queue_threshold].")
+ log_admin("[key_name(usr)] has set the queue threshold to [SSqueue.queue_threshold].")
+ if("Toggle Setting Persistence")
+ SSqueue.persist_queue = !SSqueue.persist_queue
+ to_chat(usr, "Queue server setting persistence is now [SSqueue.persist_queue ? "Enabled" : "Disabled"]")
+ message_admins("[key_name_admin(usr)] has [SSqueue.persist_queue ? "enabled" : "disabled"] the server queue settings persistence.")
+ log_admin("[key_name(usr)] has [SSqueue.persist_queue ? "enabled" : "disabled"] the server queue settings persistence.")
+
+/client/proc/add_queue_server_bypass()
+ set name = "Add Queue Server Bypass"
+ set desc = "Allow a ckey to bypass the server queue"
+ set category = "Server"
+
+ if(!check_rights(R_SERVER))
+ return
+
+ var/bypass_ckey = input(usr, "Please, enter a ckey", "Queue Server Bypass")
+
+ if(!bypass_ckey)
+ return
+
+ var/clean_ckey = ckey(bypass_ckey)
+
+ if(!clean_ckey)
+ to_chat(usr, "Invalid ckey supplied")
+ return
+
+ SSqueue.queue_bypass_list.Add(clean_ckey)
+ message_admins("[key_name_admin(usr)] has added the ckey [clean_ckey] to the queue bypass list.")
+ log_admin("[key_name(usr)] has added the ckey [clean_ckey] to the queue bypass list.")
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 073e74caf26..1388601dc3f 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -379,17 +379,6 @@
if(M.client)
playercount += 1
- // Update the state of the panic bunker based on current playercount
- var/threshold = GLOB.configuration.general.panic_bunker_threshold
-
- if((playercount > threshold) && (GLOB.panic_bunker_enabled == FALSE))
- GLOB.panic_bunker_enabled = TRUE
- message_admins("Panic bunker has been automatically enabled due to playercount rising above [threshold]")
-
- if((playercount < threshold) && (GLOB.panic_bunker_enabled == TRUE))
- GLOB.panic_bunker_enabled = FALSE
- message_admins("Panic bunker has been automatically disabled due to playercount dropping below [threshold]")
-
// Tell clients about active testmerges
if(world.TgsAvailable() && length(GLOB.revision_info.testmerges))
to_chat(src, GLOB.revision_info.get_testmerge_chatmessage(TRUE))
diff --git a/code/modules/world_topic/queue_status.dm b/code/modules/world_topic/queue_status.dm
new file mode 100644
index 00000000000..02649e61fa8
--- /dev/null
+++ b/code/modules/world_topic/queue_status.dm
@@ -0,0 +1,36 @@
+/datum/world_topic_handler/queue_status
+ topic_key = "queue_status"
+ requires_commskey = TRUE
+
+// This topic is sent every 10 seconds from the bouncer
+/datum/world_topic_handler/queue_status/execute(list/input, key_valid)
+ var/ckey_check = input["ckey_check"]
+
+ if(!ckey_check)
+ return json_encode(list("error" = "No ckey supplied"))
+
+ var/list/output_data = list()
+ output_data["queue_enabled"] = SSqueue.queue_enabled
+
+ // Decide whether we should hold the player in queue
+ // NOTE: We only queue never seen before players
+ if(SSqueue.queue_enabled)
+ // If they are in the bypass list, let em in
+ if(ckey_check in SSqueue.queue_bypass_list)
+ output_data["allow_player"] = TRUE
+ else // Otherwise
+ // If we have more than the threshold, queue
+ if(length(GLOB.clients) > SSqueue.queue_threshold)
+ output_data["allow_player"] = FALSE
+ else // We have less than the threshold, allow if were in the timeframe
+ if(world.time > (SSqueue.last_letin_time + 3 SECONDS))
+ output_data["allow_player"] = TRUE
+ SSqueue.last_letin_time = world.time
+ else
+ output_data["allow_player"] = FALSE
+
+ else
+ // We arent enabled. Just let them in anyway.
+ output_data["allow_player"] = TRUE
+
+ return json_encode(output_data)
diff --git a/config/example/config.toml b/config/example/config.toml
index 254f793221e..e1cd5f2b49d 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -301,8 +301,6 @@ allow_character_metadata = true
lobby_time = 240
# Forbid people without a BYOND account joining the server
guest_ban = true
-# Player threshold before the automatic panic bunker engages
-panic_bunker_threshold = 150
# Allow players to use antagHUD?
allow_antag_hud = true
# Forbid players from rejoining if they use antag hud
diff --git a/paradise.dme b/paradise.dme
index 11176b1befe..bf1bb831dfa 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -246,6 +246,7 @@
#include "code\controllers\subsystem\radiation.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\runechat.dm"
+#include "code\controllers\subsystem\server_queue.dm"
#include "code\controllers\subsystem\shuttles.dm"
#include "code\controllers\subsystem\sounds.dm"
#include "code\controllers\subsystem\spacedrift.dm"
@@ -1203,6 +1204,7 @@
#include "code\modules\admin\verbs\gimmick_team.dm"
#include "code\modules\admin\verbs\infiltratorteam_syndicate.dm"
#include "code\modules\admin\verbs\logging_view.dm"
+#include "code\modules\admin\verbs\manage_queue.dm"
#include "code\modules\admin\verbs\map_template_loadverb.dm"
#include "code\modules\admin\verbs\mapping.dm"
#include "code\modules\admin\verbs\massmodvar.dm"
@@ -2508,6 +2510,7 @@
#include "code\modules\world_topic\manifest.dm"
#include "code\modules\world_topic\ping.dm"
#include "code\modules\world_topic\players.dm"
+#include "code\modules\world_topic\queue_status.dm"
#include "code\modules\world_topic\status.dm"
#include "goon\code\datums\browserOutput.dm"
#include "interface\interface.dm"