Hello dear community !
Reading about the issues caused by the asynchronous Z values returned by the different related native, I have found a workaround, with the feedback of @crunch.
The idea is really simple : the issue with the Z values is that the natives to get them can potentially return different values, depending on your operating system and your graphic settings, and other things like terrain deformations not affecting the height equally across clients.
Some clever people are even currently working on ways to synchronize the entire map's Z values, or even create a Height Map at compile time.
The Height Map would probably work perfectly, but is not that easy to implement for the normal map maker.
As for synchronizing the entire z values with SendSyncData native, in spite of the amazing work done by @ScrewTheTrees, it currently still takes too much time.
Trying to synchronize Z values on the fly is unpractical, because of the delay introduced, the Z values cannot be used immediately.
My method doesn't need anything complex or heavy at all.
It is based on the idea of Reasonable Precision Loss Compromise.
The principle is really basic : there is a difference between Z values returned by the natives.
Let's call this difference ZDelta.
If you reduce the Z values precision by ZDelta, you will remove all differences between Z values returned by the natives.
To make sure this was possible, I first made a map with messy and uneven terrain, with walkable doodads, various heights, various cliff heights, different water depths, etc...
Then I placed units around at map init.
I added a function triggered by a key event that displays all the units Z values given by BlzGetUnitZ.
For my test, I ran 2 instances of Reforged in LAN :
- one instance with the lowest graphic settings on SD
- one instance with the highest graphic settings on HD
Then I tested the map and compare the results for both clients.
My results showed that the highest ZDelta was just over 16.0.
Some might argue that it is a bit high, but if you synchronize Z values, you'll have the same problem.
So If we consider this value with a small extra margin, we end up with a maximum ZDelta of 20.0
I believe a precision of 20.0 on the Z axis is not too bad considering height values can go from 0.0 to 8,000.0. You could also try slightly smaller or bigger ZDelta to favor Precision or Safety.
Now let's use the simple maths :
All we need is reduce the Z natives precision to never be lower than 20.0.
The easiest way to do that is simply by dividing the Z value by 20.0, Round the result to the nearest integer, and multiply that integer by 20.0.
Here is an example function made in Lua :
You can apply this simple trick to any complex system using Z values, and you will have real Z values, with the only issue being a loss of precision.
You could also hook all Z related natives and keep your code identical in Jass, Lua and GUI or other scripting languages.
NOTE THAT THE LOSS OF PRECISION CANNOT HAVE AN IMPACT WORSE THAN SYNCHRONIZING THE Z VALUE OF ONE OF THE CLIENTS.
I hope this will help people with desync issues related to the Z natives...
EDIT :
IT SEEMS THIS WAS A FALSE HOPE :
The issue is that, because the Z async values are continuous, there is no way to predict what bound of the precision interval the approximated value will end up.
I am currently trying to identify a pattern that would allow to differentiate between the precision interval bounds.
Reading about the issues caused by the asynchronous Z values returned by the different related native, I have found a workaround, with the feedback of @crunch.
The idea is really simple : the issue with the Z values is that the natives to get them can potentially return different values, depending on your operating system and your graphic settings, and other things like terrain deformations not affecting the height equally across clients.
Some clever people are even currently working on ways to synchronize the entire map's Z values, or even create a Height Map at compile time.
The Height Map would probably work perfectly, but is not that easy to implement for the normal map maker.
As for synchronizing the entire z values with SendSyncData native, in spite of the amazing work done by @ScrewTheTrees, it currently still takes too much time.
Trying to synchronize Z values on the fly is unpractical, because of the delay introduced, the Z values cannot be used immediately.
My method doesn't need anything complex or heavy at all.
It is based on the idea of Reasonable Precision Loss Compromise.
The principle is really basic : there is a difference between Z values returned by the natives.
Let's call this difference ZDelta.
If you reduce the Z values precision by ZDelta, you will remove all differences between Z values returned by the natives.
To make sure this was possible, I first made a map with messy and uneven terrain, with walkable doodads, various heights, various cliff heights, different water depths, etc...
Then I placed units around at map init.
I added a function triggered by a key event that displays all the units Z values given by BlzGetUnitZ.
For my test, I ran 2 instances of Reforged in LAN :
- one instance with the lowest graphic settings on SD
- one instance with the highest graphic settings on HD
Then I tested the map and compare the results for both clients.
My results showed that the highest ZDelta was just over 16.0.
Some might argue that it is a bit high, but if you synchronize Z values, you'll have the same problem.
So If we consider this value with a small extra margin, we end up with a maximum ZDelta of 20.0
I believe a precision of 20.0 on the Z axis is not too bad considering height values can go from 0.0 to 8,000.0. You could also try slightly smaller or bigger ZDelta to favor Precision or Safety.
Now let's use the simple maths :
All we need is reduce the Z natives precision to never be lower than 20.0.
The easiest way to do that is simply by dividing the Z value by 20.0, Round the result to the nearest integer, and multiply that integer by 20.0.
Here is an example function made in Lua :
Lua:
function Round(r)
if (r<0) then return math.floor(r-0.5) else return math.floor(r+0.5) end
end
function GetApproximateZ( myUnit )
return (20.0 * ( Round( BlzGetUnitZ( myUnit ) / 20.0 ) ) )
end
You can apply this simple trick to any complex system using Z values, and you will have real Z values, with the only issue being a loss of precision.
You could also hook all Z related natives and keep your code identical in Jass, Lua and GUI or other scripting languages.
NOTE THAT THE LOSS OF PRECISION CANNOT HAVE AN IMPACT WORSE THAN SYNCHRONIZING THE Z VALUE OF ONE OF THE CLIENTS.
I hope this will help people with desync issues related to the Z natives...
EDIT :
IT SEEMS THIS WAS A FALSE HOPE :
The issue is that, because the Z async values are continuous, there is no way to predict what bound of the precision interval the approximated value will end up.
I am currently trying to identify a pattern that would allow to differentiate between the precision interval bounds.
Last edited: