portal_gameplay_monitor.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. /**************************************************************************/
  2. /* portal_gameplay_monitor.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "portal_gameplay_monitor.h"
  31. #include "portal_renderer.h"
  32. #include "portal_types.h"
  33. #include "servers/visual/visual_server_globals.h"
  34. #include "servers/visual/visual_server_scene.h"
  35. PortalGameplayMonitor::PortalGameplayMonitor() {
  36. _active_moving_pool_ids_prev = &_active_moving_pool_ids[0];
  37. _active_moving_pool_ids_curr = &_active_moving_pool_ids[1];
  38. _active_rghost_pool_ids_curr = &_active_rghost_pool_ids[0];
  39. _active_rghost_pool_ids_prev = &_active_rghost_pool_ids[1];
  40. _active_room_ids_prev = &_active_room_ids[0];
  41. _active_room_ids_curr = &_active_room_ids[1];
  42. _active_roomgroup_ids_prev = &_active_roomgroup_ids[0];
  43. _active_roomgroup_ids_curr = &_active_roomgroup_ids[1];
  44. _active_sghost_ids_prev = &_active_sghost_ids[0];
  45. _active_sghost_ids_curr = &_active_sghost_ids[1];
  46. }
  47. bool PortalGameplayMonitor::_source_rooms_changed(const int *p_source_room_ids, int p_num_source_rooms) {
  48. bool source_rooms_changed = false;
  49. if (p_num_source_rooms == _source_rooms_prev.size()) {
  50. for (int n = 0; n < p_num_source_rooms; n++) {
  51. if (p_source_room_ids[n] != (int)_source_rooms_prev[n]) {
  52. source_rooms_changed = true;
  53. break;
  54. }
  55. }
  56. } else {
  57. source_rooms_changed = true;
  58. }
  59. if (source_rooms_changed) {
  60. _source_rooms_prev.clear();
  61. for (int n = 0; n < p_num_source_rooms; n++) {
  62. _source_rooms_prev.push_back(p_source_room_ids[n]);
  63. }
  64. }
  65. return source_rooms_changed;
  66. }
  67. void PortalGameplayMonitor::unload(PortalRenderer &p_portal_renderer) {
  68. // First : send gameplay exit signals for any objects still in gameplay
  69. ////////////////////////////////////////////////////////////////////
  70. // lock output
  71. VisualServerCallbacks *callbacks = VSG::scene->get_callbacks();
  72. callbacks->lock();
  73. // Remove any movings
  74. for (int n = 0; n < _active_moving_pool_ids_prev->size(); n++) {
  75. int pool_id = (*_active_moving_pool_ids_prev)[n];
  76. PortalRenderer::Moving &moving = p_portal_renderer.get_pool_moving(pool_id);
  77. moving.last_gameplay_tick_hit = 0;
  78. VisualServerCallbacks::Message msg;
  79. msg.object_id = VSG::scene->_instance_get_object_ID(moving.instance);
  80. msg.type = _exit_callback_type;
  81. callbacks->push_message(msg);
  82. }
  83. // Remove any roaming ghosts
  84. for (int n = 0; n < _active_rghost_pool_ids_prev->size(); n++) {
  85. int pool_id = (*_active_rghost_pool_ids_prev)[n];
  86. PortalRenderer::RGhost &moving = p_portal_renderer.get_pool_rghost(pool_id);
  87. moving.last_gameplay_tick_hit = 0;
  88. VisualServerCallbacks::Message msg;
  89. msg.object_id = moving.object_id;
  90. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
  91. callbacks->push_message(msg);
  92. }
  93. // Rooms
  94. for (int n = 0; n < _active_room_ids_prev->size(); n++) {
  95. int room_id = (*_active_room_ids_prev)[n];
  96. VSRoom &room = p_portal_renderer.get_room(room_id);
  97. room.last_room_tick_hit = 0;
  98. VisualServerCallbacks::Message msg;
  99. msg.object_id = room._godot_instance_ID;
  100. msg.type = _exit_callback_type;
  101. callbacks->push_message(msg);
  102. }
  103. // RoomGroups
  104. for (int n = 0; n < _active_roomgroup_ids_prev->size(); n++) {
  105. int roomgroup_id = (*_active_roomgroup_ids_prev)[n];
  106. VSRoomGroup &roomgroup = p_portal_renderer.get_roomgroup(roomgroup_id);
  107. roomgroup.last_room_tick_hit = 0;
  108. VisualServerCallbacks::Message msg;
  109. msg.object_id = roomgroup._godot_instance_ID;
  110. msg.type = _exit_callback_type;
  111. callbacks->push_message(msg);
  112. }
  113. // Static Ghosts
  114. for (int n = 0; n < _active_sghost_ids_prev->size(); n++) {
  115. int id = (*_active_sghost_ids_prev)[n];
  116. VSStaticGhost &ghost = p_portal_renderer.get_static_ghost(id);
  117. ghost.last_room_tick_hit = 0;
  118. VisualServerCallbacks::Message msg;
  119. msg.object_id = ghost.object_id;
  120. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
  121. callbacks->push_message(msg);
  122. }
  123. // unlock
  124. callbacks->unlock();
  125. // Clear all remaining data
  126. for (int n = 0; n < 2; n++) {
  127. _active_moving_pool_ids[n].clear();
  128. _active_rghost_pool_ids[n].clear();
  129. _active_room_ids[n].clear();
  130. _active_roomgroup_ids[n].clear();
  131. _active_sghost_ids[n].clear();
  132. }
  133. _source_rooms_prev.clear();
  134. // Lets not reset this just in case because it may be possible to have a moving outside the room system
  135. // which is preserved between levels, and has a stored gameplay tick. And with uint32_t this should take
  136. // a *long* time to rollover... (828 days?). And I don't think a rollover would actually cause a problem in practice.
  137. // But can revisit this in the case of e.g. servers running continuously.
  138. // We could alternatively go through all movings (not just active) etc and reset the last_gameplay_tick_hit to 0.
  139. // _gameplay_tick = 1;
  140. }
  141. void PortalGameplayMonitor::set_params(bool p_use_secondary_pvs, bool p_use_signals) {
  142. _use_secondary_pvs = p_use_secondary_pvs;
  143. _use_signals = p_use_signals;
  144. if (_use_signals) {
  145. _enter_callback_type = VisualServerCallbacks::CALLBACK_SIGNAL_ENTER_GAMEPLAY;
  146. _exit_callback_type = VisualServerCallbacks::CALLBACK_SIGNAL_EXIT_GAMEPLAY;
  147. } else {
  148. _enter_callback_type = VisualServerCallbacks::CALLBACK_NOTIFICATION_ENTER_GAMEPLAY;
  149. _exit_callback_type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
  150. }
  151. }
  152. // can work with 1 or multiple cameras
  153. void PortalGameplayMonitor::update_gameplay(PortalRenderer &p_portal_renderer, const int *p_source_room_ids, int p_num_source_rooms) {
  154. const PVS &pvs = p_portal_renderer.get_pvs();
  155. _gameplay_tick++;
  156. // if there is no change in the source room IDs, then we can optimize out a lot of the checks
  157. // (anything not to do with roamers)
  158. bool source_rooms_changed = _source_rooms_changed(p_source_room_ids, p_num_source_rooms);
  159. if (source_rooms_changed) {
  160. _room_tick++;
  161. }
  162. // lock output
  163. VisualServerCallbacks *callbacks = VSG::scene->get_callbacks();
  164. callbacks->lock();
  165. for (int n = 0; n < p_num_source_rooms; n++) {
  166. const VSRoom &source_room = p_portal_renderer.get_room(p_source_room_ids[n]);
  167. if (_use_secondary_pvs) {
  168. int pvs_size = source_room._secondary_pvs_size;
  169. int pvs_first = source_room._secondary_pvs_first;
  170. for (int r = 0; r < pvs_size; r++) {
  171. int room_id = pvs.get_secondary_pvs_room_id(pvs_first + r);
  172. _update_gameplay_room(p_portal_renderer, room_id, source_rooms_changed);
  173. } // for r through the rooms hit in the pvs
  174. } else {
  175. int pvs_size = source_room._pvs_size;
  176. int pvs_first = source_room._pvs_first;
  177. for (int r = 0; r < pvs_size; r++) {
  178. int room_id = pvs.get_pvs_room_id(pvs_first + r);
  179. _update_gameplay_room(p_portal_renderer, room_id, source_rooms_changed);
  180. } // for r through the rooms hit in the pvs
  181. }
  182. } // for n through source rooms
  183. // find any moving that were active last tick that are no longer active, and send notifications
  184. for (int n = 0; n < _active_moving_pool_ids_prev->size(); n++) {
  185. int pool_id = (*_active_moving_pool_ids_prev)[n];
  186. PortalRenderer::Moving &moving = p_portal_renderer.get_pool_moving(pool_id);
  187. // gone out of view
  188. if (moving.last_gameplay_tick_hit != _gameplay_tick) {
  189. VisualServerCallbacks::Message msg;
  190. msg.object_id = VSG::scene->_instance_get_object_ID(moving.instance);
  191. msg.type = _exit_callback_type;
  192. callbacks->push_message(msg);
  193. }
  194. }
  195. // find any roaming ghosts that were active last tick that are no longer active, and send notifications
  196. for (int n = 0; n < _active_rghost_pool_ids_prev->size(); n++) {
  197. int pool_id = (*_active_rghost_pool_ids_prev)[n];
  198. PortalRenderer::RGhost &moving = p_portal_renderer.get_pool_rghost(pool_id);
  199. // gone out of view
  200. if (moving.last_gameplay_tick_hit != _gameplay_tick) {
  201. VisualServerCallbacks::Message msg;
  202. msg.object_id = moving.object_id;
  203. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
  204. callbacks->push_message(msg);
  205. }
  206. }
  207. if (source_rooms_changed) {
  208. // find any rooms that were active last tick that are no longer active, and send notifications
  209. for (int n = 0; n < _active_room_ids_prev->size(); n++) {
  210. int room_id = (*_active_room_ids_prev)[n];
  211. const VSRoom &room = p_portal_renderer.get_room(room_id);
  212. // gone out of view
  213. if (room.last_room_tick_hit != _room_tick) {
  214. VisualServerCallbacks::Message msg;
  215. msg.object_id = room._godot_instance_ID;
  216. msg.type = _exit_callback_type;
  217. callbacks->push_message(msg);
  218. }
  219. }
  220. // find any roomgroups that were active last tick that are no longer active, and send notifications
  221. for (int n = 0; n < _active_roomgroup_ids_prev->size(); n++) {
  222. int roomgroup_id = (*_active_roomgroup_ids_prev)[n];
  223. const VSRoomGroup &roomgroup = p_portal_renderer.get_roomgroup(roomgroup_id);
  224. // gone out of view
  225. if (roomgroup.last_room_tick_hit != _room_tick) {
  226. VisualServerCallbacks::Message msg;
  227. msg.object_id = roomgroup._godot_instance_ID;
  228. msg.type = _exit_callback_type;
  229. callbacks->push_message(msg);
  230. }
  231. }
  232. // find any static ghosts that were active last tick that are no longer active, and send notifications
  233. for (int n = 0; n < _active_sghost_ids_prev->size(); n++) {
  234. int id = (*_active_sghost_ids_prev)[n];
  235. VSStaticGhost &ghost = p_portal_renderer.get_static_ghost(id);
  236. // gone out of view
  237. if (ghost.last_room_tick_hit != _room_tick) {
  238. VisualServerCallbacks::Message msg;
  239. msg.object_id = ghost.object_id;
  240. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_EXIT_GAMEPLAY;
  241. callbacks->push_message(msg);
  242. }
  243. }
  244. } // only need to check these if the source rooms changed
  245. // unlock
  246. callbacks->unlock();
  247. // swap the current and previous lists
  248. _swap(source_rooms_changed);
  249. }
  250. void PortalGameplayMonitor::_update_gameplay_room(PortalRenderer &p_portal_renderer, int p_room_id, bool p_source_rooms_changed) {
  251. // get the room
  252. VSRoom &room = p_portal_renderer.get_room(p_room_id);
  253. int num_roamers = room._roamer_pool_ids.size();
  254. VisualServerCallbacks *callbacks = VSG::scene->get_callbacks();
  255. for (int n = 0; n < num_roamers; n++) {
  256. uint32_t pool_id = room._roamer_pool_ids[n];
  257. PortalRenderer::Moving &moving = p_portal_renderer.get_pool_moving(pool_id);
  258. // done already?
  259. if (moving.last_gameplay_tick_hit == _gameplay_tick)
  260. continue;
  261. // add to the active list
  262. _active_moving_pool_ids_curr->push_back(pool_id);
  263. // if wasn't present in the tick before, add the notification to enter
  264. if (moving.last_gameplay_tick_hit != (_gameplay_tick - 1)) {
  265. VisualServerCallbacks::Message msg;
  266. msg.object_id = VSG::scene->_instance_get_object_ID(moving.instance);
  267. msg.type = _enter_callback_type;
  268. callbacks->push_message(msg);
  269. }
  270. // mark as done
  271. moving.last_gameplay_tick_hit = _gameplay_tick;
  272. }
  273. // roaming ghosts
  274. int num_rghosts = room._rghost_pool_ids.size();
  275. for (int n = 0; n < num_rghosts; n++) {
  276. uint32_t pool_id = room._rghost_pool_ids[n];
  277. PortalRenderer::RGhost &moving = p_portal_renderer.get_pool_rghost(pool_id);
  278. // done already?
  279. if (moving.last_gameplay_tick_hit == _gameplay_tick)
  280. continue;
  281. // add to the active list
  282. _active_rghost_pool_ids_curr->push_back(pool_id);
  283. // if wasn't present in the tick before, add the notification to enter
  284. if (moving.last_gameplay_tick_hit != (_gameplay_tick - 1)) {
  285. VisualServerCallbacks::Message msg;
  286. msg.object_id = moving.object_id;
  287. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_ENTER_GAMEPLAY;
  288. callbacks->push_message(msg);
  289. }
  290. // mark as done
  291. moving.last_gameplay_tick_hit = _gameplay_tick;
  292. }
  293. // no need to progress from here
  294. if (!p_source_rooms_changed) {
  295. return;
  296. }
  297. // has the room come into gameplay?
  298. // later tests only relevant if a room has just come into play
  299. bool room_came_into_play = false;
  300. if (room.last_room_tick_hit != _room_tick) {
  301. room_came_into_play = true;
  302. // add the room to the active list
  303. _active_room_ids_curr->push_back(p_room_id);
  304. // if wasn't present in the tick before, add the notification to enter
  305. if (room.last_room_tick_hit != (_room_tick - 1)) {
  306. VisualServerCallbacks::Message msg;
  307. msg.object_id = room._godot_instance_ID;
  308. msg.type = _enter_callback_type;
  309. callbacks->push_message(msg);
  310. }
  311. // mark as done
  312. room.last_room_tick_hit = _room_tick;
  313. }
  314. // no need to do later tests
  315. if (!room_came_into_play) {
  316. return;
  317. }
  318. ///////////////////////////////////////////////////////////////////
  319. // has the roomgroup come into gameplay?
  320. for (int n = 0; n < room._roomgroup_ids.size(); n++) {
  321. int roomgroup_id = room._roomgroup_ids[n];
  322. VSRoomGroup &roomgroup = p_portal_renderer.get_roomgroup(roomgroup_id);
  323. if (roomgroup.last_room_tick_hit != _room_tick) {
  324. // add the room to the active list
  325. _active_roomgroup_ids_curr->push_back(roomgroup_id);
  326. // if wasn't present in the tick before, add the notification to enter
  327. if (roomgroup.last_room_tick_hit != (_room_tick - 1)) {
  328. VisualServerCallbacks::Message msg;
  329. msg.object_id = roomgroup._godot_instance_ID;
  330. msg.type = _enter_callback_type;
  331. callbacks->push_message(msg);
  332. }
  333. // mark as done
  334. roomgroup.last_room_tick_hit = _room_tick;
  335. }
  336. } // for through roomgroups
  337. // static ghosts
  338. int num_sghosts = room._static_ghost_ids.size();
  339. for (int n = 0; n < num_sghosts; n++) {
  340. uint32_t id = room._static_ghost_ids[n];
  341. VSStaticGhost &ghost = p_portal_renderer.get_static_ghost(id);
  342. // done already?
  343. if (ghost.last_room_tick_hit == _room_tick)
  344. continue;
  345. // add to the active list
  346. _active_sghost_ids_curr->push_back(id);
  347. // if wasn't present in the tick before, add the notification to enter
  348. if (ghost.last_room_tick_hit != (_room_tick - 1)) {
  349. VisualServerCallbacks::Message msg;
  350. msg.object_id = ghost.object_id;
  351. msg.type = VisualServerCallbacks::CALLBACK_NOTIFICATION_ENTER_GAMEPLAY;
  352. callbacks->push_message(msg);
  353. }
  354. // mark as done
  355. ghost.last_room_tick_hit = _room_tick;
  356. }
  357. }
  358. void PortalGameplayMonitor::_swap(bool p_source_rooms_changed) {
  359. LocalVector<uint32_t, int32_t> *temp = _active_moving_pool_ids_curr;
  360. _active_moving_pool_ids_curr = _active_moving_pool_ids_prev;
  361. _active_moving_pool_ids_prev = temp;
  362. _active_moving_pool_ids_curr->clear();
  363. temp = _active_rghost_pool_ids_curr;
  364. _active_rghost_pool_ids_curr = _active_rghost_pool_ids_prev;
  365. _active_rghost_pool_ids_prev = temp;
  366. _active_rghost_pool_ids_curr->clear();
  367. if (p_source_rooms_changed) {
  368. temp = _active_room_ids_curr;
  369. _active_room_ids_curr = _active_room_ids_prev;
  370. _active_room_ids_prev = temp;
  371. _active_room_ids_curr->clear();
  372. temp = _active_roomgroup_ids_curr;
  373. _active_roomgroup_ids_curr = _active_roomgroup_ids_prev;
  374. _active_roomgroup_ids_prev = temp;
  375. _active_roomgroup_ids_curr->clear();
  376. temp = _active_sghost_ids_curr;
  377. _active_sghost_ids_curr = _active_sghost_ids_prev;
  378. _active_sghost_ids_prev = temp;
  379. _active_sghost_ids_curr->clear();
  380. }
  381. }