Warp/src/Index/Server/ServerProcess.luau

254 lines
8 KiB
Text
Raw Normal View History

2024-01-05 12:14:38 +00:00
--!strict
local ServerProcess = {}
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local Util = script.Parent.Parent.Util
local Type = require(script.Parent.Parent.Type)
local Event = require(script.Parent.Parent.Event)
local Spawn = require(Util.Spawn)
local Key = require(Util.Key)
local RateLimit = require(Util.RateLimit)
local serverQueue: Type.QueueMap = {}
local unreliableServerQueue: Type.QueueMap = {}
local serverCallback: Type.CallbackMap = {}
local serverRequestQueue: Type.QueueMap = {}
local queueOut: {
[Player]: {
[string]: {any},
}
} = {}
local queueIn: {
[string]: {
[Player]: {any},
}
} = {}
local queueInRequest: {
[number]: {
[string]: {
[Player]: {any}
}
}
} = {}
local queueOutRequest: {
[number]: {
[string]: {
[Player]: {any}
}
}
} = {}
queueInRequest[1] = {}
queueInRequest[2] = {}
queueOutRequest[1] = {}
queueOutRequest[2] = {}
local ReliableEvent = Event.Reliable
local UnreliableEvent = Event.Unreliable
local RequestEvent = Event.Request
local function initializeEachPlayer(player: Player)
if not player then return end
if not queueOut[player] then
queueOut[player] = {}
end
for Identifier: string,_ in serverQueue do
if not queueOut[player][Identifier] then
queueOut[player][Identifier] = {}
end
if not serverRequestQueue[Identifier][player] then
serverRequestQueue[Identifier][player] = {}
end
if not queueIn[Identifier][player] then
queueIn[Identifier][player] = {}
end
if not queueInRequest[1][Identifier][player] then
queueInRequest[1][Identifier][player] = {}
queueInRequest[2][Identifier][player] = {}
end
if not queueOutRequest[1][Identifier][player] then
queueOutRequest[1][Identifier][player] = {}
queueOutRequest[2][Identifier][player] = {}
end
end
end
Players.PlayerAdded:Connect(initializeEachPlayer)
function ServerProcess.insertQueue(Identifier: string, reliable: boolean, player: Player, ...: any)
if not reliable then
if not unreliableServerQueue[Identifier][player] then
unreliableServerQueue[Identifier][player] = {}
end
table.insert(unreliableServerQueue[Identifier][player], { ... })
return
end
if not serverQueue[Identifier][player] then
serverQueue[Identifier][player] = {}
end
table.insert(serverQueue[Identifier][player], { ... })
end
function ServerProcess.insertRequest(Identifier: string, timeout: number, player: Player, ...: any)
if not serverQueue[Identifier][player] then
serverQueue[Identifier][player] = {}
end
local yieldThread: thread, start = coroutine.running(), os.clock()
local cancel = task.delay(timeout, function()
task.spawn(yieldThread, nil)
end)
table.insert(serverRequestQueue[Identifier][player], { tostring(Key()), function(...: any)
if (os.clock() - start) > timeout then return end
task.cancel(cancel)
task.spawn(yieldThread, ...)
end :: any, { ... } :: any })
return coroutine.yield()
end
function ServerProcess.add(Identifier: string, originId: string, ratelimit: Type.rateLimitArg)
if not serverQueue[Identifier] then
RateLimit.create(originId, ratelimit.maxEntrance, ratelimit.interval)
serverQueue[Identifier] = {}
unreliableServerQueue[Identifier] = {}
serverCallback[Identifier] = {}
serverRequestQueue[Identifier] = {}
queueIn[Identifier] = {}
queueInRequest[1][Identifier] = {}
queueInRequest[2][Identifier] = {}
queueOutRequest[1][Identifier] = {}
queueOutRequest[2][Identifier] = {}
end
end
function ServerProcess.addCallback(Identifier: string, key: string, callback)
serverCallback[Identifier][key] = callback
end
function ServerProcess.removeCallback(Identifier: string, key: string)
serverCallback[Identifier][key] = nil
end
function ServerProcess.start()
RunService.PostSimulation:Connect(function()
for Identifier: string, players in unreliableServerQueue do
for player: Player, data: any in players do
if #data == 0 then continue end
UnreliableEvent:FireClient(player, Identifier, data)
table.clear(data)
end
end
for _, player: Player in ipairs(Players:GetPlayers()) do
if not queueOut[player] then continue end
for Identifier: string, data: any in queueOut[player] do
if #data == 0 then continue end
ReliableEvent:FireClient(player, Identifier, data)
table.clear(data)
end
end
for Identifier: string, players in serverQueue do
for player: Player, data in players do
if #data > 0 then
queueOut[player][Identifier] = table.clone(data)
table.clear(data)
end
if #serverRequestQueue[Identifier][player] > 0 then
for _, requestData in serverRequestQueue[Identifier][player] do
if not requestData[3] then continue end
table.insert(queueOutRequest[1][Identifier][player], { requestData[1], requestData[3] })
table.remove(requestData, #requestData)
end
end
if serverCallback[Identifier] ~= nil then
if #queueIn[Identifier][player] > 0 then
for _, packedDatas: any in queueIn[Identifier][player] do
if #packedDatas == 0 then continue end
for _, v: any in packedDatas do
for _, fn: any in serverCallback[Identifier] do
Spawn(fn, player, table.unpack(v))
end
end
end
table.clear(queueIn[Identifier][player])
end
if #queueInRequest[1][Identifier][player] > 0 then
for idx, packetDatas: any in queueInRequest[1][Identifier][player] do
if #packetDatas == 0 then continue end
for _, packetData in packetDatas do
for _, fn: any in serverCallback[Identifier] do
Spawn(function()
local requestReturn = { fn(player, table.unpack(packetData[2])) }
table.insert(queueOutRequest[2][Identifier][player], { packetData[1], requestReturn })
end)
end
end
end
table.clear(queueInRequest[1][Identifier][player])
end
if #queueInRequest[2][Identifier][player] > 0 then
for _, packetDatas: any in queueInRequest[2][Identifier][player] do
for _, packetData in packetDatas do
if #packetData == 1 then continue end
for idx, serverRequest in serverRequestQueue[Identifier][player] do
if serverRequest[1] == packetData[1] then
Spawn(serverRequest[2], table.unpack(packetData[2]))
table.remove(serverRequestQueue[Identifier], idx)
break
end
end
end
end
table.clear(queueInRequest[2][Identifier][player])
end
for player: Player, requestsData: any in queueOutRequest[1][Identifier] do
if #requestsData == 0 then continue end
RequestEvent:FireClient(player, Identifier, "\1", requestsData)
table.clear(requestsData)
end
for player: Player, toReturnDatas: any in queueOutRequest[2][Identifier] do
if #toReturnDatas == 0 then continue end
RequestEvent:FireClient(player, Identifier, "\0", toReturnDatas)
table.clear(toReturnDatas)
end
end
end
end
end)
local function onServerNetworkReceive(player: Player, Identifier: string, data: any)
if not Identifier or not data then return end
if not serverQueue[Identifier] then
serverQueue[Identifier] = {}
end
if not serverQueue[Identifier][player] then
serverQueue[Identifier][player] = {}
end
if not queueIn[Identifier][player] then
queueIn[Identifier][player] = {}
end
table.insert(queueIn[Identifier][player], data)
end
ReliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
UnreliableEvent.OnServerEvent:Connect(onServerNetworkReceive)
RequestEvent.OnServerEvent:Connect(function(player: Player, Identifier: string, action: string, data: any)
if not Identifier or not data then return end
if not queueInRequest[1][Identifier][player] then
queueInRequest[1][Identifier][player] = {}
queueInRequest[2][Identifier][player] = {}
end
if not serverQueue[Identifier][player] then
serverQueue[Identifier][player] = {}
end
if action == "\1" then
table.insert(queueInRequest[1][Identifier][player], data)
else
table.insert(queueInRequest[2][Identifier][player], data)
end
end)
end
for _, player: Player in ipairs(Players:GetPlayers()) do
task.spawn(initializeEachPlayer, player)
end
return ServerProcess