if IsServer then function HandlerFireClient(t,J,...)if LoggingNetwork then table.insert(LoggingNetwork[J][t.Remote].dataOut,GetParamString(...))end return t.Remote:FireClient(J,...)end function Network.GetPlayers(t)return Players:GetPlayers()end function Network.GetPlayerPosition(J,t)return t and(t.Character and(t.Character.PrimaryPart and t.Character.PrimaryPart.Position))or nil end function Network.FireClient(a,t,J,...)local f=GetEventHandler(J)if not f then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(J))end HandlerFireClient(f,t,...)end function Network.FireAllClients(J,t,...)local a=GetEventHandler(t)if not a then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(t))end for t,J in pairs(J:GetPlayers())do HandlerFireClient(a,J,...)end end function Network.FireOtherClients(a,t,J,...)local f=GetEventHandler(J)if not f then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(J))end for J,a in pairs(a:GetPlayers())do if a~=t then HandlerFireClient(f,a,...)end end end function Network.FireOtherClientsWithinDistance(f,t,J,a,...)local u=GetEventHandler(a)if not u then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(a))end local R=f:GetPlayerPosition(t)if not R then return end for a,v in pairs(f:GetPlayers())do if v~=t then local t=f:GetPlayerPosition(v)if t and(R-t).Magnitude<=J then HandlerFireClient(u,v,...)end end end end function Network.FireAllClientsWithinDistance(f,t,J,a,...)local u=GetEventHandler(a)if not u then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(a))end for a,R in pairs(f:GetPlayers())do local v=f:GetPlayerPosition(R)if v and(t-v).Magnitude<=J then HandlerFireClient(u,R,...)end end end function Network.InvokeClientWithTimeout(f,t,J,a,...)local u=GetEventHandler(a)if not u then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(a))end return SafeInvoke(t,u,J,...)end function Network.InvokeClient(t,...)return t:InvokeClientWithTimeout(60,...)end function Network.LogTraffic(t,...)FastSpawn(t.LogTrafficAsync,t,...)end function Network.LogTrafficAsync(a,t,J)J=J or warn if LoggingNetwork then return end J("\076\111\103\103\105\110\103 \078\101\116\119\111\114\107 \084\114\097\102\102\105\099\046\046\046")LoggingNetwork=setmetatable({},{__index=function(t,J)t[J]=setmetatable({},{__index=function(t,J)t[J]={dataIn={};dataOut={}}return t[J]end})return t[J]end})local f=os.clock()wait(t)local u=os.clock()-f local R=LoggingNetwork LoggingNetwork=nil for t,a in pairs(R)do local f=0 local R=0 for t,J in pairs(a)do f+=#J.dataIn R+=#J.dataOut end J(string.format("\080\108\097\121\101\114 \'\037\115\'\044 \116\111\116\097\108 \114\101\099\101\105\118\101\100\047\115\101\110\116\058 \037\100\047\037\100",t.Name,f,R))for t,a in pairs(a)do local f=a.dataIn if#f>0 then J(string.format("   \037\115 \037\115\058 \037\100 \040\037\046\050\102\047\115\041","\070\105\114\101\083\101\114\118\101\114",t.Name,#f,#f/u))local a=math.min(#f,3)for t=1,a,1 do local u=math.floor((1+((t-1)/math.max(1,a-1))*(#f-1))+.5)J(string.format("      \037\100\058 \037\115",u,f[u]))end end local R=a.dataOut if#R>0 then J(string.format("   \037\115 \037\115\058 \037\100 \040\037\046\050\102\047\115\041","\070\105\114\101\067\108\105\101\110\116",t.Name,#R,#R/u))local a=math.min(#R,3)for t=1,a,1 do local f=math.floor((1+((t-1)/math.max(1,a-1))*(#R-1))+.5)J(string.format("      \037\100\058 \037\115",f,R[f]))end end end end end else EventsFolder.ChildAdded:Connect(function(t)GetEventHandler(t.Name)end)for t,J in pairs(EventsFolder:GetChildren())do GetEventHandler(J.Name)end FunctionsFolder.ChildAdded:Connect(function(t)GetFunctionHandler(t.Name)end)for t,J in ipairs(FunctionsFolder:GetChildren())do GetFunctionHandler(J.Name)end function Network.FireServer(J,t,...)local a=GetEventHandler(t)if not a then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\069\118\101\110\116"):format(t))end if a.Remote then a.Remote:FireServer(...)else local t=table.pack(...)AddToQueue(a,function()a.Remote:FireServer(unpack(t))end,true)end end function Network.InvokeServerWithTimeout(a,t,J,...)local f=GetFunctionHandler(J)if not f then error(("\'\037\115\' \105\115 \110\111\116 \097 \118\097\108\105\100 \082\101\109\111\116\101\070\117\110\099\116\105\111\110"):format(J))end if not f.Remote then local t=coroutine.running()AddToQueue(f,function()ResumeThread(t)end,true)YieldThread()end local u=table.pack(SafeInvoke(t,f,...))assert(u[1]==true,"\073\110\118\111\107\101\083\101\114\118\101\114 \101\114\114\111\114")return unpack(u,2)end function Network.InvokeServer(J,t,...)return J:InvokeServerWithTimeout(nil,t,...)end end



