Parcourir la source

Name the new wire literals and log every unmatched damage level

The union-arm, raw 64-bit, nullable-u32 and nested-custom type codes, the
type-32 volume body widths and the ten-bit damage scale are named
constants. A damage level that finds no mission is logged at debug every
time instead of the first eight times in the process.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stan il y a 4 jours
Parent
commit
5eff8d014e

+ 18 - 15
Sunrise/src/middleware/bap/activity_message/activity_scriptable_auth_fixed_body_codec.cpp

@@ -455,23 +455,22 @@ bool encode_type24(const Type24Body& body,
     return true;
 }
 
-/**
- * Encodes one schema-0x8080955A body: registry key (32 bits), volume slot type + 1 (7), slot index
- * + 32768 (16), then state + 1 (2), where state 1 turns the volume on and 0 turns it off.
- */
+/** Encodes one schema-0x8080955A body: the volume's ClientRef, then its on or off state. */
 bool encode_type32_volume(const Type32VolumeBody& body,
                           std::span<std::byte> output,
                           std::size_t& written) noexcept {
     written = 0;
-    if (body.registryKey == 0 || body.registryKey == 0xFFFFFFFFU || body.slotIndex < 0
-        || output.size() < kType32ByteCount) {
+    if (body.registryKey == 0 || body.registryKey == (std::numeric_limits<std::uint32_t>::max)()
+        || body.slotIndex < 0 || output.size() < kType32ByteCount) {
         return false;
     }
     bits::Writer writer(output.first(kType32ByteCount));
-    return writer.write(body.registryKey, 32) && writer.write(kVolumeSlotType + 1U, 7)
-           && writer.write(static_cast<std::uint32_t>(body.slotIndex) + kSigned16Bias, 16)
-           && writer.write(body.active ? 2U : 1U, 2) && writer.bit_count() == kType32BitCount
-           && writer.finish(written) && written == kType32ByteCount;
+    return auth_fields::write_client_ref(writer,
+                                         body.registryKey,
+                                         kVolumeSlotType,
+                                         static_cast<std::uint16_t>(body.slotIndex))
+           && writer.write(body.active ? kType32StateOn : kType32StateOff, kType32StateWidth)
+           && auth_fields::finish_exact(writer, kType32BitCount, kType32ByteCount, written);
 }
 
 /** Decodes one volume toggle; the no-op state, a non-volume target and nonzero padding fail. */
@@ -483,14 +482,18 @@ bool decode_type32_volume(std::span<const std::byte> input,
     }
     bits::Reader reader(input);
     std::uint64_t key = 0, type = 0, index = 0, state = 0;
-    if (!reader.read(32, key) || key == 0 || key == 0xFFFFFFFFU || !reader.read(7, type)
-        || type != kVolumeSlotType + 1U || !reader.read(16, index) || index < kSigned16Bias
-        || !reader.read(2, state) || (state != 1 && state != 2) || !finish_padding(reader)) {
+    if (!reader.read(auth_fields::kClientRefKeyWidth, key) || key == 0
+        || key == (std::numeric_limits<std::uint32_t>::max)()
+        || !reader.read(kClientRefTypeWidth, type)
+        || type != kVolumeSlotType + auth_fields::kClientRefTypeBias
+        || !reader.read(kClientRefIndexWidth, index) || index < kClientRefIndexBias
+        || !reader.read(kType32StateWidth, state)
+        || (state != kType32StateOff && state != kType32StateOn) || !finish_padding(reader)) {
         return false;
     }
     body = {static_cast<std::uint32_t>(key),
-            static_cast<std::int16_t>(index - kSigned16Bias),
-            state == 2};
+            static_cast<std::int16_t>(index - kClientRefIndexBias),
+            state == kType32StateOn};
     return true;
 }
 

+ 4 - 0
Sunrise/src/middleware/bap/activity_message/volume_toggle_auth.h

@@ -13,6 +13,10 @@ inline constexpr std::uint32_t kType32Schema = 0x8080955A;
 inline constexpr std::uint8_t kVolumeSlotType = 60;
 inline constexpr std::size_t kType32BitCount = 57;
 inline constexpr std::size_t kType32ByteCount = 8;
