Initial commit
This commit is contained in:
parent
6f71e2e1aa
commit
d93dbacee2
@ -1 +0,0 @@
|
|||||||
RunConsoleCommand("cl_timeout '240'")
|
|
3
lua/autorun/client/cl_init.lua
Normal file
3
lua/autorun/client/cl_init.lua
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
RunConsoleCommand("cl_timeout '240'")
|
||||||
|
include("cfc_disconnect_interface/client/cl_ponger.lua")
|
||||||
|
include("cfc_disconnect_interface/client/cl_interface.lua")
|
9
lua/autorun/server/sv_init.lua
Normal file
9
lua/autorun/server/sv_init.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
util.AddNetworkString("cfc_di_ping")
|
||||||
|
util.AddNetworkString("cfc_di_loaded")
|
||||||
|
util.AddNetworkString("cfc_di_shutdown")
|
||||||
|
|
||||||
|
AddCSLuaFile("cfc_disconnect_interface/client/cl_ponger.lua")
|
||||||
|
AddCSLuaFile("cfc_disconnect_interface/client/cl_api.lua")
|
||||||
|
AddCSLuaFile("cfc_disconnect_interface/client/cl_interface.lua")
|
||||||
|
|
||||||
|
include("cfc_disconnect_interface/server/sv_pinger.lua")
|
86
lua/cfc_disconnect_interface/client/cl_api.lua
Normal file
86
lua/cfc_disconnect_interface/client/cl_api.lua
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
crashApi = {}
|
||||||
|
|
||||||
|
local cfc_endpoint = "https://scripting.cfcservers.org/cfc3-ping"
|
||||||
|
local global_endpoint = "https://www.google.com"
|
||||||
|
|
||||||
|
local api = crashApi
|
||||||
|
|
||||||
|
api.INACTIVE = 0
|
||||||
|
api.PINGING_API = 1
|
||||||
|
api.NO_INTERNET = 2
|
||||||
|
api.SERVER_DOWN = 3
|
||||||
|
api.SERVER_UP = 4
|
||||||
|
|
||||||
|
local responses = {cfc = nil, global = nil} -- Does nothing but helps with clarity
|
||||||
|
|
||||||
|
local state = api.INACTIVE
|
||||||
|
|
||||||
|
local pingCancelled = false
|
||||||
|
|
||||||
|
local function getState()
|
||||||
|
return state
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
else
|
||||||
|
-- Internet is down
|
||||||
|
state = api.NO_INTERNET
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
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 not data or data["server-is-up"] == nil then -- Can't use dot notation cuz api field has dashes >:(
|
||||||
|
responses.cfc = false
|
||||||
|
handleResponses()
|
||||||
|
else
|
||||||
|
responses.cfc = data["server-is-up"]
|
||||||
|
handleResponses()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
function(err)
|
||||||
|
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
|
327
lua/cfc_disconnect_interface/client/cl_interface.lua
Normal file
327
lua/cfc_disconnect_interface/client/cl_interface.lua
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
include("cfc_disconnect_interface/client/cl_api.lua")
|
||||||
|
|
||||||
|
surface.CreateFont( "CFC_Normal",
|
||||||
|
{
|
||||||
|
font = "arial",
|
||||||
|
size = 18,
|
||||||
|
weight = 500
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
surface.CreateFont( "CFC_Special",
|
||||||
|
{
|
||||||
|
font = "coolvetica",
|
||||||
|
size = 26,
|
||||||
|
weight = 500
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
surface.CreateFont( "CFC_Button",
|
||||||
|
{
|
||||||
|
font = "arial",
|
||||||
|
size = 18,
|
||||||
|
weight = 1500
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
local interfaceDerma = false
|
||||||
|
|
||||||
|
local TIME_TO_RESTART = 10
|
||||||
|
local timeDown
|
||||||
|
local apiState
|
||||||
|
local previouslyShown = false
|
||||||
|
|
||||||
|
-- Colors
|
||||||
|
primaryCol = Color( 36, 41, 67 )
|
||||||
|
secondaryCol = Color( 42, 47, 74 )
|
||||||
|
accentCol = Color( 84, 84, 150 )
|
||||||
|
|
||||||
|
local function lerpColor(fraction, from, to)
|
||||||
|
return Color(from.r + (to.r - from.r) * fraction,
|
||||||
|
from.g + (to.g - from.g) * fraction,
|
||||||
|
from.b + (to.b - from.b) * fraction)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function secondsAsTime(s)
|
||||||
|
return string.FormattedTime( s, "%02i:%02i" )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Delay Function
|
||||||
|
local delayId = 0
|
||||||
|
local function delaycall(time, callback)
|
||||||
|
local wait = RealTime() + time
|
||||||
|
delayId = delayId + 1
|
||||||
|
local hookName = "cfc_di_delay_" .. delayId
|
||||||
|
hook.Add("Tick", hookName, function()
|
||||||
|
if RealTime() > wait then
|
||||||
|
hook.Remove("Tick", hookName)
|
||||||
|
callback()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rejoin()
|
||||||
|
delaycall(1, function() -- gm_crashsys does this, I don't feel like going through finding out why, so I'm just gonna do the same :)
|
||||||
|
RunConsoleCommand( "snd_restart" )
|
||||||
|
RunConsoleCommand( "retry" )
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function leave()
|
||||||
|
delaycall(1, function()
|
||||||
|
RunConsoleCommand( "disconnect" )
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addTitleBar(frame)
|
||||||
|
local frameW, frameH = frame:GetSize()
|
||||||
|
local titleBarHeight = 32
|
||||||
|
local titleBar = vgui.Create( "DPanel", frame )
|
||||||
|
titleBar:SetSize( frameW, titleBarHeight )
|
||||||
|
titleBar:SetPos( 0, 0 )
|
||||||
|
function titleBar:Paint(w, h)
|
||||||
|
surface.SetDrawColor( secondaryCol )
|
||||||
|
surface.DrawRect( 0, 0, w, h )
|
||||||
|
end
|
||||||
|
|
||||||
|
local closeBtnPadding = (titleBarHeight - 16) / 2
|
||||||
|
|
||||||
|
local closeBtn = vgui.Create( "DImageButton", titleBar )
|
||||||
|
closeBtn:SetSize( 16, 16 )
|
||||||
|
closeBtn:SetPos( frameW - 16 - closeBtnPadding, closeBtnPadding)
|
||||||
|
closeBtn:SetImage( "icon16/cross.png" )
|
||||||
|
function closeBtn:DoClick()
|
||||||
|
frame:Close()
|
||||||
|
end
|
||||||
|
|
||||||
|
local titleLabelPadding = (titleBarHeight - 26) / 2
|
||||||
|
|
||||||
|
local titleLabel = vgui.Create( "DLabel", titleBar )
|
||||||
|
titleLabel:SetFont( "CFC_Special" )
|
||||||
|
titleLabel:SetText( "Oops! Looks like the server crashed..." )
|
||||||
|
titleLabel:SizeToContents()
|
||||||
|
titleLabel:SetPos( 0, titleLabelPadding + 2 )
|
||||||
|
titleLabel:CenterHorizontal()
|
||||||
|
|
||||||
|
return titleBar
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makeButton(frame, text, xFraction, doClick, outlineCol, fillCol, hoverOutlineCol, hoverFillCol)
|
||||||
|
outlineCol = outlineCol or Color( 255, 255, 255 )
|
||||||
|
fillCol = fillCol or primaryCol
|
||||||
|
hoverOutlineCol = hoverOutlineCol or Color(155,241,255)
|
||||||
|
hoverFillCol = hoverFillCol or primaryCol
|
||||||
|
|
||||||
|
local frameW, frameH = frame:GetSize()
|
||||||
|
local btn = vgui.Create( "DButton", frame )
|
||||||
|
btn:SetText( text )
|
||||||
|
btn:SetTextColor( Color( 255, 255, 255 ) )
|
||||||
|
btn:SetFont( "CFC_Button" )
|
||||||
|
btn:SetSize( frameW * 0.4, frameH * 0.6 )
|
||||||
|
btn:CenterHorizontal( xFraction )
|
||||||
|
btn:CenterVertical()
|
||||||
|
btn.DoClick = doClick
|
||||||
|
|
||||||
|
btn.fadeState = 0
|
||||||
|
btn.prevTime = CurTime()
|
||||||
|
|
||||||
|
local btnAnimSpeed = 0.05 * 60
|
||||||
|
|
||||||
|
function btn:Think()
|
||||||
|
-- Make anim same speed for all framerates
|
||||||
|
local dt = CurTime() - self.prevTime
|
||||||
|
self.prevTime = CurTime()
|
||||||
|
if dt > 1 then dt = 0 end
|
||||||
|
|
||||||
|
if self:IsHovered() and self.fadeState < 1 then
|
||||||
|
self.fadeState = math.Clamp(self.fadeState + btnAnimSpeed * dt, 0, 1)
|
||||||
|
elseif not self:IsHovered() and self.fadeState > 0 then
|
||||||
|
self.fadeState = math.Clamp(self.fadeState - btnAnimSpeed * dt, 0, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local btnBorderWeight = 2
|
||||||
|
function btn:Paint(w, h)
|
||||||
|
local lineCol
|
||||||
|
local bgCol
|
||||||
|
if self:GetDisabled() then
|
||||||
|
lineCol = Color( 74, 74, 74 )
|
||||||
|
bgCol = fillCol
|
||||||
|
self:SetCursor( "no" )
|
||||||
|
else
|
||||||
|
lineCol = lerpColor(self.fadeState, outlineCol, hoverOutlineCol)
|
||||||
|
bgCol = lerpColor(self.fadeState, fillCol, hoverFillCol)
|
||||||
|
self:SetCursor( "hand" )
|
||||||
|
end
|
||||||
|
|
||||||
|
self:SetTextColor( lineCol )
|
||||||
|
|
||||||
|
surface.SetDrawColor( lineCol )
|
||||||
|
surface.DrawRect( 0, 0, w, h )
|
||||||
|
surface.SetDrawColor( bgCol )
|
||||||
|
surface.DrawRect( btnBorderWeight, btnBorderWeight,
|
||||||
|
w - (btnBorderWeight*2), h - (btnBorderWeight*2) )
|
||||||
|
end
|
||||||
|
|
||||||
|
return btn
|
||||||
|
end
|
||||||
|
|
||||||
|
local function addButtonsBar(frame)
|
||||||
|
local frameW, frameH = frame:GetSize()
|
||||||
|
|
||||||
|
local buttonBarHeight = 64
|
||||||
|
|
||||||
|
local barPanel = vgui.Create( "DPanel", frame )
|
||||||
|
barPanel:SetSize( frameW, buttonBarHeight )
|
||||||
|
barPanel:SetPos( 0, frameH - buttonBarHeight )
|
||||||
|
function barPanel:Paint(w, h)
|
||||||
|
surface.SetDrawColor( accentCol )
|
||||||
|
surface.DrawLine( 16, 0, w - 16, 0 )
|
||||||
|
end
|
||||||
|
|
||||||
|
barPanel.reconBtn = makeButton(barPanel, "RECONNECT", 0.25, rejoin,
|
||||||
|
Color( 74, 251, 191 ), nil, Color( 74, 251, 191 ), Color( 64, 141, 131 ))
|
||||||
|
barPanel.reconBtn:SetDisabled( true )
|
||||||
|
barPanel.disconBtn = makeButton(barPanel, "DISCONNECT", 0.75, leave)
|
||||||
|
|
||||||
|
return barPanel
|
||||||
|
end
|
||||||
|
|
||||||
|
local function makeLabel(frame, text, top, col, xFraction)
|
||||||
|
col = col or Color( 255, 255, 255 )
|
||||||
|
local label = vgui.Create( "DLabel", frame )
|
||||||
|
label:SetText( text )
|
||||||
|
label:SetFont( "CFC_Special" )
|
||||||
|
label:SizeToContents()
|
||||||
|
label:SetPos( 0, top )
|
||||||
|
label:SetTextColor( col )
|
||||||
|
label:CenterHorizontal( xFraction )
|
||||||
|
return label
|
||||||
|
end
|
||||||
|
|
||||||
|
local function populateBodyInternetDown(body)
|
||||||
|
local label1 = makeLabel(body, "Looks like your internet has gone down!", 20)
|
||||||
|
local label2 = makeLabel(body, "Stick around for when it comes back", 64)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function populateBodyServerDown(body)
|
||||||
|
|
||||||
|
local frameW, frameH = body:GetSize()
|
||||||
|
local restartTimeStr = "The server normally takes about " .. secondsAsTime(TIME_TO_RESTART) .. " to restart!"
|
||||||
|
local restartTimeLabel = makeLabel(body, restartTimeStr, 0)
|
||||||
|
local curTimePreLabel = makeLabel(body, "It has been down for", 32)
|
||||||
|
function curTimePreLabel:Think()
|
||||||
|
if apiState == crashApi.SERVER_UP and not self.backUp then
|
||||||
|
self:SetText( "It was down for" )
|
||||||
|
self:SizeToContents()
|
||||||
|
self:CenterHorizontal()
|
||||||
|
self.backUp = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local tooLongLabel = makeLabel(body, "Uh oh, seems it's taking a little longer than usual!", 70, Color( 251, 191, 83 ), 0.8)
|
||||||
|
tooLongLabel:SetAlpha(0)
|
||||||
|
tooLongLabel:Hide()
|
||||||
|
|
||||||
|
local curTimeLabel = makeLabel(body, secondsAsTime(math.floor(timeDown)), 70, Color( 251, 191, 83 ))
|
||||||
|
function curTimeLabel:Think()
|
||||||
|
if apiState ~= crashApi.SERVER_UP then
|
||||||
|
self:SetText(secondsAsTime(math.floor(timeDown)))
|
||||||
|
if timeDown > TIME_TO_RESTART then
|
||||||
|
self:SetTextColor(Color(255, 0, 0))
|
||||||
|
if not tooLongLabel:IsVisible() then
|
||||||
|
tooLongLabel:Show()
|
||||||
|
tooLongLabel:AlphaTo(255, 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:SetTextColor(Color(0, 255, 0))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function populateBody(body)
|
||||||
|
body.Paint = nil
|
||||||
|
if apiState == crashApi.NO_INTERNET then
|
||||||
|
populateBodyInternetDown(body)
|
||||||
|
else -- Server down or up via api, and down via net
|
||||||
|
populateBodyServerDown(body)
|
||||||
|
end
|
||||||
|
|
||||||
|
local frameW, frameH = 0.8 * ScrW(), 0.8 * ScrH()
|
||||||
|
|
||||||
|
local playGameLabel = makeLabel(body, "Why not play a game while you wait? (Press space)", 108)
|
||||||
|
|
||||||
|
local gamePanel = vgui.Create( "DPanel", body )
|
||||||
|
gamePanel:SetSize( frameW - 20, frameH - 134 - 15 )
|
||||||
|
gamePanel:SetPos( -6, 134 + 10 )
|
||||||
|
gamePanel.Paint = nil
|
||||||
|
|
||||||
|
local gameHtml = vgui.Create( "DHTML", gamePanel )
|
||||||
|
gameHtml:Dock( FILL )
|
||||||
|
gameHtml:OpenURL("https://cdn.cfcservers.org/media/dinosaur/index.html")
|
||||||
|
function gameHtml:Think()
|
||||||
|
if not gameHtml:HasFocus() then gameHtml:RequestFocus() end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function createInterface()
|
||||||
|
|
||||||
|
local frameW, frameH = 0.8 * ScrW(), 0.8 * ScrH()
|
||||||
|
|
||||||
|
local startTime = SysTime()
|
||||||
|
|
||||||
|
local frame = vgui.Create( "DFrame" )
|
||||||
|
interfaceDerma = frame
|
||||||
|
frame:SetSize( frameW, frameH )
|
||||||
|
frame:Center()
|
||||||
|
frame:SetTitle("")
|
||||||
|
frame:SetDraggable( false )
|
||||||
|
frame:MakePopup()
|
||||||
|
frame:ShowCloseButton( false )
|
||||||
|
|
||||||
|
function frame:Paint(w, h)
|
||||||
|
Derma_DrawBackgroundBlur(self, startTime)
|
||||||
|
surface.SetDrawColor( primaryCol )
|
||||||
|
surface.DrawRect( 0, 0, w, h )
|
||||||
|
end
|
||||||
|
|
||||||
|
local titlePanel = addTitleBar(frame)
|
||||||
|
local btnsPanel = addButtonsBar(frame)
|
||||||
|
|
||||||
|
local body = vgui.Create( "DPanel", frame )
|
||||||
|
body:SetSize(frameW - 32, frameH - 32 - titlePanel:GetTall() - btnsPanel:GetTall())
|
||||||
|
body:SetPos(16, titlePanel:GetTall() + 16)
|
||||||
|
populateBody(body)
|
||||||
|
|
||||||
|
function frame:Think()
|
||||||
|
if apiState == crashApi.INACTIVE then
|
||||||
|
frame:Close() -- Server recovered without ever closing
|
||||||
|
elseif apiState == crashApi.SERVER_UP then
|
||||||
|
if btnsPanel.reconBtn:GetDisabled() == true then
|
||||||
|
btnsPanel.reconBtn:SetDisabled( false ) -- Server back up
|
||||||
|
-- Maybe show a "The server is back up, click here to reconnect?"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function frame:OnClose()
|
||||||
|
interfaceDerma = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hook.Add("cfc_di_crashTick", "cfc_di_interfaceUpdate", function(isCrashing, _timeDown, _apiState)
|
||||||
|
timeDown = _timeDown or 0
|
||||||
|
apiState = _apiState
|
||||||
|
if isCrashing and apiState ~= crashApi.PINGING_API and not interfaceDerma and not previouslyShown then
|
||||||
|
createInterface()
|
||||||
|
previouslyShown = true
|
||||||
|
end
|
||||||
|
if not isCrashing then
|
||||||
|
previouslyShown = false
|
||||||
|
if interfaceDerma then
|
||||||
|
interfaceDerma:Close()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
65
lua/cfc_disconnect_interface/client/cl_ponger.lua
Normal file
65
lua/cfc_disconnect_interface/client/cl_ponger.lua
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
include("cfc_disconnect_interface/client/cl_api.lua")
|
||||||
|
|
||||||
|
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 = 15 -- How often to call the api
|
||||||
|
|
||||||
|
local lastPing
|
||||||
|
local lastApiCall
|
||||||
|
|
||||||
|
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
|
||||||
|
lastPong = RealTime()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function shutdown()
|
||||||
|
timer.Remove("cfc_di_startup")
|
||||||
|
hook.Remove("Tick", "cfc_di_tick")
|
||||||
|
end
|
||||||
|
|
||||||
|
net.Receive("cfc_di_shutdown", shutdown)
|
||||||
|
hook.Add("ShutDown", "crashsys", shutdown)
|
||||||
|
|
||||||
|
local function crashTick(timedown)
|
||||||
|
local apiState = crashApi.getState();
|
||||||
|
if (apiState == crashApi.INACTIVE) or -- No ping sent
|
||||||
|
(apiState ~= crashApi.SERVER_UP and RealTime() - lastApiCall > API_TIMEOUT) then -- Previous ping failed, and API_TIMEOUT has passed
|
||||||
|
crashApi.triggerPing();
|
||||||
|
lastApiCall = RealTime();
|
||||||
|
|
||||||
|
apiState = crashApi.getState();
|
||||||
|
end
|
||||||
|
hook.Run("cfc_di_crashTick", true, timedown, apiState);
|
||||||
|
end
|
||||||
|
|
||||||
|
local function checkCrashTick()
|
||||||
|
if not lastPong then return end
|
||||||
|
if not LocalPlayer():IsValid() then return end -- disconnected or connecting
|
||||||
|
|
||||||
|
local timeout = RealTime() - lastPong
|
||||||
|
|
||||||
|
if timeout > GRACE_TIME then
|
||||||
|
crashTick(timeout)
|
||||||
|
else
|
||||||
|
if crashApi.getState() ~= crashApi.INACTIVE then
|
||||||
|
crashApi.cancelPing();
|
||||||
|
end
|
||||||
|
hook.Run("cfc_di_crashTick", false);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ping the server when the client is ready.
|
||||||
|
timer.Create("cfc_di_startup", 0.01, 0, function()
|
||||||
|
local ply = LocalPlayer()
|
||||||
|
if ply:IsValid() then
|
||||||
|
net.Start("cfc_di_loaded")
|
||||||
|
net.SendToServer()
|
||||||
|
timer.Remove("cfc_di_startup")
|
||||||
|
print("cfc_disconnect_interface loaded.")
|
||||||
|
hook.Add("Tick", "cfc_di_tick", checkCrashTick)
|
||||||
|
end
|
||||||
|
end)
|
30
lua/cfc_disconnect_interface/server/sv_pinger.lua
Normal file
30
lua/cfc_disconnect_interface/server/sv_pinger.lua
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
local PING_TIME = 3
|
||||||
|
|
||||||
|
local players = {}
|
||||||
|
local table = table
|
||||||
|
|
||||||
|
local function ping(ply)
|
||||||
|
net.Start("cfc_di_ping")
|
||||||
|
net.Send(ply or players)
|
||||||
|
end
|
||||||
|
|
||||||
|
net.Receive("cfc_di_loaded", function(len, ply)
|
||||||
|
if not IsValid(ply) then return end
|
||||||
|
if not table.HasValue(players, ply) then
|
||||||
|
table.insert(players, ply)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("PlayerDisconnected", "crashsys", function(ply)
|
||||||
|
ping(ply)
|
||||||
|
table.RemoveByValue(players, ply)
|
||||||
|
end)
|
||||||
|
|
||||||
|
timer.Create("cfc_di_pingTimer", PING_TIME, 0, function()
|
||||||
|
ping()
|
||||||
|
end)
|
||||||
|
|
||||||
|
hook.Add("ShutDown", "cfc_di", function()
|
||||||
|
net.Start("cfc_di_shutdown")
|
||||||
|
net.Send(players)
|
||||||
|
end)
|
BIN
resource/fonts/coolvetica.ttf
Normal file
BIN
resource/fonts/coolvetica.ttf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user