local O=game:GetService("\080\108\097\121\101\114\115")local z=game:GetService("\082\117\110\083\101\114\118\105\099\101")local b=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local i=require((game.ReplicatedStorage:WaitForChild("\098\101\110\099\104\109\097\114\107")):WaitForChild("\080\108\097\121\101\114\067\111\110\116\114\097\099\116"))local X=require((game.ReplicatedStorage:WaitForChild("\098\101\110\099\104\109\097\114\107")):WaitForChild("\079\098\106\101\099\116\115"))local y local q=X.SEED local M=X.GetList()local h=i(function() end)local o=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")o.Parent=game.ReplicatedStorage o.Name="\066\101\110\099\104\109\097\114\107\067\111\110\116\114\111\108\069\118\101\110\116"local c=Instance.new("\082\101\109\111\116\101\069\118\101\110\116")c.Parent=game.ReplicatedStorage c.Name="\083\105\109\112\108\101\069\118\101\110\116"local B={function(O)c:FireClient(y,O)end,function(O)c:FireClient(y,b:JSONEncode(O))end;function(O)h.Send(h.Encode(O),nil,y)end,function(O)h.Send(h.Diff(q,O),nil,y)end}local e={"\084\065\066\076\069","\074\083\079\078";"\067\079\078\084\082\065\067\084";"\067\079\078\084\082\065\067\084\095\068\069\076\084\065"}local V={512,1024,2048;4096}local l={1024,5000;25000}local j=true local P=false local K=1 local J=1 local Q=1 local E=1 local w=0 local m=0 local A=0 local U=nil local N=nil local function k()j=true if l[Q]==nil then Q=1 E=E+1 if V[E]==nil then E=1 K=K+1 if B[K]==nil then P=true end end end if not P then J=1 m=0 N=nil U=B[K]A=l[Q]w=(1000/V[E])/1000 Q=Q+1 o:FireClient(y,"\073\078\073\084",e[K],V[E],A)else o:FireClient(y,"\070\073\078\073\083\072")end end o.OnServerEvent:Connect(function(O,z)if z=="\073\078\073\084"then j=false end end)z.Stepped:Connect(function()if j or P then return end local O=os.clock()if N==nil then N=O end O=O-N if m==0 then m=O end while m<O do if J>A then k()break end U(M[J])J=J+1 m=m+w end end)O.PlayerAdded:Connect(function(O)y=O k()end)

local Players        = game:GetService("Players")
local RunService     = game:GetService('RunService')
local HttpService    = game:GetService('HttpService')
local ContractFN     = require(game.ReplicatedStorage:WaitForChild('benchmark'):WaitForChild('PlayerContract'))
local Objects        = require(game.ReplicatedStorage:WaitForChild('benchmark'):WaitForChild('Objects'))

local PLAYER
local SEED     = Objects.SEED
local OBJECTS  = Objects.GetList()
local Contract = ContractFN(function() end)

-- used to control benchmark execution
local EventControl   = Instance.new('RemoteEvent')
EventControl.Parent  = game.ReplicatedStorage
EventControl.Name    = 'BenchmarkControlEvent'

-- used for normal executions (Table and Json)
local EventSimple   = Instance.new('RemoteEvent')
EventSimple.Parent  = game.ReplicatedStorage
EventSimple.Name    = 'SimpleEvent'

-- message sending algorithms
local ALGOS = {
   -- Table
   function(data)
      EventSimple:FireClient(PLAYER, data)
   end,
   -- JSON
   function(data)
      EventSimple:FireClient(PLAYER, HttpService:JSONEncode(data))
   end,
   -- NetworkContract
   function(data)
      Contract.Send(Contract.Encode(data), nil, PLAYER)
   end,
   -- NetworkContract + Delta
   function(data)
      Contract.Send(Contract.Diff(SEED, data), nil, PLAYER)
   end
}

local ALGO_NAME         = { 'TABLE', 'JSON', 'CONTRACT', 'CONTRACT_DELTA' }
local SEND_FREQUENCY    = { 512, 1024, 2048, 4096 }   -- messages per second to be sent
local SEND_MESSAGES     = { 1024, 5000, 25000 }       -- total number of messages to be sent

local WAITING           = true
local FINISHED          = false
local CURR_ALGO         = 1
local CURR_OBJECT       = 1
local CURR_MESSAGES     = 1
local CURR_FREQUENCY    = 1

local FixDeltaTime      = 0   -- Ensures execution frequency
local TimeAccumulator   = 0   -- Ensures execution frequency
local NumMessages       = 0   -- number of objects that will be sent in this batch
local AlgoSend          = nil -- Algorithm currently being used for sending messages
local FIRST_UPDATE_TIME = nil

local function RunNext()
   WAITING = true

   if SEND_MESSAGES[CURR_MESSAGES] == nil then
      CURR_MESSAGES  = 1
      CURR_FREQUENCY = CURR_FREQUENCY + 1

      if SEND_FREQUENCY[CURR_FREQUENCY] == nil then
         CURR_FREQUENCY = 1
         CURR_ALGO      = CURR_ALGO + 1

         if ALGOS[CURR_ALGO] == nil then
            FINISHED = true
         end
      end
   end

   if not FINISHED then
      CURR_OBJECT       = 1
      TimeAccumulator   = 0
      FIRST_UPDATE_TIME = nil
      AlgoSend          = ALGOS[CURR_ALGO]
      NumMessages       = SEND_MESSAGES[CURR_MESSAGES]
      FixDeltaTime      = 1000/SEND_FREQUENCY[CURR_FREQUENCY]/1000
      CURR_MESSAGES     = CURR_MESSAGES + 1

      -- inform the player about which test will be performed now, wait for the client to inform when they can start 
      -- the next test
      EventControl:FireClient(PLAYER, 'INIT', ALGO_NAME[CURR_ALGO], SEND_FREQUENCY[CURR_FREQUENCY], NumMessages)
   else
      EventControl:FireClient(PLAYER, 'FINISH')
   end
end

-- When the player informs, starts sending messages
-- It is necessary to wait for the client because he has information about when the messages stop arriving
EventControl.OnServerEvent:Connect(function(player, message)
   if message == 'INIT' then
      WAITING = false
   end
end)

RunService.Stepped:Connect(function()
   if WAITING or FINISHED then
      return
   end

   local now = os.clock()
   if FIRST_UPDATE_TIME == nil then
      FIRST_UPDATE_TIME = now
   end

   now = now - FIRST_UPDATE_TIME

   if TimeAccumulator == 0 then
      TimeAccumulator = now
   end

   while TimeAccumulator < now do

      if CURR_OBJECT >  NumMessages then
         RunNext()
         break
      end

      AlgoSend(OBJECTS[CURR_OBJECT])
      CURR_OBJECT = CURR_OBJECT+1
      TimeAccumulator = TimeAccumulator + FixDeltaTime
   end
end)

-- waits for player to connect to start execution
Players.PlayerAdded:Connect(function(player)
   PLAYER = player

   RunNext()
end)
