Recover AI communication start from Opus wave

This commit is contained in:
Jonas Volman committed 2026-09-02 16:30:47 -07:00
1 parent 1cc83929d2
commit 75bbaee4c4
4 files changed
+446 -7

No files matched your search

+2 -2
View File
@@ -572,10 +572,10 @@
{ "file_offset": 204416, "flags": 32, "name": "_code_00031e80" },
{ "file_offset": 204848, "flags": 32, "name": "_code_00032030" },
{ "file_offset": 205504, "flags": 32, "name": "_code_000322c0" },
{ "file_offset": 205552, "flags": 32, "name": "_code_000322f0" },
{ "file_offset": 205552, "flags": 32, "name": "_actor_reset_idle_vocalization_timer" },
{ "file_offset": 205760, "flags": 32, "name": "_actor_communication_update" },
{ "file_offset": 206000, "flags": 32, "name": "_code_000324b0" },
{ "file_offset": 206128, "flags": 32, "name": "_code_00032530" },
{ "file_offset": 206128, "flags": 32, "name": "_ai_communication_update_speech_timers" },
{ "file_offset": 206800, "flags": 32, "name": "_ai_communication_get_player_rating" },
{ "file_offset": 207632, "flags": 32, "name": "_ai_conversation_stop" },
{ "file_offset": 207776, "flags": 32, "name": "_ai_conversation_actor_deleted" },
@@ -0,0 +1,158 @@
# Opus 44,928-byte wave canonical reconciliation (2026-09-02)
## Purpose
This ledger reconciles the reported `+44,928` padded-byte Claude/Opus session on
`claude/breakable-surfaces-20260830` at `617703d2e` against canonical
`jonas/ai-debug-integration-20260829` at `1cc83929d`. It answers a narrower and
more useful question than comparing branch totals: which January code sections
were strict exact in Opus but were not strict exact in canonical?
The comparison used rename-stable keys of `unit + target code-section ordinal`.
That is important because canonical has replaced many `code_...` names with
evidence-backed semantic names. The frozen inputs are:
- `scratch/opus-617703d2e-stable.json`: 8,246 sections, 4,982 strict exact;
- `scratch/canonical-1cc83929d-stable.json`: 8,245 sections, 5,309 strict exact.
Canonical has 375 strict-exact sections absent from the older Opus snapshot.
Opus has only 48 strict-exact sections, totaling 15,696 padded bytes, which are
not strict exact in that canonical snapshot. Therefore the reported 44,928-byte
session gain is not a 44,928-byte canonical deficit.
## Reconciliation result
| Disposition at the frozen canonical baseline | Functions | Padded bytes |
| --- | ---: | ---: |
| Truthful ordinary-C implementation retained as a measured residual | 25 | 11,456 |
| Credible strict-exact recovery admitted in this wave | 1 | 704 |
| Deliberately not emitted pending a real owner/caller or credible source | 22 | 3,536 |
| **Total Opus-only strict set audited** | **48** | **15,696** |
The 25 residual functions are not lost source progress. Canonical deliberately
keeps their semantic implementation and refuses only the Opus spelling that
made the bytes exact through assembly, forced inlining, `noinline`, volatile or
barrier steering, false BSS/type ownership, duplicate inline-helper bodies, or
other unsupported source shapes. Their detailed evidence remains in the object
ledgers cited below.
## Recovered exception: `ai_communication_started`
`_ai_communication_started` is the credible exception. The Opus body is
ordinary typed C, contains no compiler-steering construct, and compiles to all
704 January bytes with all 48 relocation identities. Its normalized SHA-256 is
`eed6aa664227288ffe15a7f7c3c51791dbe88c42ef947d279e9a66c5422a0cd8`
for both target and candidate.
The genuine call graph also emits the 672-byte speech-timer helper. Independent
HCEA source names that helper `ai_communication_update_speech_timers`, and names
the already-exact 208-byte reset helper
`actor_reset_idle_vocalization_timer`. Those semantic names replace
`code_00032530` and `code_000322f0` in source and `config/symbols.json`; neither
address-based name is retained.
The speech-timer helper itself is an honest residual: target and candidate are
both 672 padded bytes with 43 relocations, with target normalized SHA-256
`91999fda75815c6638478824f3efa53ee6717572f365530362cbec00e78a50ac`
and candidate SHA-256
`885fdb81966284acf5ba7138a4bbbd2221e7c61c89114fdd6f53b96ba89b81a0`.
Objdiff reports 97.68 percent similarity. The remaining differences are the
previously documented natural register/lifetime schedule; no register hint,
volatile access, barrier, pragma, assembly, false alias, or inert dependency is
introduced. The typed implementation is retained and fuzzy-parked.
The public `ai_communication_started` declaration is placed in its owner header,
`source/ai/ai_communication.h`. The two private helper declarations remain in
their owner translation unit. Typed object access uses `unit_get` and
`actor_get`; tag-block access uses `TAG_BLOCK_GET_ELEMENT`.
## Already retained as credible residuals
These 25 Opus-exact sections, totaling 11,456 padded bytes, already exist in
canonical as ordinary-C residual implementations:
- AI: `action_converse_perform`, `actor_combat_find_nearby_target`,
`actor_combat_check_collateral_damage`, `infection_swarm_aim_jump`, and
`find_turning_point`;
- platform/camera/cutscene: `get_mutex_from_pool`, `dead_camera_update`, and
`recorded_animation_play_internal`;
- game engine: `game_engine_verify_current_map`;
- players: `unit_should_autopick_weapon`, `player_set_action_result`,
`player_handle_powerup`, `player_handle_powerup_minor`,
`player_update_powerups`, `player_teleport_on_bsp_switch`,
`players_reconnect_to_structure_bsp`, `player_examine_nearby_vehicle`,
`player_examine_nearby_device`, `player_examine_nearby_item`, and
`player_examine_nearby_objects`;
- memory: `lrar_allocate`;
- object lights: `light_unmarked`, `light_mark`,
`find_point_lights_for_object_in_cluster`, and `light_reconnect_to_map`.
The exact Opus forms are intentionally not restored. In particular:
- `action_converse_perform` used gameplay inline assembly to force a register;
- `actor_combat_find_nearby_target` disturbed the protected
`point_from_line3d` inline schedule;
- `actor_combat_check_collateral_damage` requires an owner-header shape that
regresses `units.obj` from 189/189;
- `game_engine_verify_current_map` and `lrar_allocate` depended on unsupported
`noinline`/manual helper expansion;
- the Players group used combinations of copied helpers, dead emitters,
volatile/barrier steering, duplicate fetches, and representation tricks;
- `recorded_animation_play_internal` reached exact only with false BSS
ownership; and
- the Object Lights forms relied on duplicate bodies, false linkage, or helpers
without a genuine emitting caller.
## Deliberately unwritten boundaries
The remaining 22 Opus-exact sections total 3,536 padded bytes. They stay
unwritten until their authentic source owner naturally emits them:
- `midpoint3d` (64): a fake standalone duplicate of the shared real-math inline;
- Decals helpers (704 total): `projection_from_vector3d`, `project_point2d`,
`triple_product3d`, `plane2d_from_points`,
`plane3d_from_point_and_normal`, and
`bsp3d_get_plane_from_designator`; these were unowned standalone copies of
shared inline/macro behavior;
- `player_examine_nearby_unit` (16): no truthful source form currently emits
the target-only leaf;
- Weapons (1,632 total): `animation_update` has an unresolved authentic COMDAT
grouping; `weapon_trigger_get_charged_fraction` and
`weapon_export_function_values` atomically introduce a candidate-owned float
constant that January treats as external; and
`weapon_handle_potential_inventory_item` depended on a layout-only surrogate
contradicting authenticated fields;
- Object Lights (1,056 total): the two anonymous 32-byte leaves,
`render_debug_light`, `light_get_bounding_sphere`, `sample_lightmap`, and
`sample_diffuse_texture` still lack a credible complete owner/caller graph;
- four Active Camouflage XDK wrappers (64 total): the targets are correctly
named `IDirect3DDevice8` wrappers, but must be instantiated by the real typed
cache/draw functions rather than copied, assembled, or emitted by a dummy
anchor.
## Validation and provenance
The isolated gate moved `ai_communication.obj` from 36 exact / 0 residual / 12
unwritten to 37 exact / 1 residual / 10 unwritten. Every inherited exact section
remained exact. The exact recovery and helper were compiled from the complete
translation unit; no proof-only caller or dormant emission anchor is retained.
Primary local evidence:
- `docs/object_matching_logs/claude_wave_small_exact_batch_20260831.md`;
- `docs/object_matching_logs/players_obj_january_safe_harvest_20260902.md`;
- `docs/object_matching_logs/object_lights_obj_small_family_recovery_20260902.md`;
- `docs/object_matching_logs/game_engine_obj_jonas_no_fake_inline_cleanup_20260831.md`;
- `docs/object_matching_logs/thread_win32_obj_house_rule_correction_20260831.md`;
- `docs/object_matching_logs/action_converse_obj_jonas_ordinary_c_fuzzy_park_20260830.md`;
- `docs/object_matching_logs/actor_combat_obj_collateral_damage_rejected_20260831.md`;
- `docs/object_matching_logs/recorded_animations_obj_natural_recovery_20260830.md`;
- `docs/object_matching_logs/weapons_obj_jonas_safe_trio_and_charged_export_blocker_20260901.md`;
- `docs/object_matching_logs/lrar_cache_obj_jonas_no_fake_inline_cleanup_20260831.md`;
- `docs/object_matching_logs/rasterizer_xbox_active_camouflage_obj_jonas_credibility_recovery_20260830.md`;
- HCEA `src/blam/ai/ai_communication_started.c`,
`ai_communication_update_speech_timers.c`, and
`actor_reset_idle_vocalization_timer.c`.
January's Halo CE Xbox COFF remains the sole byte-admission authority. Donor
source supplies names, types, and control-flow evidence only.
+281 -5
View File
@@ -59,13 +59,13 @@ symbols in this file:
000322C0 0030:
_code_000322c0 (0000)
000322F0 00d0:
_code_000322f0 (0000)
_actor_reset_idle_vocalization_timer (0000)
000323C0 00f0:
_actor_communication_update (0000)
000324B0 0080:
_code_000324b0 (0000)
00032530 02a0:
_code_00032530 (0000)
_ai_communication_update_speech_timers (0000)
000327D0 0340:
_ai_communication_get_player_rating (0000)
00032B10 0090:
@@ -549,6 +549,7 @@ symbols in this file:
#include "actor_definitions.h"
#include "actors.h"
#include "actor_types.h"
#include "ai_scenario_definitions.h"
#include "game/game.h"
#include "main/console.h"
#include "memory/data.h"
@@ -557,6 +558,7 @@ symbols in this file:
#include "scenario/scenario.h"
#include "scenario/scenario_definitions.h"
#include "tag_files/tag_files.h"
#include "units/dialogue_definitions.h"
#include "units/units.h"
/* ---------- constants */
@@ -597,6 +599,7 @@ enum
_ai_sound_volume_shout = 3,
_ai_communication_team_human = 0,
_ai_communication_team_covenant = 1,
NUMBER_OF_AI_COMMUNICATION_TEAMS = 2,
};
#define COMMUNICATION_CLOSE_DISTANCE 5.0f
@@ -911,12 +914,18 @@ static struct scenario_conversation_definition_view *code_000322c0(
long conversation_index);
boolean actor_is_fighting(
long actor_index);
static void code_000322f0(
static void actor_reset_idle_vocalization_timer(
long actor_index);
static void code_000324b0(
long actor_index,
long prop_index,
struct ai_information_packet *information);
static void ai_communication_update_speech_timers(
long unit_index,
short priority,
short vocalization_type,
short dialogue_type_index,
short reply_table_index);
short unit_test_speech(
long unit_index,
short priority,
@@ -2088,7 +2097,7 @@ short ai_conversation_status(
return status;
}
static void code_000322f0(
static void actor_reset_idle_vocalization_timer(
long actor_index)
{
struct actor_datum *actor;
@@ -2331,7 +2340,7 @@ void actor_communication_update(
if (actor->control.idle_vocalization_timer == 0 ||
actor->control.idle_vocalization_combat != in_combat)
{
code_000322f0(actor_index);
actor_reset_idle_vocalization_timer(actor_index);
}
if (actor->control.idle_vocalization_timer > 0 &&
@@ -2406,6 +2415,273 @@ static void code_000324b0(
return;
}
static void ai_communication_update_speech_timers(
long unit_index,
short priority,
short vocalization_type,
short dialogue_type_index,
short reply_table_index)
{
struct unit_datum *unit = unit_get(unit_index);
struct actor_datum *actor;
long time;
long notification_time;
if (unit->unit.actor_index == NONE)
{
actor = NULL;
}
else
{
actor = actor_get(unit->unit.actor_index);
}
time = game_time_get();
notification_time = time +
MAX(0, unit->unit.speech.sound_timer - communication_overlap_time_modifier);
unit->unit.speech.last_speech_finished_time = notification_time;
if (actor)
{
short communication_team;
actor_reset_idle_vocalization_timer(unit->unit.actor_index);
communication_team = actor_communication_team(unit->unit.actor_index);
if (communication_team != NONE)
{
if (priority <= _unit_speech_shout)
{
ai_globals->last_chatter_time[communication_team] = MAX(
ai_globals->last_chatter_time[communication_team],
notification_time);
if (priority >= _unit_speech_talk)
{
ai_globals->last_talk_time[communication_team] = MAX(
ai_globals->last_talk_time[communication_team],
notification_time);
}
if (priority >= _unit_speech_shout)
{
ai_globals->last_shout_time[communication_team] = MAX(
ai_globals->last_shout_time[communication_team],
notification_time);
}
if (ai_debug.print_speech_timers)
{
char const *team_name =
global_communication_team_names[communication_team * 2];
error(
2,
"%s %s %d/%s: %s %d",
team_name,
unit_get_speech_priority_name(priority),
dialogue_type_index,
dialogue_get_vocalization_name(vocalization_type, TRUE),
priority >= _unit_speech_talk ? "talk" : "chatter",
notification_time - time);
}
}
if (dialogue_type_index != NONE)
{
struct dialogue_event_status *event;
struct dialogue_usage const *usage;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xC9C,
(dialogue_type_index >= 0) && (dialogue_type_index < global_dialogue_event_count));
usage = &global_dialogue_table[dialogue_type_index];
event = &global_dialogue_events[
dialogue_type_index * NUMBER_OF_AI_COMMUNICATION_TEAMS + communication_team];
event->last_time_spoken = time;
if (game_connection() ||
!ai_debug.communication_timeout_disabled)
{
if (usage->repeat_delay > 0.0f)
{
event->disable_until_time = (long)(
usage->repeat_delay * TICKS_PER_SECOND +
notification_time);
}
}
}
if (reply_table_index != NONE)
{
struct dialogue_event_status *event;
struct reply_usage const *usage;
match_assert(
"c:\\halo\\SOURCE\\ai\\ai_communication.c",
0xCAF,
(reply_table_index >= 0) && (reply_table_index < global_reply_event_count));
usage = &global_reply_table[reply_table_index];
event = &global_reply_events[
reply_table_index * NUMBER_OF_AI_COMMUNICATION_TEAMS + communication_team];
event->last_time_spoken = time;
if (game_connection() ||
!ai_debug.communication_timeout_disabled)
{
if (usage->repeat_delay > 0.0f)
{
event->disable_until_time = (long)(
usage->repeat_delay * TICKS_PER_SECOND +
notification_time);
}
}
}
}
}
return;
}
void ai_communication_started(
long unit_index,
short priority,
short vocalization_type,
struct ai_information_packet *information)
{
switch (priority)
{
case _unit_speech_none:
case _unit_speech_idle:
case _unit_speech_pain:
case _unit_speech_involuntary:
case _unit_speech_death:
break;
default:
if (ai_debug.print_vocalizations)
{
char string[1024];
char name[256];
struct unit_datum *unit = unit_get(unit_index);
if (unit->unit.actor_index != NONE)
{
struct actor_datum *actor = actor_get(unit->unit.actor_index);
if (actor->meta.encounter_index == NONE)
{
csstrcpy(name, "<no encounter>");
}
else
{
struct encounter_definition *encounter = TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->ai_encounters,
DATUM_INDEX_TO_ABSOLUTE_INDEX(actor->meta.encounter_index),
struct encounter_definition);
sprintf(
name,
"%s/%s",
encounter->name,
TAG_BLOCK_GET_ELEMENT(
&encounter->squads,
actor->meta.squad_index,
struct squad_definition)->name);
}
sprintf(
string,
"%s/%s: ",
name,
actor_type_get_name(actor->meta.type));
}
else if (unit->object.name_index != NONE)
{
sprintf(
string,
"%s: ",
TAG_BLOCK_GET_ELEMENT(
&global_scenario_get()->object_names,
unit->object.name_index,
struct scenario_object_name)->name);
}
else if (unit->unit.player_index != NONE)
{
sprintf(
string,
"player %d: ",
DATUM_INDEX_TO_ABSOLUTE_INDEX(unit->unit.player_index));
}
else
{
sprintf(
string,
"unit %04X: ",
DATUM_INDEX_TO_ABSOLUTE_INDEX(unit_index));
}
sprintf(
temporary,
"%s %s",
unit_get_speech_priority_name(priority),
vocalization_type == NONE ?
"non-voc" :
dialogue_get_vocalization_name(vocalization_type, FALSE));
csstrcat(string, temporary);
if (information &&
information->dialogue_type_index != NONE)
{
sprintf(
temporary,
" [%d/%s]",
information->dialogue_type_index,
ai_communication_get_type_name(
global_dialogue_table[information->dialogue_type_index].communication_type));
csstrcat(string, temporary);
}
console_printf(FALSE, string);
}
if (ai_debug.print_speech)
{
struct unit_datum *unit = unit_get(unit_index);
if (unit->unit.speech.current.priority > _unit_speech_none)
{
char actor_string[512];
char speech_string[512];
error(
2,
"%s: %s",
ai_debug_describe_actor(
unit->unit.actor_index,
unit_index,
FALSE,
actor_string,
sizeof(actor_string)),
unit_describe_speech(
unit_index,
TRUE,
sizeof(speech_string),
speech_string));
}
}
if (!information->updated_dialogue_timers)
{
ai_communication_update_speech_timers(
unit_index,
priority,
vocalization_type,
information->dialogue_type_index,
NONE);
}
break;
}
return;
}
void ai_communication_notify(
long unit_index,
short priority,
+5
View File
@@ -119,6 +119,11 @@ void ai_communication_dispose_from_old_map(
void);
void ai_communication_packet_new(
struct ai_information_packet *information);
void ai_communication_started(
long unit_index,
short priority,
short vocalization_type,
struct ai_information_packet *information);
void actor_communication_update(
long actor_index);
void actor_handle_communication(