if not IsDuplicityVersion() then Citizen.CreateThread(function() while true do Citizen.Wait(0) if NetworkIsSessionStarted() then TriggerServerEvent("Queue:playerActivated") return end end end) return end local Queue = {} -- EDIT THESE IN SERVER.CFG + OTHER OPTIONS IN CONFIG.LUA Queue.MaxPlayers = GetConvarInt("sv_maxclients", 30) Queue.Debug = GetConvar("sv_debugqueue", "true") == "true" and true or false Queue.DisplayQueue = GetConvar("sv_displayqueue", "true") == "true" and true or false Queue.InitHostName = GetConvar("sv_hostname") -- This is needed because msgpack will break when tables are too large local _Queue = {} _Queue.QueueList = {} _Queue.PlayerList = {} _Queue.PlayerCount = 0 _Queue.Priority = {} _Queue.Connecting = {} _Queue.JoinCbs = {} _Queue.TempPriority = {} _Queue.JoinDelay = GetGameTimer() + Config.JoinDelay and Config.JoinDelay or 0 local tostring = tostring local tonumber = tonumber local ipairs = ipairs local pairs = pairs local print = print local string_len = string.len local string_sub = string.sub local string_format = string.format local string_lower = string.lower local math_abs = math.abs local math_floor = math.floor local math_random = math.random local os_time = os.time local table_insert = table.insert local table_remove = table.remove Queue.InitHostName = Queue.InitHostName ~= "default FXServer" and Queue.InitHostName or false for id, power in pairs(Config.Priority) do _Queue.Priority[string_lower(id)] = power end -- ╔════════════════════════════════════════════════════════════════════════════╗ -- ║ ADVANCED LOGGING SYSTEM ║ -- ╚════════════════════════════════════════════════════════════════════════════╝ local LogLevel = { DEBUG = { level = 1, color = "^5", icon = "🔍", name = "DEBUG" }, INFO = { level = 2, color = "^2", icon = "ℹ️", name = "INFO" }, WARN = { level = 3, color = "^3", icon = "⚠️", name = "WARN" }, ERROR = { level = 4, color = "^1", icon = "❌", name = "ERROR" }, SUCCESS = { level = 2, color = "^2", icon = "✅", name = "SUCCESS" }, PLAYER = { level = 2, color = "^6", icon = "👤", name = "PLAYER" }, QUEUE = { level = 2, color = "^4", icon = "📋", name = "QUEUE" }, PRIORITY = { level = 2, color = "^8", icon = "⭐", name = "PRIORITY" }, TIMEOUT = { level = 3, color = "^1", icon = "⏱️", name = "TIMEOUT" }, SYSTEM = { level = 2, color = "^9", icon = "⚙️", name = "SYSTEM" } } local function GetTimestamp() local time = os.date("*t") return string_format("^7[^3%02d:%02d:%02d^7]", time.hour, time.min, time.sec) end local function GetFormattedDate() return os.date("%Y-%m-%d %H:%M:%S") end local function FormatDuration(seconds) if seconds < 60 then return string_format("%ds", seconds) elseif seconds < 3600 then return string_format("%dm %ds", math_floor(seconds / 60), seconds % 60) else return string_format("%dh %dm %ds", math_floor(seconds / 3600), math_floor((seconds % 3600) / 60), seconds % 60) end end local function CreateSeparator(length, char) char = char or "─" return string.rep(char, length or 50) end local function Log(logType, msg, details) if not Queue.Debug then return end local config = LogLevel[logType] or LogLevel.INFO local timestamp = GetTimestamp() local prefix = string_format("%s %s[%s]^7", timestamp, config.color, config.name) local icon = config.icon or "•" local formattedMsg = string_format("%s %s %s^7", prefix, icon, tostring(msg)) print(formattedMsg) if details and type(details) == "table" then for key, value in pairs(details) do print(string_format(" ^7├─ ^3%s: ^7%s", tostring(key), tostring(value))) end end end function Queue:DebugPrint(msg) Log("DEBUG", msg) end function Queue:LogInfo(msg, details) Log("INFO", msg, details) end function Queue:LogWarn(msg, details) Log("WARN", msg, details) end function Queue:LogError(msg, details) Log("ERROR", msg, details) end function Queue:LogSuccess(msg, details) Log("SUCCESS", msg, details) end function Queue:LogPlayer(msg, details) Log("PLAYER", msg, details) end function Queue:LogQueue(msg, details) Log("QUEUE", msg, details) end function Queue:LogPriority(msg, details) Log("PRIORITY", msg, details) end function Queue:LogTimeout(msg, details) Log("TIMEOUT", msg, details) end function Queue:LogSystem(msg, details) Log("SYSTEM", msg, details) end function Queue:PrintHeader(title) if not Queue.Debug then return end local sep = CreateSeparator(60, "═") print("^4╔" .. sep .. "╗^7") local padding = math_floor((60 - string_len(title)) / 2) print(string_format("^4║^7%s%s%s^4║^7", string.rep(" ", padding), title, string.rep(" ", 60 - padding - string_len(title)))) print("^4╚" .. sep .. "╝^7") end function Queue:PrintStats() if not Queue.Debug then return end self:PrintHeader("📊 QUEUE STATISTICS") print(string_format(" ^2Players Online:^7 %d / %d", Queue:GetPlayerCount(), Queue.MaxPlayers)) print(string_format(" ^3Queue Size:^7 %d", Queue:GetSize())) print(string_format(" ^5Connecting:^7 %d", Queue:ConnectingSize())) print(string_format(" ^6Available Slots:^7 %d", math.max(0, Queue.MaxPlayers - Queue:GetPlayerCount() - Queue:ConnectingSize()))) print("^7" .. CreateSeparator(62, "─")) end function Queue:HexIdToSteamId(hexId) local cid = math_floor(tonumber(string_sub(hexId, 7), 16)) local steam64 = math_floor(tonumber(string_sub( cid, 2))) local a = steam64 % 2 == 0 and 0 or 1 local b = math_floor(math_abs(6561197960265728 - steam64 - a) / 2) local sid = "steam_0:"..a..":"..(a == 1 and b -1 or b) return sid end function Queue:IsSteamRunning(src) for _, id in ipairs(GetPlayerIdentifiers(src)) do if string_sub(id, 1, 5) == "steam" then return true end end return false end function Queue:GetPlayerCount() return _Queue.PlayerCount end function Queue:GetSize() return #_Queue.QueueList end function Queue:ConnectingSize() return #_Queue.Connecting end function Queue:GetQueueList() return _Queue.QueueList end function Queue:GetPriorityList() return _Queue.Priority end function Queue:GetPlayerList() return _Queue.PlayerList end function Queue:GetTempPriorityList() return _Queue.TempPriority end function Queue:GetConnectingList() return _Queue.Connecting end function Queue:IsInQueue(ids, rtnTbl, bySource, connecting) local connList = Queue:GetConnectingList() local queueList = Queue:GetQueueList() for genericKey1, genericValue1 in ipairs(connecting and connList or queueList) do local inQueue = false if not bySource then for genericKey2, genericValue2 in ipairs(genericValue1.ids) do if inQueue then break end for genericKey3, genericValue3 in ipairs(ids) do if genericValue3 == genericValue2 then inQueue = true break end end end else inQueue = ids == genericValue1.source end if inQueue then if rtnTbl then return genericKey1, connecting and connList[genericKey1] or queueList[genericKey1] end return true end end return false end function Queue:IsPriority(ids) local prio = false local tempPower, tempEnd = Queue:HasTempPriority(ids) local prioList = Queue:GetPriorityList() for _, id in ipairs(ids) do id = string_lower(id) if prioList[id] then prio = prioList[id] break end if string_sub(id, 1, 5) == "steam" then local steamid = Queue:HexIdToSteamId(id) if prioList[steamid] then prio = prioList[steamid] break end end end if tempPower or prio then if tempPower and prio then return tempPower > prio and tempPower or prio else return tempPower or prio end end return false end function Queue:HasTempPriority(ids) local tmpPrio = Queue:GetTempPriorityList() for _, id in pairs(ids) do id = string_lower(id) if tmpPrio[id] then return tmpPrio[id].power, tmpPrio[id].endTime, id end if string_sub(id, 1, 5) == "steam" then local steamid = Queue:HexIdToSteamId(id) if tmpPrio[steamid] then return tmpPrio[steamid].power, tmpPrio[steamid].endTime, id end end end return false end function Queue:AddToQueue(ids, connectTime, name, src, deferrals) if Queue:IsInQueue(ids) then return end local tmp = { source = src, ids = ids, name = name, priority = Queue:IsPriority(ids) or (src == "debug" and math_random(0, 15)), timeout = 0, deferrals = deferrals, firstconnect = connectTime, queuetime = function() return (os_time() - connectTime) end } local _pos = false local queueCount = Queue:GetSize() + 1 local queueList = Queue:GetQueueList() for pos, data in ipairs(queueList) do if tmp.priority then if not data.priority then _pos = pos else if tmp.priority > data.priority then _pos = pos end end if _pos then Queue:LogPriority(string_format("Player prioritized in queue"), { Player = tmp.name, Identifier = ids[1], Position = string_format("%d/%d", _pos, queueCount), Priority_Power = tostring(tmp.priority) }) break end end end if not _pos then _pos = Queue:GetSize() + 1 Queue:LogQueue(string_format("Player added to queue"), { Player = tmp.name, Identifier = ids[1], Position = string_format("%d/%d", _pos, queueCount), Priority = tmp.priority and tostring(tmp.priority) or "None" }) end table_insert(queueList, _pos, tmp) end function Queue:RemoveFromQueue(ids, bySource, byIndex) local queueList = Queue:GetQueueList() if byIndex then if queueList[byIndex] then table_remove(queueList, byIndex) end return end if Queue:IsInQueue(ids, false, bySource) then local pos, data = Queue:IsInQueue(ids, true, bySource) table_remove(queueList, pos) end end function Queue:TempSize() local count = 0 for _pos, data in pairs(Queue:GetQueueList()) do if Queue:HasTempPriority(data.ids) then count = count +1 end end return count > 0 and count or false end function Queue:IsInConnecting(ids, bySource, refresh) local inConnecting, tbl = Queue:IsInQueue(ids, refresh and true or false, bySource and true or false, true) if not inConnecting then return false end if refresh and inConnecting and tbl then Queue:GetConnectingList()[inConnecting].timeout = 0 end return true end function Queue:RemoveFromConnecting(ids, bySource, byIndex) local connList = Queue:GetConnectingList() if byIndex then if connList[byIndex] then table_remove(connList, byIndex) end return end for genericKey1, genericValue1 in ipairs(connList) do local inConnecting = false if not bySource then for genericKey2, genericValue2 in ipairs(genericValue1.ids) do if inConnecting then break end for genericKey3, genericValue3 in ipairs(ids) do if genericValue3 == genericValue2 then inConnecting = true break end end end else inConnecting = ids == genericValue1.source end if inConnecting then table_remove(connList, genericKey1) return true end end return false end function Queue:AddToConnecting(ids, ignorePos, autoRemove, done) local function remove() if not autoRemove then return end done(Config.Language.connectingerr) Queue:RemoveFromConnecting(ids) Queue:RemoveFromQueue(ids) Queue:LogError("Failed to add player to connecting list", { Identifier = ids[1] or "Unknown", Reason = "Server may be full or position invalid" }) end local connList = Queue:GetConnectingList() if Queue:ConnectingSize() + Queue:GetPlayerCount() + 1 > Queue.MaxPlayers then remove() return false end if ids[1] == "debug" then table_insert(connList, {source = ids[1], ids = ids, name = ids[1], firstconnect = ids[1], priority = ids[1], timeout = 0}) return true end if Queue:IsInConnecting(ids) then Queue:RemoveFromConnecting(ids) end local pos, data = Queue:IsInQueue(ids, true) if not ignorePos and (not pos or pos > 1) then remove() return false end table_insert(connList, data) Queue:RemoveFromQueue(ids) return true end function Queue:GetIds(src) local ids = GetPlayerIdentifiers(src) local ip = GetPlayerEndpoint(src) ids = (ids and ids[1]) and ids or (ip and {"ip:" .. ip} or false) ids = ids ~= nil and ids or false if ids and #ids > 1 then for k, id in ipairs(ids) do if string_sub(id, 1, 3) == "ip:" and not Queue:IsPriority({id}) then table_remove(ids, k) end end end return ids end function Queue:AddPriority(id, power, temp) if not id then return false end if type(id) == "table" then for _id, power in pairs(id) do if _id and type(_id) == "string" and power and type(power) == "number" then Queue:GetPriorityList()[_id] = power else Queue:DebugPrint("Error adding a priority id, invalid data passed") return false end end return true end power = (power and type(power) == "number") and power or 10 if temp then local tempPower, tempEnd, tempId = Queue:HasTempPriority({id}) id = tempId or id Queue:GetTempPriorityList()[string_lower(id)] = {power = power, endTime = os_time() + temp} else Queue:GetPriorityList()[string_lower(id)] = power end return true end function Queue:RemovePriority(id) if not id then return false end id = string_lower(id) Queue:GetPriorityList()[id] = nil return true end function Queue:UpdatePosData(src, ids, deferrals) local pos, data = Queue:IsInQueue(ids, true) data.source = src data.ids = ids data.timeout = 0 data.firstconnect = os_time() data.name = GetPlayerName(src) data.deferrals = deferrals end function Queue:NotFull(firstJoin) local canJoin = Queue:GetPlayerCount() + Queue:ConnectingSize() < Queue.MaxPlayers if firstJoin and canJoin then canJoin = Queue:GetSize() <= 1 end return canJoin end function Queue:SetPos(ids, newPos) if newPos <= 0 or newPos > Queue:GetSize() then return false end local pos, data = Queue:IsInQueue(ids, true) local queueList = Queue:GetQueueList() table_remove(queueList, pos) table_insert(queueList, newPos, data) end function Queue:CanJoin(src, cb) local allow = true for _, data in ipairs(_Queue.JoinCbs) do local await = true data.func(src, function(reason) if reason and type(reason) == "string" then allow = false cb(reason) end await = false end) while await do Citizen.Wait(0) end if not allow then return end end if allow then cb(false) end end function Queue:OnJoin(cb, resource) if not cb then return end local tmp = {resource = resource, func = cb} table_insert(_Queue.JoinCbs, tmp) end exports("GetQueueExports", function() return Queue end) local function playerConnect(name, setKickReason, deferrals) local src = source local ids = Queue:GetIds(src) local name = GetPlayerName(src) local connectTime = os_time() local connecting = true deferrals.defer() if Config.AntiSpam then for i=Config.AntiSpamTimer,0,-1 do deferrals.update(string.format(Config.PleaseWait, i)) Citizen.Wait(1000) end end Citizen.CreateThread(function() while connecting do Citizen.Wait(100) if not connecting then return end deferrals.update(Config.Language.connecting) end end) Citizen.Wait(500) local function done(msg, _deferrals) connecting = false local deferrals = _deferrals or deferrals if msg then deferrals.update(tostring(msg) or "") end Citizen.Wait(500) if not msg then deferrals.done() if Config.EnableGrace then Queue:AddPriority(ids[1], Config.GracePower, Config.GraceTime) end else deferrals.done(tostring(msg) or "") CancelEvent() end return end local function update(msg, _deferrals) local deferrals = _deferrals or deferrals connecting = false deferrals.update(tostring(msg) or "") end if not ids then -- prevent joining done(Config.Language.idrr) CancelEvent() Queue:LogError("Player connection rejected - No identifiers", { Player = name, Source = src, Reason = "Could not retrieve any valid identifiers" }) return end if Config.RequireSteam and not Queue:IsSteamRunning(src) then -- prevent joining done(Config.Language.steam) CancelEvent() return end local allow Queue:CanJoin(src, function(reason) if reason == nil or allow ~= nil then return end if reason == false or #_Queue.JoinCbs <= 0 then allow = true return end if reason then -- prevent joining allow = false done(reason and tostring(reason) or "You were blocked from joining") Queue:RemoveFromQueue(ids) Queue:RemoveFromConnecting(ids) Queue:LogWarn("Player blocked from joining", { Player = name, Identifier = ids[1], Reason = tostring(reason), Action = "Connection Denied" }) CancelEvent() return end allow = true end) while allow == nil do Citizen.Wait(0) end if not allow then return end if Config.PriorityOnly and not Queue:IsPriority(ids) then done(Config.Language.wlonly) return end local rejoined = false if Queue:IsInConnecting(ids, false, true) then Queue:RemoveFromConnecting(ids) if Queue:NotFull() then -- let them in the server if not Queue:IsInQueue(ids) then Queue:AddToQueue(ids, connectTime, name, src, deferrals) end local added = Queue:AddToConnecting(ids, true, true, done) if not added then CancelEvent() return end done() return else rejoined = true end end if Queue:IsInQueue(ids) then rejoined = true Queue:UpdatePosData(src, ids, deferrals) Queue:LogPlayer("Player rejoined queue", { Player = name, Identifier = ids[1], Status = "Reconnected after cancel" }) else Queue:AddToQueue(ids, connectTime, name, src, deferrals) if rejoined then Queue:SetPos(ids, 1) rejoined = false end end local pos, data = Queue:IsInQueue(ids, true) if not pos or not data then done(Config.Language.err .. " [1]") Queue:RemoveFromQueue(ids) Queue:RemoveFromConnecting(ids) CancelEvent() return end if Queue:NotFull(true) and _Queue.JoinDelay <= GetGameTimer() then -- let them in the server local added = Queue:AddToConnecting(ids, true, true, done) if not added then CancelEvent() return end done() Queue:LogSuccess("Player connecting to server (Direct Join)", { Player = name, Identifier = ids[1], Slots_Available = Queue.MaxPlayers - Queue:GetPlayerCount() - Queue:ConnectingSize(), Server_Population = string_format("%d/%d", Queue:GetPlayerCount(), Queue.MaxPlayers) }) return end update(string_format(Config.Language.pos .. ((Queue:TempSize() and Config.ShowTemp) and " (" .. Queue:TempSize() .. " temp)" or "00:00:00"), pos, Queue:GetSize(), "")) if rejoined then return end while true do Citizen.Wait(500) local pos, data = Queue:IsInQueue(ids, true) local function remove(msg) if data then if msg then update(msg, data.deferrals) end Queue:RemoveFromQueue(data.source, true) Queue:RemoveFromConnecting(data.source, true) else Queue:RemoveFromQueue(ids) Queue:RemoveFromConnecting(ids) end end if not data or not data.deferrals or not data.source or not pos then remove("[Queue] Removed from queue, queue data invalid :(") Queue:LogError("Player removed - Invalid queue data", { Player = name, Identifier = ids[1], Reason = "Missing or corrupted queue data", Data_Deferrals = data and data.deferrals and "Valid" or "Invalid", Data_Source = data and data.source and "Valid" or "Invalid", Data_Position = pos and "Valid" or "Invalid" }) return end local endPoint = GetPlayerEndpoint(data.source) if not endPoint then data.timeout = data.timeout + 0.5 else data.timeout = 0 end if data.timeout >= Config.QueueTimeOut and os_time() - connectTime > 5 then remove("[Queue] Removed due to timeout") Queue:LogTimeout("Player removed - Queue timeout", { Player = name, Identifier = ids[1], Timeout_Duration = FormatDuration(data.timeout), Config_Timeout = FormatDuration(Config.QueueTimeOut), Queue_Time = FormatDuration(os_time() - connectTime) }) return end if pos <= 1 and Queue:NotFull() and _Queue.JoinDelay <= GetGameTimer() then -- let them in the server local added = Queue:AddToConnecting(ids) update(Config.Language.joining, data.deferrals) Citizen.Wait(500) if not added then done(Config.Language.connectingerr) CancelEvent() return end done(nil, data.deferrals) if Config.EnableGrace then Queue:AddPriority(ids[1], Config.GracePower, Config.GraceTime) end Queue:RemoveFromQueue(ids) Queue:LogSuccess("Player joining server from queue", { Player = name, Identifier = ids[1], Wait_Time = FormatDuration(data.queuetime()), Priority = data.priority and tostring(data.priority) or "None", Grace_Enabled = Config.EnableGrace and "Yes" or "No" }) return end local seconds = data.queuetime() local qTime = string_format("%02d", math_floor((seconds % 86400) / 3600)) .. ":" .. string_format("%02d", math_floor((seconds % 3600) / 60)) .. ":" .. string_format("%02d", math_floor(seconds % 60)) local msg = string_format(Config.Language.pos .. ((Queue:TempSize() and Config.ShowTemp) and " (" .. Queue:TempSize() .. " temp)" or ""), pos, Queue:GetSize(), qTime) update(msg, data.deferrals) end end AddEventHandler("playerConnecting", playerConnect) Citizen.CreateThread(function() local function remove(data, pos, msg) if data and data.source then Queue:RemoveFromQueue(data.source, true) Queue:RemoveFromConnecting(data.source, true) elseif pos then table_remove(Queue:GetQueueList(), pos) end end while true do Citizen.Wait(1000) local i = 1 while i <= Queue:ConnectingSize() do local data = Queue:GetConnectingList()[i] local endPoint = GetPlayerEndpoint(data.source) data.timeout = data.timeout + 1 if ((data.timeout >= 300 and not endPoint) or data.timeout >= Config.ConnectTimeOut) and data.source ~= "debug" and os_time() - data.firstconnect > 5 then remove(data) Queue:LogTimeout("Player removed - Connection timeout", { Player = data.name, Identifier = data.ids[1], Timeout_Duration = FormatDuration(data.timeout), Has_Endpoint = endPoint and "Yes" or "No", Connect_Time = FormatDuration(os_time() - data.firstconnect) }) else i = i + 1 end end for id, data in pairs(Queue:GetTempPriorityList()) do if os_time() >= data.endTime then Queue:GetTempPriorityList()[id] = nil end end Queue.MaxPlayers = GetConvarInt("sv_maxclients", 30) Queue.Debug = GetConvar("sv_debugqueue", "true") == "true" and true or false Queue.DisplayQueue = GetConvar("sv_displayqueue", "true") == "true" and true or false local qCount = Queue:GetSize() if Queue.DisplayQueue then if Queue.InitHostName then SetConvar("sv_hostname", (qCount > 0 and "[" .. tostring(qCount) .. "] " or "") .. Queue.InitHostName) else Queue.InitHostName = GetConvar("sv_hostname") Queue.InitHostName = Queue.InitHostName ~= "default FXServer" and Queue.InitHostName or false end end end end) RegisterServerEvent("Queue:playerActivated") AddEventHandler("Queue:playerActivated", function() local src = source local ids = Queue:GetIds(src) local name = GetPlayerName(src) local endpoint = GetPlayerEndpoint(src) local ping = GetPlayerPing(src) if not Queue:GetPlayerList()[src] then _Queue.PlayerCount = Queue:GetPlayerCount() + 1 Queue:GetPlayerList()[src] = true Queue:RemoveFromQueue(ids) Queue:RemoveFromConnecting(ids) -- Get all identifiers for logging local allIds = GetPlayerIdentifiers(src) local steamId, discordId, license, xbl, live, fivem = "N/A", "N/A", "N/A", "N/A", "N/A", "N/A" for _, id in ipairs(allIds) do local lowerId = string_lower(id) if string.find(lowerId, "^steam:") then steamId = id elseif string.find(lowerId, "^discord:") then discordId = id elseif string.find(lowerId, "^license:") then license = id elseif string.find(lowerId, "^xbl:") then xbl = id elseif string.find(lowerId, "^live:") then live = id elseif string.find(lowerId, "^fivem:") then fivem = id end end Queue:LogSuccess("Player fully connected to server", { Player = name, Source = src, Steam = steamId, Discord = discordId, License = license, FiveM = fivem, IP = endpoint or "Unknown", Ping = ping .. "ms", Server_Population = string_format("%d/%d", Queue:GetPlayerCount(), Queue.MaxPlayers), Available_Slots = Queue.MaxPlayers - Queue:GetPlayerCount() }) end end) AddEventHandler("playerDropped", function(reason) local src = source local ids = Queue:GetIds(src) local name = GetPlayerName(src) if Queue:GetPlayerList()[src] then -- Get all identifiers before cleanup local allIds = GetPlayerIdentifiers(src) local steamId, discordId, license = "N/A", "N/A", "N/A" for _, id in ipairs(allIds) do local lowerId = string_lower(id) if string.find(lowerId, "^steam:") then steamId = id elseif string.find(lowerId, "^discord:") then discordId = id elseif string.find(lowerId, "^license:") then license = id end end _Queue.PlayerCount = Queue:GetPlayerCount() - 1 Queue:GetPlayerList()[src] = nil Queue:RemoveFromQueue(ids) Queue:RemoveFromConnecting(ids) local graceStatus = "Disabled" if Config.EnableGrace then Queue:AddPriority(ids[1], Config.GracePower, Config.GraceTime) graceStatus = string_format("Granted (%d power for %s)", Config.GracePower, FormatDuration(Config.GraceTime)) end Queue:LogPlayer("Player disconnected from server", { Player = name or "Unknown", Source = src, Steam = steamId, Discord = discordId, License = license, Reason = reason or "Unknown", Grace_Period = graceStatus, Server_Population = string_format("%d/%d", Queue:GetPlayerCount(), Queue.MaxPlayers), Available_Slots = Queue.MaxPlayers - Queue:GetPlayerCount() }) end end) AddEventHandler("onResourceStop", function(resource) if Queue.DisplayQueue and Queue.InitHostName and resource == GetCurrentResourceName() then SetConvar("sv_hostname", Queue.InitHostName) end for k, data in ipairs(_Queue.JoinCbs) do if data.resource == resource then table_remove(_Queue.JoinCbs, k) end end end) if Config.DisableHardCap then Queue:LogSystem("Hardcap resource disabled", { Resource = "hardcap", Action = "Blocked from starting", Reason = "Config.DisableHardCap = true" }) AddEventHandler("onResourceStarting", function(resource) if resource == "hardcap" then CancelEvent() return end end) StopResource("hardcap") end local testAdds = 0 local commands = {} commands.addq = function() Queue:LogInfo("Debug queue entry added", { Test_ID = testAdds, Steam_ID = "steam:110000103fd1bb1" .. testAdds }) Queue:AddToQueue({"steam:110000103fd1bb1"..testAdds}, os_time(), "TestAdd: " .. testAdds, "debug") testAdds = testAdds + 1 end commands.removeq = function(args) args[1] = tonumber(args[1]) local name = Queue:GetQueueList()[args[1]] and Queue:GetQueueList()[args[1]].name or nil Queue:RemoveFromQueue(nil, nil, args[1]) Queue:LogInfo("Player removed from queue", { Player = tostring(name), Position = args[1] }) end commands.printq = function() Queue:PrintHeader("📋 CURRENT QUEUE LIST") local queueList = Queue:GetQueueList() if #queueList == 0 then print(" ^7No players in queue^7") else for pos, data in ipairs(queueList) do print(string_format(" ^3#%d^7 ┃ ^2%s^7", pos, data.name)) print(string_format(" ├─ ^5Source:^7 %s", data.source)) print(string_format(" ├─ ^5ID:^7 %s", data.ids[1])) print(string_format(" ├─ ^5Priority:^7 %s", tostring(data.priority or "None"))) print(string_format(" ├─ ^5Last Msg:^7 %s", data.source ~= "debug" and GetPlayerLastMsg(data.source) or "debug")) print(string_format(" ├─ ^5Timeout:^7 %s", FormatDuration(data.timeout))) print(string_format(" └─ ^5Queue Time:^7 %s", FormatDuration(data.queuetime()))) if pos < #queueList then print(" ^7" .. CreateSeparator(40, "─")) end end end print("^7" .. CreateSeparator(62, "═")) end commands.addc = function() Queue:AddToConnecting({"debug"}) Queue:LogInfo("Debug connecting entry added", { Status = "Added to connecting list" }) end commands.removec = function(args) args[1] = tonumber(args[1]) local name = Queue:GetConnectingList()[args[1]] and Queue:GetConnectingList()[args[1]].name or nil Queue:RemoveFromConnecting(nil, nil, args[1]) Queue:LogInfo("Player removed from connecting list", { Player = tostring(name), Position = args[1] }) end commands.printc = function() Queue:PrintHeader("🔗 CURRENT CONNECTING LIST") local connList = Queue:GetConnectingList() if #connList == 0 then print(" ^7No players connecting^7") else for pos, data in ipairs(connList) do print(string_format(" ^3#%d^7 ┃ ^2%s^7", pos, data.name)) print(string_format(" ├─ ^5Source:^7 %s", data.source)) print(string_format(" ├─ ^5ID:^7 %s", data.ids[1])) print(string_format(" ├─ ^5Priority:^7 %s", tostring(data.priority or "None"))) print(string_format(" ├─ ^5Last Msg:^7 %s", data.source ~= "debug" and GetPlayerLastMsg(data.source) or "debug")) print(string_format(" └─ ^5Timeout:^7 %s", FormatDuration(data.timeout))) if pos < #connList then print(" ^7" .. CreateSeparator(40, "─")) end end end print("^7" .. CreateSeparator(62, "═")) end commands.printl = function() Queue:PrintHeader("👥 ACTIVE PLAYERS") local count = 0 for k, joined in pairs(Queue:GetPlayerList()) do count = count + 1 print(string_format(" ^2●^7 Source ID: ^3%s^7 | Active: ^2%s^7", k, tostring(joined))) end print(string_format(" ^7Total: ^2%d^7 players", count)) print("^7" .. CreateSeparator(62, "═")) end commands.printp = function() Queue:PrintHeader("⭐ PRIORITY LIST") local count = 0 for id, power in pairs(Queue:GetPriorityList()) do count = count + 1 print(string_format(" ^8★^7 %s ^7=> Power: ^3%s^7", id, tostring(power))) end print(string_format(" ^7Total: ^3%d^7 priority entries", count)) print("^7" .. CreateSeparator(62, "═")) end commands.printcount = function() Queue:PrintStats() end commands.printtp = function() Queue:PrintHeader("⏳ TEMP PRIORITY LIST") local count = 0 local currentTime = os_time() for k, data in pairs(Queue:GetTempPriorityList()) do count = count + 1 local remaining = data.endTime - currentTime print(string_format(" ^6◆^7 %s", k)) print(string_format(" ├─ ^5Power:^7 %s", tostring(data.power))) print(string_format(" ├─ ^5Expires In:^7 %s", FormatDuration(math.max(0, remaining)))) print(string_format(" └─ ^5End Time:^7 %s", os.date("%H:%M:%S", data.endTime))) end print(string_format(" ^7Total: ^6%d^7 temp priority entries", count)) print("^7" .. CreateSeparator(62, "═")) end commands.removetp = function(args) if not args[1] then return end Queue:GetTempPriorityList()[args[1]] = nil Queue:LogInfo("Temp priority removed", { Identifier = args[1], Action = "Removed from temp priority list" }) end commands.setpos = function(args) if not args[1] or not args[2] then return end args[1], args[2] = tonumber(args[1]), tonumber(args[2]) local data = Queue:GetQueueList()[args[1]] Queue:SetPos(data.ids, args[2]) Queue:LogInfo("Queue position updated", { Player = data.name, Old_Position = args[1], New_Position = args[2] }) end commands.setdata = function(args) if not args[1] or not args[2] or not args[3] then return end args[1] = tonumber(args[1]) local num = tonumber(args[3]) local data = Queue:GetQueueList()[args[1]] if args[2] == "queuetime" then local time = data.queuetime() local dif = time - num data.firstconnect = data.firstconnect + dif data.queuetime = function() return (os_time() - data.firstconnect) end else data[args[2]] = num and num or args[3] end Queue:LogInfo("Player data modified", { Player = data.name, Field = args[2], New_Value = args[3] }) end commands.commands = function() Queue:PrintHeader("📚 AVAILABLE COMMANDS") print(" ^3addq^7 - Add debug queue entry") print(" ^3removeq^7 - Remove from queue by index") print(" ^3printq^7 - Print current queue list") print(" ^3addc^7 - Add debug connecting entry") print(" ^3removec^7 - Remove from connecting by index") print(" ^3printc^7 - Print connecting list") print(" ^3printl^7 - Print active players") print(" ^3printp^7 - Print priority list") print(" ^3printcount^7- Print server statistics") print(" ^3printtp^7 - Print temp priority list") print(" ^3removetp^7 - Remove temp priority") print(" ^3setpos^7 - Set queue position") print(" ^3setdata^7 - Modify player data") print(" ^3commands^7 - Show this help") print("^7" .. CreateSeparator(62, "═")) end commands.stats = function() Queue:PrintStats() end AddEventHandler("rconCommand", function(command, args) if command == "queue" and commands[args[1]] then command = args[1] table_remove(args, 1) commands[command](args) CancelEvent() end end)