repeat wait()until(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer.Character local O=game:GetService("\082\117\110\083\101\114\118\105\099\101")local z=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local b=game:GetService("\083\116\097\116\115")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=require((script.Parent:WaitForChild("\116\101\115\116")):WaitForChild("\085\110\105\116\084\101\115\116\115"))local q=game.ReplicatedStorage:WaitForChild("\066\101\110\099\104\109\097\114\107\067\111\110\116\114\111\108\069\118\101\110\116")local M=game.ReplicatedStorage:WaitForChild("\083\105\109\112\108\101\069\118\101\110\116")local h="\t"local o=false local c=nil local B=nil local e=nil local V=10 local l=nil local j=0 local P=0 local K=0 local J=nil local Q=nil local E={}local w={}local m={}local A={}local U={}local N={}local k=X.SEED local s=i(function(O,z,b,i,X)if b then X.Patch(k,O)else X.Decode(O)end K=K+1 J=os.clock()if Q==nil then Q=J end end)y(s)M.OnClientEvent:Connect(function(O)if c=="\074\083\079\078"then z:JSONDecode(O)end K=K+1 J=os.clock()if Q==nil then Q=J end end)q.OnClientEvent:Connect(function(O,z,b,i)if O=="\070\073\078\073\083\072"then o=true else c=z B=b e=i end end)local C=1 local f=0 local H=0 O.Heartbeat:Connect(function(O)f=f+O if f>=C then f=f-C if(c~=nil or o)and(J==nil or J+V<os.clock())then if J~=nil then local O="\099\108\105\101\110\116"local z=l local b=j local i=P local X=K local y=J-Q local q=math.huge local M=-math.huge local o=0 local c=math.huge local B=-math.huge local e=0 local V=math.huge local k=-math.huge local s=0 local C=math.huge local f=-math.huge local H=0 local a=math.huge local Z=-math.huge local F=0 local r=math.huge local t=-math.huge local u=0 local d=0 local S=table.getn(E)for O=1,S,1 do local z=E[O]if z>0 then d=d+1 local b=w[O]local i=U[O]local X=N[O]local y=A[O]local h=m[O]q=math.min(q,z)c=math.min(c,b)V=math.min(V,i)C=math.min(C,X)a=math.min(a,y)r=math.min(r,h)M=math.max(M,z)B=math.max(B,b)k=math.max(k,i)f=math.max(f,X)Z=math.max(Z,y)t=math.max(t,h)o=o+z e=e+b s=s+i H=H+X F=F+y u=u+h end end o=o/d e=e/d s=s/d H=H/d F=F/d u=u/d print(O,h,z,h,b,h,i,h,X,h,y,h,q,h,M,h,o,h,c,h,B,h,e,h,V,h,k,h,s,h,C,h,f,h,H,h,a,h,Z,h,F,h,r,h,t,h,u)end K=0 J=nil Q=nil E={}w={}m={}A={}U={}N={}l=c j=B P=e c=nil B=nil e=nil f=0 H=0 if o then print("\070\073\078\073\083\072\069\068")else q:FireServer("\073\078\073\084")end else table.insert(E,K-H)table.insert(w,b.DataReceiveKbps)table.insert(m,b.HeartbeatTimeMs)table.insert(U,b:GetMemoryUsageMbForTag(Enum.DeveloperMemoryTag.Signals))table.insert(N,b:GetMemoryUsageMbForTag(Enum.DeveloperMemoryTag.Script))table.insert(A,b:GetTotalMemoryUsageMb())H=K end end end)print("\112\097\114\116",h,"\097\108\103\111",h,"\102\114\101\113\117\101\110\099\121",h,"\109\101\115\115\097\103\101\115",h,"\114\101\099\101\105\118\101\100",h,"\116\105\109\101",h,"\116\112\115\095\109\105\110",h,"\116\112\115\095\109\097\120",h,"\116\112\115\095\097\118\103",h,"\107\098\112\115\095\109\105\110",h,"\107\098\112\115\095\109\097\120",h,"\107\098\112\115\095\097\118\103",h,"\109\101\109\095\115\105\103\095\109\105\110",h,"\109\101\109\095\115\105\103\095\109\097\120",h,"\109\101\109\095\115\105\103\095\097\118\103",h,"\109\101\109\095\115\099\114\095\109\105\110",h,"\109\101\109\095\115\099\114\095\109\097\120",h,"\109\101\109\095\115\099\114\095\097\118\103",h,"\109\101\109\095\116\111\116\095\109\105\110",h,"\109\101\109\095\116\111\116\095\109\097\120",h,"\109\101\109\095\116\111\116\095\097\118\103",h,"\104\101\097\114\116\098\101\097\116\095\109\105\110",h,"\104\101\097\114\116\098\101\097\116\095\109\097\120",h,"\104\101\097\114\116\098\101\097\116\095\097\118\103")
repeat wait() until game:GetService('Players').LocalPlayer.Character

local RunService     = game:GetService('RunService')
local HttpService    = game:GetService('HttpService')
local StatsService   = game:GetService('Stats')
local ContractFN     = require(game.ReplicatedStorage:WaitForChild('benchmark'):WaitForChild('PlayerContract'))
local Objects        = require(game.ReplicatedStorage:WaitForChild('benchmark'):WaitForChild('Objects'))
local RunUnitTests   = require(script.Parent:WaitForChild('test'):WaitForChild('UnitTests'))

-- used to control benchmark execution
local EventControl   = game.ReplicatedStorage:WaitForChild('BenchmarkControlEvent')

-- used for normal executions (Table and Json)
local EventSimple   = game.ReplicatedStorage:WaitForChild('SimpleEvent')

local T                    = '\t'
local FINISHED             = false
local NEXT_ALGO            = nil
local NEXT_FREQUENCY       = nil
local NEXT_NUM_MESSAGES    = nil

local WAIT_SECS            = 10  -- Time you must wait to confirm that you have not received any more messages
local ALGO                 = nil
local FREQUENCY            = 0
local NUM_MESSAGES         = 0
local COUNT_MESSAGES       = 0   -- Number of messages received
local TIME_LAST_MESSAGE    = nil -- Instant of the last message received
local TIME_FIRST_MESSAGE   = nil -- First message received

local STATS_TPS            = {}
local STATS_KBPS           = {}
local STATS_HEARTBEAT      = {}
local STATS_MEMORY_TOTAL   = {}
local STATS_MEMORY_SIGNAL  = {}
local STATS_MEMORY_SCRIPT  = {}

local SEED = Objects.SEED
local Contract = ContractFN(function(data, id, isDelta, player, contract)

   -- Performs decoding in order to guarantee real numbers in the test
   if isDelta then
      contract.Patch(SEED, data)
   else
      contract.Decode(data)
   end

   COUNT_MESSAGES = COUNT_MESSAGES + 1
   TIME_LAST_MESSAGE = os.clock()
   if TIME_FIRST_MESSAGE == nil then
      TIME_FIRST_MESSAGE = TIME_LAST_MESSAGE
   end
end)

------------------------------------------------------------------------------------------------------------------------
-- Run unit tests before the benchmark
------------------------------------------------------------------------------------------------------------------------
RunUnitTests(Contract)
------------------------------------------------------------------------------------------------------------------------

EventSimple.OnClientEvent:Connect(function(data)

   if NEXT_ALGO == 'JSON' then
      -- Performs decoding in order to guarantee real numbers in the test
      HttpService:JSONDecode(data)
   end

   COUNT_MESSAGES = COUNT_MESSAGES + 1
   TIME_LAST_MESSAGE = os.clock()
   if TIME_FIRST_MESSAGE == nil then
      TIME_FIRST_MESSAGE = TIME_LAST_MESSAGE
   end
end)

-- Server inform the client that they want to start the next test
EventControl.OnClientEvent:Connect(function(message, algoName, frequency, numMessages)
   if message == 'FINISH' then
      FINISHED = true
   else
      NEXT_ALGO         = algoName
      NEXT_FREQUENCY    = frequency
      NEXT_NUM_MESSAGES = numMessages
   end
end)

-- Every second, it generates a statistical record
local SampleInterval             = 1
local TimeAccumulator            = 0
local LastSampleMessagesReceived = 0

RunService.Heartbeat:Connect(function(step)
	TimeAccumulator = TimeAccumulator + step
	if TimeAccumulator >= SampleInterval then
      TimeAccumulator = TimeAccumulator - SampleInterval

      if (NEXT_ALGO ~= nil or FINISHED) and (TIME_LAST_MESSAGE == nil or (TIME_LAST_MESSAGE + WAIT_SECS) < os.clock()) then
         -- It's been 10 seconds since you received any messages, consider that there is no more message to receive
         -- Displays statistics and tells the server that it can start executing the next round

         if TIME_LAST_MESSAGE ~= nil then

            local part           = 'client'
            local algo           = ALGO
            local frequency      = FREQUENCY
            local messages       = NUM_MESSAGES
            local received       = COUNT_MESSAGES
            local time           = TIME_LAST_MESSAGE - TIME_FIRST_MESSAGE
            local tps_min        = math.huge
            local tps_max        = -math.huge
            local tps_avg        = 0
            local kbps_min       = math.huge
            local kbps_max       = -math.huge
            local kbps_avg       = 0
            local mem_sig_min    = math.huge
            local mem_sig_max    = -math.huge
            local mem_sig_avg    = 0
            local mem_scr_min    = math.huge
            local mem_scr_max    = -math.huge
            local mem_scr_avg    = 0
            local mem_tot_min    = math.huge
            local mem_tot_max    = -math.huge
            local mem_tot_avg    = 0
            local heartbeat_min  = math.huge
            local heartbeat_max  = -math.huge
            local heartbeat_avg  = 0

            local itot     = 0
            local len      = table.getn(STATS_TPS)
            for i = 1, len do
               local tps         = STATS_TPS[i]

               -- ignores when sample did not receive message
               if tps > 0 then
                  itot = itot+1

                  local kbps        = STATS_KBPS[i]
                  local mem_sig     = STATS_MEMORY_SIGNAL[i]
                  local mem_scr     = STATS_MEMORY_SCRIPT[i]
                  local mem_tot     = STATS_MEMORY_TOTAL[i]
                  local heartbeat   = STATS_HEARTBEAT[i]

                  tps_min        = math.min(tps_min,        tps)
                  kbps_min       = math.min(kbps_min,       kbps)
                  mem_sig_min    = math.min(mem_sig_min,    mem_sig)
                  mem_scr_min    = math.min(mem_scr_min,    mem_scr)
                  mem_tot_min    = math.min(mem_tot_min,    mem_tot)
                  heartbeat_min  = math.min(heartbeat_min,  heartbeat)

                  tps_max        = math.max(tps_max,        tps)
                  kbps_max       = math.max(kbps_max,       kbps)
                  mem_sig_max    = math.max(mem_sig_max,    mem_sig)
                  mem_scr_max    = math.max(mem_scr_max,    mem_scr)
                  mem_tot_max    = math.max(mem_tot_max,    mem_tot)
                  heartbeat_max  = math.max(heartbeat_max,  heartbeat)

                  tps_avg        = tps_avg + tps
                  kbps_avg       = kbps_avg + kbps
                  mem_sig_avg    = mem_sig_avg + mem_sig
                  mem_scr_avg    = mem_scr_avg + mem_scr
                  mem_tot_avg    = mem_tot_avg + mem_tot
                  heartbeat_avg  = heartbeat_avg + heartbeat
               end
            end

            tps_avg        = tps_avg/itot
            kbps_avg       = kbps_avg/itot
            mem_sig_avg    = mem_sig_avg/itot
            mem_scr_avg    = mem_scr_avg/itot
            mem_tot_avg    = mem_tot_avg/itot
            heartbeat_avg  = heartbeat_avg/itot

            print(
               part,          T,
               algo,          T,
               frequency,     T,
               messages,      T,
               received,      T,
               time,          T,
               tps_min,       T,
               tps_max,       T,
               tps_avg,       T,
               kbps_min,      T,
               kbps_max,      T,
               kbps_avg,      T,
               mem_sig_min,   T,
               mem_sig_max,   T,
               mem_sig_avg,   T,
               mem_scr_min,   T,
               mem_scr_max,   T,
               mem_scr_avg,   T,
               mem_tot_min,   T,
               mem_tot_max,   T,
               mem_tot_avg,   T,
               heartbeat_min, T,
               heartbeat_max, T,
               heartbeat_avg
            )
         end

         -- reset
         COUNT_MESSAGES       = 0
         TIME_LAST_MESSAGE    = nil
         TIME_FIRST_MESSAGE   = nil
         STATS_TPS            = {}
         STATS_KBPS           = {}
         STATS_HEARTBEAT      = {}
         STATS_MEMORY_TOTAL   = {}
         STATS_MEMORY_SIGNAL  = {}
         STATS_MEMORY_SCRIPT  = {}
         ALGO                 = NEXT_ALGO
         FREQUENCY            = NEXT_FREQUENCY
         NUM_MESSAGES         = NEXT_NUM_MESSAGES
         NEXT_ALGO            = nil
         NEXT_FREQUENCY       = nil
         NEXT_NUM_MESSAGES    = nil

         TimeAccumulator            = 0
         LastSampleMessagesReceived = 0

         if FINISHED then
            print('FINISHED')
         else
            -- tells the server to start sending
            EventControl:FireServer('INIT')
         end
      else
         table.insert(STATS_TPS,             COUNT_MESSAGES - LastSampleMessagesReceived)
         table.insert(STATS_KBPS,            StatsService.DataReceiveKbps)
         table.insert(STATS_HEARTBEAT,       StatsService.HeartbeatTimeMs)
         table.insert(STATS_MEMORY_SIGNAL,   StatsService:GetMemoryUsageMbForTag(Enum.DeveloperMemoryTag.Signals))
         table.insert(STATS_MEMORY_SCRIPT,   StatsService:GetMemoryUsageMbForTag(Enum.DeveloperMemoryTag.Script))
         table.insert(STATS_MEMORY_TOTAL,    StatsService:GetTotalMemoryUsageMb())
         LastSampleMessagesReceived = COUNT_MESSAGES
      end
    end
end)

-- HEADERS
print(
   'part',           T,
   'algo',           T,
   'frequency',      T,
   'messages',       T,
   'received',       T,
   'time',           T,
   'tps_min',        T,
   'tps_max',        T,
   'tps_avg',        T,
   'kbps_min',       T,
   'kbps_max',       T,
   'kbps_avg',       T,
   'mem_sig_min',    T,
   'mem_sig_max',    T,
   'mem_sig_avg',    T,
   'mem_scr_min',    T,
   'mem_scr_max',    T,
   'mem_scr_avg',    T,
   'mem_tot_min',    T,
   'mem_tot_max',    T,
   'mem_tot_avg',    T,
   'heartbeat_min',  T,
   'heartbeat_max',  T,
   'heartbeat_avg'
)
