--[[
	READ ME
	

	-- Server API
	
	Network:BindFunctions(functions) 
	Network:BindEvents(events)
	
	Network:FireClient(client, name, ...)
	Network:FireAllClients(name, ...)
	Network:FireOtherClients(ignoreclient, name, ...)
	Network:FireOtherClientsWithinDistance(ignoreclient, distance, name, ...)
	Network:FireAllClientsWithinDistance(position, distance, name, ...)
	
	Network:InvokeClient(client, name, ...)  (same as below with timeout = 60)
	Network:InvokeClientWithTimeout(timeout, client, name, ...)
	
	Network:LogTraffic(duration)
	
	-- Internal overrideable methods, used for custom AllClients/OtherClients/WithinDistance selectors
	
	Network:GetPlayers()
	Network:GetPlayerPosition(player)
	
	-- Client API
	
	Network:BindFunctions(functions) 
	Network:BindEvents(events) 
	
	Network:FireServer(name, ...)
	
	Network:InvokeServer(name, ...) 
	Network:InvokeServerWithTimeout(timeout, name, ...)
	
	
	
	Notes:
	- The first return value of InvokeClient (but not InvokeServer) is bool success, which is false if the invocation timed out
	  or the handler errored.
	
	- InvokeServer will error if it times out or the handler errors

	- InvokeServer/InvokeClient do not return instantly on an error, but instead check for failure every 0.5 seconds. This is
	  because it is not possible to both instantly detect errors and have them be logged in the output with full stacktraces.
	
	
	
	For detailed API Use/Documentation, see
	https://devforum.roblox.com/t/easynetwork-creates-remotes-events-for-you-so-you-dont-have-to/
--]]



local Network = {}

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

local EventHandlers = {}
local FunctionHandlers = {}

local IsStudio = RunService:IsStudio()
local IsServer = RunService:IsServer()

local LoggingNetwork
local function GetParamString(...)
	local tab = table.pack(...)
	local n = math.min(10, tab.n)
	
	for index = 1, n do 
		local value = tab[index]
		local valueType = typeof(tab[index])
		
		if valueType == "string" then
			tab[index] = string.format("%q[%d]", #value <= 18 and value or value:sub(1, 15) .. "...", #value)
		elseif valueType == "Instance" then
			local success, className = pcall(function() return value.ClassName end)
			tab[index] = success and string.format("%s<%s>", valueType, className) or valueType
		else
			tab[index] = valueType
		end
	end
	
	return table.concat(tab, ", ", 1, n) .. (tab.n > n and string.format(", ... (%d more)", tab.n - n) or "")
end

local DeferredHandlers = {}

local ReceiveCounter = 0
local InvokeCounter = 0

local Communication,FunctionsFolder,EventsFolder

if IsServer then
	Communication = Instance.new("Folder",ReplicatedStorage)
	Communication.Name = "Communication"
	FunctionsFolder = Instance.new("Folder",Communication)
	FunctionsFolder.Name = "Functions"
	EventsFolder = Instance.new("Folder",Communication)
	EventsFolder.Name = "Events"
else
	Communication = ReplicatedStorage:WaitForChild("Communication")
	FunctionsFolder = Communication:WaitForChild("Functions")
	EventsFolder = Communication:WaitForChild("Events")
end