Hooking to losing or regaining connection

Is anybody aware of a way to hook to losing connection or regaining connection on the client (perhaps hooking to the events that cause the red timeout box to appear in the top right corner in some way)?

Currently, I am constantly sending ping messages and if no message is received from the server within 1 second a connection problem is assumed until something (there’s a bunch of stuff I am checking for) tells the client we’re connected again. This works most of the time, but I don’t feel like this is the best way to do it of it, because sometimes there’s no connection problem while it still detects a problem or a connection problem isn’t detected at all.

Ping, wait for pong, if 5-10 seconds have past rejoin, 1 seconds is way too short, it’s easy to lag for that.

That’s kind of missing the point, what I posted was just to give you an idea and I just checked; currently I am waiting 5 seconds.
It’s still not a great way of doing it. What I’m looking for is a way that does not involve pinging and timing out.

You can’t hook when the client is timing out, the client times-out when bytes haven’t been written within a certain period of time, your best shot would be to do the same.

This guy found a way, but his file isn’t on garrysmod.org anymore.
If you can contact him he’ll probably help you.
http://forum.facepunch.com/showthread.php?t=989733

Likely the older version, but:


-- This addon is a bombsite.
-- This is not code I would ever consider putting on a CV / personal statement - it's quite honestly the worst code I've written recently.
-- But hey, I fixed the problems. I'm too busy to neaten it up right now.

-- Flapadar

-- 30/8/10

if SERVER then
	AddCSLuaFile("anticrash.lua")
	
	concommand.Add("_ping" , function(p , c , a)
		if (p.LastPing and p.LastPing + 5 < CurTime()) or not p.LastPing then
			p.LastPing = CurTime()
			umsg.Start("pong" , p)
				umsg.String("")
			umsg.End()
		end
	end )
	return
end

-- A stupid number of variables, just to keep a check on everything that can and will go wrong
-- I added a couple to prevent the "It pops up on spawn" thing.

local lastmovetime = CurTime() + 10 -- Variable to check when move packets were last received
local reconnecttime = math.min(GetConVarNumber("sv_timeout") - 6 , GetConVarNumber("cl_timeout") - 6 , 30) -- in seconds
local enabled = util.tobool(math.floor(CreateConVar("anticrash_enabled" , 1 , true , true):GetInt()))

cvars.AddChangeCallback("anticrash_enabled" , function( c , o , n )
	MsgN("[AntiCrash] ".. (math.floor(n) == 1 and "Enabled" or "Disabled").. " AntiCrash.")
	enabled = util.tobool(math.floor(n))
end )

local crashtime = 1
local shouldretry = true
local crashed = false
local spawned = false
local pending = false
local spawntime

-- Slightly neater having all the stuff in a function, but the script overall is still a mess

local function IsCrashed()
	if enabled then
		if not crashed then -- Prevent repetition.
			if spawned and spawntime < CurTime() then
				if lastmovetime + crashtime < CurTime() then
					if (LocalPlayer and ValidEntity(LocalPlayer()) and not LocalPlayer():IsFrozen() and not LocalPlayer():InVehicle()) then
						return true
					end
				end
			end
		end
	end
end

usermessage.Hook("pong" , function(um)
	lastmovetime = CurTime() + 10
	MsgN("[AntiCrash] Connection regained - received pong")
end )

hook.Add("Move" , "CrashReconnect" , function()
	lastmovetime = CurTime() -- Set the last move packet to the current time.
end )

hook.Add("InitPostEntity" , "CrashReconnect" , function()
	spawned = true
	spawntime = CurTime() + 5
end )

