Change ponger and api top use network promises, refactor and cleanup

This commit is contained in:
Samuel Williams 2020-12-06 00:24:47 +00:00 committed by Brandon Sturgeon
parent 8ad711091f
commit 21cefc125c
5 changed files with 69 additions and 149 deletions

View File

@ -1,8 +1,10 @@
local minTimeout = CreateConVar( "sv_mintimeout", 900, FCVAR_REPLICATED + FCVAR_ARCHIVE + FCVAR_PROTECTED ):GetInt()
local timeout = GetConVar( "cl_timeout" )
if not timeout or timeout:GetInt() < minTimeout then
RunConsoleCommand( "cl_timeout", minTimeout )
end
include( "cfc_disconnect_interface/client/cl_detached_timer.lua" )
include( "cfc_disconnect_interface/client/cl_ponger.lua" )
include( "cfc_disconnect_interface/client/cl_interface.lua" )

View File

@ -1,12 +1,14 @@
crashApi = {}
require( "cfc_promises" )
-- local references
local http, concommand = http, concommand
CFCCrashAPI = {}
local cfc_endpoint = "https://nanny.cfcservers.org/cfc3-ping"
local global_endpoint = "https://www.google.com"
local endpointDirectory = "cfc/cfc_disconnect_interface/endpoint.txt"
local api = crashApi
local endpointCFC = file.Read( endpointDirectory ) or "https://nanny.cfcservers.org/cfc3-ping"
local endpointGlobal = "https://www.google.com"
local api = CFCCrashAPI
api.state = api.INACTIVE
api.INACTIVE = 0
api.PINGING_API = 1
@ -14,116 +16,36 @@ api.NO_INTERNET = 2
api.SERVER_DOWN = 3
api.SERVER_UP = 4
local DEV_MODE = false
function api._checkCFCEndpoint()
local success, body = await( NP.http.fetch( endpointCFC ) )
api.inDebug = false
api.debugMode = api.INACTIVE
if not success then return false end
if DEV_MODE then
-- Testing
local function testServerCrash()
api.inDebug = true
api.debugMode = api.SERVER_DOWN
end
concommand.Add( "cfc_di_testcrash", testServerCrash )
local function testNoInternet()
api.inDebug = true
api.debugMode = api.NO_INTERNET
end
concommand.Add( "cfc_di_testnointernet", testNoInternet )
local function serverRestarted()
api.inDebug = true
api.debugMode = api.SERVER_UP
end
concommand.Add( "cfc_di_testrestart", serverRestarted )
local function serverRecovered()
api.inDebug = false
end
concommand.Add( "cfc_di_testrecover", serverRecovered )
local data = util.JSONToTable( body )
return tobool( data and data["server-is-up"] )
end
api.checkCFCEndpoint = async( api._checkCFCEndpoint )
local responses = { cfc = nil, global = nil } -- Does nothing but helps with clarity
local state = api.INACTIVE
local pingCancelled = false
local function getState()
return state
function api._checkGlobalEndpoint()
local success = await( NP.http.fetch( endpointGlobal ) )
return success
end
api.checkGlobalEndpoint = async( api._checkGlobalEndpoint )
-- Check both websites responded, set state accordingly
local function handleResponses()
if pingCancelled then -- Ignore responses if ping was cancelled
return
end
if responses.cfc == nil or responses.global == nil then -- Not all responses arrived yet
return
end
function api._ping()
api.state = api.PINGING_API
-- If in debug mode, set state to debug state
if api.inDebug then state = api.debugMode return end
local _, data = await( promises.all( api.checkCFCEndpoint(), api.checkGlobalEndpoint() ) )
local cfcStatus, globalStatus = data[1][1], data[2][1]
if responses.cfc then
-- Server is up
state = api.SERVER_UP
elseif not responses.cfc and responses.global then
-- Server is down
state = api.SERVER_DOWN
if cfcStatus and globalStatus then
api.state = api.SERVER_UP
elseif globalStatus then
api.state = api.SERVER_DOWN
else
-- Internet is down
state = api.NO_INTERNET
api.state = api.NO_INTERNET
end
return api.state
end
-- Fetch cfc and global end points
local function triggerPing()
pingCancelled = false
state = api.PINGING_API
responses = { cfc = nil, global = nil }
http.Fetch( cfc_endpoint,
function( body, size, headers, code )
local data = util.JSONToTable( body )
-- If response is malformed, or empty, set cfc false
if not data or data.status == nil then -- Can't use dot notation cuz api field has dashes >:(
responses.cfc = false
handleResponses()
else
responses.cfc = data.status == "server-is-up"
handleResponses()
end
end,
function( err )
-- If cfc doesn't respond, set cfc false, might want to do something special here, as this means cfcservers had a heart attack
responses.cfc = false
handleResponses()
end
)
http.Fetch( global_endpoint,
function( body, size, headers, code )
responses.global = true
handleResponses()
end,
function( err )
responses.global = false
handleResponses()
end
)
end
local function cancelPing()
state = api.INACTIVE
pingCancelled = true
end
api.getState = getState
api.triggerPing = triggerPing
api.cancelPing = cancelPing
api.ping = async( api._ping )

