SmartlyDressedGames / Unturned-3.x-Community

Community portion of the Unturned-3.x repo. If you have access to the source code you can find it here:
https://github.com/SmartlyDressedGames/Unturned-3.x/
88 stars 18 forks source link

[IMPORTANT] Broken barricades rotation after game update #4719

Closed QERT2002 closed 1 month ago

QERT2002 commented 1 month ago

When you place barricade, on the serverside it has normal rotation. But after server send the barricade, uses SendSingleBarricade or maybe SendMultipleBarricades, client receive broken values. Logs: OV_dropNonPlantedBarricade angle_x:270 angle_y:261,8 angle_z:0 OV_ReceiveSingleBarricade angle_x:270,1282 angle_y:40,91203 angle_z:220,912

This 270,1282 / 270 different broke some mods, what use triggers on big coordinates, like trigger on 0,0,-1500 due to additional values, it will shift its position very much. It happends after you update the barricades to use Quaternion instead of angle_x angle_y angle_z. Please fix this as fast as you can, because many mods on my server broken after this update and so long time i can't find the reason.

SDGNelson commented 1 month ago

Is OV_* your modified version of those methods? If I understand correctly, It sounds like your plugins need updating to use quaternions.

QERT2002 commented 1 month ago

Is OV_* your modified version of those methods? If I understand correctly, It sounds like your plugins need updating to use quaternions.

no, its mean Override or Patch, your net methods loose Quaternion values i think

QERT2002 commented 1 month ago

try to place barricade serverside with Quaternion.Euler(270,0,0) after that, with "Ctrl", try to rotate it on some angles after see clientside barricade rotaton, instead of 270 15 30 maybe it be 270,012331 14,9647, 29,9128

QERT2002 commented 1 month ago

https://github.com/user-attachments/assets/ef32ef0d-2ab5-4da7-8280-d51689e73cce

when I release the button, a packet is sent and breaks the clientside coordinates, the server don't have any plugins

QERT2002 commented 1 month ago

It's broken only when you play on server, in singleplayer all works correctly. Based on this, I think that the problem is in the networking code, that serialize and deserialize Quaternion

GazziFX commented 1 month ago

After 24.4.0 there was a precision loss of replicated angles, and now if you place two barricades at the same angle they will be slightly misaligned and collision modhook wont activate event

DanielWillett commented 1 month ago

Could assets have a High_Precision_Placement option to use full precision for all values? Might be an easier way to fix it

SDGNelson commented 1 month ago

What do you mean by breaking the coordinates?

The reason the euler angles can be so different is that there are multiple ways to represent a given quaternion with euler angles. The newer quaternion replication uses the "smallest three" compression described here: https://gafferongames.com/post/snapshot_compression/ Which can result in a slightly different quaternion on each end.

QERT2002 commented 1 month ago

What do you mean by breaking the coordinates?

The reason the euler angles can be so different is that there are multiple ways to represent a given quaternion with euler angles. The newer quaternion replication uses the "smallest three" compression described here: https://gafferongames.com/post/snapshot_compression/ Which can result in a slightly different quaternion on each end.

Why use a quaternion if earlier using Euler angles everything was correct? If you wanted greater accuracy, then why are you killing it using compression methods?

I think you should remove compression methods or give an alternative, like Euler angles but with 100% accuracy. For example, if I rotate an object by 270 15 65 on the server, on the client its coordinates will exactly match, without tenths and other crap :/

SDGNelson commented 1 month ago

Previously, the euler angle replication had a resolution of 2 degrees per axis. In a lot of cases this was sufficient and was useful for snapping items together, but it was also an underlying cause of the common complaints about rotating barricades and structures with the admin editor snapping to weird angles.

For example, if a map object was rotated at 45 degrees then you could either rotate the barricade to 44 or 46 degrees, but not 45 exactly. With large barricades like hangar doors this was particularly noticeable. If we wanted 1 degree of resolution on each axis we'd need to be able to represent 359, so 9 bits per axis. (with 8 bits we'd have a resolution of 1.4 degrees which is arguably worse than 1 or 2)

With the new quaternion replication we did increase it to 9 bits per component (+2 for largest component index) but thanks to the quaternion compression we actually (should?) get better resolution than with we would've with the 9 bits per component euler angles! I'm not sure how to calculate how much better, though. There's also a special case for structures that replicates as euler angles yaw if it's not otherwise rotated with the admin editor.

What's your mod on the server doing with euler angles? I might be able to help suggest an alternative approach.

One thing that comes to mind is if you're overriding rotation with the transform requested event there's a big loss of precision:

Vector3 eulerAngles = rotation.eulerAngles;
byte preEventAngle_x = MeasurementTool.angleToByte(eulerAngles.x);
byte preEventAngle_y = MeasurementTool.angleToByte(eulerAngles.y);
byte preEventAngle_z = MeasurementTool.angleToByte(eulerAngles.z);
byte postEventAngle_x = preEventAngle_x;
byte postEventAngle_y = preEventAngle_y;
byte postEventAngle_z = preEventAngle_z;

