From aff890f4ebf0a5ed4861716d69251e5b5e53c9cd Mon Sep 17 00:00:00 2001 From: Philipp Deschain Date: Mon, 29 Mar 2021 16:53:10 -0400 Subject: [PATCH 01/19] # --- ARCHITECTURE.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index c92e6ba58..a3a10184a 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -3,8 +3,20 @@ This document describes the high-level architecture of BossRoom. If you want to familiarize yourself with the code base, you are just in the right place! -> __IMPORTANT__: -> This doc is heavily WIP +// + - Connection flow +The BossRoom network connection flow is owned by the GameNetPortal. The Host will invoke either GameNetPortal.StartHost, or StartRelayHost (if Photon relay is being used). The client will invoke either ClientGameNetPortal.StartClient, or StartClientRelayMode. Bossroom's own connection validation logic is performed in ServerGameNetPortal.ApprovalCheck, which is plugged in to the NetworkingManager's connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a ServerFull scenario). + - host model +BossRoom uses a Host model for its server. This means one client acts as a server and hosts the other clients. A common pitfall of this pattern is writing the game in such a way that it is virtually impossible to adapt to a dedicated server model. We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined is single modules). On the Host, each GameObject has {Server, Shared, Client} components. If you start up the game as a dedicated server, the client components will disable themselves, leaving you with {Server, Shared} components. If you start up as a client, you get the complementary set of {Shared, Client} components. This approach works, but requires some care: if you have server and clients of a shared base class, you need to remember that the shared code will run TWICE on the host; you also need to take care about code executing in Start+Awake; if this code runs contemporaneously with the NetworkingManager's initialization, it may not know yet whether the player is a host or client. We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. + - data model: gamedatasource, characters and actions (scriptable objects) +Game data in BossRoom is defined in ScriptableObjects. The ScriptableObjects are organized by enum and made available in a singleton class: the GameDataSource, in particular ActionDescription and CharacterData. Actions represent discrete verbs (like swinging a weapon, or reviving someone), and are substantially data driven. Characters represent both the different player classes, and also monsters, and represent basic details like health, as well as what "Skill" Actions are available to each Character. + - touch on transports used (ip and relay?) +Currently two network transport mechanisms are supported: IP/port connection, and Relay connection. In the former, Client users must know the IP and port they are connecting to, and establish connection directly. In the latter, the develop must have signed up with Photon and defined a Photon Realtime app. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. + - GameNetPortal +[I think this was already touched on adequately in Connection Flow ] + - GameStateBehaviour (scene stuff) +In BossRoom, scenes correspond to top-level Game States in a 1:1 way. That is, there is a MainMenu scene (and state), Character Select scene (and state), and so on. Because it is currently challenging to have a client be in a different scene than the server it's connected to, the options for MLAPI developers are either to not use scenes at all, or to use scenes, and let game state transitions on the host drive game state transitions on the client indirectly by forcing client scene transitions through MLAPI's networked scene management. We chose the latter approach. Each scene has exactly one GameStateBehaviour (a specialization of MLAPI.NetworkBehaviour), that is responsible for running the global state logic for that scene. States are transitioned by triggered scene transitions. The GameStateBehaviour was written in such a way that it could be adapted to many-to-one usage, with multiple "game" scenes using the same state object, but the BossRoom demo doesn't use this concept. +// ## Bird's Eye View From b0ce5c4e92536a84b57f9f16bf1eda6f8089e015 Mon Sep 17 00:00:00 2001 From: Philipp Deschain Date: Tue, 6 Apr 2021 00:58:00 -0400 Subject: [PATCH 02/19] Incorporated David's input --- ARCHITECTURE.md | 96 ++++++++++++++++++++++++------------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index a3a10184a..de47257e8 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,47 +1,63 @@ # Architecture - -This document describes the high-level architecture of BossRoom. +This document describes the high-level architecture of Boss Room. If you want to familiarize yourself with the code base, you are just in the right place! -// - - Connection flow -The BossRoom network connection flow is owned by the GameNetPortal. The Host will invoke either GameNetPortal.StartHost, or StartRelayHost (if Photon relay is being used). The client will invoke either ClientGameNetPortal.StartClient, or StartClientRelayMode. Bossroom's own connection validation logic is performed in ServerGameNetPortal.ApprovalCheck, which is plugged in to the NetworkingManager's connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a ServerFull scenario). - - host model -BossRoom uses a Host model for its server. This means one client acts as a server and hosts the other clients. A common pitfall of this pattern is writing the game in such a way that it is virtually impossible to adapt to a dedicated server model. We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined is single modules). On the Host, each GameObject has {Server, Shared, Client} components. If you start up the game as a dedicated server, the client components will disable themselves, leaving you with {Server, Shared} components. If you start up as a client, you get the complementary set of {Shared, Client} components. This approach works, but requires some care: if you have server and clients of a shared base class, you need to remember that the shared code will run TWICE on the host; you also need to take care about code executing in Start+Awake; if this code runs contemporaneously with the NetworkingManager's initialization, it may not know yet whether the player is a host or client. We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. - - data model: gamedatasource, characters and actions (scriptable objects) -Game data in BossRoom is defined in ScriptableObjects. The ScriptableObjects are organized by enum and made available in a singleton class: the GameDataSource, in particular ActionDescription and CharacterData. Actions represent discrete verbs (like swinging a weapon, or reviving someone), and are substantially data driven. Characters represent both the different player classes, and also monsters, and represent basic details like health, as well as what "Skill" Actions are available to each Character. - - touch on transports used (ip and relay?) -Currently two network transport mechanisms are supported: IP/port connection, and Relay connection. In the former, Client users must know the IP and port they are connecting to, and establish connection directly. In the latter, the develop must have signed up with Photon and defined a Photon Realtime app. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. - - GameNetPortal -[I think this was already touched on adequately in Connection Flow ] - - GameStateBehaviour (scene stuff) -In BossRoom, scenes correspond to top-level Game States in a 1:1 way. That is, there is a MainMenu scene (and state), Character Select scene (and state), and so on. Because it is currently challenging to have a client be in a different scene than the server it's connected to, the options for MLAPI developers are either to not use scenes at all, or to use scenes, and let game state transitions on the host drive game state transitions on the client indirectly by forcing client scene transitions through MLAPI's networked scene management. We chose the latter approach. Each scene has exactly one GameStateBehaviour (a specialization of MLAPI.NetworkBehaviour), that is responsible for running the global state logic for that scene. States are transitioned by triggered scene transitions. The GameStateBehaviour was written in such a way that it could be adapted to many-to-one usage, with multiple "game" scenes using the same state object, but the BossRoom demo doesn't use this concept. -// +Boss Room is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. Control model is click-to-move, with skills triggered by mouse button or hotkey. + +Code is organized into three separate assemblies: `Client`, `Server` and `Shared` (which, as it's name implies, contains shared functionality that both client and the server require). + +## Host model +Boss Room uses a Host model for its server. This means one client acts as a server and hosts the other clients. + +A common pitfall of this pattern is writing the game in such a way that it is virtually impossible to adapt to a dedicated server model. + +We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined is single modules): + - On the Host, each GameObject has `{Server, Shared, Client}` components. + - If you start up the game as a dedicated server, the client components will disable themselves, leaving you with `{Server, Shared}` components. + - If you start up as a client, you get the complementary set of `{Shared, Client}` components. + +This approach works, but requires some care: + - if you have server and clients of a shared base class, you need to remember that the shared code will run twice on the host; + - you also need to take care about code executing in `Start` and `Awake`: if this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet whether the player is a host or client. + - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. + +## Connection flow +The Boss Room network connection flow is owned by the `GameNetPortal`: + - The Host will invoke either `GameNetPortal.StartHost`, or `StartRelayHost` (if Photon relay is being used). + - The client will invoke either `ClientGameNetPortal.StartClient`, or `StartClientRelayMode`. + - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a `ServerFull` scenario). + +## Data model +Game data in Boss Room is defined in `ScriptableObjects`. The `ScriptableObjects` are organized by enum and made available in a singleton class: the `GameDataSource`, in particular `ActionDescription` and `CharacterData`. `Actions` represent discrete verbs (like swinging a weapon, or reviving someone), and are substantially data driven. Characters represent both the different player classes, and also monsters, and represent basic details like health, as well as what "Skill" Actions are available to each Character. -## Bird's Eye View +## Transports +Currently two network transport mechanisms are supported: + - IP/port connection; + - Relay connection; -## Exploring the project -BossRoom is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. Control model is click-to-move, with skills triggered by mouse button or hotkey. +In the former, Client users must know the IP and port they are connecting to, and establish connection directly. -One of the 8 clients acts as the host/server. That client will use a compositional approach so that its entities have both server and client components. +In the latter, the developer must have signed up with Photon and defined a Photon Realtime app id. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. -The game is server-authoritative, with latency-masking animations. Position updates are done through NetworkTransforms. NetworkedVars and RPC endpoints are isolated in a class that is shared between the server and client specialized logic components. All gamelogic runs in FixedUpdate at 30 Hz, matching our network update rate. +Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one. -Code is organized into three separate assemblies: **Client**, **Shared** and **Server** which reference each other when appropriate. +## GameStateBehaviour (scene stuff) +In Boss Room, scenes correspond to top-level Game States in a 1:1 way. That is, there is a `MainMenu` scene (and state), `Character Select` scene (and state), and so on. -For an in-depth overview of the project's architecture please check out our [ARCHITECTURE.md](ARCHITECTURE.md). +Because it is currently challenging to have a client be in a different scene than the server it's connected to, the options for MLAPI developers are either to not use scenes at all, or to use scenes, and let game state transitions on the host drive game state transitions on the client indirectly by forcing client scene transitions through MLAPI's networked scene management. -### Key classes +We chose the latter approach. +Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.NetworkBehaviour`), that is responsible for running the global state logic for that scene. States are transitioned by triggered scene transitions. -### Key classes +## Important classes **Shared** - - `NetworkCharacterState` Contains all NetworkedVars, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they don’t do any logic internally. + - `NetworkCharacterState` Contains NetworkedVars that store the state of any given character, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they don’t do any logic internally. **Server** - `ServerCharacterMovement` manages the movement Finite State Machine (FSM) on the server. Updates the NetworkedVars that synchronize position, rotation and movement speed of the entity on its FixedUpdate. - - `ServerCharacter` has the AIBrain, as well as the ActionQueue. Receives action requests (either from the AIBrain in case of NPCs, or user input in case of player characters), and executes them. + - `ServerCharacter` has the `AIBrain`, as well as the ActionQueue. Receives action requests (either from the AIBrain in case of NPCs, or user input in case of player characters), and executes them. - `AIBrain` contains main AI FSM. - `Action` is the abstract base class for all server actions - `MeleeAction`, `AoeAction`, etc. contain logic for their respective action types. @@ -51,27 +67,11 @@ For an in-depth overview of the project's architecture please check out our [ARC - `ClientInputSender `. On a shadow entity, will self-destruct. Listens to inputs, interprets them, and then calls appropriate RPCs on the RPCStateComponent. - `ActionFX` is the abstract base class for all the client-side action visualizers - `MeleeActionFX`, `AoeActionFX`, etc. Contain graphics information for their respective action types. - - - -### Movement action flow + +## Movement action flow - Client clicks mouse on target destination. - Client->server RPC, containing target destination. - Anticipatory animation plays immediately on client. - - Server path-plans. - - Once path-plan is finished, server representation of entity starts updating its NetworkedTransform at 30fps. Graphics is on a separate GO and is connected to the networked GO via a spring, to smooth out small corrections. - - Graphics GO never passes the simulation GO; if it catches up to the sim due to a network delay, the user will see a hitch. - -### Navigation System - -#### Building a navigation mesh -The project is using NavMeshComponents. This means direct building from the Navigation window will not give the desired results. Instead find a NavMeshComponent in the given scene e.g. a **NavMeshSurface** and use the **Bake** button of that script. Also make sure that there is always only one navmesh file per scene. Navmesh files are stored in a folder with the same name as the corresponding scene. You can recognize them based on their icon in the editor. They follow the naming pattern "NavMesh-\" - -#### Dynamic Navigation Objects -A dynamic navigation object is an object which affects the state of the navigation mesh such as a door which can be openend or closed. -To create a dynamic navigation object add a NavMeshObstacle to it and configure the shape (in most cases this should just match the corresponding collider). Then add a DynamicNavObstacle component to it. - -#### Navigation System -Each scene which uses navigation or dynamic navigation objects should have a NavigationSystem component on a scene gameobject. That object also needs to have the "NavigationSystem" tag. - ---------------------------- \ No newline at end of file + - Server performs pathfinding. + - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. Unit visuals are in a separate GameObject hierarchy and is connected to the networked GO via a spring, to smooth out small corrections. + - Visuals game obejct never outpaces the simulation game object, always slightly behind and smoothly catching up. \ No newline at end of file From 3090a9e55cbbf78d2639f19ee27d298617617e59 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 01:06:47 -0400 Subject: [PATCH 03/19] Update ARCHITECTURE.md --- ARCHITECTURE.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index de47257e8..b3121d984 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -41,8 +41,8 @@ In the latter, the developer must have signed up with Photon and defined a Photo Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one. -## GameStateBehaviour (scene stuff) -In Boss Room, scenes correspond to top-level Game States in a 1:1 way. That is, there is a `MainMenu` scene (and state), `Character Select` scene (and state), and so on. +## Game state / Scene flow +In Boss Room, scenes correspond to top-level Game States (see `GameStateBehaviour` class) in a 1:1 way. That is, there is a `MainMenu` scene, `Character Select` scene (and state), and so on. Because it is currently challenging to have a client be in a different scene than the server it's connected to, the options for MLAPI developers are either to not use scenes at all, or to use scenes, and let game state transitions on the host drive game state transitions on the client indirectly by forcing client scene transitions through MLAPI's networked scene management. @@ -74,4 +74,4 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw - Anticipatory animation plays immediately on client. - Server performs pathfinding. - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. Unit visuals are in a separate GameObject hierarchy and is connected to the networked GO via a spring, to smooth out small corrections. - - Visuals game obejct never outpaces the simulation game object, always slightly behind and smoothly catching up. \ No newline at end of file + - Visuals game obejct never outpaces the simulation game object, always slightly behind and smoothly catching up. From 848cebf39269db9f1d074ea01fdd0eca33afc69c Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 12:49:56 -0400 Subject: [PATCH 04/19] GameObject term used consistently --- ARCHITECTURE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b3121d984..72968c39f 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -73,5 +73,5 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw - Client->server RPC, containing target destination. - Anticipatory animation plays immediately on client. - Server performs pathfinding. - - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. Unit visuals are in a separate GameObject hierarchy and is connected to the networked GO via a spring, to smooth out small corrections. - - Visuals game obejct never outpaces the simulation game object, always slightly behind and smoothly catching up. + - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. + - Visuals GameObject never outpaces the simulation GameObject, always slightly behind and interpolating towards the networked position and rotation. From 3d085d92199d4d9cfdd2b12befa2b3b7727f2349 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:35:35 -0400 Subject: [PATCH 05/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 72968c39f..9958e105d 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2,7 +2,7 @@ This document describes the high-level architecture of Boss Room. If you want to familiarize yourself with the code base, you are just in the right place! -Boss Room is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. Control model is click-to-move, with skills triggered by mouse button or hotkey. +Boss Room is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. The control model is click-to-move, with skills triggered by mouse button or hotkey. Code is organized into three separate assemblies: `Client`, `Server` and `Shared` (which, as it's name implies, contains shared functionality that both client and the server require). From 6cc84f5dd05a25e498ee90b2a30e172d92bc249b Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:36:26 -0400 Subject: [PATCH 06/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9958e105d..9e267e967 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -11,7 +11,7 @@ Boss Room uses a Host model for its server. This means one client acts as a serv A common pitfall of this pattern is writing the game in such a way that it is virtually impossible to adapt to a dedicated server model. -We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined is single modules): +We attempted to resolve this by using a compositional model for our client and server logic, rather than having it all combined is single modules: - On the Host, each GameObject has `{Server, Shared, Client}` components. - If you start up the game as a dedicated server, the client components will disable themselves, leaving you with `{Server, Shared}` components. - If you start up as a client, you get the complementary set of `{Shared, Client}` components. From 50ed79225b4f9bb56cf676eb3e195b181e11f9dd Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:36:44 -0400 Subject: [PATCH 07/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9e267e967..b79f72f0e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -73,5 +73,5 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw - Client->server RPC, containing target destination. - Anticipatory animation plays immediately on client. - Server performs pathfinding. - - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. + - Once pathfinding is finished, server representation of entity starts updating its NetworkVariables at 30fps. - Visuals GameObject never outpaces the simulation GameObject, always slightly behind and interpolating towards the networked position and rotation. From 73d46ad7eb1306aed6b491b8bd0152f5bf04ee23 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:37:57 -0400 Subject: [PATCH 08/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b79f72f0e..eaf989d5b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -53,7 +53,7 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw ## Important classes **Shared** - - `NetworkCharacterState` Contains NetworkedVars that store the state of any given character, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they don’t do any logic internally. + - `NetworkCharacterState` contains NetworkVariables that store the state of any given character, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they do not have any additional internal logic. **Server** - `ServerCharacterMovement` manages the movement Finite State Machine (FSM) on the server. Updates the NetworkedVars that synchronize position, rotation and movement speed of the entity on its FixedUpdate. From f6ff651e9451c8a14d93166d4d99e46330b7cc57 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:38:08 -0400 Subject: [PATCH 09/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index eaf989d5b..b32ec0c79 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -23,8 +23,8 @@ This approach works, but requires some care: ## Connection flow The Boss Room network connection flow is owned by the `GameNetPortal`: - - The Host will invoke either `GameNetPortal.StartHost`, or `StartRelayHost` (if Photon relay is being used). - - The client will invoke either `ClientGameNetPortal.StartClient`, or `StartClientRelayMode`. + - The Host will invoke either `GameNetPortal.StartHost` or `StartRelayHost` (if Photon relay is being used). + - The client will invoke either `ClientGameNetPortal.StartClient` or `StartClientRelayMode`. - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a `ServerFull` scenario). ## Data model From 6cafd82ab629807efe45edd12e1ade2b08503436 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:38:13 -0400 Subject: [PATCH 10/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b32ec0c79..bd32350c9 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -32,8 +32,8 @@ Game data in Boss Room is defined in `ScriptableObjects`. The `ScriptableObjects ## Transports Currently two network transport mechanisms are supported: - - IP/port connection; - - Relay connection; + - IP/port connection + - Relay connection In the former, Client users must know the IP and port they are connecting to, and establish connection directly. From 8a28bffa85b0650d0c7b8eba6d6708d83cb5994f Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 13:38:38 -0400 Subject: [PATCH 11/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index bd32350c9..3783addea 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -18,7 +18,7 @@ We attempted to resolve this by using a compositional model for our client and s This approach works, but requires some care: - if you have server and clients of a shared base class, you need to remember that the shared code will run twice on the host; - - you also need to take care about code executing in `Start` and `Awake`: if this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet whether the player is a host or client. + - Be careful with code executing in `Start` and `Awake`: If this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet if the player is a host or client. - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. ## Connection flow From 29d64fd2fb1d02e196d47e82c14e05f577f8baf6 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 14:00:48 -0400 Subject: [PATCH 12/19] Update ARCHITECTURE.md Co-authored-by: Lori Krell <76010626+lkrell@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 3783addea..8dd081d90 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -17,7 +17,7 @@ We attempted to resolve this by using a compositional model for our client and s - If you start up as a client, you get the complementary set of `{Shared, Client}` components. This approach works, but requires some care: - - if you have server and clients of a shared base class, you need to remember that the shared code will run twice on the host; + - If you have server and clients of a shared base class, the shared code will run twice on the host. - Be careful with code executing in `Start` and `Awake`: If this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet if the player is a host or client. - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. From bced46ced610ff9b2607b2c8982d3d1ef0cdcafe Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 14:00:57 -0400 Subject: [PATCH 13/19] Update ARCHITECTURE.md Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com> --- ARCHITECTURE.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 8dd081d90..5621b5c9b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2,7 +2,6 @@ This document describes the high-level architecture of Boss Room. If you want to familiarize yourself with the code base, you are just in the right place! -Boss Room is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. The control model is click-to-move, with skills triggered by mouse button or hotkey. Code is organized into three separate assemblies: `Client`, `Server` and `Shared` (which, as it's name implies, contains shared functionality that both client and the server require). From abbb1c9ec6541e037269c9063c2213f29b6ea584 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 16:08:01 -0400 Subject: [PATCH 14/19] Update ARCHITECTURE.md Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 5621b5c9b..f9d450e51 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -19,7 +19,7 @@ This approach works, but requires some care: - If you have server and clients of a shared base class, the shared code will run twice on the host. - Be careful with code executing in `Start` and `Awake`: If this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet if the player is a host or client. - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. - +- Client server separation also allows not having god-classes where both client and server code are intermingled. This way, when reading server code, you do not have to mentally skip client code and vice versa. This helps making bigger classes more readable and maintainable. Please note that this pattern can be applied on a case by case basis. If your class never grows too big, having a single `NetworkBehaviour` is perfectly fine. ## Connection flow The Boss Room network connection flow is owned by the `GameNetPortal`: - The Host will invoke either `GameNetPortal.StartHost` or `StartRelayHost` (if Photon relay is being used). From a9d0cee79a61a21f322c94e9d30c033d49ea07e9 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 16:08:16 -0400 Subject: [PATCH 15/19] Update ARCHITECTURE.md Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index f9d450e51..684763dd5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -24,7 +24,7 @@ This approach works, but requires some care: The Boss Room network connection flow is owned by the `GameNetPortal`: - The Host will invoke either `GameNetPortal.StartHost` or `StartRelayHost` (if Photon relay is being used). - The client will invoke either `ClientGameNetPortal.StartClient` or `StartClientRelayMode`. - - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a `ServerFull` scenario). + - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. ## Data model Game data in Boss Room is defined in `ScriptableObjects`. The `ScriptableObjects` are organized by enum and made available in a singleton class: the `GameDataSource`, in particular `ActionDescription` and `CharacterData`. `Actions` represent discrete verbs (like swinging a weapon, or reviving someone), and are substantially data driven. Characters represent both the different player classes, and also monsters, and represent basic details like health, as well as what "Skill" Actions are available to each Character. From 11ba9f34829f7be6492ac1ed43691f209d0a10c0 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 16:08:31 -0400 Subject: [PATCH 16/19] Update ARCHITECTURE.md Co-authored-by: Sam Bellomo <71790295+SamuelBellomo@users.noreply.github.com> --- ARCHITECTURE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 684763dd5..18e629b4e 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -36,7 +36,7 @@ Currently two network transport mechanisms are supported: In the former, Client users must know the IP and port they are connecting to, and establish connection directly. -In the latter, the developer must have signed up with Photon and defined a Photon Realtime app id. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. +In the latter, some setup is required. Please see our guide [here](Documentation/Photon-Realtime/Readme.md) on how to setup our current relay. Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one. From 54610f47524fee634bedc0c18513b23c692f14d1 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 17:36:43 -0400 Subject: [PATCH 17/19] Update ARCHITECTURE.md --- ARCHITECTURE.md | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9356dae41..91d29451b 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -2,6 +2,8 @@ This document describes the high-level architecture of Boss Room. If you want to familiarize yourself with the code base, you are just in the right place! +Boss Room is an 8-player co-op RPG game experience, where players collaborate to take down some minions, and then a boss. Players can select between classes that each have skills with didactically interesting networking characteristics. Control model is click-to-move, with skills triggered by mouse button or hotkey. + Code is organized into three separate assemblies: `Client`, `Server` and `Shared` (which, as it's name implies, contains shared functionality that both client and the server require). ## Host model @@ -9,36 +11,43 @@ Boss Room uses a Host model for its server. This means one client acts as a serv A common pitfall of this pattern is writing the game in such a way that it is virtually impossible to adapt to a dedicated server model. -We attempted to resolve this by using a compositional model for our client and server logic, rather than having it all combined is single modules: +We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined is single modules): - On the Host, each GameObject has `{Server, Shared, Client}` components. - If you start up the game as a dedicated server, the client components will disable themselves, leaving you with `{Server, Shared}` components. - If you start up as a client, you get the complementary set of `{Shared, Client}` components. This approach works, but requires some care: - - If you have server and clients of a shared base class, the shared code will run twice on the host. - - Be careful with code executing in `Start` and `Awake`: If this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet if the player is a host or client. + - if you have server and clients of a shared base class, you need to remember that the shared code will run twice on the host; + - you also need to take care about code executing in `Start` and `Awake`: if this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet whether the player is a host or client. - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. -- Client server separation also allows not having god-classes where both client and server code are intermingled. This way, when reading server code, you do not have to mentally skip client code and vice versa. This helps making bigger classes more readable and maintainable. Please note that this pattern can be applied on a case by case basis. If your class never grows too big, having a single `NetworkBehaviour` is perfectly fine. + ## Connection flow The Boss Room network connection flow is owned by the `GameNetPortal`: - - The Host will invoke either `GameNetPortal.StartHost` or `StartRelayHost` (if Photon relay is being used). - - The client will invoke either `ClientGameNetPortal.StartClient` or `StartClientRelayMode`. - - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. + - The Host will invoke either `GameNetPortal.StartHost`, or `StartRelayHost` (if Photon relay is being used). + - The client will invoke either `ClientGameNetPortal.StartClient`, or `StartClientRelayMode`. + - Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkingManager`'s connection approval callback. Here some basic information about the connection is recorded (including a GUID, to facilitate future reconnect logic), and success or failure is returned. In the future, additional game-level failures will be detected and returned (such as a `ServerFull` scenario). ## Data model Game data in Boss Room is defined in `ScriptableObjects`. The `ScriptableObjects` are organized by enum and made available in a singleton class: the `GameDataSource`, in particular `ActionDescription` and `CharacterData`. `Actions` represent discrete verbs (like swinging a weapon, or reviving someone), and are substantially data driven. Characters represent both the different player classes, and also monsters, and represent basic details like health, as well as what "Skill" Actions are available to each Character. ## Transports Currently two network transport mechanisms are supported: - - IP/port connection - - Relay connection +- IP based: The clients connect directy to a host via IP address. This will only work if both are in the same local are network or if the host forwards ports. +- Relay Based: The clients and the host connect to a relay server with a room key and run all traffic over this relay server. In the former, Client users must know the IP and port they are connecting to, and establish connection directly. -In the latter, some setup is required. Please see our guide [here](Documentation/Photon-Realtime/Readme.md) on how to setup our current relay. +In the latter, the developer must have signed up with Photon and defined a Photon Realtime app id. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one. +To allow for both of these options to be chosen at runtime we created `TransportPicker`. It allows to chose between an IP-based and a Relay-based transport and will hook up the game UI to use those transports. The transport field in the `NetworkManager` will be ignored. Currently we support the following transports: +- **UNet(IP):** UNet is the default MLAPI transport and the default IP transport for Boss Room. +- **LiteNetLib(IP):** We use LiteNetLib in Boss Room because it has a built in way to simulate latency which is useful for spotting networking issues early during development. +- **Photon Realtime (Relay):** Photon Realtime is a relay transport using the [Photon Realtime Service](https://www.photonengine.com/Realtime). + +To add new transport in the project parts of `GameNetPortal` and `ClientGameNetPortal` (transport switches) need to be extended. + ## Game state / Scene flow In Boss Room, scenes correspond to top-level Game States (see `GameStateBehaviour` class) in a 1:1 way. That is, there is a `MainMenu` scene, `Character Select` scene (and state), and so on. @@ -51,7 +60,7 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw ## Important classes **Shared** - - `NetworkCharacterState` contains NetworkVariables that store the state of any given character, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they do not have any additional internal logic. + - `NetworkCharacterState` Contains NetworkedVars that store the state of any given character, and both server and client RPC endpoints. The RPC endpoints only read out the call parameters and then raise events from them; they don’t do any logic internally. **Server** - `ServerCharacterMovement` manages the movement Finite State Machine (FSM) on the server. Updates the NetworkedVars that synchronize position, rotation and movement speed of the entity on its FixedUpdate. @@ -71,5 +80,15 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw - Client->server RPC, containing target destination. - Anticipatory animation plays immediately on client. - Server performs pathfinding. - - Once pathfinding is finished, server representation of entity starts updating its NetworkVariables at 30fps. + - Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps. - Visuals GameObject never outpaces the simulation GameObject, always slightly behind and interpolating towards the networked position and rotation. + +## Navigation System +Each scene which uses navigation or dynamic navigation objects should have a `NavigationSystem` component on a scene GameObject. That object also needs to have the `NavigationSystem` tag. + +### Building a navigation mesh +The project is using `NavMeshComponents`. This means direct building from the Navigation window will not give the desired results. Instead find a `NavMeshComponent` in the given scene e.g. a **NavMeshSurface** and use the **Bake** button of that script. Also make sure that there is always only one navmesh file per scene. Navmesh files are stored in a folder with the same name as the corresponding scene. You can recognize them based on their icon in the editor. They follow the naming pattern "NavMesh-\" + +### Dynamic Navigation Objects +A dynamic navigation object is an object which affects the state of the navigation mesh such as a door which can be openend or closed. +To create a dynamic navigation object add a NavMeshObstacle to it and configure the shape (in most cases this should just match the corresponding collider). Then add a DynamicNavObstacle component to it. From 2ba82562b54388094f53817f55c42c3506f1522b Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 17:43:13 -0400 Subject: [PATCH 18/19] Update ARCHITECTURE.md --- ARCHITECTURE.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 91d29451b..07727d942 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -32,12 +32,12 @@ Game data in Boss Room is defined in `ScriptableObjects`. The `ScriptableObjects ## Transports Currently two network transport mechanisms are supported: -- IP based: The clients connect directy to a host via IP address. This will only work if both are in the same local are network or if the host forwards ports. -- Relay Based: The clients and the host connect to a relay server with a room key and run all traffic over this relay server. +- IP based +- Relay Based -In the former, Client users must know the IP and port they are connecting to, and establish connection directly. +In the former, the clients connect directy to a host via IP address. This will only work if both are in the same local area network or if the host forwards ports. -In the latter, the developer must have signed up with Photon and defined a Photon Realtime app id. Then Hosts create Rooms in Photon (essentially a string name associated with the photon app), and share that information with clients. The advantage of the Relay is that Host users are often on local networks and may need to perform actions like port forwarding (which they may or may not have permissions to do) in order to accept incoming connections. The disadvantage is that it effectively doubles latency to the clients (or potentially worse than that, if host and client are close, but relay is in a different region). The complexity of the Relay is mostly abstracted away from MLAPI. Assuming the right configuration settings in Assets/Photon/Resources/PhotonAppSettings are set, the Photon Relay appears as simply another Transport from the point of view of MLAPI. +In the latter, some setup is required. Please see our guide [here](Documentation/Photon-Realtime/Readme.md) on how to setup our current relay. Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one. From 99e77da79b080b35f1be9411c2bf1de2aeff8ba3 Mon Sep 17 00:00:00 2001 From: Phil Deschain Date: Tue, 6 Apr 2021 17:45:43 -0400 Subject: [PATCH 19/19] Update ARCHITECTURE.md --- ARCHITECTURE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 07727d942..12a3324b6 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -20,6 +20,7 @@ This approach works, but requires some care: - if you have server and clients of a shared base class, you need to remember that the shared code will run twice on the host; - you also need to take care about code executing in `Start` and `Awake`: if this code runs contemporaneously with the `NetworkingManager`'s initialization, it may not know yet whether the player is a host or client. - We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers. + - Client server separation also allows not having god-classes where both client and server code are intermingled. This way, when reading server code, you do not have to mentally skip client code and vice versa. This helps making bigger classes more readable and maintainable. Please note that this pattern can be applied on a case by case basis. If your class never grows too big, having a single `NetworkBehaviour` is perfectly fine. ## Connection flow The Boss Room network connection flow is owned by the `GameNetPortal`: