diff --git a/src/game_legacy.h b/src/game_legacy.h index 4eb281fb83..4bd9dd9e5b 100644 --- a/src/game_legacy.h +++ b/src/game_legacy.h @@ -136,6 +136,51 @@ struct LogThingDesyncInfo { HitPoints health; GameTurn creation_turn; uint32_t random_seed; + unsigned char current_frame; + unsigned char max_frames; + HitPoints max_health; + CrtrExpLevel exp_level; + unsigned short inst_turn; + CrInstance instance_id; + unsigned char active_state; + unsigned char continue_state; + GameTurn tasks_check_turn; + struct Coord3d moveto_pos; + ThingIndex dragtng_idx; + ThingIndex arming_thing_id; + ThingIndex pickup_object_id; + ThingIndex pickup_creature_id; + unsigned char move_flags; + ThingIndex players_prev_creature_idx; + ThingIndex players_next_creature_idx; + short last_work_room_id; + short work_room_id; + short target_room_id; + int32_t turns_at_job; + short blocking_door_id; + CrInstance active_instance_id; + unsigned char active_state_bkp; + unsigned char continue_state_bkp; + unsigned short damage_wall_coords; + unsigned short job_assigned; + GameTurn healing_sleep_check_turn; + GameTurn job_assigned_check_turn; + unsigned char stopped_for_hand_turns; + GameTurn idle_start_gameturn; + SlabKind current_slab_kind; + PlayerNumber current_slab_owner; + SlabKind task_slab_kind; + PlayerNumber task_slab_owner; + SlabKind working_slab_kind; + PlayerNumber working_slab_owner; + GameTurn digger_stack_update_turn; + SubtlCodedCoords digger_working_stl; + SubtlCodedCoords digger_task_stl; + unsigned short digger_task_idx; + unsigned char digger_consecutive_reinforcements; + unsigned char digger_last_did_job; + unsigned char digger_task_stack_pos; + unsigned short digger_task_repeats; TbBigChecksum checksum; }; @@ -157,6 +202,32 @@ struct LogRoomDesyncInfo { TbBigChecksum checksum; }; +struct LogMapTaskDesyncInfo { + unsigned char kind; + SubtlCodedCoords coords; + TbBigChecksum checksum; +}; + +struct LogDiggerStackDesyncInfo { + SubtlCodedCoords stl_num; + SpDiggerTaskType task_type; + TbBigChecksum checksum; +}; + +struct LogImpSchedulerDesyncInfo { + PlayerNumber id; + short digger_list_start; + int task_count; + short highest_task_number; + GameTurn digger_stack_update_turn; + uint32_t digger_stack_length; + TbBigChecksum task_list_checksum; + TbBigChecksum digger_stack_checksum; + TbBigChecksum checksum; + struct LogMapTaskDesyncInfo tasks[MAPTASKS_COUNT]; + struct LogDiggerStackDesyncInfo digger_tasks[DIGGER_TASK_MAX_COUNT]; +}; + struct DesyncChecksums { TbBigChecksum creatures; TbBigChecksum traps; @@ -168,6 +239,7 @@ struct DesyncChecksums { TbBigChecksum doors; TbBigChecksum rooms; TbBigChecksum players; + TbBigChecksum imp_schedulers; TbBigChecksum action_seed; TbBigChecksum ai_seed; TbBigChecksum player_seed; @@ -181,6 +253,8 @@ struct LogDetailedSnapshot { int player_count; struct LogRoomDesyncInfo rooms[ROOMS_COUNT]; int room_count; + struct LogImpSchedulerDesyncInfo imp_schedulers[PLAYERS_COUNT]; + int imp_scheduler_count; }; struct Game { diff --git a/src/net_checksums.c b/src/net_checksums.c index d01d37fab5..0e22441463 100644 --- a/src/net_checksums.c +++ b/src/net_checksums.c @@ -28,6 +28,7 @@ #include "creature_control.h" #include "frontend.h" #include "thing_list.h" +#include "thing_creature.h" #include "post_inc.h" #ifdef __cplusplus @@ -49,6 +50,120 @@ static struct ChecksumSnapshot snapshot_buffer[SNAPSHOT_BUFFER_SIZE]; static int snapshot_head = 0; static GameTurn desync_turn = 0; +static void log_thing_snapshot(const char *side, const struct LogThingDesyncInfo *thing_snapshot) +{ + ERRORLOG(" [%s] Thing[%d] class_id=%d model=%d owner=%d mappos=(%ld,%ld,%ld) health=%ld creation_turn=%lu random_seed=%08x current_frame=%u max_frames=%u max_health=%ld exp_level=%u inst_turn=%u instance_id=%u active_state=%u continue_state=%u tasks_check_turn=%lu moveto_pos=(%ld,%ld,%ld) dragtng_idx=%d arming_thing_id=%d pickup_object_id=%d pickup_creature_id=%d move_flags=%u players_prev_creature_idx=%d players_next_creature_idx=%d last_work_room_id=%d work_room_id=%d target_room_id=%d turns_at_job=%ld blocking_door_id=%d active_instance_id=%u active_state_bkp=%u continue_state_bkp=%u damage_wall_coords=%u job_assigned=%u healing_sleep_check_turn=%lu job_assigned_check_turn=%lu stopped_for_hand_turns=%u idle_start_gameturn=%lu current_slab_kind=%u current_slab_owner=%d task_slab_kind=%u task_slab_owner=%d working_slab_kind=%u working_slab_owner=%d digger_stack_update_turn=%lu digger_working_stl=%u digger_task_stl=%u digger_task_idx=%u digger_consecutive_reinforcements=%u digger_last_did_job=%u digger_task_stack_pos=%u digger_task_repeats=%u", + side, thing_snapshot->index, thing_snapshot->class_id, thing_snapshot->model, thing_snapshot->owner, + (long)thing_snapshot->mappos.x.val, (long)thing_snapshot->mappos.y.val, (long)thing_snapshot->mappos.z.val, + (long)thing_snapshot->health, (unsigned long)thing_snapshot->creation_turn, thing_snapshot->random_seed, + (unsigned)thing_snapshot->current_frame, (unsigned)thing_snapshot->max_frames, (long)thing_snapshot->max_health, (unsigned)thing_snapshot->exp_level, + (unsigned)thing_snapshot->inst_turn, (unsigned)thing_snapshot->instance_id, + (unsigned)thing_snapshot->active_state, (unsigned)thing_snapshot->continue_state, (unsigned long)thing_snapshot->tasks_check_turn, + (long)thing_snapshot->moveto_pos.x.val, (long)thing_snapshot->moveto_pos.y.val, (long)thing_snapshot->moveto_pos.z.val, + thing_snapshot->dragtng_idx, thing_snapshot->arming_thing_id, thing_snapshot->pickup_object_id, thing_snapshot->pickup_creature_id, + (unsigned)thing_snapshot->move_flags, thing_snapshot->players_prev_creature_idx, thing_snapshot->players_next_creature_idx, + thing_snapshot->last_work_room_id, thing_snapshot->work_room_id, + thing_snapshot->target_room_id, (long)thing_snapshot->turns_at_job, thing_snapshot->blocking_door_id, + (unsigned)thing_snapshot->active_instance_id, (unsigned)thing_snapshot->active_state_bkp, + (unsigned)thing_snapshot->continue_state_bkp, (unsigned)thing_snapshot->damage_wall_coords, (unsigned)thing_snapshot->job_assigned, + (unsigned long)thing_snapshot->healing_sleep_check_turn, (unsigned long)thing_snapshot->job_assigned_check_turn, (unsigned)thing_snapshot->stopped_for_hand_turns, + (unsigned long)thing_snapshot->idle_start_gameturn, (unsigned)thing_snapshot->current_slab_kind, thing_snapshot->current_slab_owner, + (unsigned)thing_snapshot->task_slab_kind, thing_snapshot->task_slab_owner, (unsigned)thing_snapshot->working_slab_kind, + thing_snapshot->working_slab_owner, (unsigned long)thing_snapshot->digger_stack_update_turn, + (unsigned)thing_snapshot->digger_working_stl, (unsigned)thing_snapshot->digger_task_stl, (unsigned)thing_snapshot->digger_task_idx, + (unsigned)thing_snapshot->digger_consecutive_reinforcements, (unsigned)thing_snapshot->digger_last_did_job, + (unsigned)thing_snapshot->digger_task_stack_pos, (unsigned)thing_snapshot->digger_task_repeats); +} + +static void fill_slab_snapshot(MapSlabCoord slab_x, MapSlabCoord slab_y, SlabKind *kind, PlayerNumber *owner) +{ + *kind = 0; + *owner = 0; + if ((slab_x < 0) || (slab_x >= game.map_tiles_x) || (slab_y < 0) || (slab_y >= game.map_tiles_y)) { + return; + } + struct SlabMap *slab = get_slabmap_block(slab_x, slab_y); + *kind = slab->kind; + *owner = slab->owner; +} + +static void fill_slab_snapshot_from_subtile(SubtlCodedCoords subtile, SlabKind *kind, PlayerNumber *owner) +{ + if (subtile == 0) { + *kind = 0; + *owner = 0; + return; + } + fill_slab_snapshot(subtile_slab(stl_num_decode_x(subtile)), subtile_slab(stl_num_decode_y(subtile)), kind, owner); +} + +static TbBigChecksum get_map_task_checksum(const struct MapTask *task, long index) +{ + TbBigChecksum checksum = 0; + CHECKSUM_ADD(checksum, index); + CHECKSUM_ADD(checksum, task->kind); + CHECKSUM_ADD(checksum, task->coords); + return checksum; +} + +static TbBigChecksum get_digger_stack_checksum(const struct DiggerStack *digger_task, long index) +{ + TbBigChecksum checksum = 0; + CHECKSUM_ADD(checksum, index); + CHECKSUM_ADD(checksum, digger_task->stl_num); + CHECKSUM_ADD(checksum, digger_task->task_type); + return checksum; +} + +static TbBigChecksum compute_dungeon_task_list_checksum(const struct Dungeon *dungeon) +{ + TbBigChecksum checksum = 0; + long task_limit = dungeon->highest_task_number; + if (task_limit > MAPTASKS_COUNT) { + task_limit = MAPTASKS_COUNT; + } + for (long i = 0; i < task_limit; i++) { + CHECKSUM_ADD(checksum, get_map_task_checksum(&dungeon->task_list[i], i)); + } + return checksum; +} + +static TbBigChecksum compute_dungeon_digger_stack_checksum(const struct Dungeon *dungeon) +{ + TbBigChecksum checksum = 0; + uint32_t digger_task_limit = dungeon->digger_stack_length; + if (digger_task_limit > DIGGER_TASK_MAX_COUNT) { + digger_task_limit = DIGGER_TASK_MAX_COUNT; + } + for (uint32_t i = 0; i < digger_task_limit; i++) { + CHECKSUM_ADD(checksum, get_digger_stack_checksum(&dungeon->digger_stack[i], i)); + } + return checksum; +} + +static TbBigChecksum compute_imp_scheduler_checksum(const struct Dungeon *dungeon) +{ + TbBigChecksum checksum = 0; + CHECKSUM_ADD(checksum, dungeon->owner); + CHECKSUM_ADD(checksum, dungeon->digger_list_start); + CHECKSUM_ADD(checksum, dungeon->task_count); + CHECKSUM_ADD(checksum, dungeon->highest_task_number); + CHECKSUM_ADD(checksum, dungeon->digger_stack_update_turn); + CHECKSUM_ADD(checksum, dungeon->digger_stack_length); + CHECKSUM_ADD(checksum, compute_dungeon_task_list_checksum(dungeon)); + CHECKSUM_ADD(checksum, compute_dungeon_digger_stack_checksum(dungeon)); + return checksum; +} + +static void log_imp_scheduler_snapshot(const char *side, const struct LogImpSchedulerDesyncInfo *scheduler_snapshot) +{ + ERRORLOG(" [%s] ImpScheduler[%d] digger_list_start=%d task_count=%d highest_task_number=%d digger_stack_update_turn=%lu digger_stack_length=%lu task_list_checksum=%08lx digger_stack_checksum=%08lx checksum=%08lx", + side, scheduler_snapshot->id, scheduler_snapshot->digger_list_start, scheduler_snapshot->task_count, + scheduler_snapshot->highest_task_number, (unsigned long)scheduler_snapshot->digger_stack_update_turn, + (unsigned long)scheduler_snapshot->digger_stack_length, (unsigned long)scheduler_snapshot->task_list_checksum, + (unsigned long)scheduler_snapshot->digger_stack_checksum, (unsigned long)scheduler_snapshot->checksum); +} + TbBigChecksum get_thing_checksum(const struct Thing* thing) { if (!thing_exists(thing) || is_non_synchronized_thing_class(thing->class_id)) { return 0; @@ -60,17 +175,71 @@ TbBigChecksum get_thing_checksum(const struct Thing* thing) { CHECKSUM_ADD(checksum, thing->owner); CHECKSUM_ADD(checksum, thing->creation_turn); CHECKSUM_ADD(checksum, thing->random_seed); - CHECKSUM_ADD(checksum, thing->mappos.x.val); - CHECKSUM_ADD(checksum, thing->mappos.y.val); - CHECKSUM_ADD(checksum, thing->mappos.z.val); - CHECKSUM_ADD(checksum, thing->health); - CHECKSUM_ADD(checksum, thing->current_frame); - CHECKSUM_ADD(checksum, thing->max_frames); + CHECKSUM_ADD(checksum, thing->mappos.x.val); + CHECKSUM_ADD(checksum, thing->mappos.y.val); + CHECKSUM_ADD(checksum, thing->mappos.z.val); + CHECKSUM_ADD(checksum, thing->health); + CHECKSUM_ADD(checksum, thing->current_frame); + CHECKSUM_ADD(checksum, thing->max_frames); if (thing->class_id == TCls_Creature) { struct CreatureControl* creature_control = creature_control_get_from_thing(thing); + CHECKSUM_ADD(checksum, creature_control->max_health); + CHECKSUM_ADD(checksum, creature_control->exp_level); CHECKSUM_ADD(checksum, creature_control->inst_turn); CHECKSUM_ADD(checksum, creature_control->instance_id); + CHECKSUM_ADD(checksum, thing->active_state); + CHECKSUM_ADD(checksum, thing->continue_state); + CHECKSUM_ADD(checksum, creature_control->tasks_check_turn); + CHECKSUM_ADD(checksum, creature_control->players_prev_creature_idx); + CHECKSUM_ADD(checksum, creature_control->players_next_creature_idx); + CHECKSUM_ADD(checksum, creature_control->last_work_room_id); + CHECKSUM_ADD(checksum, creature_control->work_room_id); + CHECKSUM_ADD(checksum, creature_control->target_room_id); + CHECKSUM_ADD(checksum, creature_control->turns_at_job); + CHECKSUM_ADD(checksum, creature_control->blocking_door_id); + CHECKSUM_ADD(checksum, creature_control->active_instance_id); + CHECKSUM_ADD(checksum, creature_control->active_state_bkp); + CHECKSUM_ADD(checksum, creature_control->continue_state_bkp); + CHECKSUM_ADD(checksum, creature_control->damage_wall_coords); + CHECKSUM_ADD(checksum, creature_control->job_assigned); + CHECKSUM_ADD(checksum, creature_control->healing_sleep_check_turn); + CHECKSUM_ADD(checksum, creature_control->job_assigned_check_turn); + CHECKSUM_ADD(checksum, creature_control->stopped_for_hand_turns); + CHECKSUM_ADD(checksum, creature_control->idle.start_gameturn); + if (thing_is_creature_digger(thing)) { + SlabKind current_slab_kind; + PlayerNumber current_slab_owner; + SlabKind task_slab_kind; + PlayerNumber task_slab_owner; + SlabKind working_slab_kind; + PlayerNumber working_slab_owner; + fill_slab_snapshot(subtile_slab(thing->mappos.x.stl.num), subtile_slab(thing->mappos.y.stl.num), ¤t_slab_kind, ¤t_slab_owner); + fill_slab_snapshot_from_subtile(creature_control->digger.task_stl, &task_slab_kind, &task_slab_owner); + fill_slab_snapshot_from_subtile(creature_control->digger.working_stl, &working_slab_kind, &working_slab_owner); + CHECKSUM_ADD(checksum, current_slab_kind); + CHECKSUM_ADD(checksum, current_slab_owner); + CHECKSUM_ADD(checksum, task_slab_kind); + CHECKSUM_ADD(checksum, task_slab_owner); + CHECKSUM_ADD(checksum, working_slab_kind); + CHECKSUM_ADD(checksum, working_slab_owner); + CHECKSUM_ADD(checksum, creature_control->moveto_pos.x.val); + CHECKSUM_ADD(checksum, creature_control->moveto_pos.y.val); + CHECKSUM_ADD(checksum, creature_control->moveto_pos.z.val); + CHECKSUM_ADD(checksum, creature_control->dragtng_idx); + CHECKSUM_ADD(checksum, creature_control->arming_thing_id); + CHECKSUM_ADD(checksum, creature_control->pickup_object_id); + CHECKSUM_ADD(checksum, creature_control->pickup_creature_id); + CHECKSUM_ADD(checksum, creature_control->move_flags); + CHECKSUM_ADD(checksum, creature_control->digger.stack_update_turn); + CHECKSUM_ADD(checksum, creature_control->digger.working_stl); + CHECKSUM_ADD(checksum, creature_control->digger.task_stl); + CHECKSUM_ADD(checksum, creature_control->digger.task_idx); + CHECKSUM_ADD(checksum, creature_control->digger.consecutive_reinforcements); + CHECKSUM_ADD(checksum, creature_control->digger.last_did_job); + CHECKSUM_ADD(checksum, creature_control->digger.task_stack_pos); + CHECKSUM_ADD(checksum, creature_control->digger.task_repeats); + } } return checksum; } @@ -144,6 +313,14 @@ static void compute_checksums(struct DesyncChecksums* checksums) { checksums->players += compute_player_checksum(player); } } + checksums->imp_schedulers = 0; + for (int i = 0; i < PLAYERS_COUNT; i++) { + struct PlayerInfo* player = get_player(i); + struct Dungeon* dungeon = get_dungeon(i); + if (player_exists(player) && !dungeon_invalid(dungeon)) { + checksums->imp_schedulers += compute_imp_scheduler_checksum(dungeon); + } + } checksums->action_seed = game.action_random_seed; checksums->ai_seed = game.ai_random_seed; checksums->player_seed = game.player_random_seed; @@ -194,6 +371,7 @@ void update_turn_checksums(void) { snapshot_info->thing_count = 0; snapshot_info->player_count = 0; snapshot_info->room_count = 0; + snapshot_info->imp_scheduler_count = 0; if ((game.system_flags & GSF_NetworkActive) != 0) { for (int i = 1; i < SYNCED_THINGS_COUNT; i++) { struct Thing* thing = thing_get(i); @@ -201,6 +379,7 @@ void update_turn_checksums(void) { continue; } struct LogThingDesyncInfo* thing_snapshot = &snapshot_info->things[snapshot_info->thing_count++]; + memset(thing_snapshot, 0, sizeof(*thing_snapshot)); thing_snapshot->index = thing->index; thing_snapshot->class_id = thing->class_id; thing_snapshot->model = thing->model; @@ -209,6 +388,53 @@ void update_turn_checksums(void) { thing_snapshot->health = thing->health; thing_snapshot->creation_turn = thing->creation_turn; thing_snapshot->random_seed = thing->random_seed; + thing_snapshot->current_frame = thing->current_frame; + thing_snapshot->max_frames = thing->max_frames; + if (thing->class_id == TCls_Creature) { + struct CreatureControl* creature_control = creature_control_get_from_thing(thing); + thing_snapshot->max_health = creature_control->max_health; + thing_snapshot->exp_level = creature_control->exp_level; + thing_snapshot->inst_turn = creature_control->inst_turn; + thing_snapshot->instance_id = creature_control->instance_id; + thing_snapshot->active_state = thing->active_state; + thing_snapshot->continue_state = thing->continue_state; + thing_snapshot->tasks_check_turn = creature_control->tasks_check_turn; + thing_snapshot->moveto_pos = creature_control->moveto_pos; + thing_snapshot->dragtng_idx = creature_control->dragtng_idx; + thing_snapshot->arming_thing_id = creature_control->arming_thing_id; + thing_snapshot->pickup_object_id = creature_control->pickup_object_id; + thing_snapshot->pickup_creature_id = creature_control->pickup_creature_id; + thing_snapshot->move_flags = creature_control->move_flags; + thing_snapshot->players_prev_creature_idx = creature_control->players_prev_creature_idx; + thing_snapshot->players_next_creature_idx = creature_control->players_next_creature_idx; + thing_snapshot->last_work_room_id = creature_control->last_work_room_id; + thing_snapshot->work_room_id = creature_control->work_room_id; + thing_snapshot->target_room_id = creature_control->target_room_id; + thing_snapshot->turns_at_job = creature_control->turns_at_job; + thing_snapshot->blocking_door_id = creature_control->blocking_door_id; + thing_snapshot->active_instance_id = creature_control->active_instance_id; + thing_snapshot->active_state_bkp = creature_control->active_state_bkp; + thing_snapshot->continue_state_bkp = creature_control->continue_state_bkp; + thing_snapshot->damage_wall_coords = creature_control->damage_wall_coords; + thing_snapshot->job_assigned = creature_control->job_assigned; + thing_snapshot->healing_sleep_check_turn = creature_control->healing_sleep_check_turn; + thing_snapshot->job_assigned_check_turn = creature_control->job_assigned_check_turn; + thing_snapshot->stopped_for_hand_turns = creature_control->stopped_for_hand_turns; + thing_snapshot->idle_start_gameturn = creature_control->idle.start_gameturn; + if (thing_is_creature_digger(thing)) { + fill_slab_snapshot(subtile_slab(thing->mappos.x.stl.num), subtile_slab(thing->mappos.y.stl.num), &thing_snapshot->current_slab_kind, &thing_snapshot->current_slab_owner); + fill_slab_snapshot_from_subtile(creature_control->digger.task_stl, &thing_snapshot->task_slab_kind, &thing_snapshot->task_slab_owner); + fill_slab_snapshot_from_subtile(creature_control->digger.working_stl, &thing_snapshot->working_slab_kind, &thing_snapshot->working_slab_owner); + thing_snapshot->digger_stack_update_turn = creature_control->digger.stack_update_turn; + thing_snapshot->digger_working_stl = creature_control->digger.working_stl; + thing_snapshot->digger_task_stl = creature_control->digger.task_stl; + thing_snapshot->digger_task_idx = creature_control->digger.task_idx; + thing_snapshot->digger_consecutive_reinforcements = creature_control->digger.consecutive_reinforcements; + thing_snapshot->digger_last_did_job = creature_control->digger.last_did_job; + thing_snapshot->digger_task_stack_pos = creature_control->digger.task_stack_pos; + thing_snapshot->digger_task_repeats = creature_control->digger.task_repeats; + } + } thing_snapshot->checksum = get_thing_checksum(thing); } for (int i = 0; i < PLAYERS_COUNT; i++) { @@ -237,6 +463,36 @@ void update_turn_checksums(void) { room_snapshot->used_capacity = room->used_capacity; room_snapshot->checksum = get_room_checksum(room); } + for (int i = 0; i < PLAYERS_COUNT; i++) { + struct PlayerInfo* player = get_player(i); + struct Dungeon* dungeon = get_dungeon(i); + if (!player_exists(player) || dungeon_invalid(dungeon)) { + continue; + } + struct LogImpSchedulerDesyncInfo* scheduler_snapshot = &snapshot_info->imp_schedulers[snapshot_info->imp_scheduler_count++]; + memset(scheduler_snapshot, 0, sizeof(*scheduler_snapshot)); + scheduler_snapshot->id = i; + scheduler_snapshot->digger_list_start = dungeon->digger_list_start; + scheduler_snapshot->task_count = dungeon->task_count; + scheduler_snapshot->highest_task_number = dungeon->highest_task_number; + scheduler_snapshot->digger_stack_update_turn = dungeon->digger_stack_update_turn; + scheduler_snapshot->digger_stack_length = dungeon->digger_stack_length; + scheduler_snapshot->task_list_checksum = compute_dungeon_task_list_checksum(dungeon); + scheduler_snapshot->digger_stack_checksum = compute_dungeon_digger_stack_checksum(dungeon); + scheduler_snapshot->checksum = compute_imp_scheduler_checksum(dungeon); + for (int task_index = 0; task_index < MAPTASKS_COUNT; task_index++) { + struct LogMapTaskDesyncInfo* task_snapshot = &scheduler_snapshot->tasks[task_index]; + task_snapshot->kind = dungeon->task_list[task_index].kind; + task_snapshot->coords = dungeon->task_list[task_index].coords; + task_snapshot->checksum = get_map_task_checksum(&dungeon->task_list[task_index], task_index); + } + for (int digger_task_index = 0; digger_task_index < DIGGER_TASK_MAX_COUNT; digger_task_index++) { + struct LogDiggerStackDesyncInfo* digger_task_snapshot = &scheduler_snapshot->digger_tasks[digger_task_index]; + digger_task_snapshot->stl_num = dungeon->digger_stack[digger_task_index].stl_num; + digger_task_snapshot->task_type = dungeon->digger_stack[digger_task_index].task_type; + digger_task_snapshot->checksum = get_digger_stack_checksum(&dungeon->digger_stack[digger_task_index], digger_task_index); + } + } } snapshot_head = (snapshot_head + 1) % SNAPSHOT_BUFFER_SIZE; @@ -256,11 +512,12 @@ void update_turn_checksums(void) { packet->checksum += things_sum; packet->checksum += checksums->rooms; packet->checksum += checksums->players; + packet->checksum += checksums->imp_schedulers; packet->checksum += checksums->action_seed; packet->checksum += checksums->player_seed; packet->checksum += checksums->ai_seed; - MULTIPLAYER_LOG("update_turn_checksums: turn=%lu checksum=%08lx things=%08lx rooms=%08lx players=%08lx", (unsigned long)game.play_gameturn, (unsigned long)packet->checksum, (unsigned long)things_sum, (unsigned long)checksums->rooms, (unsigned long)checksums->players); + MULTIPLAYER_LOG("update_turn_checksums: turn=%lu checksum=%08lx things=%08lx rooms=%08lx players=%08lx imp_schedulers=%08lx", (unsigned long)game.play_gameturn, (unsigned long)packet->checksum, (unsigned long)things_sum, (unsigned long)checksums->rooms, (unsigned long)checksums->players, (unsigned long)checksums->imp_schedulers); } void pack_desync_history_for_resync(void) { @@ -270,6 +527,7 @@ void pack_desync_history_for_resync(void) { game.log_snapshot.thing_count = 0; game.log_snapshot.player_count = 0; game.log_snapshot.room_count = 0; + game.log_snapshot.imp_scheduler_count = 0; return; } game.host_checksums = snapshot->checksums; @@ -297,16 +555,86 @@ static void log_thing_differences(struct LogDetailedSnapshot* client, const char } if (host_thing == NULL || client_thing->checksum != host_thing->checksum) { if (host_thing != NULL) { - ERRORLOG(" [Host] Thing[%d] class_id=%d model=%d owner=%d mappos=(%ld,%ld,%ld) health=%ld creation_turn=%lu random_seed=%08x", host_thing->index, host_thing->class_id, host_thing->model, host_thing->owner, (long)host_thing->mappos.x.val, (long)host_thing->mappos.y.val, (long)host_thing->mappos.z.val, (long)host_thing->health, (unsigned long)host_thing->creation_turn, host_thing->random_seed); + log_thing_snapshot("Host", host_thing); } else { ERRORLOG(" [Host] Thing[%d] missing", client_thing->index); } - ERRORLOG(" [Client] Thing[%d] class_id=%d model=%d owner=%d mappos=(%ld,%ld,%ld) health=%ld creation_turn=%lu random_seed=%08x", client_thing->index, client_thing->class_id, client_thing->model, client_thing->owner, (long)client_thing->mappos.x.val, (long)client_thing->mappos.y.val, (long)client_thing->mappos.z.val, (long)client_thing->health, (unsigned long)client_thing->creation_turn, client_thing->random_seed); + log_thing_snapshot("Client", client_thing); shown++; } } } +static void log_imp_scheduler_differences(struct LogDetailedSnapshot* client, struct LogDetailedSnapshot* host, TbBigChecksum client_sum, TbBigChecksum host_sum) +{ + ERRORLOG(" ImpSchedulers %s - Host: %08lx, Client: %08lx", (client_sum == host_sum) ? "match" : "MISMATCH", (unsigned long)host_sum, (unsigned long)client_sum); + if (client_sum == host_sum) { + return; + } + for (int i = 0; i < client->imp_scheduler_count; i++) { + struct LogImpSchedulerDesyncInfo* client_scheduler = &client->imp_schedulers[i]; + struct LogImpSchedulerDesyncInfo* host_scheduler = NULL; + for (int j = 0; j < host->imp_scheduler_count; j++) { + if (host->imp_schedulers[j].id == client_scheduler->id) { + host_scheduler = &host->imp_schedulers[j]; + break; + } + } + if (host_scheduler == NULL) { + ERRORLOG(" ImpScheduler[%d] missing from host", client_scheduler->id); + continue; + } + if (client_scheduler->checksum == host_scheduler->checksum) { + continue; + } + log_imp_scheduler_snapshot("Host", host_scheduler); + log_imp_scheduler_snapshot("Client", client_scheduler); + if (client_scheduler->task_list_checksum != host_scheduler->task_list_checksum) { + int shown = 0; + int task_limit = client_scheduler->highest_task_number; + if (host_scheduler->highest_task_number > task_limit) { + task_limit = host_scheduler->highest_task_number; + } + if (task_limit > MAPTASKS_COUNT) { + task_limit = MAPTASKS_COUNT; + } + for (int task_index = 0; task_index < task_limit && shown < 10; task_index++) { + struct LogMapTaskDesyncInfo* client_task = &client_scheduler->tasks[task_index]; + struct LogMapTaskDesyncInfo* host_task = &host_scheduler->tasks[task_index]; + if (client_task->checksum == host_task->checksum) { + continue; + } + ERRORLOG(" Task[%d] kind: Host=%u Client=%u, coords: Host=%u Client=%u", + task_index, (unsigned)host_task->kind, (unsigned)client_task->kind, + (unsigned)host_task->coords, (unsigned)client_task->coords); + shown++; + } + } + if (client_scheduler->digger_stack_checksum != host_scheduler->digger_stack_checksum) { + int shown = 0; + uint32_t digger_task_limit = client_scheduler->digger_stack_length; + if (host_scheduler->digger_stack_length > digger_task_limit) { + digger_task_limit = host_scheduler->digger_stack_length; + } + if (digger_task_limit > DIGGER_TASK_MAX_COUNT) { + digger_task_limit = DIGGER_TASK_MAX_COUNT; + } + for (uint32_t digger_task_index = 0; digger_task_index < digger_task_limit && shown < 10; digger_task_index++) { + struct LogDiggerStackDesyncInfo* client_digger_task = &client_scheduler->digger_tasks[digger_task_index]; + struct LogDiggerStackDesyncInfo* host_digger_task = &host_scheduler->digger_tasks[digger_task_index]; + if (client_digger_task->checksum == host_digger_task->checksum) { + continue; + } + ERRORLOG(" DiggerTask[%lu] task_type: Host=%u Client=%u, stl_num: Host=%u Client=%u", + (unsigned long)digger_task_index, (unsigned)host_digger_task->task_type, + (unsigned)client_digger_task->task_type, (unsigned)host_digger_task->stl_num, + (unsigned)client_digger_task->stl_num); + shown++; + } + } + } +} + void compare_desync_history_from_host(void) { struct ChecksumSnapshot* snapshot = find_snapshot(desync_turn); if (snapshot == NULL) { @@ -366,6 +694,7 @@ void compare_desync_history_from_host(void) { } } } + log_imp_scheduler_differences(client_snapshot, host_snapshot, client->imp_schedulers, host->imp_schedulers); log_thing_differences(client_snapshot, "Creatures", client->creatures, host->creatures, TCls_Creature); log_thing_differences(client_snapshot, "Traps", client->traps, host->traps, TCls_Trap); log_thing_differences(client_snapshot, "Shots", client->shots, host->shots, TCls_Shot);