function SafeInvokeCallback(a,...)local n=false local t local H local O local function z(...)if not n then n=true O=table.pack(...)if H then ResumeThread(H)end end end FastSpawn(function(...)t=coroutine.running()z(true,a.Callback(...))end,...)if not n then local a=IsServer and...;(coroutine.wrap(function()while not n and coroutine.status(t)~="\100\101\097\100"do if IsServer and a.Parent~=Players then break end wait(.5)end z(false)end))()end if not n then H=coroutine.running()YieldThread()end return unpack(O)end function SafeInvoke(a,n,...)local t=coroutine.running()local H=false local O;(coroutine.wrap(function(...)if IsServer then O=table.pack(pcall(function(...)return n.Remote:InvokeClient(...)end,...))else O=table.pack(pcall(function(...)return n.Remote:InvokeServer(...)end,...))end if not H then H=true ResumeThread(t)end end))(...)if typeof(a)=="\110\117\109\098\101\114"then delay(a,function()if not H then H=true ResumeThread(t)end end)end YieldThread()if O and(O[1]==true and O[2]==true)then return true,unpack(O,3)end return false end function SafeFireEvent(a,...)local n=a.Callbacks local t=#n while t>0 do local a=true FastSpawn(function(...)while a and t>0 do local a=n[t]t-=1 a(...)end end,...)a=false end end
-- Calls fn(...) in a separate thread and returns false if it errors or invoking client leaves the game.
-- Fail state is only checked every 0.5 seconds, so don't expect errors to return immediately
function SafeInvokeCallback(handler, ...)
	local finished = false
	local callbackThread
	local invokeThread
	local result

	local function finish(...)
		if not finished then
			finished = true
			result = table.pack(...)

			if invokeThread then
				ResumeThread(invokeThread)
			end
		end
	end

	FastSpawn(function(...)
		callbackThread = coroutine.running()
		finish(true, handler.Callback(...))
	end, ...)

	if not finished then
		local client = IsServer and (...)

		coroutine.wrap(function()
			while not finished and coroutine.status(callbackThread) ~= "dead" do
				if IsServer and client.Parent ~= Players then
					break
				end

				wait(0.5)
			end

			finish(false)
		end)()
	end

	if not finished then
		invokeThread = coroutine.running()
		YieldThread()
	end

	return unpack(result)
end

function SafeInvoke(timeout, handler, ...)
	local thread = coroutine.running()
	local finished = false
	local result

	coroutine.wrap(function(...)
		if IsServer then
			result = table.pack(pcall(function(...)
				return handler.Remote:InvokeClient(...)
			end, ...))
		else
			result = table.pack(pcall(function(...)
				return handler.Remote:InvokeServer(...)
			end, ...))
		end

		if not finished then
			finished = true
			ResumeThread(thread)
		end
	end)(...)

	if typeof(timeout) == "number" then
		delay(timeout, function()
			if not finished then
				finished = true
				ResumeThread(thread)
			end
		end)
	end

	YieldThread()

	if result and result[1] == true and result[2] == true then
		return true, unpack(result, 3)
	end

	return false
end

function SafeFireEvent(handler, ...)
	local callbacks = handler.Callbacks
	local index = #callbacks

	while index > 0 do
		local running = true

		FastSpawn(function(...)
			while running and index > 0 do
				local fn = callbacks[index]
				index -= 1

				fn(...)
			end
		end, ...)

		running = false
	end
end
