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.

(cherry picked from commit 6c1e243fa2)
This commit is contained in:
lawnjelly 2022-01-21 08:48:57 +00:00 committed by Rémi Verschelde
parent 37489bcd0e
commit 579c6238bb
No known key found for this signature in database
GPG Key ID: C3336907360768E1
5 changed files with 107 additions and 0 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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() {