Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: in-house queue system #281

Merged
merged 3 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions locale/en.lua
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ local Translations = {
tp_error = 'Error While Teleporting.',
connecting_database_timeout = 'Connection to database timed out. (Is the SQL server on?)',
connecting_error = 'An error occurred while connecting to the server. (Check your server console)',
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.'
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.',
already_in_queue = 'You are already in queue.',
},
success = {
server_opened = 'The server has been opened',
Expand Down Expand Up @@ -58,7 +59,8 @@ local Translations = {
gender = 'Sex',
birth_date = 'Birth Date',
select_gender = 'Select your gender...',
confirm_delete = 'Are you sure you wish to delete this character?'
confirm_delete = 'Are you sure you wish to delete this character?',
in_queue = '🐌 You are %{queuePos}/%{queueSize} in queue.',
},
command = {
tp = {
Expand Down
7 changes: 6 additions & 1 deletion server/events.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
local serverConfig = require 'config.server'.server
local loggingConfig = require 'config.server'.logging
local logger = require 'modules.logger'
local queue = require 'server.queue'

-- Event Handler

Expand Down Expand Up @@ -108,7 +109,11 @@ local function onPlayerConnecting(name, _, deferrals)
-- wait for database to finish
databasePromise:next(function()
deferrals.update(string.format(Lang:t('info.join_server'), name))
deferrals.done()
if queue then
queue.awaitPlayerQueue(src --[[@as Source]], license, deferrals)
else
deferrals.done()
end
end, function(err)
deferrals.done(Lang:t('error.connecting_error'))
lib.print.error(err)
Expand Down
111 changes: 111 additions & 0 deletions server/queue.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
if GetConvar('qbx:enablequeue', 'true') == 'false' then return end

-- Disable hardcap because it kicks the player when the server is full

---@param resource string
AddEventHandler('onResourceStarting', function(resource)
if resource == 'hardcap' then
lib.print.info('Preventing hardcap from starting...')
CancelEvent()
end
end)

if GetResourceState('hardcap'):find('start') then
lib.print.info('Stopping hardcap...')
StopResource('hardcap')
end

-- Queue code

local maxPlayers = GlobalState.MaxPlayers

---Player license to queue position map.
---@type table<string, integer>
local playerPositions = {}
local queueSize = 0

---Map of player licenses that passed the queue and are downloading server content.
---Needs to be saved because these players won't be part of regular player counts such as `GetNumPlayerIndices`.
---@type table<string, true>
local joiningPlayers = {}
local joiningPlayerCount = 0

---@param license string
local function addPlayerJoining(license)
if not joiningPlayers[license] then
joiningPlayerCount += 1
end
joiningPlayers[license] = true
end

---@param license string
local function removePlayerJoining(license)
if joiningPlayers[license] then
joiningPlayerCount -= 1
end
joiningPlayers[license] = nil
end

---@param license string
local function enqueue(license)
queueSize += 1
playerPositions[license] = queueSize
end

---@param license string
local function dequeue(license)
local pos = playerPositions[license]

queueSize -= 1
playerPositions[license] = nil

-- decrease the positions of players who are after the current player in queue
for k, v in pairs(playerPositions) do
if v > pos then
playerPositions[k] -= 1
end
end
end

---@param source Source
---@param license string
---@param deferrals Deferrals
local function awaitPlayerQueue(source, license, deferrals)
if playerPositions[license] then
deferrals.done(Lang:t('error.already_in_queue'))
return
end

enqueue(license)

-- wait until the player disconnected or until there are available slots and the player is first in queue
while DoesPlayerExist(source --[[@as string]]) and ((GetNumPlayerIndices() + joiningPlayerCount) >= maxPlayers or playerPositions[license] > 1) do
Manason marked this conversation as resolved.
Show resolved Hide resolved
deferrals.update(Lang:t('info.in_queue', {
queuePos = playerPositions[license],
queueSize = queueSize,
}))

Wait(1000)
end

-- if the player disconnected while waiting in queue
if not DoesPlayerExist(source --[[@as string]]) then
dequeue(license)
return
end

addPlayerJoining(license)
dequeue(license)
deferrals.done()

-- wait until the player finally joins or disconnects while installing server content
-- this may result in waiting ~2 additional minutes if the player disconnects as FXServer will think that the player exists
while DoesPlayerExist(source --[[@as string]]) do
Wait(1000)
end
removePlayerJoining(license)
end

return {
awaitPlayerQueue = awaitPlayerQueue,
}
Loading