local f,Y,l,J local A local Q=0 local N=game.JobId local n={}local i={CrossServerKey="\065\100\111\110\105\115\067\114\111\115\115\083\101\114\118\101\114\077\101\115\115\097\103\105\110\103";CrossServerCommands=n}function i.SendMessage(f,...)local Y={N;...}l:Queue("\067\114\111\115\115\083\101\114\118\101\114\077\101\115\115\097\103\101\081\117\101\117\101",function()Q=Q+1 if not A then A=os.time()end if Q>=150+60*#J.Players:GetPlayers()then repeat task.wait()until os.time()-A>60 end if os.time()-A>60 then A=os.time()Q=1 end J.MessagingService:PublishAsync(f.CrossServerKey,Y)l.Events.CrossServerMessageSent:Fire(Y)end)return true end function i.ProcessCrossServerMessage(Y,J)local A=J.Data if not A or type(A)~="\116\097\098\108\101"then error("\067\114\111\115\115\083\101\114\118\101\114\058 \073\110\118\097\108\105\100 \068\097\116\097 \084\121\112\101 "..type(A))end local Q=A[2]table.remove(A,2)f.Logging:AddLog("\083\099\114\105\112\116","\067\114\111\115\115-\083\101\114\118\101\114 \077\101\115\115\097\103\101 \114\101\099\101\105\118\101\100\058 "..tostring(A and A[2]or"\110\105\108 \100\097\116\097\091\050\093"))l.Events.CrossServerMessageReceived:Fire(J)if Y.CrossServerCommands[Q]then Y.CrossServerCommands[Q](unpack(A))end end function n.Ping(Y,A)l.Events.ServerPingReceived:Fire(Y,A)f.CrossServer:SendMessage("\080\111\110\103",{JobId=game.JobId;NumPlayers=#J.Players:GetPlayers()})end function n.Pong(f,Y)l.Events.ServerPingReplyReceived:Fire(f,Y)end local function u(...)f.CrossServer:CrossServerMessage(...)end return{Init=function(A,Q)f=A Y=Q l=f.Utilities J=f.Utilities.Services f.CrossServer=i f.CrossServer.SubscribedEvent=J.MessagingService:SubscribeAsync(i.CrossServerKey,u)end,AfterInit=function(f,Y) end}
--[[

	Description: Contains various variables & methods required for crossserver communication
	Author: Sceleratis
	Date: 12/05/2021

--]]

local Root, Package, Utilities, Service

local lastTick;
local counter = 0;
local ServerId = game.JobId;


--- Cross-server commands.
--- @class Server.CrossServer.Commands
--- @server
--- @tag Core
--- @tag Package: System.Core
--- @tag Cross-Server Commands
local CrossServerCommands = {}


--- Responsible for cross-server functionality.
--- @class Server.CrossServer
--- @server
--- @tag Core
--- @tag Package: System.Core
local CrossServer = {
	CrossServerKey = "AdonisCrossServerMessaging";
	CrossServerCommands = CrossServerCommands;
}


--- Send cross-server message
--- @method SendMessage
--- @within Server.CrossServer
--- @param ... any
function CrossServer.SendMessage(self, ...)
	local data = {ServerId, ...};

	Utilities:Queue("CrossServerMessageQueue", function()
		--// Rate limiting
		counter = counter+1;

		if not lastTick then lastTick = os.time() end
		if counter >= 150 + 60 * #Service.Players:GetPlayers()  then
			repeat task.wait() until os.time()-lastTick > 60;
		end

		if os.time()-lastTick > 60 then
			lastTick = os.time();
			counter = 1;
		end

		--// Publish
		Service.MessagingService:PublishAsync(self.CrossServerKey, data)
		Utilities.Events.CrossServerMessageSent:Fire(data)
	end)

	return true;
end


--- Handles cross-server messages
--- @method ProcessCrossServerMessage
--- @within Server.CrossServer
--- @param msg string -- Message
function CrossServer.ProcessCrossServerMessage(self, msg)
	local data = msg.Data;
	if not data or type(data) ~= "table" then error("CrossServer: Invalid Data Type ".. type(data)); end
	local command = data[2];

	table.remove(data, 2);

	Root.Logging:AddLog("Script", "Cross-Server Message received: ".. tostring(data and data[2] or "nil data[2]"));
	Utilities.Events.CrossServerMessageReceived:Fire(msg)

	if self.CrossServerCommands[command] then
		self.CrossServerCommands[command](unpack(data));
	end
end


--// Cross-Server Commands
--- Runs when a "Ping" command is received, announcing this server's presence to other servers.
--- @function Ping
--- @within Server.CrossServer.Commands
--- @tag Cross-Server Command
--- @param jobId string -- Origin server's JobID
--- @param data any -- Data sent by the origin server
function CrossServerCommands.Ping(jobId, data)
	Utilities.Events.ServerPingReceived:Fire(jobId, data)
	Root.CrossServer:SendMessage("Pong", {
		JobId = game.JobId,
		NumPlayers = #Service.Players:GetPlayers()
	})
end


--- Response to "Ping" from other servers
--- @function Pong
--- @within Server.CrossServer.Commands
--- @tag Cross-Server Command
--- @param jobId string -- Origin server JobID
--- @param data any -- Data sent by the origin server
function CrossServerCommands.Pong(jobId, data)
	Utilities.Events.ServerPingReplyReceived:Fire(jobId, data)
end


--// Events
local function CrossServerReceived(...)
	Root.CrossServer:CrossServerMessage(...)
end

return {
	Init = function(cRoot, cPackage)
		Root = cRoot
		Package = cPackage
		Utilities = Root.Utilities
		Service = Root.Utilities.Services

		Root.CrossServer = CrossServer

		Root.CrossServer.SubscribedEvent = Service.MessagingService:SubscribeAsync(CrossServer.CrossServerKey, CrossServerReceived)
	end;

	AfterInit = function(Root, Package)

	end;
}
