diff --git a/game/reconstructed/mech.cpp b/game/reconstructed/mech.cpp index 5a80b82..c683c34 100644 --- a/game/reconstructed/mech.cpp +++ b/game/reconstructed/mech.cpp @@ -1267,9 +1267,19 @@ Mech::Mech( Wword(0x15a) = (int)(model->lookRightAngle * DegreesToRadians); // +0x54 Wword(0x15b) = (int)(model->lookFrontAngle * DegreesToRadians); // +0x58 Wword(0x15c) = (int)(model->lookBackAngle * DegreesToRadians); // +0x5c - Wword(0x1dc) = (int)(model->fieldA0 * DegreesToRadians); - Wword(0x1db) = model->field9c; - Wword(0x1da) = model->field98; + // The update-record deadbands, read at the RAW record offsets the binary + // itself uses ([esi+0x98/0x9c/0xa0], disasm @0x4a26a7-0x4a26ef) -- the + // Mech__ModelResource struct fieldXX members are LAYOUT-SKEWED (audit + // task: struct field98 read -1.5 where raw word 0x26 holds 0.3). A + // resource record is a byte blob, so word-indexed reads are the correct + // idiom (not the object databinding trap). + updateTurnAngleDeadband = ((Scalar *)model)[0x28] * DegreesToRadians; // @0x770 <- UpdateTurnDegreeDiffrence + updateTurnVelocityDeadband = ((Scalar *)model)[0x27]; // @0x76c <- UpdateTurnVelocityDiffrence + updatePositionDeadband = ((Scalar *)model)[0x26]; // @0x768 <- UpdatePositionDiffrence + if (getenv("BT_REPL_LOG")) + DEBUG_STREAM << "[deadband] pos=" << updatePositionDeadband + << " turnVel=" << updateTurnVelocityDeadband + << " turnAngle=" << updateTurnAngleDeadband << " (rad)" << std::endl; Wword(0x10c) = model->fieldA4; // NOTE: the decomp writes word slots this[0x15d]/this[0x15e]. Routed to the // scratch bank (unnamed pose-angle fields) -- writing through (float*)(this + diff --git a/game/reconstructed/mech.hpp b/game/reconstructed/mech.hpp index b721ed6..b9f7cb2 100644 --- a/game/reconstructed/mech.hpp +++ b/game/reconstructed/mech.hpp @@ -581,6 +581,14 @@ public: Scalar fallScalar; // binary @0x4b4 -- same record family; ctor 0 [T4 name] int heatLevelSnapshot; // binary @0x780 -- heatAlarm level at frame // entry; the type-7 impact-record deadband + // The AUTHENTIC update-record deadbands (task #3): streamed by the ctor + // from the model record (disasm @0x4a26d7-0x4a26ef: 0x770 <- rec+0xa0 x + // pi/180 [const @0x4a2d44 = 0.01745329], 0x76c <- rec+0x9c raw, 0x768 <- + // rec+0x98 raw = UpdateTurnDegreeDiffrence / UpdateTurnVelocityDiffrence / + // UpdatePositionDiffrence). Consumed by the perf-loop senders. [T1] + Scalar updatePositionDeadband; // binary @0x768 -- type-0 pose trigger + Scalar updateTurnVelocityDeadband; // binary @0x76c -- type-4 yaw-rate trigger + Scalar updateTurnAngleDeadband; // binary @0x770 -- type-4 orientation trigger (rad) // AUTHENTIC GROUND MODEL ctor products (task #15, ground-model-decode; // binary part_012.c:9938-9940 + 9974-9975). By-name access only; declared // after the layout-locked fields so nothing shifts. diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 734c6b1..0dbb4b4 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -2540,23 +2540,54 @@ void angular_deviation.Subtract( projectedOrigin.angularPosition, localOrigin.angularPosition); - if ( - error.LengthSquared() > 0.04f - || lastPerformance - lastUpdate > 2.0f - ) + // Position deadband (binary @0x4aad35: |pos - lastSent|^2 vs the + // model's UpdatePositionDiffrence @0x768) + the 2s heartbeat. + // Guard against an unstreamed/zero deadband with the old stand-in. { - ForceUpdate(); // type 0: position deadband + heartbeat + Scalar posDb = (updatePositionDeadband > 0.0f) + ? updatePositionDeadband : 0.04f; + if ( + error.LengthSquared() > posDb + || lastPerformance - lastUpdate > 2.0f + ) + { + ForceUpdate(); // type 0: pose record + } } - // ORIENTATION rides ONLY the type-4 resync record now (the - // authentic case-0 writer/reader save-restore strips it from the - // pose record) -- request it on angular deviation or the turn - // never reaches the replicant. Binary deadbands @0x4aac2b/ - // @0x4aac6c diff the live vs last-replicated angular channels - // against model constants (+0x76c/+0x770, not yet streamed); - // the quat-deviation test stands in. [T3 threshold only] - if (Abs(angular_deviation.w) < 0.997f) + // ORIENTATION rides ONLY the type-4 resync record (the authentic + // case-0 writer/reader save-restore strips it from the pose + // record). Binary triggers (@0x4aac2b / @0x4aac6c): + // |localOrigin.angular.y - projectedOrigin.angular.y| > @0x770 + // |localVelocity.angular.y - projectedVelocity.angular.y| > @0x76c + // or (live yaw-rate == 0 && replicated yaw-rate != 0) + // -- the quaternion Y component and the yaw rate, against the + // model's UpdateTurnDegreeDiffrence (deg->rad) and + // UpdateTurnVelocityDiffrence. [T1 expressions; zero-deadband + // guard falls back to the old quat-w stand-in] { - ForceUpdate(1 << MechResyncUpdateModelBit); // type 4 + Scalar angDb = (updateTurnAngleDeadband > 0.0f) ? updateTurnAngleDeadband : -1.0f; + Scalar velDb = (updateTurnVelocityDeadband > 0.0f) ? updateTurnVelocityDeadband : -1.0f; + Logical resync = False; + if (angDb > 0.0f) + { + if (Abs(localOrigin.angularPosition.y + - projectedOrigin.angularPosition.y) > angDb) + resync = True; + if (Abs((Scalar)localVelocity.angularMotion.y + - (Scalar)projectedVelocity.angularMotion.y) > velDb) + resync = True; + if ((Scalar)localVelocity.angularMotion.y == 0.0f + && (Scalar)updateVelocity.angularMotion.y != 0.0f) + resync = True; + } + else if (Abs(angular_deviation.w) < 0.997f) + { + resync = True; // stand-in when unstreamed + } + if (resync) + { + ForceUpdate(1 << MechResyncUpdateModelBit); // type 4 + } } // Commanded-speed deadband (binary @0x4aac88): the mapper's live // speedDemand vs the last-replicated bodyTargetSpeed -> the tiny diff --git a/scratchpad/scan_deadband.py b/scratchpad/scan_deadband.py new file mode 100644 index 0000000..f91cdc3 --- /dev/null +++ b/scratchpad/scan_deadband.py @@ -0,0 +1,39 @@ +import capstone, struct, sys +sys.stdout.reconfigure(encoding="utf-8", errors="replace") +data = open("../content/BTL4OPT.EXE","rb").read() +# established mapping (from dis_4b2980.py usage): file offset = VA - 0x400000 - 0xc00 adjustments; +# use the section table: .text typically RVA 0x1000, raw 0x400/0x600. Recover from PE header. +pe = struct.unpack_from("