bool shouldAllow = true;
BarricadeManager.onTransformRequested.Invoke(player.channel.owner.playerID.steamID, old_x, old_y, oldPlant, instanceID, ref point, ref postEventAngle_x, ref postEventAngle_y, ref postEventAngle_z, ref shouldAllow);

if (postEventAngle_x != preEventAngle_x || postEventAngle_y != preEventAngle_y || postEventAngle_z != preEventAngle_z)
{
    float angle_x = MeasurementTool.byteToAngle(postEventAngle_x);
    float angle_y = MeasurementTool.byteToAngle(postEventAngle_y);
    float angle_z = MeasurementTool.byteToAngle(postEventAngle_z);
    rotation = Quaternion.Euler(angle_x, angle_y, angle_z);
}
QERT2002 commented 1 month ago

Previously, the euler angle replication had a resolution of 2 degrees per axis. In a lot of cases this was sufficient and was useful for snapping items together, but it was also an underlying cause of the common complaints about rotating barricades and structures with the admin editor snapping to weird angles.

For example, if a map object was rotated at 45 degrees then you could either rotate the barricade to 44 or 46 degrees, but not 45 exactly. With large barricades like hangar doors this was particularly noticeable. If we wanted 1 degree of resolution on each axis we'd need to be able to represent 359, so 9 bits per axis. (with 8 bits we'd have a resolution of 1.4 degrees which is arguably worse than 1 or 2)

With the new quaternion replication we did increase it to 9 bits per component (+2 for largest component index) but thanks to the quaternion compression we actually (should?) get better resolution than with we would've with the 9 bits per component euler angles! I'm not sure how to calculate how much better, though. There's also a special case for structures that replicates as euler angles yaw if it's not otherwise rotated with the admin editor.

What's your mod on the server doing with euler angles? I might be able to help suggest an alternative approach.

One thing that comes to mind is if you're overriding rotation with the transform requested event there's a big loss of precision:

Vector3 eulerAngles = rotation.eulerAngles;
byte preEventAngle_x = MeasurementTool.angleToByte(eulerAngles.x);
byte preEventAngle_y = MeasurementTool.angleToByte(eulerAngles.y);
byte preEventAngle_z = MeasurementTool.angleToByte(eulerAngles.z);
byte postEventAngle_x = preEventAngle_x;
byte postEventAngle_y = preEventAngle_y;
byte postEventAngle_z = preEventAngle_z;

bool shouldAllow = true;
BarricadeManager.onTransformRequested.Invoke(player.channel.owner.playerID.steamID, old_x, old_y, oldPlant, instanceID, ref point, ref postEventAngle_x, ref postEventAngle_y, ref postEventAngle_z, ref shouldAllow);

if (postEventAngle_x != preEventAngle_x || postEventAngle_y != preEventAngle_y || postEventAngle_z != preEventAngle_z)
{
  float angle_x = MeasurementTool.byteToAngle(postEventAngle_x);
  float angle_y = MeasurementTool.byteToAngle(postEventAngle_y);
  float angle_z = MeasurementTool.byteToAngle(postEventAngle_z);
  rotation = Quaternion.Euler(angle_x, angle_y, angle_z);
}

Imagine a pillar 250 meters long, conventionally, with a rotation point on top of this pillar if we install this pillar exactly, its lower part will deviate a huge distance due to compression losses I want that when installing a given post, its bottom and top will have the same coordinates, without offset, but because of the extra tenths to the number for example 270.1568, its bottom will never be at the same coordinates image

SDGNelson commented 1 month ago

Interesting problem! If you don't mind me asking, what is the super tall pillar for? I wonder if we can do something similar to the structure yaw special case for barricades rotated around a single axis like that.

QERT2002 commented 1 month ago

Interesting problem! If you don't mind me asking, what is the super tall pillar for? I wonder if we can do something similar to the structure yaw special case for barricades rotated around a single axis like that.

Some modders use triggers on barricades. And in order to avoid accidental triggering, the triggers are positioned up or down relative to the barricade, in my case, the triggers on 2 barricades are located at -1500 coordinate in height, this is done so that when installed on an air base, people running from below do not activate the trigger.

The main problem with modders doing this is that the trigger can only react to the player tag, and players are quite agile creatures and are always trying to break something. I think something needs to be solved with the accuracy of barricade rotation, since it’s not very cool, and packages with barricades are sent only once when you load the region, I think it wouldn’t be a big problem to get rid of compression. And secondly, it would be possible to add a new custom tag that would help triggers work independently of the Player tag.

SDGNelson commented 1 month ago

Since your barricade is only rotated around the world up axis it doesn't seem like there should be any downside to using the yaw-only rotation special case, so I've changed this for the next update. Your barricade should be perfectly vertical after the update!