From 60bf5083fc1b077e7ece8ed0e3dcacc4e8d2bbba Mon Sep 17 00:00:00 2001 From: ZehM4tt Date: Mon, 12 Jun 2017 12:11:01 +0200 Subject: [PATCH] Fixed few multiplayer desync issues. (#5578) This addresses some of the desync causes: * `vehicle_create_car` was using `scenario_rand` when it shouldn't have * ghost elements affected grass growth * ghost elements affecting peep logic[1] It also adds some desync debug facilities, enabled at compile time. It also reverts part of change introduced in https://github.com/OpenRCT2/OpenRCT2/pull/5185, namely reorder of desync check vs call to `ProcessGameCommandQueue();` [1] It is not ideal to have this check in multiple locations, it is prone to human error. We already have `map_remove_provisional_elements`, but it is possible it does not work as well as it should. This needs further investigation. --- contributors.md | 1 + src/openrct2/game.c | 62 +++++---- src/openrct2/network/network.cpp | 21 ++- src/openrct2/network/network.h | 2 +- src/openrct2/peep/peep.c | 229 +++++++++++++++++++------------ src/openrct2/rct2.c | 2 +- src/openrct2/scenario/scenario.c | 70 +++++++++- src/openrct2/scenario/scenario.h | 10 ++ src/openrct2/world/map.c | 3 + 9 files changed, 283 insertions(+), 117 deletions(-) diff --git a/contributors.md b/contributors.md index ef6ceb7565..ed85e30416 100644 --- a/contributors.md +++ b/contributors.md @@ -78,6 +78,7 @@ Includes all git commit authors. Aliases are GitHub user names. * Marco Benzi Tobar (Lisergishnu) * Richard Jenkins (rwjuk) * (ceeac) +* Matthias Moninger (Zeh Matt) ## Toolchain * (Balletie) - macOS diff --git a/src/openrct2/game.c b/src/openrct2/game.c index a93d00ad50..d22396262e 100644 --- a/src/openrct2/game.c +++ b/src/openrct2/game.c @@ -305,6 +305,10 @@ void game_update() // Update the animation list. Note this does not // increment the map animation. map_animation_invalidate_all(); + + // Special case because we set numUpdates to 0, otherwise in game_logic_update. + game_handle_input(); + network_update(); } // Update the game one or more times @@ -337,6 +341,28 @@ void game_update() window_dispatch_update_all(); gGameCommandNestLevel = 0; +} + +void game_logic_update() +{ + + /////////////////////////// + gInUpdateCode = true; + /////////////////////////// + + network_update(); + + if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED && network_get_authstatus() == NETWORK_AUTH_OK) { + // Can't be in sync with server, round trips won't work if we are at same level. + if (gCurrentTicks >= network_get_server_tick()) { + // Don't run past the server + return; + } + } + + gScreenAge++; + if (gScreenAge == 0) + gScreenAge--; if (!gOpenRCT2Headless) { @@ -366,25 +392,6 @@ void game_update() gUnk141F568 = gUnk13CA740; game_handle_input(); } -} - -void game_logic_update() -{ - /////////////////////////// - gInUpdateCode = true; - /////////////////////////// - network_update(); - if (network_get_mode() == NETWORK_MODE_CLIENT && network_get_status() == NETWORK_STATUS_CONNECTED && network_get_authstatus() == NETWORK_AUTH_OK) { - if (gCurrentTicks >= network_get_server_tick()) { - // Don't run past the server - return; - } - } - gCurrentTicks++; - gScenarioTicks++; - gScreenAge++; - if (gScreenAge == 0) - gScreenAge--; sub_68B089(); scenario_update(); @@ -403,6 +410,7 @@ void game_logic_update() ride_ratings_update_all(); ride_measurements_update(); news_item_update_current(); + /////////////////////////// gInUpdateCode = false; /////////////////////////// @@ -413,8 +421,6 @@ void game_logic_update() climate_update_sound(); editor_open_windows_for_current_step(); - gSavedAge++; - // Update windows //window_dispatch_update_all(); @@ -431,8 +437,13 @@ void game_logic_update() } // Start autosave timer after update - if (gLastAutoSaveUpdate == AUTOSAVE_PAUSE) + if (gLastAutoSaveUpdate == AUTOSAVE_PAUSE) { gLastAutoSaveUpdate = platform_get_ticks(); + } + + gCurrentTicks++; + gScenarioTicks++; + gSavedAge++; } /** @@ -561,7 +572,8 @@ sint32 game_do_command_p(sint32 command, sint32 *eax, sint32 *ebx, sint32 *ecx, // Do the callback (required for multiplayer to work correctly), but only for top level commands if (gGameCommandNestLevel == 1) { - if (game_command_callback && !(flags & GAME_COMMAND_FLAG_GHOST)) { + if (game_command_callback && !(flags & GAME_COMMAND_FLAG_GHOST)) + { game_command_callback(*eax, *ebx, *ecx, *edx, *esi, *edi, *ebp); game_command_callback = 0; } @@ -1399,6 +1411,8 @@ void game_load_or_quit_no_save_prompt() */ void game_init_all(sint32 mapSize) { + gInUpdateCode = true; + map_init(mapSize); park_init(); finance_init(); @@ -1412,6 +1426,8 @@ void game_init_all(sint32 mapSize) news_item_init_queue(); user_string_clear_all(); + gInUpdateCode = false; + window_new_ride_init_vars(); window_guest_list_init_vars_a(); window_guest_list_init_vars_b(); diff --git a/src/openrct2/network/network.cpp b/src/openrct2/network/network.cpp index dc32c2c993..36a134449b 100644 --- a/src/openrct2/network/network.cpp +++ b/src/openrct2/network/network.cpp @@ -520,8 +520,11 @@ void Network::UpdateClient() } // Check synchronisation + ProcessGameCommandQueue(); + if (!_desynchronised && !CheckSRAND(gCurrentTicks, gScenarioSrand0)) { _desynchronised = true; + char str_desync[256]; format_string(str_desync, 256, STR_MULTIPLAYER_DESYNC, NULL); window_network_status_open(str_desync, NULL); @@ -530,7 +533,6 @@ void Network::UpdateClient() } } - ProcessGameCommandQueue(); break; } } @@ -614,12 +616,17 @@ bool Network::CheckSRAND(uint32 tick, uint32 srand0) return true; } - if (tick == server_srand0_tick) { + if (tick == server_srand0_tick) + { server_srand0_tick = 0; // Check that the server and client sprite hashes match - const bool sprites_mismatch = server_sprite_hash[0] != '\0' && strcmp(sprite_checksum(), server_sprite_hash); + const char *client_sprite_hash = sprite_checksum(); + const bool sprites_mismatch = server_sprite_hash[0] != '\0' && strcmp(client_sprite_hash, server_sprite_hash); // Check PRNG values and sprite hashes, if exist if ((srand0 != server_srand0) || sprites_mismatch) { +#ifdef DEBUG_DESYNC + dbg_report_desync(tick, srand0, server_srand0, client_sprite_hash, server_sprite_hash); +#endif return false; } } @@ -1315,7 +1322,13 @@ void Network::ProcessPacket(NetworkConnection& connection, NetworkPacket& packet void Network::ProcessGameCommandQueue() { - while (game_command_queue.begin() != game_command_queue.end() && game_command_queue.begin()->tick == gCurrentTicks) { + while (game_command_queue.begin() != game_command_queue.end()) { + // If our tick is higher than the command tick we are in trouble. + assert(game_command_queue.begin()->tick >= gCurrentTicks); + + if (game_command_queue.begin()->tick != gCurrentTicks) + return; + // run all the game commands at the current tick const GameCommand& gc = (*game_command_queue.begin()); if (GetPlayerID() == gc.playerid) { diff --git a/src/openrct2/network/network.h b/src/openrct2/network/network.h index 857df75997..7ff163073b 100644 --- a/src/openrct2/network/network.h +++ b/src/openrct2/network/network.h @@ -56,7 +56,7 @@ extern "C" { // This define specifies which version of network stream current build uses. // It is used for making sure only compatible builds get connected, even within // single OpenRCT2 version. -#define NETWORK_STREAM_VERSION "10" +#define NETWORK_STREAM_VERSION "11" #define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION #ifdef __cplusplus diff --git a/src/openrct2/peep/peep.c b/src/openrct2/peep/peep.c index cc4a676ea9..ffe5d75345 100644 --- a/src/openrct2/peep/peep.c +++ b/src/openrct2/peep/peep.c @@ -174,6 +174,12 @@ static void peep_update_ride_inspected(sint32 rideIndex); bool loc_690FD0(rct_peep *peep, uint8 *rideToView, uint8 *rideSeatToView, rct_map_element *esi); +#ifdef DEBUG_DESYNC +#define peep_rand() scenario_rand_data(peep) +#else +#define peep_rand() scenario_rand() +#endif + const char *gPeepEasterEggNames[] = { "MICHAEL SCHUMACHER", "JACQUES VILLENEUVE", @@ -736,7 +742,7 @@ static void peep_decide_whether_to_leave_park(rct_peep *peep) } // Approx 95% chance of staying in the park - if ((scenario_rand() & 0xFFFF) > 3276) { + if ((peep_rand() & 0xFFFF) > 3276) { return; } @@ -919,7 +925,7 @@ static void sub_68F41A(rct_peep *peep, sint32 index) * to reduce how often the content in this conditional * is executed to once every four calls. */ if (peep->peep_flags & PEEP_FLAGS_CROWDED){ - uint8 thought_type = crowded_thoughts[scenario_rand() & 0xF]; + uint8 thought_type = crowded_thoughts[peep_rand() & 0xF]; if (thought_type != PEEP_THOUGHT_TYPE_NONE){ peep_insert_new_thought(peep, thought_type, 0xFF); } @@ -1016,7 +1022,7 @@ static void sub_68F41A(rct_peep *peep, sint32 index) } } - if ((scenario_rand() & 0xFFFF) <= ((peep->item_standard_flags & PEEP_ITEM_MAP) ? 8192U : 2184U)){ + if ((peep_rand() & 0xFFFF) <= ((peep->item_standard_flags & PEEP_ITEM_MAP) ? 8192U : 2184U)){ peep_pick_ride_to_go_on(peep); } @@ -1072,7 +1078,7 @@ static void sub_68F41A(rct_peep *peep, sint32 index) } if (num_thoughts != 0){ - uint8 chosen_thought = possible_thoughts[scenario_rand() % num_thoughts]; + uint8 chosen_thought = possible_thoughts[peep_rand() % num_thoughts]; peep_insert_new_thought(peep, chosen_thought, 0xFF); @@ -1214,7 +1220,7 @@ static void sub_68F41A(rct_peep *peep, sint32 index) if (peep->state == PEEP_STATE_WALKING && peep->nausea_growth_rate >= 128){ - if ((scenario_rand() & 0xFF) <= (uint8)((peep->nausea - 128) / 2)){ + if ((peep_rand() & 0xFF) <= (uint8)((peep->nausea - 128) / 2)){ if (peep->action >= PEEP_ACTION_NONE_1){ peep->action = PEEP_ACTION_THROW_UP; peep->action_frame = 0; @@ -1623,7 +1629,7 @@ static sint32 peep_update_action(sint16* x, sint16* y, sint16* xy_distance, rct_ // Create sick at location litter_create(peep->x, peep->y, peep->z, peep->sprite_direction, (peep->sprite_index & 1) ? LITTER_TYPE_SICK_ALT: LITTER_TYPE_SICK); - sint32 sound_id = SOUND_COUGH_1 + (scenario_rand() & 3); + sint32 sound_id = SOUND_COUGH_1 + (peep_rand() & 3); audio_play_sound_at_location(sound_id, peep->x, peep->y, peep->z); invalidate_sprite_2((rct_sprite*)peep); @@ -1727,11 +1733,11 @@ void peep_update_sprite_type(rct_peep* peep) { if ( peep->sprite_type == PEEP_SPRITE_TYPE_BALLOON && - (scenario_rand() & 0xFFFF) <= 327 + (peep_rand() & 0xFFFF) <= 327 ) { bool isBalloonPopped = false; if (peep->x != SPRITE_LOCATION_NULL) { - if ((scenario_rand() & 0xFFFF) <= 13107) { + if ((peep_rand() & 0xFFFF) <= 13107) { isBalloonPopped = true; audio_play_sound_at_location(SOUND_BALLOON_POP, peep->x, peep->y, peep->z); } @@ -2260,7 +2266,7 @@ static void peep_update_sitting(rct_peep* peep){ } if (peep_has_food(peep)){ - if ((scenario_rand() & 0xFFFF) > 1310){ + if ((peep_rand() & 0xFFFF) > 1310){ peep_try_get_up_from_sitting(peep); return; } @@ -2272,7 +2278,7 @@ static void peep_update_sitting(rct_peep* peep){ return; } - sint32 rand = scenario_rand(); + sint32 rand = peep_rand(); if ((rand & 0xFFFF) > 131){ peep_try_get_up_from_sitting(peep); return; @@ -2330,10 +2336,10 @@ void remove_peep_from_queue(rct_peep* peep) * rct2: 0x00691C6E */ static rct_vehicle* peep_choose_car_from_ride(rct_peep* peep, rct_ride* ride, uint8* car_array, uint8 car_array_size){ - uint8 chosen_car = scenario_rand(); + uint8 chosen_car = peep_rand(); if (ride_type_has_flag(ride->type, RIDE_TYPE_FLAG_HAS_G_FORCES) && ((chosen_car & 0xC) != 0xC)){ - chosen_car = (scenario_rand() & 1) ? 0 : car_array_size - 1; + chosen_car = (peep_rand() & 1) ? 0 : car_array_size - 1; } else{ chosen_car = (chosen_car * (uint16)car_array_size) >> 8; @@ -2655,7 +2661,7 @@ static void peep_update_ride_sub_state_1(rct_peep* peep){ y += TileDirectionDelta[direction_entrance].y; uint8 direction = direction_entrance * 4 + 11; - if (scenario_rand() & 0x40){ + if (peep_rand() & 0x40){ direction += 4; peep->maze_last_edge += 2; } @@ -3583,7 +3589,7 @@ static void peep_update_ride_sub_state_14(rct_peep* peep){ else if (peep->current_car++ != 0){ if (ride->mode == RIDE_MODE_SINGLE_RIDE_PER_ADMISSION) last_ride = 1; - if ((uint8)(peep->current_car - 1) > (scenario_rand() & 0xF)) + if ((uint8)(peep->current_car - 1) > (peep_rand() & 0xF)) last_ride = 1; } @@ -3846,7 +3852,7 @@ static void peep_update_ride_sub_state_17(rct_peep* peep){ if (peep->action >= PEEP_ACTION_NONE_1){ if (peep->energy > 64 && - (scenario_rand() & 0xFFFF) <= 2427){ + (peep_rand() & 0xFFFF) <= 2427){ peep->action = PEEP_ACTION_JUMP; peep->action_frame = 0; @@ -3898,7 +3904,7 @@ static void peep_update_ride_sub_state_17(rct_peep* peep){ if (open_hedges == 0) open_hedges |= (1 << maze_last_edge); - uint8 chosen_edge = scenario_rand() & 0x3; + uint8 chosen_edge = peep_rand() & 0x3; while (!(open_hedges & (1 << chosen_edge))){ chosen_edge = (chosen_edge + 1) & 3; } @@ -4386,7 +4392,7 @@ static bool peep_update_fixing_sub_state_2345(bool firstRun, rct_peep *peep, rct if (!firstRun) { peep->sprite_direction = peep->direction << 3; - peep->action = (scenario_rand() & 1) ? PEEP_ACTION_STAFF_FIX_2 : PEEP_ACTION_STAFF_FIX; + peep->action = (peep_rand() & 1) ? PEEP_ACTION_STAFF_FIX_2 : PEEP_ACTION_STAFF_FIX; peep->action_sprite_image_offset = 0; peep->action_frame = 0; peep_update_current_action_sprite_type(peep); @@ -4882,7 +4888,7 @@ static void peep_update_queuing(rct_peep* peep){ peep_perform_next_action(peep); if (peep->action < 0xFE)return; if (peep->sprite_type == PEEP_SPRITE_TYPE_NORMAL) { - if (peep->time_in_queue >= 2000 && (0xFFFF & scenario_rand()) <= 119){ + if (peep->time_in_queue >= 2000 && (0xFFFF & peep_rand()) <= 119){ // Eat Food/Look at watch peep->action = PEEP_ACTION_EAT_FOOD; peep->action_frame = 0; @@ -4890,7 +4896,7 @@ static void peep_update_queuing(rct_peep* peep){ peep_update_current_action_sprite_type(peep); invalidate_sprite_2((rct_sprite*)peep); } - if (peep->time_in_queue >= 3500 && (0xFFFF & scenario_rand()) <= 93) + if (peep->time_in_queue >= 3500 && (0xFFFF & peep_rand()) <= 93) { //Create the I have been waiting in line ages thought peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_QUEUING_AGES, peep->current_ride); @@ -4933,7 +4939,7 @@ static void peep_update_queuing(rct_peep* peep){ } if (peep->time_in_queue < 4300) return; - if (peep->happiness <= 65 && (0xFFFF & scenario_rand()) < 2184){ + if (peep->happiness <= 65 && (0xFFFF & peep_rand()) < 2184){ //Give up queueing for the ride peep->sprite_direction ^= (1 << 4); invalidate_sprite_2((rct_sprite*)peep); @@ -5270,7 +5276,7 @@ static void peep_update_watching(rct_peep* peep){ } else{ if (peep_has_food(peep)){ - if ((scenario_rand() & 0xFFFF) <= 1310){ + if ((peep_rand() & 0xFFFF) <= 1310){ peep->action = PEEP_ACTION_EAT_FOOD; peep->action_frame = 0; peep->action_sprite_image_offset = 0; @@ -5280,7 +5286,7 @@ static void peep_update_watching(rct_peep* peep){ } } - if ((scenario_rand() & 0xFFFF) <= 655){ + if ((peep_rand() & 0xFFFF) <= 655){ peep->action = PEEP_ACTION_TAKE_PHOTO; peep->action_frame = 0; peep->action_sprite_image_offset = 0; @@ -5290,7 +5296,7 @@ static void peep_update_watching(rct_peep* peep){ } if ((peep->standing_flags & 1)){ - if ((scenario_rand() & 0xFFFF) <= 655){ + if ((peep_rand() & 0xFFFF) <= 655){ peep->action = PEEP_ACTION_WAVE; peep->action_frame = 0; peep->action_sprite_image_offset = 0; @@ -5380,7 +5386,7 @@ static sint32 peep_update_walking_find_bench(rct_peep* peep){ sint32 edges = (map_element->properties.path.edges & 0xF) ^ 0xF; if (edges == 0) return 0; - uint8 chosen_edge = scenario_rand() & 0x3; + uint8 chosen_edge = peep_rand() & 0x3; for (; !(edges & (1 << chosen_edge));)chosen_edge = (chosen_edge + 1) & 0x3; @@ -5405,7 +5411,7 @@ static sint32 peep_update_walking_find_bench(rct_peep* peep){ free_edge ^= 0x3; if (!free_edge){ - if (scenario_rand() & 0x8000000) free_edge = 1; + if (peep_rand() & 0x8000000) free_edge = 1; } peep->var_37 = ((free_edge & 1) << 2) | chosen_edge; @@ -5463,7 +5469,7 @@ static sint32 peep_update_walking_find_bin(rct_peep* peep){ sint32 edges = (map_element->properties.path.edges & 0xF) ^ 0xF; if (edges == 0) return 0; - uint8 chosen_edge = scenario_rand() & 0x3; + uint8 chosen_edge = peep_rand() & 0x3; // Note: Bin quantity is inverted 0 = full, 3 = empty uint8 bin_quantities = map_element->properties.path.addition_status; @@ -5516,7 +5522,7 @@ static void peep_update_walking_break_scenery(rct_peep* peep){ if ((peep->litter_count & 0xC0) != 0xC0 && (peep->disgusting_count & 0xC0) != 0xC0) return; - if ((scenario_rand() & 0xFFFF) > 3276) return; + if ((peep_rand() & 0xFFFF) > 3276) return; } if (peep->next_var_29 & 0x18) return; @@ -5793,7 +5799,7 @@ static void peep_update_using_bin(rct_peep* peep){ // OpenRCT2 modification: This previously used // the tick count as a simple random function // switched to scenario_rand as it is more reliable - if ((scenario_rand() & 7) == 0) space_left_in_bin--; + if ((peep_rand() & 7) == 0) space_left_in_bin--; peep->item_standard_flags &= ~(1 << cur_container); peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; peep_update_sprite_type(peep); @@ -5802,10 +5808,10 @@ static void peep_update_using_bin(rct_peep* peep){ uint8 bp = item_standard_litter[cur_container]; sint32 x, y; - x = peep->x + (scenario_rand() & 7) - 3; - y = peep->y + (scenario_rand() & 7) - 3; + x = peep->x + (peep_rand() & 7) - 3; + y = peep->y + (peep_rand() & 7) - 3; - litter_create(x, y, peep->z, scenario_rand() & 3, bp); + litter_create(x, y, peep->z, peep_rand() & 3, bp); peep->item_standard_flags &= ~(1 << cur_container); peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -5823,7 +5829,7 @@ static void peep_update_using_bin(rct_peep* peep){ // OpenRCT2 modification: This previously used // the tick count as a simple random function // switched to scenario_rand as it is more reliable - if ((scenario_rand() & 7) == 0) space_left_in_bin--; + if ((peep_rand() & 7) == 0) space_left_in_bin--; peep->item_extra_flags &= ~(1 << cur_container); peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -5833,10 +5839,10 @@ static void peep_update_using_bin(rct_peep* peep){ uint8 bp = item_extra_litter[cur_container]; sint32 x, y; - x = peep->x + (scenario_rand() & 7) - 3; - y = peep->y + (scenario_rand() & 7) - 3; + x = peep->x + (peep_rand() & 7) - 3; + y = peep->y + (peep_rand() & 7) - 3; - litter_create(x, y, peep->z, scenario_rand() & 3, bp); + litter_create(x, y, peep->z, peep_rand() & 3, bp); peep->item_extra_flags &= ~(1 << cur_container); peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; @@ -6103,7 +6109,7 @@ static sint32 peep_update_patrolling_find_watering(rct_peep* peep){ if (!(peep->staff_orders & STAFF_ORDERS_WATER_FLOWERS)) return 0; - uint8 chosen_position = scenario_rand() & 7; + uint8 chosen_position = peep_rand() & 7; for (sint32 i = 0; i < 8; ++i, ++chosen_position){ chosen_position &= 7; @@ -6373,7 +6379,7 @@ static void peep_update_walking(rct_peep* peep){ if (peep->peep_flags & PEEP_FLAGS_WAVING){ if (peep->action >= PEEP_ACTION_NONE_1){ - if ((0xFFFF & scenario_rand()) < 936){ + if ((0xFFFF & peep_rand()) < 936){ invalidate_sprite_2((rct_sprite*)peep); peep->action = PEEP_ACTION_WAVE_2; @@ -6388,7 +6394,7 @@ static void peep_update_walking(rct_peep* peep){ if (peep->peep_flags & PEEP_FLAGS_PHOTO){ if (peep->action >= PEEP_ACTION_NONE_1){ - if ((0xFFFF & scenario_rand()) < 936){ + if ((0xFFFF & peep_rand()) < 936){ invalidate_sprite_2((rct_sprite*)peep); peep->action = PEEP_ACTION_TAKE_PHOTO; @@ -6403,7 +6409,7 @@ static void peep_update_walking(rct_peep* peep){ if (peep->peep_flags & PEEP_FLAGS_PAINTING){ if (peep->action >= PEEP_ACTION_NONE_1){ - if ((0xFFFF & scenario_rand()) < 936){ + if ((0xFFFF & peep_rand()) < 936){ invalidate_sprite_2((rct_sprite*)peep); peep->action = PEEP_ACTION_DRAW_PICTURE; @@ -6418,17 +6424,17 @@ static void peep_update_walking(rct_peep* peep){ if (peep->peep_flags & PEEP_FLAGS_LITTER){ if (!(peep->next_var_29 & 0x18)){ - if ((0xFFFF & scenario_rand()) <= 4096){ + if ((0xFFFF & peep_rand()) <= 4096){ static const uint8 litter_types[] = { LITTER_TYPE_EMPTY_CAN, LITTER_TYPE_RUBBISH, LITTER_TYPE_EMPTY_BURGER_BOX, LITTER_TYPE_EMPTY_CUP, }; - sint32 ebp = litter_types[scenario_rand() & 0x3]; - sint32 x = peep->x + (scenario_rand() & 0x7) - 3; - sint32 y = peep->y + (scenario_rand() & 0x7) - 3; - sint32 direction = (scenario_rand() & 0x3); + sint32 ebp = litter_types[peep_rand() & 0x3]; + sint32 x = peep->x + (peep_rand() & 0x7) - 3; + sint32 y = peep->y + (peep_rand() & 0x7) - 3; + sint32 direction = (peep_rand() & 0x3); litter_create(x, y, peep->z, direction, ebp); } @@ -6437,7 +6443,7 @@ static void peep_update_walking(rct_peep* peep){ else if (peep_has_empty_container(peep)){ if ((!(peep->next_var_29 & 0x18)) && ((uint32)(peep->sprite_index & 0x1FF) == (gCurrentTicks & 0x1FF))&& - ((0xFFFF & scenario_rand()) <= 4096)){ + ((0xFFFF & peep_rand()) <= 4096)){ uint8 pos_stnd = 0; for (sint32 container = peep_empty_container_standard_flag(peep); pos_stnd < 32; pos_stnd++) @@ -6462,9 +6468,9 @@ static void peep_update_walking(rct_peep* peep){ peep->window_invalidate_flags |= PEEP_INVALIDATE_PEEP_INVENTORY; peep_update_sprite_type(peep); - sint32 x = peep->x + (scenario_rand() & 0x7) - 3; - sint32 y = peep->y + (scenario_rand() & 0x7) - 3; - sint32 direction = (scenario_rand() & 0x3); + sint32 x = peep->x + (peep_rand() & 0x7) - 3; + sint32 y = peep->y + (peep_rand() & 0x7) - 3; + sint32 direction = (peep_rand() & 0x3); litter_create(x, y, peep->z, direction, bp); } @@ -6512,7 +6518,7 @@ static void peep_update_walking(rct_peep* peep){ uint16 chance = peep_has_food(peep) ? 13107 : 2849; - if ((scenario_rand() & 0xFFFF) > chance)return; + if ((peep_rand() & 0xFFFF) > chance)return; if (peep->next_var_29 & 0x1C)return; @@ -6540,7 +6546,7 @@ static void peep_update_walking(rct_peep* peep){ sint32 edges = (map_element->properties.path.edges & 0xF) ^ 0xF; if (edges == 0) return; - uint8 chosen_edge = scenario_rand() & 0x3; + uint8 chosen_edge = peep_rand() & 0x3; for (; !(edges & (1 << chosen_edge));)chosen_edge = (chosen_edge + 1) & 3; @@ -6565,7 +6571,7 @@ static void peep_update_walking(rct_peep* peep){ if (!ebp)return; - uint8 chosen_position = scenario_rand() & 0x3; + uint8 chosen_position = peep_rand() & 0x3; for (; !(ebp & (1 << chosen_position));)chosen_position = (chosen_position + 1) & 3; @@ -7150,7 +7156,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) sprite_move(x, y, z, (rct_sprite*)peep); invalidate_sprite_2((rct_sprite*)peep); - peep->var_41 = (scenario_rand() & 0x1F) + 45; + peep->var_41 = (peep_rand() & 0x1F) + 45; peep->var_C4 = 0; peep->interaction_ride_index = 0xFF; peep->type = PEEP_TYPE_GUEST; @@ -7158,7 +7164,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) peep->thoughts->type = PEEP_THOUGHT_TYPE_NONE; peep->window_invalidate_flags = 0; - uint8 al = (scenario_rand() & 0x7) + 3; + uint8 al = (peep_rand() & 0x7) + 3; uint8 ah = min(al, 7) - 3; if (al >= 7) al = 15; @@ -7175,7 +7181,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) peep->intensity = (al << 4) | ah; - uint8 nausea_tolerance = scenario_rand() & 0x7; + uint8 nausea_tolerance = peep_rand() & 0x7; if (gParkFlags & PARK_FLAGS_PREF_MORE_INTENSE_RIDES){ nausea_tolerance += 4; } @@ -7191,7 +7197,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) if (gGuestInitialHappiness == 0) peep->happiness = 128; /* Initial value will vary by -15..16 */ - sint8 happiness_delta = (scenario_rand() & 0x1F) - 15; + sint8 happiness_delta = (peep_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ peep->happiness = clamp(0, peep->happiness + happiness_delta, 255); peep->happiness_growth_rate = peep->happiness; @@ -7203,7 +7209,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) * to any value 0..255. */ peep->hunger = gGuestInitialHunger; /* Initial value will vary by -15..16 */ - sint8 hunger_delta = (scenario_rand() & 0x1F) - 15; + sint8 hunger_delta = (peep_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ peep->hunger = clamp(0, peep->hunger + hunger_delta, 255); @@ -7212,7 +7218,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) * to any value 0..255. */ peep->thirst = gGuestInitialThirst; /* Initial value will vary by -15..16 */ - sint8 thirst_delta = (scenario_rand() & 0x1F) - 15; + sint8 thirst_delta = (peep_rand() & 0x1F) - 15; /* Adjust by the delta, clamping at min=0 and max=255. */ peep->thirst = clamp(0, peep->thirst + thirst_delta, 0xFF); @@ -7225,7 +7231,7 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) peep->id = gNextGuestNumber++; peep->name_string_idx = STR_GUEST_X; - money32 cash = (scenario_rand() & 0x3) * 100 - 100 + gGuestInitialCash; + money32 cash = (peep_rand() & 0x3) * 100 - 100 + gGuestInitialCash; if (cash < 0) cash = 0; if (gGuestInitialCash == 0){ @@ -7265,15 +7271,15 @@ rct_peep *peep_generate(sint32 x, sint32 y, sint32 z) peep->angriness = 0; peep->var_F4 = 0; - uint8 tshirt_colour = scenario_rand() % countof(tshirt_colours); + uint8 tshirt_colour = peep_rand() % countof(tshirt_colours); peep->tshirt_colour = tshirt_colours[tshirt_colour]; - uint8 trousers_colour = scenario_rand() % countof(trouser_colours); + uint8 trousers_colour = peep_rand() % countof(trouser_colours); peep->trousers_colour = trouser_colours[trousers_colour]; /* It looks like 65 is about 50% energy level, so this initialises * a peep with approx 50%-100% energy. */ - uint8 energy = (scenario_rand() & 0x3F) + 65; + uint8 energy = (peep_rand() & 0x3F) + 65; peep->energy = energy; peep->energy_growth_rate = energy; @@ -8228,7 +8234,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct if (peep->var_EF & 0x3E && !(peep->var_EF & 0xC0)){ - if ((scenario_rand() & 0xFFFF) <= 10922){ + if ((peep_rand() & 0xFFFF) <= 10922){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_VANDALISM, 0xFF); peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17); } @@ -8237,7 +8243,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct } if (peep->var_EF & 0xC0 && - (scenario_rand()&0xFFFF) <= 4369){ + (peep_rand()&0xFFFF) <= 4369){ peep->var_EF -= 0x40; } @@ -8273,7 +8279,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct if (crowded >= 10 && peep->state == PEEP_STATE_WALKING && - (scenario_rand() & 0xFFFF) <= 21845){ + (peep_rand() & 0xFFFF) <= 21845){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_CROWDED, 0xFF); peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 14); @@ -8287,7 +8293,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct peep->disgusting_count = disgusting_count | disgusting_time; if (disgusting_time & 0xC0 && - (scenario_rand() & 0xFFFF) <= 4369){ + (peep_rand() & 0xFFFF) <= 4369){ // Reduce the disgusting time peep->disgusting_count -= 0x40; } @@ -8298,7 +8304,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct } if (total_sick >= 3 && - (scenario_rand() & 0xFFFF) <= 10922){ + (peep_rand() & 0xFFFF) <= 10922){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_PATH_DISGUSTING, 0xFF); peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17); // Reset disgusting time @@ -8311,7 +8317,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct peep->litter_count = litter_count | litter_time; if (litter_time & 0xC0 && - (scenario_rand() & 0xFFFF) <= 4369){ + (peep_rand() & 0xFFFF) <= 4369){ // Reduce the litter time peep->litter_count -= 0x40; } @@ -8322,7 +8328,7 @@ static sint32 peep_footpath_move_forward(rct_peep* peep, sint16 x, sint16 y, rct } if (total_litter >= 3 && - (scenario_rand() & 0xFFFF) <= 10922){ + (peep_rand() & 0xFFFF) <= 10922){ peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_BAD_LITTER, 0xFF); peep->happiness_growth_rate = max(0, peep->happiness_growth_rate - 17); // Reset litter time @@ -8549,7 +8555,7 @@ static sint32 peep_move_one_tile(uint8 direction, rct_peep* peep){ peep->destination_y = y + 16; peep->destination_tolerence = 2; if (peep->state != PEEP_STATE_QUEUING){ - peep->destination_tolerence = (scenario_rand() & 7) + 2; + peep->destination_tolerence = (peep_rand() & 7) + 2; } return 0; } @@ -8562,7 +8568,7 @@ static sint32 guest_surface_path_finding(rct_peep* peep){ sint16 x = peep->next_x; sint16 y = peep->next_y; sint16 z = peep->next_z; - uint8 randDirection = scenario_rand() & 3; + uint8 randDirection = peep_rand() & 3; if (!fence_in_the_way(x, y, z, z + 4, randDirection)){ x += TileDirectionDelta[randDirection].x; @@ -8577,7 +8583,7 @@ static sint32 guest_surface_path_finding(rct_peep* peep){ } randDirection++; - uint8 rand_backwards = scenario_rand() & 1; + uint8 rand_backwards = peep_rand() & 1; if (rand_backwards){ randDirection -= 2; } @@ -8854,7 +8860,7 @@ static uint8 footpath_element_destination_in_direction(sint16 x, sint16 y, sint1 * rct2: 0x00695225 */ static sint32 guest_path_find_aimless(rct_peep* peep, uint8 edges){ - if (scenario_rand() & 1){ + if (peep_rand() & 1){ // If possible go straight if (edges & (1 << peep->direction)){ return peep_move_one_tile(peep->direction, peep); @@ -8862,7 +8868,7 @@ static sint32 guest_path_find_aimless(rct_peep* peep, uint8 edges){ } while (1){ - uint8 direction = scenario_rand() & 3; + uint8 direction = peep_rand() & 3; // Otherwise go in a random direction allowed from the tile. if (edges & (1 << direction)){ return peep_move_one_tile(direction, peep); @@ -8880,7 +8886,7 @@ static uint8 peep_pathfind_get_max_number_junctions(rct_peep* peep){ // PEEP_FLAGS_2? It's cleared here but not set anywhere! if ((peep->peep_flags & PEEP_FLAGS_2)){ - if ((scenario_rand() & 0xFFFF) <= 7281) + if ((peep_rand() & 0xFFFF) <= 7281) peep->peep_flags &= ~PEEP_FLAGS_2; return 8; @@ -10274,7 +10280,7 @@ static sint32 guest_path_finding(rct_peep* peep) * In principle, peeps with food are not paying as much attention to * where they are going and are consequently more like to walk up * dead end paths, paths to ride exits, etc. */ - if (!peep_has_food(peep) && (scenario_rand() & 0xFFFF) >= 2184) { + if (!peep_has_food(peep) && (peep_rand() & 0xFFFF) >= 2184) { uint8 adjustedEdges = edges; for (sint32 chosenDirection = 0; chosenDirection < 4; chosenDirection++) { // If there is no path in that direction try another @@ -10305,7 +10311,7 @@ static sint32 guest_path_finding(rct_peep* peep) if (peep_heading_for_ride_or_park_exit(peep)) { probability = 9362; } - if ((scenario_rand() & 0xFFFF) < probability) { + if ((peep_rand() & 0xFFFF) < probability) { peep_read_map(peep); } } @@ -10829,7 +10835,7 @@ static bool peep_should_go_on_ride_again(rct_peep *peep, rct_ride *ride) if (peep->thirst < 20) return false; if (peep->balloon_colour > 170) return false; - uint8 r = (scenario_rand() & 0xFF); + uint8 r = (peep_rand() & 0xFF); if (r <= 128) { if (peep->no_of_rides > 7) return false; if (r > 64) return false; @@ -10843,7 +10849,7 @@ static bool peep_should_preferred_intensity_increase(rct_peep *peep) if (gParkFlags & PARK_FLAGS_PREF_LESS_INTENSE_RIDES) return false; if (peep->happiness < 200) return false; - return (scenario_rand() & 0xFF) >= peep->intensity; + return (peep_rand() & 0xFF) >= peep->intensity; } static bool peep_really_liked_ride(rct_peep *peep, rct_ride *ride) @@ -10897,7 +10903,7 @@ static void peep_on_exit_ride(rct_peep *peep, sint32 rideIndex) if (peep_really_liked_ride(peep, ride)) { peep_insert_new_thought(peep, PEEP_THOUGHT_TYPE_WAS_GREAT, rideIndex); - sint32 laugh = scenario_rand() & 7; + sint32 laugh = peep_rand() & 7; if (laugh < 3) { audio_play_sound_at_location(SOUND_LAUGH_1 + laugh, peep->x, peep->y, peep->z); } @@ -10988,7 +10994,7 @@ static bool peep_decide_and_buy_item(rct_peep *peep, sint32 rideIndex, sint32 sh goto loc_69B119; if ((shopItem != SHOP_ITEM_MAP) && shop_item_is_souvenir(shopItem) && !has_voucher) { - if (((scenario_rand() & 0x7F) + 0x73) > peep->happiness) + if (((peep_rand() & 0x7F) + 0x73) > peep->happiness) return 0; else if (peep->no_of_rides < 3) return 0; @@ -11028,7 +11034,7 @@ loc_69B119: if (peep->happiness >= 180) value /= 2; - if (value > ((money16)(scenario_rand() & 0x07))) { + if (value > ((money16)(peep_rand() & 0x07))) { // "I'm not paying that much for x" uint8 thought_type = (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2_MUCH + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON_MUCH + shopItem)); peep_insert_new_thought(peep, thought_type, rideIndex); @@ -11040,7 +11046,7 @@ loc_69B119: value = max(8, value); if (!(gParkFlags & PARK_FLAGS_NO_MONEY)) { - if (value >= (money32)(scenario_rand() & 0x07)) { + if (value >= (money32)(peep_rand() & 0x07)) { // "This x is a really good value" uint8 thought_item = (shopItem >= 32 ? (PEEP_THOUGHT_TYPE_PHOTO2 + (shopItem - 32)) : (PEEP_THOUGHT_TYPE_BALLOON + shopItem)); peep_insert_new_thought(peep, thought_item, rideIndex); @@ -11176,7 +11182,7 @@ static bool peep_should_use_cash_machine(rct_peep *peep, sint32 rideIndex) if (gParkFlags & PARK_FLAGS_NO_MONEY) return false; if (peep->peep_flags & PEEP_FLAGS_LEAVING_PARK) return false; if (peep->cash_in_pocket > MONEY(20,00)) return false; - if (115 + (scenario_rand() % 128) > peep->happiness) return false; + if (115 + (peep_rand() % 128) > peep->happiness) return false; if (peep->energy < 80) return false; rct_ride *ride = get_ride(rideIndex); @@ -11321,7 +11327,7 @@ static void peep_easter_egg_peep_interactions(rct_peep *peep) } if (peep->peep_flags & PEEP_FLAGS_JOY) { - if (scenario_rand() <= 1456) { + if (peep_rand() <= 1456) { if (peep->action == PEEP_ACTION_NONE_1 || peep->action == PEEP_ACTION_NONE_2) { peep->action = PEEP_ACTION_JOY; peep->action_frame = 0; @@ -11344,6 +11350,13 @@ static void peep_easter_egg_peep_interactions(rct_peep *peep) */ static bool peep_should_watch_ride(rct_map_element *mapElement) { rct_ride *ride = get_ride(mapElement->properties.track.ride_index); + + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) return false; + } + if (gRideClassifications[ride->type] != RIDE_CLASS_RIDE) { return false; } @@ -11394,6 +11407,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_WALL) continue; if (map_element_get_direction(mapElement) != edge) continue; if (get_wall_entry(mapElement->properties.wall.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue; @@ -11415,6 +11433,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_WALL) continue; if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue; if (get_wall_entry(mapElement->properties.wall.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue; @@ -11429,6 +11452,12 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: Extract loop B mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } + if (mapElement->clearance_height + 1 < peep->next_z) continue; if (peep->next_z + 6 < mapElement->base_height) continue; @@ -11458,6 +11487,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: Extract loop C mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (mapElement->clearance_height + 1 < peep->next_z) continue; if (peep->next_z + 6 < mapElement->base_height) continue; if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SURFACE) continue; @@ -11485,6 +11519,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: extract loop A mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_WALL) continue; if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue; if (get_wall_entry(mapElement->properties.wall.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue; @@ -11498,6 +11537,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: Extract loop B mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (mapElement->clearance_height + 1 < peep->next_z) continue; if (peep->next_z + 8 < mapElement->base_height) continue; @@ -11527,6 +11571,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: Extract loop C mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (mapElement->clearance_height + 1 < peep->next_z) continue; if (peep->next_z + 8 < mapElement->base_height) continue; if (map_element_get_type(mapElement) == MAP_ELEMENT_TYPE_SURFACE) continue; @@ -11553,6 +11602,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: extract loop A mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (map_element_get_type(mapElement) != MAP_ELEMENT_TYPE_WALL) continue; if ((map_element_get_direction(mapElement) ^ 0x2) != edge) continue; if (get_wall_entry(mapElement->properties.wall.type)->wall.flags2 & WALL_SCENERY_2_FLAG4) continue; @@ -11566,6 +11620,11 @@ static bool peep_find_ride_to_look_at(rct_peep *peep, uint8 edge, uint8 *rideToV // TODO: Extract loop B mapElement = surfaceElement; do { + // Ghosts are purely this-client-side and should not cause any interaction, + // as that may lead to a desync. + if (network_get_mode() != NETWORK_MODE_NONE) { + if (map_element_is_ghost(mapElement)) continue; + } if (mapElement->clearance_height + 1 < peep->next_z) continue; if (peep->next_z + 10 < mapElement->base_height) continue; @@ -11848,7 +11907,7 @@ static bool peep_should_go_on_ride(rct_peep *peep, sint32 rideIndex, sint32 entr // If the ride has not yet been rated and is capable of having g-forces, // there's a 90% chance that the peep will ignore it. if (!ride_has_ratings(ride) && (RideData4[ride->type].flags & RIDE_TYPE_FLAG4_PEEP_CHECK_GFORCES)) { - if ((scenario_rand() & 0xFFFF) > 0x1999U) { + if ((peep_rand() & 0xFFFF) > 0x1999U) { peep_chose_not_to_go_on_ride(peep, rideIndex, peepAtRide, false); return false; } diff --git a/src/openrct2/rct2.c b/src/openrct2/rct2.c index 6e242fa305..1b0fd6e576 100644 --- a/src/openrct2/rct2.c +++ b/src/openrct2/rct2.c @@ -386,7 +386,7 @@ void rct2_update() // TODO: screenshot countdown process - network_update(); + // network_update() is called in game_update // check_cmdline_arg(); // Screens diff --git a/src/openrct2/scenario/scenario.c b/src/openrct2/scenario/scenario.c index 73041a6143..df64ccc058 100644 --- a/src/openrct2/scenario/scenario.c +++ b/src/openrct2/scenario/scenario.c @@ -566,20 +566,84 @@ static sint32 scenario_create_ducks() * * @return eax */ +#ifndef DEBUG_DESYNC uint32 scenario_rand() +#else +static FILE *fp = NULL; +static const char *realm = "LC"; + +uint32 dbg_scenario_rand(const char *file, const char *function, const uint32 line, const void *data) +#endif { + uint32 originalSrand0 = gScenarioSrand0; + gScenarioSrand0 += ror32(gScenarioSrand1 ^ 0x1234567F, 7); + gScenarioSrand1 = ror32(originalSrand0, 3); + #ifdef DEBUG_DESYNC + if (fp == NULL) + { + if (network_get_mode() == NETWORK_MODE_SERVER) + { + fp = fopen("server_rand.txt", "wt"); + realm = "SV"; + } + else if (network_get_mode() == NETWORK_MODE_CLIENT) + { + fp = fopen("client_rand.txt", "wt"); + realm = "CL"; + } + else + { + if (fp) + fclose(fp); + fp = NULL; + realm = "LC"; + } + } + if (fp) + { + fprintf(fp, "Tick: %d, Rand: %08X - REF: %s:%u %s (%p)\n", gCurrentTicks, gScenarioSrand1, file, line, function, data); + } if (!gInUpdateCode) { log_warning("scenario_rand called from outside game update"); assert(false); } #endif - uint32 originalSrand0 = gScenarioSrand0; - gScenarioSrand0 += ror32(gScenarioSrand1 ^ 0x1234567F, 7); - return gScenarioSrand1 = ror32(originalSrand0, 3); + return gScenarioSrand1; } +#ifdef DEBUG_DESYNC +void dbg_report_desync(uint32 tick, uint32 srand0, uint32 server_srand0, const char *clientHash, const char *serverHash) +{ + if (fp == NULL) + { + if (network_get_mode() == NETWORK_MODE_SERVER) + { + fp = fopen("server_rand.txt", "wt"); + realm = "SV"; + } + else if (network_get_mode() == NETWORK_MODE_CLIENT) + { + fp = fopen("client_rand.txt", "wt"); + realm = "CL"; + } + } + if (fp) + { + const bool sprites_mismatch = serverHash[0] != '\0' && strcmp(clientHash, serverHash); + + fprintf(fp, "[%s] !! DESYNC !! Tick: %d, Client Hash: %s, Server Hash: %s, Client Rand: %08X, Server Rand: %08X - %s\n", realm, + tick, + clientHash, + ( (serverHash[0] != '\0') ? serverHash : "" ), + srand0, + server_srand0, + (sprites_mismatch ? "Sprite hash mismatch" : "scenario rand mismatch")); + } +} +#endif + uint32 scenario_rand_max(uint32 max) { if (max < 2) return 0; diff --git a/src/openrct2/scenario/scenario.h b/src/openrct2/scenario/scenario.h index 34adf9d49a..ed7a2c35f2 100644 --- a/src/openrct2/scenario/scenario.h +++ b/src/openrct2/scenario/scenario.h @@ -393,8 +393,18 @@ sint32 scenario_load(const char *path); sint32 scenario_load_and_play_from_path(const char *path); void scenario_begin(); void scenario_update(); + +#ifdef DEBUG_DESYNC +uint32 dbg_scenario_rand(const char *file, const char *function, const uint32 line, const void *data); +#define scenario_rand() dbg_scenario_rand(__FILE__, __FUNCTION__, __LINE__, NULL) +#define scenario_rand_data(data) dbg_scenario_rand(__FILE__, __FUNCTION__, __LINE__, data) +void dbg_report_desync(uint32 tick, uint32 srand0, uint32 server_srand0, const char *clientHash, const char *serverHash); +#else uint32 scenario_rand(); +#endif + uint32 scenario_rand_max(uint32 max); + sint32 scenario_prepare_for_save(); sint32 scenario_save(const utf8 * path, sint32 flags); void scenario_remove_trackless_rides(rct_s6_data *s6); diff --git a/src/openrct2/world/map.c b/src/openrct2/world/map.c index 573dca3842..e931bec6b1 100644 --- a/src/openrct2/world/map.c +++ b/src/openrct2/world/map.c @@ -3719,6 +3719,9 @@ static void map_update_grass_length(sint32 x, sint32 y, rct_map_element *mapElem mapElementAbove++; if (map_element_get_type(mapElementAbove) == MAP_ELEMENT_TYPE_WALL) continue; + // Grass should not be affected by ghost elements. + if (map_element_is_ghost(mapElementAbove)) + continue; if (z0 >= mapElementAbove->clearance_height) continue; if (z1 < mapElementAbove->base_height)