Skip to content

Commit b451307

Browse files
committed
feat: in-house queue system
1 parent c1323a6 commit b451307

File tree

3 files changed

+121
-3
lines changed

3 files changed

+121
-3
lines changed

locale/en.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ local Translations = {
1919
tp_error = 'Error While Teleporting.',
2020
connecting_database_timeout = 'Connection to database timed out. (Is the SQL server on?)',
2121
connecting_error = 'An error occurred while connecting to the server. (Check your server console)',
22-
no_match_character_registration = 'Anything other than letters aren\'t allowed, trailing whitespaces aren\'t allowed either and words must start with a capital letter in input fields. You can however add words with spaces inbetween.'
22+
no_match_character_registration = 'Anything other than letters aren\'t allowed, trailing whitespaces aren\'t allowed either and words must start with a capital letter in input fields. You can however add words with spaces inbetween.',
23+
already_in_queue = 'You are already in queue.',
2324
},
2425
success = {
2526
server_opened = 'The server has been opened',
@@ -58,7 +59,8 @@ local Translations = {
5859
gender = 'Sex',
5960
birth_date = 'Birth Date',
6061
select_gender = 'Select your gender...',
61-
confirm_delete = 'Are you sure you wish to delete this character?'
62+
confirm_delete = 'Are you sure you wish to delete this character?',
63+
in_queue = '🐌 You are %{queuePos}/%{queueSize} in queue.',
6264
},
6365
command = {
6466
tp = {

server/events.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local serverConfig = require 'config.server'.server
22
local loggingConfig = require 'config.server'.logging
33
local logger = require 'modules.logger'
4+
local queue = require 'server.queue'
45

56
-- Event Handler
67

@@ -108,7 +109,11 @@ local function onPlayerConnecting(name, _, deferrals)
108109
-- wait for database to finish
109110
databasePromise:next(function()
110111
deferrals.update(string.format(Lang:t('info.join_server'), name))
111-
deferrals.done()
112+
if queue then
113+
queue.awaitPlayerQueue(src --[[@as Source]], license, deferrals)
114+
else
115+
deferrals.done()
116+
end
112117
end, function(err)
113118
deferrals.done(Lang:t('error.connecting_error'))
114119
lib.print.error(err)

server/queue.lua

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
if GetConvar('qbx:enablequeue', 'true') == 'false' then return end
2+
3+
-- Disable hardcap because it kicks the player when the server is full
4+
5+
---@param resource string
6+
AddEventHandler('onResourceStarting', function(resource)
7+
if resource == 'hardcap' then
8+
lib.print.info('Preventing hardcap from starting...')
9+
CancelEvent()
10+
end
11+
end)
12+
13+
if GetResourceState('hardcap'):find('start') then
14+
lib.print.info('Stopping hardcap...')
15+
StopResource('hardcap')
16+
end
17+
18+
-- Queue code
19+
20+
local maxPlayers = GlobalState.MaxPlayers
21+
22+
---Player license to queue position map.
23+
---@type table<string, integer>
24+
local playerPositions = {}
25+
local queueSize = 0
26+
27+
---Map of player licenses that passed the queue and are downloading server content.
28+
---Needs to be saved because these players won't be part of regular player counts such as `GetNumPlayerIndices`.
29+
---@type table<string, true>
30+
local joiningPlayers = {}
31+
local joiningPlayerCount = 0
32+
33+
---@param license string
34+
local function addPlayerJoining(license)
35+
if not joiningPlayers[license] then
36+
joiningPlayerCount += 1
37+
end
38+
joiningPlayers[license] = true
39+
end
40+
41+
---@param license string
42+
local function removePlayerJoining(license)
43+
if joiningPlayers[license] then
44+
joiningPlayerCount -= 1
45+
end
46+
joiningPlayers[license] = nil
47+
end
48+
49+
---@param license string
50+
local function enqueue(license)
51+
queueSize += 1
52+
playerPositions[license] = queueSize
53+
end
54+
55+
---@param license string
56+
local function dequeue(license)
57+
local pos = playerPositions[license]
58+
59+
queueSize -= 1
60+
playerPositions[license] = nil
61+
62+
-- decrease the positions of players who are after the current player in queue
63+
for k, v in pairs(playerPositions) do
64+
if v > pos then
65+
playerPositions[k] -= 1
66+
end
67+
end
68+
end
69+
70+
---@param source Source
71+
---@param license string
72+
---@param deferrals Deferrals
73+
local function awaitPlayerQueue(source, license, deferrals)
74+
if playerPositions[license] then
75+
deferrals.done(Lang:t('error.already_in_queue'))
76+
return
77+
end
78+
79+
enqueue(license)
80+
81+
-- wait until the player disconnected or until there are available slots and the player is first in queue
82+
while DoesPlayerExist(source --[[@as string]]) and ((GetNumPlayerIndices() + joiningPlayerCount) >= maxPlayers or playerPositions[license] > 1) do
83+
deferrals.update(Lang:t('info.in_queue', {
84+
queuePos = playerPositions[license],
85+
queueSize = queueSize,
86+
}))
87+
88+
Wait(1000)
89+
end
90+
91+
-- if the player disconnected while waiting in queue
92+
if not DoesPlayerExist(source --[[@as string]]) then
93+
dequeue(license)
94+
return
95+
end
96+
97+
addPlayerJoining(license)
98+
dequeue(license)
99+
deferrals.done()
100+
101+
-- wait until the player finally joins or disconnects while installing server content
102+
-- this may result in waiting ~2 additional minutes if the player disconnects as FXServer will think that the player exists
103+
while DoesPlayerExist(source --[[@as string]]) do
104+
Wait(1000)
105+
end
106+
removePlayerJoining(license)
107+
end
108+
109+
return {
110+
awaitPlayerQueue = awaitPlayerQueue,
111+
}

0 commit comments

Comments
 (0)