I finally narrowed down my desync to a fairly small bit of code. I can't figure out why this would be desyncing though, so I was hoping to get the eyes of some people smarter than me on it and see if anyone could spot something obvious I didn't see.
I'm 99% sure it's caused by the really simple projectile system I built. When I tighten the projectile loop, it happens faster. I can repro it 100% on LAN with 5 wc3 instances open. With the loop at 0.00390625 and me spamming the projectile roughly every 0.5 sec, it happens at around 30 sec nearly every time. Reducing the loop speed increases the amount of time it takes.
Here's the lua for the projectile module that causes a desync:
It's used like this:
The fact that tightening the loop causes the desync to occur faster is concerning to me. My instincts would guess that some of the instances aren't able to keep up and maintain the fast loop speed, and eventually fall behind and desync, but to my understanding that shouldn't be how wc3 works.
I've also uploaded a version of the map with a really simple repro. Just spam Q on red and it'll desync the other players in about 30 sec. At least for my machine it does.
I'm 99% sure it's caused by the really simple projectile system I built. When I tighten the projectile loop, it happens faster. I can repro it 100% on LAN with 5 wc3 instances open. With the loop at 0.00390625 and me spamming the projectile roughly every 0.5 sec, it happens at around 30 sec nearly every time. Reducing the loop speed increases the amount of time it takes.
Here's the lua for the projectile module that causes a desync:
Code:
local vector = require('src/vector.lua')
local projectiles = {}
local LOOP_SPEED = 0.00390625
local isCloseTo = function(val, expected, range)
return val + range >= expected and val - range <= expected
end
local clearProjectiles = function()
local elapsedTime = LOOP_SPEED
for idx, projectile in pairs(projectiles) do
local curProjectileX = GetUnitX(projectile.unit)
local curProjectileY = GetUnitY(projectile.unit)
if isCloseTo(curProjectileX, projectile.options.toV.x, 15) and
isCloseTo(curProjectileY, projectile.options.toV.y, 15)
then
-- Already at destination, can finish
projectile.toRemove = true
else
-- Linear projectile
local totalDistX = projectile.options.toV.x - curProjectileX
local totalDistY = projectile.options.toV.y - curProjectileY
local distVector = vector.create(totalDistX, totalDistY)
local v1 = vector.normalize(distVector)
v1 = vector.multiply(v1, projectile.options.speed * elapsedTime)
if vector.magnitude(v1) >= vector.magnitude(distVector) then
v1 = distVector
end
v1 = vector.add(
v1,
vector.create(curProjectileX, curProjectileY))
SetUnitPosition(projectile.unit, v1.x, v1.y)
end
end
for idx, projectile in pairs(projectiles) do
if projectile.toRemove then
projectiles[idx] = nil
end
end
end
local init = function()
local trig = CreateTrigger()
TriggerRegisterTimerEvent(trig, LOOP_SPEED, true)
TriggerAddAction(trig, clearProjectiles)
end
local createProjectile = function(options)
if options.length ~= nil then
local fromVector = vector.create(options.fromV.x, options.fromV.y)
local toVector = vector.create(options.toV.x, options.toV.y)
local totalVector = vector.subtract(toVector, fromVector)
totalVector = vector.normalize(totalVector)
totalVector = vector.multiply(totalVector, options.length)
totalVector = vector.add(fromVector, totalVector)
options.toV = totalVector
end
local proj = {
unit = options.projectile,
options = options,
alreadyCollided = {},
}
table.insert(projectiles, proj)
return proj
end
return {
init = init,
createProjectile = createProjectile,
}
It's used like this:
Code:
local hero = gg_unit_etst_0117
local heroV = vector.create(GetUnitX(hero), GetUnitY(hero))
local mouseV = vector.create(GetSpellTargetX(), GetSpellTargetY())
projectile.createProjectile{
playerId = playerId,
projectile = hero,
fromV = heroV,
toV = mouseV,
speed = 800,
length = 250,
radius = 75,
}
The fact that tightening the loop causes the desync to occur faster is concerning to me. My instincts would guess that some of the instances aren't able to keep up and maintain the fast loop speed, and eventually fall behind and desync, but to my understanding that shouldn't be how wc3 works.
I've also uploaded a version of the map with a really simple repro. Just spam Q on red and it'll desync the other players in about 30 sec. At least for my machine it does.