if IsServer then
	function HandlerFireClient(handler, client, ...)
		if LoggingNetwork then
			table.insert(LoggingNetwork[client][handler.Remote].dataOut, GetParamString(...))
		end
		
		return handler.Remote:FireClient(client, ...)
	end
	
	--
	
	function Network:GetPlayers()
		return Players:GetPlayers()
	end

	function Network:GetPlayerPosition(player)
		return player and player.Character and player.Character.PrimaryPart and player.Character.PrimaryPart.Position or nil
	end
	
	--
	
	function Network:FireClient(client, name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		HandlerFireClient(handler, client, ...)
	end
	
	function Network:FireAllClients(name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		for i,v in pairs(self:GetPlayers()) do
			HandlerFireClient(handler, v, ...)
		end
	end
	
	function Network:FireOtherClients(client, name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		for i,v in pairs(self:GetPlayers()) do
			if v ~= client then
				HandlerFireClient(handler, v, ...)
			end
		end
	end
	
	function Network:FireOtherClientsWithinDistance(client, dist, name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		local pos = self:GetPlayerPosition(client)
		if not pos then
			return
		end
		
		for _,player in pairs(self:GetPlayers()) do
			if player ~= client then
				local otherPos = self:GetPlayerPosition(player)
				
				if otherPos and (pos - otherPos).Magnitude <= dist then
					HandlerFireClient(handler, player, ...)
				end
			end
		end
	end
	
	function Network:FireAllClientsWithinDistance(pos, dist, name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		for _,player in pairs(self:GetPlayers()) do
			local otherPos = self:GetPlayerPosition(player)
			
			if otherPos and (pos - otherPos).Magnitude <= dist then
				HandlerFireClient(handler, player, ...)
			end
		end
	end
	
	function Network:InvokeClientWithTimeout(timeout, client, name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		return SafeInvoke(timeout, handler, client, ...)
	end
	
	function Network:InvokeClient(...)
		return self:InvokeClientWithTimeout(60, ...)
	end
	
	
	function Network:LogTraffic(...)
		FastSpawn(self.LogTrafficAsync, self, ...)
	end
	
	function Network:LogTrafficAsync(duration, output)
		output = output or warn
		
		if LoggingNetwork then return end
		output("Logging Network Traffic...")
		
		LoggingNetwork = setmetatable({}, { __index = function(t, i)
			t[i] = setmetatable({}, { __index = function(t, i) t[i] = { dataIn = {}, dataOut = {} } return t[i] end })
			return t[i]
		end})
		
		local start = os.clock()
		wait(duration)
		local effDur = os.clock() - start
			
		local clientTraffic = LoggingNetwork
		LoggingNetwork = nil
		
		for player,remotes in pairs(clientTraffic) do
			local totalReceived = 0
			local totalSent = 0
			
			for remote,data in pairs(remotes) do
				totalReceived += #data.dataIn
				totalSent += #data.dataOut
			end
			
			output(string.format("Player '%s', total received/sent: %d/%d", player.Name, totalReceived, totalSent))
			
			for remote,data in pairs(remotes) do
				-- Incoming
				
				local list = data.dataIn
				if #list > 0 then
					output(string.format("   %s %s: %d (%.2f/s)", "FireServer", remote.Name, #list, #list / effDur))

					local count = math.min(#list, 3)
					for i = 1, count do
						local index = math.floor(1 + (i - 1) / math.max(1, count - 1) * (#list - 1) + 0.5)
						output(string.format("      %d: %s", index, list[index]))
					end
				end

				-- Outgoing
				
				local list = data.dataOut
				if #list > 0 then
					output(string.format("   %s %s: %d (%.2f/s)", "FireClient", remote.Name, #list, #list / effDur))
					
					local count = math.min(#list, 3)
					for i = 1, count do
						local index = math.floor(1 + (i - 1) / math.max(1, count - 1) * (#list - 1) + 0.5)
						output(string.format("      %d: %s", index, list[index]))
					end
				end
			end
		end
	end
else
	EventsFolder.ChildAdded:Connect(function(child) GetEventHandler(child.Name) end)
	for _,child in pairs(EventsFolder:GetChildren()) do GetEventHandler(child.Name) end
	
	FunctionsFolder.ChildAdded:Connect(function(child) GetFunctionHandler(child.Name) end)
	for _,child in ipairs(FunctionsFolder:GetChildren()) do GetFunctionHandler(child.Name) end
	
	--
	
	function Network:FireServer(name, ...)
		local handler = GetEventHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteEvent"):format(name))
		end
		
		if handler.Remote then
			handler.Remote:FireServer(...)
		else
			local params = table.pack(...)
			
			AddToQueue(handler, function()
				handler.Remote:FireServer(unpack(params))
			end, true)
		end
	end
	
	function Network:InvokeServerWithTimeout(timeout, name, ...)
		local handler = GetFunctionHandler(name)
		if not handler then
			error(("'%s' is not a valid RemoteFunction"):format(name))
		end
		
		if not handler.Remote then
			-- Code below will break if the callback passed to AddToQueue is called
			-- before the function returns. This should never happen unless somebody
			-- changed how AddToQueue works.
			local thread = coroutine.running()
			
			AddToQueue(handler, function()
				ResumeThread(thread)
			end, true)
			
			YieldThread()
		end

		local result = table.pack(SafeInvoke(timeout, handler, ...))
		assert(result[1] == true, "InvokeServer error")

		return unpack(result, 2)
	end
	
	function Network:InvokeServer(name, ...)
		return self:InvokeServerWithTimeout(nil, name, ...)
	end
end

