From 9bcc3e45dfa18a1e8ffc9a3fcbd43ced5630fd53 Mon Sep 17 00:00:00 2001 From: rainlizard <15337628+rainlizard@users.noreply.github.com> Date: Wed, 15 Apr 2026 06:59:53 +1000 Subject: [PATCH 1/3] analysis --- src/game_legacy.h | 2 ++ src/net_checksums.c | 22 +++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/game_legacy.h b/src/game_legacy.h index edcd8ba73f..def8397281 100644 --- a/src/game_legacy.h +++ b/src/game_legacy.h @@ -149,6 +149,8 @@ struct LogThingDesyncInfo { struct CoordDelta3d veloc_base; struct CoordDelta3d veloc_push_once; struct CoordDelta3d veloc_push_add; + TbBool is_creature; + struct Coord3d moveto_pos; TbBool is_special_digger; struct Coord3d digger_moveto_pos; short digger_dragtng_idx; diff --git a/src/net_checksums.c b/src/net_checksums.c index f61ac07632..d59291dc98 100644 --- a/src/net_checksums.c +++ b/src/net_checksums.c @@ -86,11 +86,14 @@ TbBigChecksum get_thing_checksum(const struct Thing* thing) { CHECKSUM_ADD(checksum, thing->veloc_push_add.x.val); CHECKSUM_ADD(checksum, thing->veloc_push_add.y.val); CHECKSUM_ADD(checksum, thing->veloc_push_add.z.val); - if (thing_is_creature_special_digger(thing)) { + if (thing->class_id == TCls_Creature) { struct CreatureControl* cctrl = creature_control_get_from_thing(thing); CHECKSUM_ADD(checksum, cctrl->moveto_pos.x.val); CHECKSUM_ADD(checksum, cctrl->moveto_pos.y.val); CHECKSUM_ADD(checksum, cctrl->moveto_pos.z.val); + } + if (thing_is_creature_special_digger(thing)) { + struct CreatureControl* cctrl = creature_control_get_from_thing(thing); CHECKSUM_ADD(checksum, cctrl->dragtng_idx); CHECKSUM_ADD(checksum, cctrl->arming_thing_id); CHECKSUM_ADD(checksum, cctrl->pickup_object_id); @@ -255,6 +258,11 @@ void update_turn_checksums(void) { thing_snapshot->veloc_base = thing->veloc_base; thing_snapshot->veloc_push_once = thing->veloc_push_once; thing_snapshot->veloc_push_add = thing->veloc_push_add; + thing_snapshot->is_creature = (thing->class_id == TCls_Creature); + if (thing_snapshot->is_creature) { + struct CreatureControl* cctrl = creature_control_get_from_thing(thing); + thing_snapshot->moveto_pos = cctrl->moveto_pos; + } thing_snapshot->is_special_digger = thing_is_creature_special_digger(thing); if (thing_snapshot->is_special_digger) { struct CreatureControl* cctrl = creature_control_get_from_thing(thing); @@ -388,9 +396,17 @@ static void log_thing_differences(struct LogDetailedSnapshot* client, const char (long)client_thing->veloc_base.x.val, (long)client_thing->veloc_base.y.val, (long)client_thing->veloc_base.z.val, (long)client_thing->veloc_push_once.x.val, (long)client_thing->veloc_push_once.y.val, (long)client_thing->veloc_push_once.z.val, (long)client_thing->veloc_push_add.x.val, (long)client_thing->veloc_push_add.y.val, (long)client_thing->veloc_push_add.z.val); + if (client_thing->is_creature) { + if (host_thing != NULL) { + ERRORLOG(" [Host] moveto_pos=(%ld,%ld,%ld)", + (long)host_thing->moveto_pos.x.val, (long)host_thing->moveto_pos.y.val, (long)host_thing->moveto_pos.z.val); + } + ERRORLOG(" [Client] moveto_pos=(%ld,%ld,%ld)", + (long)client_thing->moveto_pos.x.val, (long)client_thing->moveto_pos.y.val, (long)client_thing->moveto_pos.z.val); + } if (client_thing->is_special_digger) { if (host_thing != NULL) { - ERRORLOG(" [Host] digger moveto=(%ld,%ld,%ld) dragtng=%d arming=%d pickup_obj=%d pickup_cr=%d move_flags=%u stack_update_turn=%ld working_stl=%u task_stl=%u task_idx=%u consecutive_reinforcements=%u last_did_job=%u task_stack_pos=%u task_repeats=%u", + ERRORLOG(" [Host] digger_moveto_pos=(%ld,%ld,%ld) dragtng_idx=%d arming_thing_id=%d pickup_object_id=%d pickup_creature_id=%d move_flags=%u stack_update_turn=%ld working_stl=%u task_stl=%u task_idx=%u consecutive_reinforcements=%u last_did_job=%u task_stack_pos=%u task_repeats=%u", (long)host_thing->digger_moveto_pos.x.val, (long)host_thing->digger_moveto_pos.y.val, (long)host_thing->digger_moveto_pos.z.val, (int)host_thing->digger_dragtng_idx, (int)host_thing->digger_arming_thing_id, (int)host_thing->digger_pickup_object_id, (int)host_thing->digger_pickup_creature_id, @@ -400,7 +416,7 @@ static void log_thing_differences(struct LogDetailedSnapshot* client, const char (unsigned)host_thing->digger_last_did_job, (unsigned)host_thing->digger_task_stack_pos, (unsigned)host_thing->digger_task_repeats); } - ERRORLOG(" [Client] digger moveto=(%ld,%ld,%ld) dragtng=%d arming=%d pickup_obj=%d pickup_cr=%d move_flags=%u stack_update_turn=%ld working_stl=%u task_stl=%u task_idx=%u consecutive_reinforcements=%u last_did_job=%u task_stack_pos=%u task_repeats=%u", + ERRORLOG(" [Client] digger_moveto_pos=(%ld,%ld,%ld) dragtng_idx=%d arming_thing_id=%d pickup_object_id=%d pickup_creature_id=%d move_flags=%u stack_update_turn=%ld working_stl=%u task_stl=%u task_idx=%u consecutive_reinforcements=%u last_did_job=%u task_stack_pos=%u task_repeats=%u", (long)client_thing->digger_moveto_pos.x.val, (long)client_thing->digger_moveto_pos.y.val, (long)client_thing->digger_moveto_pos.z.val, (int)client_thing->digger_dragtng_idx, (int)client_thing->digger_arming_thing_id, (int)client_thing->digger_pickup_object_id, (int)client_thing->digger_pickup_creature_id, From bd1b185ed3f15f1863351baa7bf42d231b8fe154 Mon Sep 17 00:00:00 2001 From: rainlizard <15337628+rainlizard@users.noreply.github.com> Date: Wed, 15 Apr 2026 07:10:01 +1000 Subject: [PATCH 2/3] log desynced seed function --- src/game_merge.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/game_merge.h b/src/game_merge.h index 63c39a031a..6560fcc69c 100644 --- a/src/game_merge.h +++ b/src/game_merge.h @@ -52,8 +52,17 @@ extern "C" { // Random number generation system with synchronized and unsynchronized variants +static inline unsigned long thing_random_impl(struct Thing *thing, unsigned long range, const char *func_name, unsigned long line) +{ + unsigned long result = LbRandomSeries(range, &thing->random_seed, func_name, line); + if (thing_is_creature_special_digger(thing)) { + SYNCLOG("imp THING_RANDOM called from %s line %lu range=%lu result=%lu seed_after=%08lx", func_name, line, range, result, (unsigned long)thing->random_seed); + } + return result; +} + // Thing-specific random for deterministic per-thing behavior (creatures, objects, etc.) -#define THING_RANDOM(thing, range) LbRandomSeries(range, &((struct Thing*)(thing))->random_seed, __func__, __LINE__) +#define THING_RANDOM(thing, range) thing_random_impl((struct Thing*)(thing), range, __func__, __LINE__) // Global game events requiring synchronization across network (scripts, AI decisions, player events) #define GAME_RANDOM(range) LbRandomSeries(range, &game.action_random_seed, __func__, __LINE__) // Unsynchronized random for visual/audio effects that don't affect game state (lighting, particles) From ee7813e1c68dad708752bab2016f98709409f7b3 Mon Sep 17 00:00:00 2001 From: rainlizard <15337628+rainlizard@users.noreply.github.com> Date: Wed, 15 Apr 2026 07:43:22 +1000 Subject: [PATCH 3/3] imp log --- src/game_merge.h | 4 +++- src/net_checksums.c | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/net_checksums.h | 2 ++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/game_merge.h b/src/game_merge.h index 6560fcc69c..c4f0b8df37 100644 --- a/src/game_merge.h +++ b/src/game_merge.h @@ -52,11 +52,13 @@ extern "C" { // Random number generation system with synchronized and unsynchronized variants +void imp_random_log_push(uint16_t thing_idx, const char *func, unsigned long line, unsigned long range, unsigned long result, unsigned long seed_after); + static inline unsigned long thing_random_impl(struct Thing *thing, unsigned long range, const char *func_name, unsigned long line) { unsigned long result = LbRandomSeries(range, &thing->random_seed, func_name, line); if (thing_is_creature_special_digger(thing)) { - SYNCLOG("imp THING_RANDOM called from %s line %lu range=%lu result=%lu seed_after=%08lx", func_name, line, range, result, (unsigned long)thing->random_seed); + imp_random_log_push(thing->index, func_name, line, range, result, (unsigned long)thing->random_seed); } return result; } diff --git a/src/net_checksums.c b/src/net_checksums.c index d59291dc98..0bc0f4acb2 100644 --- a/src/net_checksums.c +++ b/src/net_checksums.c @@ -40,6 +40,49 @@ extern "C" { /******************************************************************************/ #define CHECKSUM_ADD(checksum, value) checksum = ((checksum << 5) | (checksum >> 27)) ^ (ulong)(value) #define SNAPSHOT_BUFFER_SIZE 15 +#define IMP_RANDOM_LOG_SIZE 16384 + +struct ImpRandomEntry { + const char *func; + unsigned long line; + unsigned long range; + unsigned long result; + unsigned long seed_after; + uint16_t thing_idx; +}; + +static struct ImpRandomEntry imp_random_log[IMP_RANDOM_LOG_SIZE]; +static int imp_random_log_pos = 0; +static int imp_random_log_count = 0; + +void imp_random_log_push(uint16_t thing_idx, const char *func, unsigned long line, unsigned long range, unsigned long result, unsigned long seed_after) +{ + struct ImpRandomEntry *entry = &imp_random_log[imp_random_log_pos]; + entry->thing_idx = thing_idx; + entry->func = func; + entry->line = line; + entry->range = range; + entry->result = result; + entry->seed_after = seed_after; + imp_random_log_pos = (imp_random_log_pos + 1) % IMP_RANDOM_LOG_SIZE; + if (imp_random_log_count < IMP_RANDOM_LOG_SIZE) { + imp_random_log_count++; + } +} + +void imp_random_log_dump(uint16_t thing_idx) +{ + ERRORLOG(" Imp Thing[%u] THING_RANDOM history (last %d entries):", (unsigned)thing_idx, imp_random_log_count); + int start = (imp_random_log_pos - imp_random_log_count + IMP_RANDOM_LOG_SIZE) % IMP_RANDOM_LOG_SIZE; + for (int i = 0; i < imp_random_log_count; i++) { + struct ImpRandomEntry *entry = &imp_random_log[(start + i) % IMP_RANDOM_LOG_SIZE]; + if (entry->thing_idx != thing_idx) { + continue; + } + ERRORLOG(" Thing[%u] %s:%lu range=%lu result=%lu seed_after=%08lx", + (unsigned)entry->thing_idx, entry->func, entry->line, entry->range, entry->result, entry->seed_after); + } +} struct ChecksumSnapshot { GameTurn turn; @@ -346,6 +389,11 @@ void pack_desync_history_for_resync(void) { } game.host_checksums = snapshot->checksums; game.log_snapshot = snapshot->log_details; + for (int i = 0; i < game.log_snapshot.thing_count; i++) { + if (game.log_snapshot.things[i].is_special_digger) { + imp_random_log_dump(game.log_snapshot.things[i].index); + } + } } static void log_thing_differences(struct LogDetailedSnapshot* client, const char* name, TbBigChecksum client_sum, TbBigChecksum host_sum, ThingClass filter_class) { @@ -425,6 +473,7 @@ static void log_thing_differences(struct LogDetailedSnapshot* client, const char (unsigned)client_thing->digger_task_idx, (unsigned)client_thing->digger_consecutive_reinforcements, (unsigned)client_thing->digger_last_did_job, (unsigned)client_thing->digger_task_stack_pos, (unsigned)client_thing->digger_task_repeats); + imp_random_log_dump(client_thing->index); } shown++; } diff --git a/src/net_checksums.h b/src/net_checksums.h index bb24b2f5df..4a15af9a22 100644 --- a/src/net_checksums.h +++ b/src/net_checksums.h @@ -38,6 +38,8 @@ TbBigChecksum get_thing_checksum(const struct Thing *thing); short checksums_different(void); CoroutineLoopState perform_checksum_verification(CoroutineLoop *con); CoroutineLoopState verify_sprite_zip_checksums(CoroutineLoop *con); +void imp_random_log_push(uint16_t thing_idx, const char *func, unsigned long line, unsigned long range, unsigned long result, unsigned long seed_after); +void imp_random_log_dump(uint16_t thing_idx); /******************************************************************************/ #ifdef __cplusplus