+/** The state follows the volume ClientRef as two bits of state plus one; zero is the no-op. */
+inline constexpr std::uint8_t kType32StateWidth = 2;
+inline constexpr std::uint32_t kType32StateOff = 1;
+inline constexpr std::uint32_t kType32StateOn = 2;
 /** One volume and an explicit on/off state; the no-op state -1 is not representable. */
 struct Type32VolumeBody final {
     std::uint32_t registryKey{};

+ 7 - 0
Sunrise/src/middleware/bap/activity_message/wire_schema/activity_wire_codec.h

@@ -21,6 +21,13 @@ namespace runtime {
 
 /** Row index meaning "no row"; the SDK never numbers a real row this high. */
 inline constexpr std::uint32_t kAbsentRuntimeRow = 0xFFFFFFFFU;
+/** Field type 1 walks one nested schema row. */
+inline constexpr std::uint8_t kNestedType = 1;
+/** Field type 34 carries its schema handle and reads through the selected-schema reader. */
+inline constexpr std::uint8_t kSelectedType = 34;
+/** Field types 12 and 35 are raw 64-bit words, read and written whole. */
+inline constexpr std::uint8_t kRaw64AliasType = 12;
+inline constexpr std::uint8_t kRaw64Type = 35;
 
 /** One runtime schema row supplied by the generated Activity SDK. */
 struct SchemaView final {

+ 2 - 2
Sunrise/src/middleware/bap/activity_message/wire_schema/activity_wire_codec_decode_runtime.h

@@ -788,10 +788,10 @@ private:
             }
             // The native union reader dispatches through the per-type table, where a nested
             // arm is a no-op and a type-34 arm is the ordinary selected-schema reader.
-            if (field.typeCode == 1) {
+            if (field.typeCode == runtime::kNestedType) {
                 return RuntimeWalkStatus::complete;
             }
-            if (field.typeCode == 34) {
+            if (field.typeCode == runtime::kSelectedType) {
                 return walk_selected(owner, field, occurrence, depth);
             }
             return walk_inline_type(owner, field, occurrence, memory, depth + 1);

+ 2 - 2
Sunrise/src/middleware/bap/activity_message/wire_schema/activity_wire_codec_encode_runtime.h

@@ -787,10 +787,10 @@ private:
             }
             // The native union reader dispatches through the per-type table, where a nested
             // arm is a no-op and a type-34 arm is the ordinary selected-schema reader.
-            if (field.typeCode == 1) {
+            if (field.typeCode == runtime::kNestedType) {
                 return RuntimeWalkStatus::complete;
             }
-            if (field.typeCode == 34) {
+            if (field.typeCode == runtime::kSelectedType) {
                 return write_selected(owner, field, occurrence, depth);
             }
             return write_inline_type(owner, field, occurrence, memory, depth + 1);

+ 2 - 2
Sunrise/src/middleware/bap/activity_message/wire_schema/activity_wire_codec_internal.h

@@ -114,9 +114,9 @@ public:
     }
 }
 
-/** Types 12 and 35 are raw 64-bit words, read and written whole. */
+/** @return True for a raw 64-bit field type. */
 [[nodiscard]] constexpr bool is_raw64(std::uint8_t typeCode) noexcept {
-    return typeCode == 12 || typeCode == 35;
+    return typeCode == runtime::kRaw64AliasType || typeCode == runtime::kRaw64Type;
 }
 
 /** @return A low bit mask of the given width. Width 0 masks everything away. */

+ 28 - 18
Sunrise/src/middleware/gameplay/external/composite_entity_codec.cpp

@@ -1,5 +1,6 @@
 #include <algorithm>
 #include <array>
+#include <cmath>
 #include <cstring>
 #include <limits>
 #include <memory>
@@ -18,6 +19,21 @@ namespace bits = middleware::encoding::bits;
 constexpr std::uint32_t kMaximumByteField = 0xFFU;
 /** A schema reference the mirror retains must fit one 32-bit tag. */
 constexpr std::uint64_t kMaximumSemanticTag = 0xFFFFFFFFULL;
+/** Field type 22 is the native nullable-u32 reader: one present bit, then 32 bits. */
+constexpr std::uint8_t kNullableU32Type = 22;
+/** Field type 21 reads through the nullable-u32 reader in every family. */
+constexpr std::uint8_t kNullableU32AliasType = 21;
+/** In mode one, field type 37 is a plain nested walk of kNestedCustomSchema. */
+constexpr std::uint8_t kNestedCustomType = 37;
+/** The type-37 schema: a 19-bit real and a 5-bit int8, 24 bits in all. */
+constexpr std::uint32_t kNestedCustomSchema = 0x808092FFU;
+/** The damage component replicates each level pool as its own walk of kDamagePoolSchema. */
+constexpr std::uint32_t kDamageComponent = 0x80804BEEU;
+/** One pool level; its single field is the only value it decodes. */
+constexpr std::uint32_t kDamagePoolSchema = 0x80804C5CU;
+/** Pool 0 is health and pool 1 the shield. */
+constexpr std::uint8_t kHealthPool = 0;
+constexpr std::uint8_t kShieldPool = 1;
 
 /** Resolves one schema only when it supports the selected codec family. */
 [[nodiscard]] bool
@@ -85,9 +101,8 @@ read_schema(const void* raw, std::uint32_t rowIndex, wire::runtime::SchemaView&
     if (family != format::RuntimeCodecFamily::sobjectModeZero && type == 30) {
         return 18;
     }
-    // Type 21 uses the native nullable-u32 reader in every family: one present bit, then 32.
-    if (type == 21) {
-        return 22;
+    if (type == kNullableU32AliasType) {
+        return kNullableU32Type;
     }
     if (family == format::RuntimeCodecFamily::sobjectModeOne) {
         // Types 18, 20, 30 and 31 share one native mode-one reader.
@@ -106,15 +121,13 @@ read_schema(const void* raw, std::uint32_t rowIndex, wire::runtime::SchemaView&
                                       format::RuntimeCodecFamily family,
                                       wire::runtime::FieldView& output) noexcept {
     output.typeCode = reference_alias(family, output.typeCode);
-    // In mode one, a type-37 custom field is a plain nested walk of one fixed schema: a 19-bit real
-    // and a 5-bit int8, 24 bits in all.
-    constexpr std::uint32_t kNestedCustomSchema = 0x808092FFU;
-    if (family == format::RuntimeCodecFamily::sobjectModeOne && output.typeCode == 37) {
+    if (family == format::RuntimeCodecFamily::sobjectModeOne
+        && output.typeCode == kNestedCustomType) {
         wire::runtime::SchemaView nested{};
         if (!find_schema(raw, kNestedCustomSchema, nested)) {
             return false;
         }
-        output.typeCode = 1;
+        output.typeCode = wire::runtime::kNestedType;
         output.nestedSchemaRow = nested.row;
         // Its fields carry no presence bits, so it needs no slot in the component bitmap.
         output.hasBitmapOffset = true;
@@ -549,10 +562,6 @@ runtime_schema(const CompositeEntityCodecContext& context, std::uint32_t handle)
         }
         *semanticTag = tag;
     }
-    // The damage component sends each level pool as its own ten-bit walk; pool 0 is health.
-    constexpr std::uint32_t kDamageComponent = 0x80804BEEU;
-    // Schema 80804C5C is one pool level; its single field is the only value it decodes.
-    constexpr std::uint32_t kDamagePoolSchema = 0x80804C5CU;
     if (resolverContext.componentTag == kDamageComponent) {
         for (const auto& value : std::span(values).first(result.valueCount)) {
             if (value.schemaHandle != kDamagePoolSchema || !value.present) {
@@ -563,16 +572,17 @@ runtime_schema(const CompositeEntityCodecContext& context, std::uint32_t handle)
                                  : value.kind == wire::ValueKind::signedInteger
                                      ? static_cast<double>(value.signedValue)
                                      : static_cast<double>(value.unsignedValue);
-            if (!(level >= 0.0 && level <= 1023.0)) {
+            if (!(level >= 0.0 && level <= static_cast<double>(kDamageLevelFull))) {
                 continue;
             }
-            if (mirror.damagePools == 0) {
-                mirror.damageHealth = static_cast<std::uint16_t>(level + 0.5);
+            const auto rounded = static_cast<std::uint16_t>(std::lround(level));
+            if (mirror.damagePools == kHealthPool) {
+                mirror.damageHealth = rounded;
                 mirror.damageKnown = true;
-            } else if (mirror.damagePools == 1) {
-                mirror.damageShield = static_cast<std::uint16_t>(level + 0.5);
+            } else if (mirror.damagePools == kShieldPool) {
+                mirror.damageShield = rounded;
             }
-            if (mirror.damagePools != 0xFF) {
+            if (mirror.damagePools != (std::numeric_limits<std::uint8_t>::max)()) {
                 ++mirror.damagePools;
             }
         }

+ 2 - 0
Sunrise/src/middleware/gameplay/external/external_entity_codec.h

@@ -27,6 +27,8 @@ inline constexpr std::uint16_t kNoEntityCell = 0xFFFF;
 inline constexpr std::size_t kMaximumTypePayloadBits = 16'384;
 /** Callback state can retain one complete bounded body plus its typed mirror header. */
 inline constexpr std::size_t kTypePayloadStateCapacity = kMaximumTypePayloadBits / 8U + 16U;
+/** A damage pool replicates a ten-bit level, so 1023 is full. */
+inline constexpr std::uint16_t kDamageLevelFull = 1023;
 
 /** Record flags transported by the five explicit envelope bits. */
 enum EntityRecordFlag : std::uint16_t {

+ 12 - 14
Sunrise/src/server/activity/mission/mission_script_runtime.cpp

@@ -18,6 +18,7 @@
 
 #include "../../../core/logging/log.h"
 #include "../../../core/settings/settings.h"
+#include "../../../middleware/gameplay/external/external_entity_codec.h"
 #include "../../../state/activity/mission/runtime.h"
 #include "../../../state/activity/runtime.h"
 #include "../../gameplay/squad_entity_retirement.h"
@@ -261,8 +262,8 @@ void report_squad_provoked(const state::activity::SessionBinding& binding,
  * @param registryKey Authored registry key.
  * @param slotType Authored slot type: the squad's, or the combatant's own.
  * @param slotIndex Authored slot index.
- * @param health Pool 0 of the damage component, 0..1023.
- * @param shield Pool 1 of the damage component, 0..1023.
+ * @param health Pool 0 of the damage component, 0 to kDamageLevelFull.
+ * @param shield Pool 1 of the damage component, 0 to kDamageLevelFull.
  */
 void report_squad_damage(const state::activity::SessionBinding& binding,
                          std::uint64_t sourceGeneration,
@@ -271,7 +272,8 @@ void report_squad_damage(const state::activity::SessionBinding& binding,
                          std::uint16_t slotIndex,
                          std::uint16_t health,
                          std::uint16_t shield) noexcept {
-    if (sourceGeneration == 0 || registryKey == 0 || health > 1023 || shield > 1023
+    constexpr std::uint16_t full = middleware::gameplay::external::kDamageLevelFull;
+    if (sourceGeneration == 0 || registryKey == 0 || health > full || shield > full
         || slotIndex > static_cast<std::uint16_t>((std::numeric_limits<std::int16_t>::max)())) {
         return;
     }
@@ -282,19 +284,15 @@ void report_squad_damage(const state::activity::SessionBinding& binding,
                            registryKey,
                            slotType,
                            slotIndex,
-                           static_cast<float>(health) / 1023.0F,
-                           static_cast<float>(shield) / 1023.0F,
+                           static_cast<float>(health) / static_cast<float>(full),
+                           static_cast<float>(shield) / static_cast<float>(full),
                            GetTickCount64());
     } else {
-        // Log the first few reports that find no mission; nothing else records them.
-        static unsigned refused = 0;
-        if (refused < 8) {
-            ++refused;
-            log_line(core::log::Level::debug,
-                     instance,
-                     "combatant_damage",
-                     instance == nullptr ? "no_instance" : "stale_generation");
-        }
+        // Nothing else records a level that finds no mission.
+        log_line(core::log::Level::debug,
+                 instance,
+                 "combatant_damage",
+                 instance == nullptr ? "no_instance" : "stale_generation");
     }
     ReleaseSRWLockExclusive(&g_lock);
 }