#How can I incorporate lag compensation into my game?

1 messages · Page 1 of 1 (latest)

vapid mist
#

I'm working on a soccer game and one of the big issues is that low ms players will always have the advantage because they are able to send remote events faster than other players. If there are two players that ended up touching the ball within a short time-span, the low ms player will always get the ball first.

#

Isuee happens at 0:10

digital swift
vapid mist
# digital swift specifically what are you sending with the remote event?
ReplicatedStorage.Events.Remotes.React.OnServerEvent:Connect(function(
    player: Player, 
    ball: BasePart,
    touchedAt: CFrame,
    touchingPart: BasePart,
    reactDeclineCooldown: number, 
    timestamp: number
)
    if not validateReact(player, ball, touchedAt, touchingPart, reactDeclineCooldown, timestamp) then
        warn(`{player.Name} react declined`)
        return
    end
    
    reactDeclineCooldown = math.clamp(reactDeclineCooldown, 0.1, 0.75)

    local playerToReact = player
    local finalCFrame = touchedAt
    
    if not reactRequests[player] then
        reactRequests[player] = {timestamp = timestamp, touchedAt = touchedAt}
    end
    
    task.wait(0.2)

    if getLengthOfDictionary(reactRequests) > 1 then
        local earliestTime = math.huge
        local earliestPlayer = nil

        for player, reactData in reactRequests do
            if reactData.timestamp < earliestTime then
                earliestTime = reactData.timestamp
                finalCFrame = reactData.touchedAt
                earliestPlayer = player
            end
        end
        
        playerToReact = earliestPlayer
    end

    if ball:GetAttribute("NetworkOwner") ~= playerToReact.UserId then
        if ball:GetAttribute("ReactDecline") then
            return
        end

        ball:SetAttribute("ReactDecline", true)
        task.delay(reactDeclineCooldown, function()
            ball:SetAttribute("ReactDecline", false)
        end)
        
        if ball.AssemblyLinearVelocity.Magnitude > 75 then
            if finalCFrame then
                ball.CFrame = CFrame.new(finalCFrame.Position)
            end
        end
    end

    table.clear(reactRequests)

    ball:SetNetworkOwner(playerToReact)
    ball:SetAttribute("NetworkOwner", playerToReact.UserId)
end)
#

so

#

I actually tried to implement my own lag compensation before this

#

using timestamps that the client sends

#

and the server waits a small amount and checks whoever sends it the earliest

#

didnt end up being a good system, was buggy and didnt work at times

digital swift
#

that way it would be laggy for everyone

#

instead of for some people

#

😁

dark ruin
digital swift
vapid mist
#

a lot of people told me to just use an invisible server ball and visualize it on all clients but the server is slow with physics

#

compared to just using network ownership

dark ruin
dark ruin
vapid mist
dark ruin
#

or if this doesnt effect those with <150 ping you could also leave it be 🤷‍♂️

vapid mist
#

but adding interpolation or whatever to that is something that's too advanced for me id only know the basics

dark ruin
vapid mist
#

@dark ruin

dark ruin
# vapid mist

yup this is what i suggested? the ballinair boolean will minimize the ball to one player at a time, that being who the server thinks was first.

vapid mist
#

and again how would I verify "who the server thinks was first."

#

thats what i failed to do with my timestamps

dark ruin
dark ruin
#

this should hypothetically limit the ball to one player

#

and ofcourse set the variable to false when the velocity is applied

vapid mist
#

should it be fired on every touch?

if ball:GetAttribute("NetworkOwner") ~= playerToReact.UserId then
        if ball:GetAttribute("ReactDecline") then
            return
        end

        ball:SetAttribute("ReactDecline", true)
        task.delay(reactDeclineCooldown, function()
            ball:SetAttribute("ReactDecline", false)
        end)
    end

thats basically what I was doing with my react decline attribute except it only done on your first ever touch with the ball where you didnt have network ownership with it

#

would it be good to

#

do this on EVERY touch?

#

never tried how it would affect gameplay so

dark ruin
dark ruin
vapid mist
#

theres still one more issue which is sort of related to this

#

lemme see if i can find a clip

dark ruin
vapid mist
#

its basically what i was talking about in the beginning with "lag compensation" but the video I sent was something different, basically if you're on 300 ms and you are receiving the ball mid air (in a close duel situation with other players) then you probably are almost never going to touch that ball first because you arent going to send the remote event signal to touch the ball as fast as say a 50 ms player would

#

even if you touched it first, if a 50 ms player was right behind you and they also touched the ball too they would still get it

dark ruin
vapid mist
#

its an annoying mechanic in my game and its become like a big thing

vapid mist
dark ruin
# vapid mist i guess so

this is more or less a roblox issue and not something a developer could fix, atleast i'd imagine so 🤷‍♂️

vapid mist
#

i was just told by someone that this could work

dark ruin
# vapid mist

this could definitely work but it could be annoying to those with high pings, especially on how much delay you add.

#

you need to compensate for the norm, not for everyone

vapid mist
dark ruin
#

say for instance i have 1000 ping, would it be fair to the norm that has low ping that they wait a full second just to shoot a ball?

vapid mist
#

i guess like even just enough delay so that a 150 ping player has a chance against a 50 ping player

#

but when I test

dark ruin
vapid mist
#

is there any way I can make the transition between the player touching and the delay better

#

because rn the ball just kinda freezes

#

or

#

stutters

#

id say

dark ruin
#

if thats not possible id possible recommend a animation of sorts to play?

vapid mist
dark ruin
#

that way shooting will look appealing

vapid mist
vapid mist
#

im just not sure if i implemented the idea correctly

#

im just adding a player to the reactRequest table and checkign it after .1 seconds ideally

dark ruin
vapid mist
#

im gonna make these changes and see how it goes

#

tysm for ur time

dark ruin
#
    reactDeclineCooldown = math.clamp(reactDeclineCooldown, 0.1, 0.75)

    local playerToReact = player
    local finalCFrame = touchedAt
    
    if not reactRequests[player] then
        reactRequests[player] = {timestamp = timestamp, touchedAt = touchedAt}
    end

this is fine

    if getLengthOfDictionary(reactRequests) > 1 then
        local earliestTime = math.huge
        local earliestPlayer = nil

        for player, reactData in reactRequests do
            if reactData.timestamp < earliestTime then
                earliestTime = reactData.timestamp
                finalCFrame = reactData.touchedAt
                earliestPlayer = player
            end
        end
        
        playerToReact = earliestPlayer
    end

this too but doesnt the event still fire for all players who touched it despite the earliest player? you should check if player == earliestplayer in else cases return end

#

running it multiple times is likely going to cause issues

dark ruin
dark ruin
#

OH WAIT

vapid mist
#

"this too but doesnt the event still fire for all players who touched it despite the earliest player? " i understand what you mean by this

dark ruin
#

sorry my bad

#

i read it wrong

#

its fine

vapid mist
#

oh

#

Ok