Hello guys, as you can tell from the title I am struggling with implementing rollback netcode in a roblox environment. Here is the code I have right now:
local UIS = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local InputRelay = game.ReplicatedStorage.InputRelay
local Player = game.Players.LocalPlayer
local Character: Model = Player.Character or Player.CharacterAdded:Wait()
local HumanoidRootPart: Part = Character:WaitForChild('HumanoidRootPart')
local inputStorage = {}
local currentLocalInput = "Idle"
local OppositePlayerInput
local OppositePlayerFrame
local currentFrame = 0
local MAX_ENTRIES = 7
local function getOppositePlayer(player: Player)
local oppositePlayer: Player
for _, players: Player in pairs(game:GetService("Players"):GetPlayers()) do
if players.Name ~= player.Name then
oppositePlayer = players
end
end
return oppositePlayer
end
local function SimulateLocalMovement(Name: string)
local inputFunctionTable = {
['W'] = function()
HumanoidRootPart.CFrame = HumanoidRootPart.CFrame + HumanoidRootPart.CFrame.LookVector*5
end,
['S'] = function ()
HumanoidRootPart.CFrame = HumanoidRootPart.CFrame - HumanoidRootPart.CFrame.LookVector*5
end
}
if inputFunctionTable[Name] then
inputFunctionTable[Name]()
end
end
local function SimulateNewState(correctedInput: string)
local currentCFrame = getOppositePlayer(Player).Character:WaitForChild('HumanoidRootPart').CFrame
local calculateTable = {
["W"] = function ()
local newCFrame = currentCFrame + currentCFrame.LookVector*5
return newCFrame
end,
["S"] = function()
local newCFrame = currentCFrame - currentCFrame.LookVector*5
return newCFrame
end,
["Idle"] = function()
return currentFrame
end,
}
return calculateTable[correctedInput]
end
local function addEntry(specTable, entry)
table.insert(specTable, entry)
-- Check if the array exceeds the maximum limit
if #specTable > MAX_ENTRIES then
-- Remove the oldest entry (first entry in the array)
table.remove(specTable, 1)
end
end
UIS.InputBegan:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
currentLocalInput = input.KeyCode.Name
SimulateLocalMovement(currentLocalInput)
end
end)
UIS.InputEnded:Connect(function(input: InputObject, gameProcessedEvent: boolean)
if gameProcessedEvent then return end
if input.UserInputType == Enum.UserInputType.Keyboard then
currentLocalInput = "Idle"
end
end)
InputRelay.OnClientEvent:Connect(function(InputName: string, oppositeFrame: number)
addEntry(inputStorage, {oppositeFrame, InputName})
for _, v in pairs(inputStorage) do
if v[1] == oppositeFrame and v[2] ~= InputName then
print(`Rollback! {v[2]} at {v[1]} does not equal {InputName} at {oppositeFrame}`)
v[2] = InputName -- Correct the wrongly predicted statement
-- Simulate the player's state (position as of now)
-- Take the player's current CFrame and calculate their new CFrame using the newly corrected input
local simulatedCFrame = SimulateNewState(v[2])
-- Render
-- Set the player's CFrame with the newly calculated CFrame
getOppositePlayer(Player).Character:WaitForChild('HumanoidRootPart').CFrame = simulatedCFrame
end
end
end)
RunService.RenderStepped:Connect(function(deltaTime: number)
currentFrame += 1
InputRelay:FireServer(currentLocalInput, currentFrame)
-- Predict other player's input based off of their previous inputs, if there are no current inputs, start at "Idle" on frame 1.
if #inputStorage < 1 then
addEntry(inputStorage, {currentFrame, "Idle"})
else
addEntry(inputStorage, {currentFrame, inputStorage[#inputStorage][2]})
end
end)
Here is what the current game looks like:
Any help would be amazing. Please and thank you.