Closed Emmanuel-Tsapekis closed 9 years ago
The difficulty for this story is that the Sorcerer is being referenced all across the codebase. When a client disconnects, all the references to the old Sorcerer need to be updated to the new one created locally, for the game to run properly (for example to allow the AI to properly take over control of the Sorcerer). For this task a SorcererInstanceManager was created, which takes care of the swapping logic onClientConnect, while providing a .getSorcerer() that should be used across the code instead of .findObjectByType the sorcerer.
This class (SorcererInstanceManager) also implements an Observer pattern, updating all sorcerer references upon new connection, in all classes that use it (those classes must implement interface ISorcererSubscriber.cs).
(total time spent: approx. 19h)
--signifies task is done // signifies task was not necessary signifies task was not done
(2h) --design and create SorcererInstanceManager.cs swap function to be called when client connects getSorcerer() function to be used across the code
(0.5h) --replace all occurrences of .findObjectByType sorcerer with SorcererInstanceManager.getSorcerer()
(2h) remove player: --server: swap them --client: destroy sorcerer locally (rpc call)
(4h) connect with no sorcerer on the client, and not knowing if the server already has an AI sorcerer or not: --client: swap (network.instantiate) --client: send rpc: tellServerToDetectAndKeepNewSorcerer --server: find and swap new sorcerer network.instantiated by client: it's a third case of using the swap method: in the update method: (it needs to wait for a second sorcerer to appear) //make sure it's notMine --make sure it's not the same reference. --then swap them.
// logout: client: send rpc server: remove client. (...client: destroy locally (rpc call))
// connect for first time: normal -- fine
// connect with server, with a sorcerer already in the client's game impossible, put check to err if happens
------------------------ Below was done in following sprint: -------------------------
(2h) --implement observer pattern (ISorcererSubscriber.cs) so all classes using a reference to the sorcerer can have their references updated immediately upon new client connection.
(1h) --make sure that checkfornewsorcerer() runs on server after client connects. does it find the sorcerer that the client created? does it delete any pre-existing sorcerer?
(5h) --PROBLEM: 2 sorcerers are present after reconnecting. There were 2 different causes for this (fixed with 1/ removeRPCS() and 2/ a late detection if statement in update method to get a new sorcerer after reconnection): // Debug server and see why third sorcerer gets deleted but not second one -- there's always two!!! // why 2 sorcerers when I don't close the window, but only one when I do close it? force close the window for now? NO, it happens in both cases, it's just that the sorcerer was at 0,0,0 so I thought the second one disappeared. but they were still two. --Does the client find 2 sorcerers after connecting for a second time? yes! why is the old one not being deleted? // network.destroy instead of destroy --UNITY QUIRK → removeRPCs() .. when it's not called on disconnect, the client will receive game objects that were already network.destroyed!!! i.e. network.instantiate calls are buffered for players who join the game late, but not network.destroy calls, so a client connecting late by default gets any game object that ever existed on the network, at some point. --FIX: player game object is not immediately accessible at the time OnPlayerConnected() gets called !!! It is received a few frames later. --use a late detection if statement (after 100 frames) in the udpate function
(0.5h) --FIX: server still has 2 sorcerers, 1 fighter. why length is 1, when there is actually 2 sorcerers in the game? --when in debug mode, it just needed even more frames (3000) before doing the detection / swap because one instance was going slower at executing the code than the other one.
(0.5h) --FIX: client has 2 fighters, 1 sorcerer. // onPlayerConnected() → Destroy(fighter.gameobject); // onPlayerDisconnected() → removeRPCs? --onDisconnectedFromServer() → Destroy(fighter.gameobject); // local destroy also, go back to start scene → just network load start menu scene // or just prevent script from creating fighter again! NOPE fighter creation only runs in the start scene.
(0.5h) --CHG: refactor createAndSwapNewSorcerer(Sorcerer sorcererPrefab, Transform transf have only one method for both.
(0.1h) --FIX: to fix camera follow → bring back to start menu scene. OnPlayerDIsconnected() → NetworkLevelLoader.loadLevel(...)
(0.25h) --remove the choose class buttons when a SorcererInstanceManager.getSorcerer() != null // implement logging out directly from the client too. (just RPC call removeClient on the server so we keep the same procedure that already works). actually this already works simply from disconnecting, which triggers the current code.
(0.5h) --set new sorcerer position to that of old sorcerer (that was controlled by the AI on the server) done with RPC call to notify the client (who owns the sorcerer) of the current sorcerer position.
== STILL TODO: ==
clean code, remove commented out code. make the rpc call for walls? singleton pattern: small refactor make the sorcererPrefab be in the SorcerrerInstanceManager // remove onClientConencts // remove oncheck … boolean // rename check…() to detectAndSwapSorcerer() // have a function newSorcerer to simplify the interface / interaction / caller code?
Tests # | Inputs | Expected Output | Status |
---|---|---|---|
1. Connect players | Open StartScreen scene in two game instances. Create a Fighter in one, and a Sorcerer in the other | --> Verify that the game can be played in 2 player mode, as usual | Pass |
2. Remove Client | Then, in the server instance, click "Remove Client" (next to the chat window) | --> In the client instance, verify that the Menu screen reappears --> Back in the server instance, verify that the Sorcerer is now controlled by the AI (on the server instance) | Pass |
3. Reconnect Client | Then in the client instance, click "Connect as Client" and select a Room. | --> Verify that gameplay resumes normally in both instances (server's sorcerer is now controlled by client, and client can play as usual) | Pass |
4. Logout Client | Repeat step 2. and 3. but by clicking "Logout" in the client instance instead first, instead of clicking "Remove Client" | Observe identical effects as for steps 2 and 3 | Pass |
These issues have been retested: #87, #73, #92, #91, #78, #77, #64, #45 Tests failed: None Last edit: 09/04/2015
[Priority: 1] [SP: 8] As a Sorcerer, I would like to be able to jump into an existent game at any time.