View File

@ -1,7 +1,5 @@
include( "cfc_disconnect_interface/client/cl_api.lua" )
local vgui, hook = vgui, hook
surface.CreateFont( "CFC_Normal",
{
font = "arial",
@ -47,9 +45,9 @@ local timeDown = 0
local apiState
local previouslyShown = false
local disconnectMessages = {}
disconnectMessages[crashApi.SERVER_DOWN] = "Are you sure? Hang in there, the server will restart soon..."
disconnectMessages[crashApi.SERVER_UP] = "Are you sure? The server is already back up and ready!"
disconnectMessages[crashApi.NO_INTERNET] = "Are you sure? If your internet comes back, you can easily rejoin from this page."
disconnectMessages[CFCCrashAPI.SERVER_DOWN] = "Are you sure? Hang in there, the server will restart soon..."
disconnectMessages[CFCCrashAPI.SERVER_UP] = "Are you sure? The server is already back up and ready!"
disconnectMessages[CFCCrashAPI.NO_INTERNET] = "Are you sure? If your internet comes back, you can easily rejoin from this page."
-- Helper function
local function getFrom( i, ... )
@ -275,7 +273,7 @@ local function addButtonsBar( frame )
end
if not self.disconMode then return end
if apiState ~= crashApi.SERVER_UP then return end
if apiState ~= CFCCrashAPI.SERVER_UP then return end
if self.backUp then return end
showMessage( getDisconnectMessage() )
@ -365,7 +363,7 @@ local function populateBodyServerDown( body )
-- When the server comes back up, "It has been down for" => "It was down for"
-- Then resize and move
function curTimePreLabel:Think()
if apiState == crashApi.SERVER_UP and not self.backUp then
if apiState == CFCCrashAPI.SERVER_UP and not self.backUp then
self:setTextAndAlign( "It was down for" )
self.backUp = true
end
@ -376,7 +374,7 @@ local function populateBodyServerDown( body )
-- If timeDown > averageTimeDown, make it red and show the messageLabel
local curTimeLabel = makeLabel( body, secondsAsTime( math.floor( timeDown ) ), 70, Color( 251, 191, 83 ), 0.5, "CFC_Mono" )
function curTimeLabel:Think()
if apiState ~= crashApi.SERVER_UP then
if apiState ~= CFCCrashAPI.SERVER_UP then
self:setTextAndAlign( secondsAsTime( math.floor( timeDown ) ) )
if timeDown > TIME_TO_RESTART then
self:SetTextColor( Color( 255, 0, 0 ) )
@ -403,8 +401,8 @@ local function populateBody( body )
interfaceDerma.messageLabel:SetAlpha( 0 )
interfaceDerma.messageLabel:Hide()
-- Fill top text based on crashApi state
if apiState == crashApi.NO_INTERNET then
-- Fill top text based on CFCCrashAPI state
if apiState == CFCCrashAPI.NO_INTERNET then
title = populateBodyInternetDown( body )
else -- Server down or up via api, and down via net
title = populateBodyServerDown( body )
@ -468,9 +466,9 @@ local function createInterface()
-- If server fully recovers without crashing, close menu
-- If server reboots, enabled the reconnect button
function frame:Think()
if apiState == crashApi.INACTIVE then
if apiState == CFCCrashAPI.INACTIVE then
frame:Close() -- Server recovered without ever closing
elseif apiState == crashApi.SERVER_UP then
elseif apiState == CFCCrashAPI.SERVER_UP then
if btnsPanel.reconBtn:GetDisabled() == true and not btnsPanel.reconBtn.dontEnable then
btnsPanel.reconBtn:SetDisabled( false ) -- Server back up
end
@ -484,14 +482,14 @@ end
hook.Add( "cfc_di_crashTick", "cfc_di_interfaceUpdate", function( isCrashing, _timeDown, _apiState )
timeDown = _timeDown or 0
if _apiState ~= crashApi.PINGING_API then
if _apiState ~= CFCCrashAPI.PINGING_API then
apiState = _apiState
end
if isCrashing then
-- Open interface if server is crashing, API has responded, interface isn't already open, and interface has not yet been opened
if _apiState == crashApi.PINGING_API or _apiState == crashApi.SERVER_UP then return end
if _apiState == CFCCrashAPI.PINGING_API or _apiState == CFCCrashAPI.SERVER_UP then return end
if interfaceDerma or previouslyShown then return end
createInterface()
previouslyShown = true

View File

@ -1,20 +1,19 @@
include( "cfc_disconnect_interface/client/cl_api.lua" )
local net, hook = net, hook
local GRACE_TIME = 3.5 -- How many seconds of lag should we have before showing the panel?
local PING_MISS = 2 -- How many pings can we miss on join?
local API_TIMEOUT = 5 -- How often to call the api
local lastPong
local lastApiCall
local pongerStatus = false
local pingLoopRunning = false
net.Receive( "cfc_di_ping", function()
if PING_MISS > 0 then -- Allow some pings before actually starting crash systems. ( Avoid bugs on join stutter. )
PING_MISS = PING_MISS - 1
else
if crashApi.inDebug then return end
lastPong = SysTime()
end
end )
@ -27,17 +26,18 @@ end
net.Receive( "cfc_di_shutdown", shutdown )
hook.Add( "ShutDown", "cfc_di_shutdown", shutdown )
local function crashTick( timedown )
local apiState = crashApi.getState();
if ( apiState == crashApi.INACTIVE ) or -- No ping sent
( SysTime() - lastApiCall > API_TIMEOUT ) then -- API_TIMEOUT has passed
crashApi.triggerPing()
lastApiCall = SysTime()
local function _pingLoop()
if pingLoopRunning then return end
pingLoopRunning = true
apiState = crashApi.getState();
while pongerStatus do
await( CFCCrashAPI.ping() )
await( NP.timeout( API_TIMEOUT ) )
end
hook.Run( "cfc_di_crashTick", true, timedown, apiState );
pingLoopRunning = false
end
pingLoop = async( _pingLoop )
local function checkCrashTick()
if not lastPong then return end
@ -45,24 +45,25 @@ local function checkCrashTick()
local timeout = SysTime() - lastPong
if timeout > GRACE_TIME then
crashTick( timeout )
else
-- Server recovered while crashApi was running, cancel the request
if crashApi.getState() ~= crashApi.INACTIVE then
crashApi.cancelPing();
end
hook.Run( "cfc_di_crashTick", false );
local inGrace = timeout > GRACE_TIME
if pongerStatus ~= inGrace then
pongerStatus = inGrace
pingLoop()
end
hook.Run( "cfc_di_crashTick", pongerStatus, timedown, CFCCrashAPI.state )
end
-- Ping the server when the client is ready.
dTimer.Create( "cfc_di_startup", 0.01, 0, function()
local ply = LocalPlayer()
if ply:IsValid() then
if LocalPlayer():IsValid() then
dTimer.Remove( "cfc_di_startup" )
net.Start( "cfc_di_loaded" )
net.SendToServer()
dTimer.Remove( "cfc_di_startup" )
print( "cfc_disconnect_interface loaded." )
hook.Add( "Tick", "cfc_di_tick", checkCrashTick )
end

View File

@ -1,7 +1,6 @@
local PING_TIME = 1
local players = {}
local table = table
local function ping( ply )
net.Start( "cfc_di_ping" )
@ -20,9 +19,7 @@ hook.Add( "PlayerDisconnected", "crashsys", function( ply )
table.RemoveByValue( players, ply )
end )
timer.Create( "cfc_di_pingTimer", PING_TIME, 0, function()
ping()
end )
timer.Create( "cfc_di_pingTimer", PING_TIME, 0, ping )
hook.Add( "ShutDown", "cfc_di", function()
net.Start( "cfc_di_shutdown" )