Hi guys! I have created the following code for hitreg systems in my game!
local entities = {}
local savedRewinds = {}
local index = 1
local Current_Step = 0
local Max_Savelength = 60
--function that inserts an entity into the array
local function AddNewEntity(e)
local data = {}
for _, i in pairs(e:GetChildren()) do
if i:IsA("BasePart") then
data[#data+1] = i
end
end
data.Hum = e:FindFirstChildOfClass("Humanoid")
entities[index] = data
index = index + 1
end
--function to locate data based on a given humanoid
local function getDataFromHumanoid(hum)
local humSet
local ind
for x, i in pairs(entities) do
if i.Hum == hum then
humSet = i
ind = x
break
end
end
if humSet then
return humSet, ind
else
return false
end
end
--function to retrieve data at given time
local function getEntityCopyAtTime(humanoid, timestamp)
--retrieve entity parts
local humSet, ind = getDataFromHumanoid(humanoid)
if not humSet then print("humanoid not found in data!") return false end
--retrieve chronologically closest data
local nearestData
local nearestDist = math.huge
for _, i in pairs(savedRewinds) do
local saveTime = i.timestamp
if not nearestData then nearestData = i continue end
local timeDist = math.abs(timestamp-saveTime)
if timeDist < nearestDist then
nearestData = i
nearestDist = timeDist
end
end
--get data specific to this humanoid
local humData
for x, i in pairs(nearestData) do
if x == "timestamp" then continue end
local saveHum = i.Hum
if saveHum == humanoid then
humData = i
end
end
if not humData then print("humanoid not found at timestamp!") return false end
--build copy and position it based on saved data
local entityCopy = {}
for x, i in pairs(humSet) do
if i:IsA("BasePart") then
local partCopy = i:Clone()
partCopy:ClearAllChildren()
entityCopy[x] = partCopy
end
end
for x, i in pairs(entityCopy) do
if x == "Hum" then continue end
i.Anchored = true
i.CFrame = humData[x]
end
return entityCopy
end
--insert all objects that already exists
for _, i in pairs(game.Workspace.Entities:GetChildren()) do
AddNewEntity(i)
end
--connect the childadded event to the addnewentity function
game.Workspace.Entities.ChildAdded:Connect(AddNewEntity)
--update function
game:GetService("RunService").Heartbeat:Connect(function()
--new entry
local data = {}
data.timestamp = tick()
--increment step
Current_Step = (Current_Step+1)%Max_Savelength
--build entry
for x, entity in pairs(entities) do
local entityEntry = {}
for v = 1, #entity do -- -1
local part = entity[v]
if part then
entityEntry[v] = part.CFrame
end
end
entityEntry.Hum = entity.Hum
data[x] = entityEntry
end
--insert entry
savedRewinds[Current_Step] = data
--update server time
game.ReplicatedStorage.ServerTime.Value = tick()
end)
The program registers each entity in the game, saving in an array all of that entity’s pieces and the entity’s humanoid. Then using the same indices in which the pieces were registered, it saves the CFrame of each piece in an array. Later on, the function getEntityCopyAtTime() can accept a humanoid and a timestamp as parameters, and will look for the dataset containing that humanoid that is chronologically closest to the time provided. the code is configured to save CFrames up to 60 instances, at which point the code will start overwriting the oldest instances in the array. assuming the average heartbeat is .05 seconds, this configuration will allow the game to look up to 3 seconds into the past to see where players were, allowing a maximum effective ping of 3000 for accurate hit registration.
In the gif below, I tested the code by having it place a copy of my character in the world from 2 seconds prior every time I press the L key.
current pros and cons of this code:
pros:
- allows for up to 3 seconds of rewind vision for the server
- accurate up to approximately .05 seconds of error margin
cons:
- data heavy. ~16 players per server * 16 pieces per player character * 60 time instances saved = roughly 15,360 CFrame values saved to memory at any given moment, or 983,040 bytes, which is equal to almost a megabyte. (okay, maybe not actually that bad)
- lossy. Every time this data needs to be referenced for hit registration, the code duplicates the character model and positions it on the indicated CFrames. this duplicated model is not cached, as the many differences in character piece colliders and piece sizes would make standardized caching difficult.
if you guys have any recommendations to improve upon this, I’d love to hear them!