forked from MagicBane/Server
Remove the rotation parity check
The boot run was clean: 187,824 building quaternion parts and 11,738 mob yaws matched exactly. The two NPC reports (14876, 16700) were a flaw in the replay, not the switch: their contract 2100 does not exist, so NPC.runAfterLoad returns before slotting, and old and new both send pi/2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bbf978872b
commit
740719aeb2
2 files changed
-219
No files matched your search
@@ -62,7 +62,6 @@ import engine.powers.RunePowerEntry;
|
||||
import engine.powers.RuneSkillAdjustEntry;
|
||||
import engine.server.MBServerStatics;
|
||||
import engine.util.MapLoader;
|
||||
import engine.util.RotationParityCheck;
|
||||
import engine.util.ThreadUtils;
|
||||
import engine.workthreads.DisconnectTrashTask;
|
||||
import engine.workthreads.HourlyJobThread;
|
||||
@@ -545,11 +544,6 @@ public class WorldServer {
|
||||
|
||||
Logger.info("time to load World Objects: " + (System.currentTimeMillis() - start) + " ms");
|
||||
|
||||
if (ParityCheck.isEnabled()) {
|
||||
Logger.info("Running rotation parity check");
|
||||
RotationParityCheck.run();
|
||||
}
|
||||
|
||||
// Configure realms for serialization
|
||||
// Doing this after the world is loaded
|
||||
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ .
|
||||
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
|
||||
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
|
||||
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
|
||||
// ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀
|
||||
// Magicbane Emulator Project © 2013 - 2026
|
||||
// https://www.magicbane.com
|
||||
|
||||
package engine.util;
|
||||
|
||||
import engine.gameManager.DbManager;
|
||||
import engine.math.FastMath;
|
||||
import engine.math.Quaternion;
|
||||
import engine.mbEnums.GameObjectType;
|
||||
import engine.mbEnums.MobBehaviourType;
|
||||
import engine.objects.*;
|
||||
import org.pmw.tinylog.Logger;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashMap;
|
||||
|
||||
// Dev-only, World-Boot-Plan 0.4: replays the pre-quaternion rotation code on the DB values and compares its wire
|
||||
// values with the loaded objects. Runs under MB_PARITY_CHECK; delete once a run is clean.
|
||||
|
||||
public class RotationParityCheck {
|
||||
|
||||
private static final float TOLERANCE = 1.0e-5f;
|
||||
private static final int MAX_LOGGED = 20;
|
||||
|
||||
private final String label;
|
||||
private int comparedCount;
|
||||
private int differenceCount;
|
||||
private float maxDifference;
|
||||
|
||||
private RotationParityCheck(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public static void run() {
|
||||
|
||||
HashMap<Integer, float[]> buildingRows;
|
||||
HashMap<Integer, Float> npcRows;
|
||||
|
||||
buildingRows = readBuildingRows();
|
||||
npcRows = readNpcRows();
|
||||
|
||||
checkBuildings(buildingRows);
|
||||
checkNPCs(npcRows);
|
||||
checkMobs();
|
||||
}
|
||||
|
||||
private static void checkBuildings(HashMap<Integer, float[]> buildingRows) {
|
||||
|
||||
RotationParityCheck check = new RotationParityCheck("Building");
|
||||
Building building;
|
||||
Quaternion rotation;
|
||||
float[] row;
|
||||
|
||||
for (AbstractGameObject object : DbManager.getList(GameObjectType.Building)) {
|
||||
building = (Building) object;
|
||||
row = buildingRows.get(building.getObjectUUID());
|
||||
|
||||
if (row == null)
|
||||
continue;
|
||||
|
||||
rotation = building.getRotation();
|
||||
|
||||
// The old serializer wrote (w, 0, rotY, 0).
|
||||
check.compare(building.getObjectUUID(), "w", row[0], rotation.w);
|
||||
check.compare(building.getObjectUUID(), "x", 0f, rotation.x);
|
||||
check.compare(building.getObjectUUID(), "y", row[1], rotation.y);
|
||||
check.compare(building.getObjectUUID(), "z", 0f, rotation.z);
|
||||
}
|
||||
|
||||
check.report();
|
||||
}
|
||||
|
||||
private static void checkNPCs(HashMap<Integer, Float> npcRows) {
|
||||
|
||||
RotationParityCheck check = new RotationParityCheck("NPC");
|
||||
NPC npc;
|
||||
Float halfYawCosine;
|
||||
|
||||
for (AbstractGameObject object : DbManager.getList(GameObjectType.NPC)) {
|
||||
npc = (NPC) object;
|
||||
halfYawCosine = npcRows.get(npc.getObjectUUID());
|
||||
|
||||
if (halfYawCosine == null)
|
||||
continue;
|
||||
|
||||
check.compare(npc.getObjectUUID(), "yaw", legacyYaw(npc, halfYawCosine, npc.building != null), npc.getClientYaw());
|
||||
}
|
||||
|
||||
check.report();
|
||||
}
|
||||
|
||||
private static void checkMobs() {
|
||||
|
||||
RotationParityCheck check = new RotationParityCheck("Mob");
|
||||
Mob mob;
|
||||
boolean slotted;
|
||||
|
||||
for (AbstractGameObject object : DbManager.getList(GameObjectType.Mob)) {
|
||||
mob = (Mob) object;
|
||||
slotted = mob.building != null && (mob.contract != null || mob.behaviourType == MobBehaviourType.SiegeEngine);
|
||||
|
||||
// Mob never loaded mob_rotation, so the old Vector3f default of 0 was its cos θ/2.
|
||||
check.compare(mob.getObjectUUID(), "yaw", legacyYaw(mob, 0f, slotted), mob.getClientYaw());
|
||||
}
|
||||
|
||||
check.report();
|
||||
}
|
||||
|
||||
// The old slot code, rot.y holder and serializer, from the commit before the quaternion switch.
|
||||
private static float legacyYaw(AbstractCharacter character, float halfYawCosine, boolean slotted) {
|
||||
|
||||
Quaternion buildingRotation = null;
|
||||
Quaternion slotRotation;
|
||||
float yaw;
|
||||
|
||||
if (character.building != null && character.building.getBounds() != null)
|
||||
buildingRotation = character.building.getBounds().getQuaternion();
|
||||
|
||||
if (slotted && buildingRotation != null) {
|
||||
slotRotation = new Quaternion().fromAngles(0, FastMath.acos(halfYawCosine) * 2, 0);
|
||||
slotRotation = slotRotation.mult(buildingRotation);
|
||||
halfYawCosine = slotRotation.y;
|
||||
}
|
||||
|
||||
yaw = (float) Math.acos(halfYawCosine) * 2;
|
||||
|
||||
if (buildingRotation != null)
|
||||
yaw += buildingRotation.angleY;
|
||||
|
||||
return yaw;
|
||||
}
|
||||
|
||||
private static HashMap<Integer, float[]> readBuildingRows() {
|
||||
|
||||
HashMap<Integer, float[]> rows = new HashMap<>();
|
||||
|
||||
try (Connection connection = DbManager.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT `UID`, `w`, `rotY` FROM `obj_building`;")) {
|
||||
|
||||
ResultSet rs = preparedStatement.executeQuery();
|
||||
|
||||
while (rs.next())
|
||||
rows.put(rs.getInt("UID"), new float[]{rs.getFloat("w"), rs.getFloat("rotY")});
|
||||
|
||||
} catch (SQLException e) {
|
||||
Logger.error(e);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static HashMap<Integer, Float> readNpcRows() {
|
||||
|
||||
HashMap<Integer, Float> rows = new HashMap<>();
|
||||
|
||||
try (Connection connection = DbManager.getConnection();
|
||||
PreparedStatement preparedStatement = connection.prepareStatement("SELECT `UID`, `npc_rotation` FROM `obj_npc`;")) {
|
||||
|
||||
ResultSet rs = preparedStatement.executeQuery();
|
||||
|
||||
while (rs.next())
|
||||
rows.put(rs.getInt("UID"), rs.getFloat("npc_rotation"));
|
||||
|
||||
} catch (SQLException e) {
|
||||
Logger.error(e);
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Angles are compared modulo 2π: the client only feeds them to MakeRotateY.
|
||||
private void compare(int id, String field, float legacyValue, float newValue) {
|
||||
|
||||
float difference;
|
||||
|
||||
comparedCount++;
|
||||
|
||||
if (Float.isNaN(legacyValue) && Float.isNaN(newValue))
|
||||
return;
|
||||
|
||||
difference = Math.abs(newValue - legacyValue);
|
||||
|
||||
if (field.equals("yaw"))
|
||||
difference = Math.abs((float) Math.IEEEremainder(newValue - legacyValue, FastMath.TWO_PI));
|
||||
|
||||
if (Float.isNaN(difference))
|
||||
difference = Float.POSITIVE_INFINITY;
|
||||
|
||||
maxDifference = Math.max(maxDifference, difference);
|
||||
|
||||
if (difference <= TOLERANCE)
|
||||
return;
|
||||
|
||||
differenceCount++;
|
||||
|
||||
if (differenceCount <= MAX_LOGGED)
|
||||
Logger.warn("Rotation parity: " + label + "/" + id + " " + field + ": old " + legacyValue + ", new " + newValue);
|
||||
}
|
||||
|
||||
private void report() {
|
||||
Logger.info("Rotation parity: " + label + ": " + comparedCount + " compared, " + differenceCount
|
||||
+ " differences over " + TOLERANCE + ", max difference " + maxDifference);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user