Howdy!
I’ve been working on a system similar to Chickynoid in that it sends humanoid movement vectors, along with the current tick, validates the movement on the server, and then sends a message to the client acknowledging the movement request. Upon the client receiving the request, we calculate the current position on the server, and the position at which the client was when it sent the request. If there’s more than a 0.01 stud position error, or velocity, we will signal to the player to reconcile all requests after the current tick data.
From my understanding, on deterministic systems, it would be as easy as adding the inputs, re-simulating the position, and then tweening/pivoting the local player to their new simulated position, with the rollback data being adjusted as well. However, since I am using CharacterControllers, this system isn’t entirely easy to calculate where the character should be.
Here’s an example of the movement code, and the packet we send to the server:
local current_tick = workspace:GetServerTimeNow()
PlayerHandler.roll_back_data[current_tick] = {
position = character.PrimaryPart.Position,
velocity = character.PrimaryPart.AssemblyLinearVelocity,
walk_speed = character.Controller.BaseMoveSpeed,
movement_vector = character.Humanoid.MoveDirection,
}
character.Controller.MovingDirection = character.Humanoid.MoveDirection
packets.character.onCharacterMovement.send({
start_tick = current_tick;
move_direction = character.Humanoid.MoveDirection;
})
And here’s where we handle the server’s request to reconcile:
packets.character.serverComputeMovement.listen(function(data)
if not data.server_position or not data.start_tick or not PlayerHandler.character or not PlayerHandler.character.Controller or not PlayerHandler.character.Humanoid then return end
local find_rollback_data = PlayerHandler.roll_back_data[data.start_tick]
if find_rollback_data then
local calc_offset = find_rollback_data.position-data.server_position
if calc_offset.Magnitude >= 0.01 then -- 0.001
PlayerHandler.roll_back_data[data.start_tick] = nil
PlayerHandler.character:PivotTo(CFrame.new(data.server_position))
local last_tick = data.start_tick
local last_position = data.server_position
warn("reconcile")
for this_tick, rollback_data in pairs(PlayerHandler.roll_back_data) do
if this_tick >= last_tick then -- Ensure this_tick is not smaller than last_tick
-- TODO: Here is where we would recalculate all rollback datas' after the previous server communication
end
end
else
print("no reconcile needed")
end
PlayerHandler.character.Controller.MovingDirection = PlayerHandler.character.Humanoid.MoveDirection
print(PlayerHandler.roll_back_data)
end
end)
Any ideas would be helpful. I’ve tried calculating the new rollback position by using move_speed/time_held * move_vector, velocity prediction, etc. but nothing seemed to have been an efficient solution. Thanks in advance.