From b4e3d6772542e8f89d00aa2e7b7d6aba095032d5 Mon Sep 17 00:00:00 2001 From: Kp Date: Sun, 14 May 2023 18:41:36 +0000 Subject: [PATCH] Recompute guidebot index on game load The index of the guidebot is set by loading the level data, and this is usually sufficient. However, if the user kills the guidebot, then uses cheats to create a new one, the new guidebot will often have a different index than the original guidebot. If such a game is saved and then reloaded, then after the reload, the computed guidebot index will be reverted to the index of the original dead guidebot. This causes various problems, including potentially a crash. Recompute the guidebot index after loading the objects from the save file, so that it matches the live guidebot. Reported-by: GitInMotion --- common/main/fwd-object.h | 6 ------ common/main/object.h | 6 ++++++ similar/main/net_udp.cpp | 9 +++++---- similar/main/object.cpp | 32 +++++++++++++++++++++++++++++--- similar/main/state.cpp | 4 ++-- 5 files changed, 42 insertions(+), 15 deletions(-) diff --git a/common/main/fwd-object.h b/common/main/fwd-object.h index ce2d929df..d0cd9833f 100644 --- a/common/main/fwd-object.h +++ b/common/main/fwd-object.h @@ -267,12 +267,6 @@ void clear_transient_objects(int clear_all); [[nodiscard]] imobjptridx_t obj_allocate(d_level_unique_object_state &); -// after calling init_object(), the network code has grabbed specific -// object slots without allocating them. Go though the objects & -// build the free list, then set the apporpriate globals Don't call -// this function if you don't know what you're doing. -void special_reset_objects(d_level_unique_object_state &); - // attaches an object, such as a fireball, to another object, such as // a robot void obj_attach(object_array &Objects, vmobjptridx_t parent, vmobjptridx_t sub); diff --git a/common/main/object.h b/common/main/object.h index 9e613a0e1..636fcd6c9 100644 --- a/common/main/object.h +++ b/common/main/object.h @@ -727,6 +727,12 @@ struct d_level_unique_boss_state : ::dcx::d_level_unique_boss_state const player &get_player_controlling_guidebot(const d_unique_buddy_state & /* reserved for future use */, const valptridx::array_managed_type &Players); #endif +// after calling init_object(), the network code has grabbed specific +// object slots without allocating them. Go though the objects & +// build the free list, then set the appropriate globals. +// Don't call this function unless you know what you're doing. +void special_reset_objects(d_level_unique_object_state &, const d_robot_info_array &Robot_info); + unsigned laser_parent_is_player(fvcobjptr &, const laser_parent &, const object_base &); unsigned laser_parent_is_object(fvcobjptr &, const laser_parent &, const object_base &); unsigned laser_parent_is_object(const laser_parent &, vcobjptridx_t); diff --git a/similar/main/net_udp.cpp b/similar/main/net_udp.cpp index 2af6aa3e0..59f06f560 100644 --- a/similar/main/net_udp.cpp +++ b/similar/main/net_udp.cpp @@ -2479,9 +2479,10 @@ static int net_udp_verify_objects(int remote, int local) return(1); } -static void net_udp_read_object_packet(const uint8_t *const data) +static void net_udp_read_object_packet(const d_level_shared_robot_info_state &LevelSharedRobotInfoState, const uint8_t *const data) { auto &Objects = LevelUniqueObjectState.Objects; + auto &Robot_info = LevelSharedRobotInfoState.Robot_info; auto &vmobjptridx = Objects.vmptridx; // Object from another net player we need to sync with sbyte obj_owner; @@ -2512,7 +2513,7 @@ static void net_udp_read_object_packet(const uint8_t *const data) // Special debug checksum marker for entire send if (mode == 1) { - special_reset_objects(LevelUniqueObjectState); + special_reset_objects(LevelUniqueObjectState, Robot_info); mode = 0; } if (remote_objnum != object_count) { @@ -2538,7 +2539,7 @@ static void net_udp_read_object_packet(const uint8_t *const data) else { if (mode == 1) { - special_reset_objects(LevelUniqueObjectState); + special_reset_objects(LevelUniqueObjectState, Robot_info); mode = 0; } objnum = obj_allocate(LevelUniqueObjectState); @@ -3476,7 +3477,7 @@ static void net_udp_process_packet(const d_level_shared_robot_info_state &LevelS case upid::object_data: if (multi_i_am_master() || length > UPID_MAX_SIZE || Network_status != network_state::waiting) break; - net_udp_read_object_packet(data); + net_udp_read_object_packet(LevelSharedRobotInfoState, data); break; case upid::ping: if (multi_i_am_master()) diff --git a/similar/main/object.cpp b/similar/main/object.cpp index b9138a91b..1510846b7 100644 --- a/similar/main/object.cpp +++ b/similar/main/object.cpp @@ -914,8 +914,8 @@ void init_objects() //after calling init_object(), the network code has grabbed specific //object slots without allocating them. Go though the objects & build -//the free list, then set the apporpriate globals -void special_reset_objects(d_level_unique_object_state &LevelUniqueObjectState) +//the free list, then set the appropriate globals +void special_reset_objects(d_level_unique_object_state &LevelUniqueObjectState, const d_robot_info_array &Robot_info) { unsigned num_objects = MAX_OBJECTS; @@ -924,12 +924,34 @@ void special_reset_objects(d_level_unique_object_state &LevelUniqueObjectState) assert(Objects.front().type != OBJ_NONE); //0 should be used DXX_POISON_VAR(LevelUniqueObjectState.free_obj_list, 0xfd); +#if defined(DXX_BUILD_DESCENT_I) + /* Descent 1 does not have a guidebot, so there is nothing to fix up. For + * simplicity, both games pass the parameter. + */ + (void)Robot_info; +#elif defined(DXX_BUILD_DESCENT_II) + icobjidx_t Buddy_objnum = object_none; +#endif for (objnum_t i = MAX_OBJECTS; i--;) - if (Objects.vcptr(i)->type == OBJ_NONE) + { + const auto &obj = *Objects.vcptr(i); +#if defined(DXX_BUILD_DESCENT_II) + if (obj.type == OBJ_ROBOT) + { + auto &robptr = Robot_info[get_robot_id(obj)]; + if (robot_is_companion(robptr)) + Buddy_objnum = i; + } +#endif + if (obj.type == OBJ_NONE) LevelUniqueObjectState.free_obj_list[--num_objects] = i; else if (i > Highest_object_index) Objects.set_count(i + 1); + } +#if defined(DXX_BUILD_DESCENT_II) + LevelUniqueObjectState.BuddyState.Buddy_objnum = Buddy_objnum; +#endif LevelUniqueObjectState.num_objects = num_objects; } @@ -2180,6 +2202,10 @@ void reset_objects(d_level_unique_object_state &LevelUniqueObjectState, const un auto &Objects = LevelUniqueObjectState.get_objects(); assert(LevelUniqueObjectState.num_objects < Objects.size()); Objects.set_count(n_objs); +#if defined(DXX_BUILD_DESCENT_II) + if (LevelUniqueObjectState.BuddyState.Buddy_objnum.get_unchecked_index() >= n_objs) + LevelUniqueObjectState.BuddyState.Buddy_objnum = object_none; +#endif for (objnum_t i = n_objs; i < MAX_OBJECTS; ++i) { diff --git a/similar/main/state.cpp b/similar/main/state.cpp index 19a08f3be..b4e6e2281 100644 --- a/similar/main/state.cpp +++ b/similar/main/state.cpp @@ -2128,7 +2128,7 @@ int state_restore_all_sub(const d_level_shared_destructible_light_state &LevelSh } #endif } - special_reset_objects(LevelUniqueObjectState); + special_reset_objects(LevelUniqueObjectState, LevelSharedRobotInfoState.Robot_info); /* Reload plrobj reference. The player's object number may have * been changed by the state_object_rw_to_object call. */ @@ -2541,7 +2541,7 @@ int state_restore_all_sub(const d_level_shared_destructible_light_state &LevelSh if (!coop_player_got[i] && vcplayerptr(i)->connected == player_connection_status::playing) multi_disconnect_player(i); Viewer = ConsoleObject = &get_local_plrobj(); // make sure Viewer and ConsoleObject are set up (which we skipped by not using InitPlayerObject but we need since objects changed while loading) - special_reset_objects(LevelUniqueObjectState); // since we juggled around with objects to remap coop players rebuild the index of free objects + special_reset_objects(LevelUniqueObjectState, LevelSharedRobotInfoState.Robot_info); // since we juggled around with objects to remap coop players rebuild the index of free objects state_set_next_autosave(GameUniqueState, Netgame.MPGameplayOptions.AutosaveInterval); } else