From 6c1e243fa2ae24f1c972cbba8d0da66b1c887e41 Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Fri, 21 Jan 2022 08:48:57 +0000 Subject: [PATCH] Portals - fix gameplay monitor unloading The gameplay monitor wasn't being unloaded correctly in between levels. This meant that exit signals were not being sent, and entered signals for the new level were being missed. This PR sends appropriate exit signals on unloading, and clear the data. --- .../portals/portal_gameplay_monitor.cpp | 89 +++++++++++++++++++ .../visual/portals/portal_gameplay_monitor.h | 2 + servers/visual/portals/portal_renderer.cpp | 12 +++ servers/visual/portals/portal_renderer.h | 3 + servers/visual/portals/portal_types.h | 1 + 5 files changed, 107 insertions(+) diff --git a/servers/visual/portals/portal_gameplay_monitor.cpp b/servers/visual/portals/portal_gameplay_monitor.cpp index 75073b78fbd..7a46cc4c85b 100644 --- a/servers/visual/portals/portal_gameplay_monitor.cpp +++ b/servers/visual/portals/portal_gameplay_monitor.cpp @@ -74,6 +74,95 @@ bool PortalGameplayMonitor::_source_rooms_changed(const int *p_source_room_ids, return source_rooms_changed; } +void PortalGameplayMonitor::unload(PortalRenderer &p_portal_renderer) { + // First : send gameplay exit signals for any objects still in gameplay + //////////////////////////////////////////////////////////////////// + // lock output + VisualServerCallbacks *callbacks = VSG::scene->get_callbacks(); + callbacks->lock(); + + // Remove any movings + for (int n = 0; n < _active_moving_pool_ids_prev->size(); n++) { + int pool_id = (*_active_moving_pool_ids_prev)[n]; + PortalRenderer::Moving &moving = p_portal_renderer.get_pool_moving(pool_id); + moving.last_gameplay_tick_hit = 0; + + VisualServerCallbacks::Message msg; + msg.object_id = VSG::scene->_instance_get_object_ID(moving.instance); + msg.type = _exit_callback_type; + callbacks->push_message(msg); + } + + // Remove any roaming ghosts + for (int n = 0; n < _active_rghost_pool_ids_prev->size(); n++) { + int pool_id = (*_active_rghost_pool_ids_prev)[n]; + PortalRenderer::RGhost &moving = p_portal_renderer.get_pool_rghost(pool_id); + moving.last_gameplay_tick_hit = 0; + + VisualServerCallbacks::Message msg; + msg.object_id = moving.object_id; + msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY; + callbacks->push_message(msg); + } + + // Rooms + for (int n = 0; n < _active_room_ids_prev->size(); n++) { + int room_id = (*_active_room_ids_prev)[n]; + VSRoom &room = p_portal_renderer.get_room(room_id); + room.last_gameplay_tick_hit = 0; + + VisualServerCallbacks::Message msg; + msg.object_id = room._godot_instance_ID; + msg.type = _exit_callback_type; + callbacks->push_message(msg); + } + + // RoomGroups + for (int n = 0; n < _active_roomgroup_ids_prev->size(); n++) { + int roomgroup_id = (*_active_roomgroup_ids_prev)[n]; + VSRoomGroup &roomgroup = p_portal_renderer.get_roomgroup(roomgroup_id); + roomgroup.last_gameplay_tick_hit = 0; + + VisualServerCallbacks::Message msg; + msg.object_id = roomgroup._godot_instance_ID; + msg.type = _exit_callback_type; + callbacks->push_message(msg); + } + + // Static Ghosts + for (int n = 0; n < _active_sghost_ids_prev->size(); n++) { + int id = (*_active_sghost_ids_prev)[n]; + VSStaticGhost &ghost = p_portal_renderer.get_static_ghost(id); + ghost.last_gameplay_tick_hit = 0; + + VisualServerCallbacks::Message msg; + msg.object_id = ghost.object_id; + msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY; + callbacks->push_message(msg); + } + + // unlock + callbacks->unlock(); + + // Clear all remaining data + for (int n = 0; n < 2; n++) { + _active_moving_pool_ids[n].clear(); + _active_rghost_pool_ids[n].clear(); + _active_room_ids[n].clear(); + _active_roomgroup_ids[n].clear(); + _active_sghost_ids[n].clear(); + } + + _source_rooms_prev.clear(); + + // Lets not reset this just in case because it may be possible to have a moving outside the room system + // which is preserved between levels, and has a stored gameplay tick. And with uint32_t this should take + // a *long* time to rollover... (828 days?). And I don't think a rollover would actually cause a problem in practice. + // But can revisit this in the case of e.g. servers running continuously. + // We could alternatively go through all movings (not just active) etc and reset the last_gameplay_tick_hit to 0. + // _gameplay_tick = 1; +} + void PortalGameplayMonitor::set_params(bool p_use_secondary_pvs, bool p_use_signals) { _use_secondary_pvs = p_use_secondary_pvs; _use_signals = p_use_signals; diff --git a/servers/visual/portals/portal_gameplay_monitor.h b/servers/visual/portals/portal_gameplay_monitor.h index d9e2811b67d..5f2833fdc9f 100644 --- a/servers/visual/portals/portal_gameplay_monitor.h +++ b/servers/visual/portals/portal_gameplay_monitor.h @@ -43,6 +43,8 @@ class PortalGameplayMonitor { public: PortalGameplayMonitor(); + void unload(PortalRenderer &p_portal_renderer); + // entering and exiting gameplay notifications (requires PVS) void update_gameplay(PortalRenderer &p_portal_renderer, const int *p_source_room_ids, int p_num_source_rooms); void set_params(bool p_use_secondary_pvs, bool p_use_signals); diff --git a/servers/visual/portals/portal_renderer.cpp b/servers/visual/portals/portal_renderer.cpp index 3b30cc16759..39cd37e5206 100644 --- a/servers/visual/portals/portal_renderer.cpp +++ b/servers/visual/portals/portal_renderer.cpp @@ -996,6 +996,7 @@ void PortalRenderer::sprawl_roaming(uint32_t p_mover_pool_id, MovingBase &r_movi void PortalRenderer::_ensure_unloaded(String p_reason) { if (_loaded) { _loaded = false; + _gameplay_monitor.unload(*this); String str; if (p_reason != String()) { @@ -1014,6 +1015,17 @@ void PortalRenderer::_ensure_unloaded(String p_reason) { void PortalRenderer::rooms_and_portals_clear() { _loaded = false; + + // N.B. We want to make sure all the tick counters on movings rooms etc to zero, + // so that on loading the next level gameplay entered signals etc will be + // correctly sent and everything is fresh. + // This is mostly done by the gameplay_monitor, but rooms_and_portals_clear() + // will also clear tick counters where possible + // (there is no TrackedList for the RoomGroup pool for example). + // This could be made neater by moving everything to TrackedPooledLists, but this + // may be overkill. + _gameplay_monitor.unload(*this); + _statics.clear(); _static_ghosts.clear(); diff --git a/servers/visual/portals/portal_renderer.h b/servers/visual/portals/portal_renderer.h index 4445193312f..4a705681fce 100644 --- a/servers/visual/portals/portal_renderer.h +++ b/servers/visual/portals/portal_renderer.h @@ -87,6 +87,9 @@ public: void destroy() { _rooms.clear(); room_id = -1; + + last_tick_hit = 0; + last_gameplay_tick_hit = 0; } // the expanded aabb allows objects to move on most frames diff --git a/servers/visual/portals/portal_types.h b/servers/visual/portals/portal_types.h index 3332992fde0..14b5b0a9060 100644 --- a/servers/visual/portals/portal_types.h +++ b/servers/visual/portals/portal_types.h @@ -257,6 +257,7 @@ struct VSRoom { _secondary_pvs_size = 0; _priority = 0; _contains_internal_rooms = false; + last_gameplay_tick_hit = 0; } void cleanup_after_conversion() {