hook.Add("ServerCrash" , "CrashReconnect" , function() -- want this to run when the server crashes.
	local menucrashtime = CurTime()
	local retrytime = menucrashtime + reconnecttime
	
	for k , v  in ipairs(player.GetAll()) do
		v.CrashedPing = v:Ping()
	end

	local dframe = vgui.Create("DFrame")
	dframe:SetSize(200 , 101)
	dframe:SetTitle("This server has crashed!")
	dframe:Center()
	dframe:MakePopup()

	function dframe:Close(...)
		shouldretry = false
		return DFrame.Close(self , ...)
	end

	local dlabel = vgui.Create("DLabel")
	dlabel:SetParent(dframe)
	dlabel:SetPos(30 , 30)
	dlabel:SetSize(195 , 25)
	dlabel:SetText(string.format("Autoreconnect in %d seconds!" , retrytime - CurTime()))

	function dlabel:Paint(...)
		self:SetText(string.format("Autoreconnect in %d seconds!" , retrytime - CurTime()))
		return DLabel.Paint(self , ...)
	end

	local dbutton = vgui.Create("DButton")
	dbutton:SetParent(dframe)
	dbutton:SetPos(5 , 55)
	dbutton:SetSize(190 , 22)
	dbutton:SetText("Reconnect now")
	dbutton.DoClick = function()
		RunConsoleCommand("retry")
	end

	local dbutton = vgui.Create("DButton")
	dbutton:SetParent(dframe)
	dbutton:SetPos(5 , 78)
	dbutton:SetSize(190 , 22)
	dbutton:SetText("Cancel")
	dbutton.DoClick = function()
		shouldretry = false
		dframe:SetVisible(false)
	end
	
	hook.Add("Think" , "Crashed" , function()
		for k , v in ipairs(player.GetAll()) do
			if v.CrashedPing != v:Ping() then
				MsgN("[AntiCrash] Connection regained - ping changed.")
				hook.Remove("Think" , "Crashed")
				crashed = false
				lastmovetime = CurTime() + 5
			end
		end
		
		local moving = false
		
		for k , v in ipairs(ents.GetAll()) do
			if v:GetVelocity():Length() > 5 then
				-- Well, not everything's stopped moving.
				-- 5 incase some props stuck in another prop and is spazzing or something
				-- It should stop moving, but i'm not entirely sure
				
				moving = true
			end
		end
		
		if moving then
			hook.Remove("Think" , "Crashed")
			MsgN("[AntiCrash] Connection regained - movement detected")
			crashed = false
			lastmovetime = CurTime() + 5
		end
		
	
		if crashed and (retrytime - CurTime() - 0.5) < 0 and lastmovetime + 5 < CurTime() then
			if shouldretry then
				RunConsoleCommand("retry")
			end
		elseif lastmovetime + crashtime - 1 > CurTime() then
			hook.Remove("Think" , "Crashed")
			crashed = false
			if dframe and dframe:IsValid() then
				dframe:Remove()
			end
		end
	end )
end ) 

hook.Add("Think" , "CrashReconnect" , function()
	if not crashed and IsCrashed() and not pending then
		pending = true
		RunConsoleCommand("_ping")
		timer.Simple(3.5 , function()
			if lastmovetime + crashtime < CurTime() then
				MsgN("[AntiCrash] Connection down - Did not receive pong")
				crashed = true
				shouldretry = true -- This is a seperate crash from the previous, the user might want to reconnect this time.
				pending = false
				hook.Call("ServerCrash" , nil) -- Incase anyone else wants to hook into server crashes.
			else
				pending = false
				crashed = false
			end
		end )
		MsgN("[AntiCrash] Connection lost - sending ping")
	end
end )



MsgN("


You are running AntiCrash by Flapadar")
MsgN("You will be reconnected to the server if it crashes")
MsgN("

Download : http://www.facepunch.com/showthread.php?t=989733

")




That is the method he is using right now.

Seems like it would work well enough. Aside from this I’m sure you could make a C++ module that detects packet flow interruption. Source does it, as is visible with the 40 second timer in the top right.

INetChannel:IsTimingOut

[editline]15th May 2013[/editline]

Oh ON the client

Honestly, as far as I’m concerned there is no better way to do it right now in Lua.
I think someone once wanted a hook for that back then in the beta, but the thread died.