Skip to content

Commit cb5c780

Browse files
Merge branch 'fix/scenebootstrapper-unblocking-test-runner' into feature/test-utils
2 parents 07cfb79 + 488db79 commit cb5c780

File tree

273 files changed

+40116
-76679
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

273 files changed

+40116
-76679
lines changed

.github/workflows/autoupdate.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: auto-update
2+
on:
3+
push:
4+
branches:
5+
- develop
6+
jobs:
7+
autoupdate:
8+
name: auto-update
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: docker://chinthakagodawita/autoupdate-action:v1
12+
env:
13+
GITHUB_TOKEN: "${{ secrets.NETCODE_CI_SERVICE_TOKEN }}"
14+
PR_FILTER: "auto_merge"
15+
PR_READY_STATE: "ready_for_review"
16+
MERGE_CONFLICT_ACTION: "ignore"
17+

.github/workflows/leak_detect.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Detect Project ID leak
2+
3+
# Controls when the workflow will run
4+
on:
5+
# Triggers the workflow on push or pull request events but only for the main branch
6+
push:
7+
8+
# Allows you to run this workflow manually from the Actions tab
9+
workflow_dispatch:
10+
11+
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
12+
jobs:
13+
# This workflow contains a single job called "build"
14+
check:
15+
# The type of runner that the job will run on
16+
runs-on: ubuntu-latest
17+
18+
# Steps represent a sequence of tasks that will be executed as part of the job
19+
steps:
20+
- name: Checkout github repo (+ download lfs dependencies)
21+
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
22+
uses: actions/checkout@v2
23+
with:
24+
lfs: true
25+
- name: Checkout LFS objects # needs to be done as a second step, lfs:true above just sets references, but doesn't actually dereference them...
26+
run: git lfs checkout
27+
# Find in project settings if there's any value in between "cloudProjectId: " and end of line. This exits 1 and fails the step if there's text there.
28+
- name: Run a one-line script
29+
run: python3 -c "import re; import sys;content=open('ProjectSettings/ProjectSettings.asset').read();res = re.search(r'.*cloudProjectId:.*\w+\s*\n', content)!=None;status = 1 if res else 0; print('status '+str(status)); sys.exit(res)"
30+
- name: Post to slack on failure
31+
if: ${{ failure() }}
32+
uses: slackapi/[email protected]
33+
with:
34+
# Slack channel id, channel name, or user id to post message.
35+
# See also: https://api.slack.com/methods/chat.postMessage#channels
36+
channel-id: 'G01H7JP4AP2' # private channel
37+
# For posting a simple plain text message
38+
slack-message: "Project ID LEAK DETECTED: ${{ job.status }}\n${{ github.event.pull_request.html_url || github.event.head_commit.url }}"
39+
env:
40+
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} # can be found in https://api.slack.com/apps/

ARCHITECTURE.md

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,67 @@ If you want to familiarize yourself with the code base, you are just in the righ
44

55
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.
66

7-
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).
7+
Code is organized into three separate assemblies: `Client`, `Server` and `Shared` (which, as its name implies, contains shared functionality that both client and the server require).
88

99
## Host model
1010
Boss Room uses a Host model for its server. This means one client acts as a server and hosts the other clients.
1111

1212
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.
1313

14-
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):
14+
We attempted to combat this by using a compositional model for our client and server logic (rather than having it all combined in single modules):
1515
- On the Host, each GameObject has `{Server, Shared, Client}` components.
1616
- If you start up the game as a dedicated server, the client components will disable themselves, leaving you with `{Server, Shared}` components.
17-
- If you start up as a client, you get the complementary set of `{Shared, Client}` components.
17+
- If you start up as a client, you get the complementary set of `{Shared, Client}` components.
1818

1919
This approach works, but requires some care:
20-
- 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;
21-
- 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.
20+
- 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.
21+
- You also need to take care about code executing in `Start` and `Awake`: if this code runs contemporaneously with the `NetworkManager`'s initialization, it may not know yet whether the player is a host or client.
2222
- We judged this extra complexity worth it, as it provides a clear road-map to supporting true dedicated servers.
23-
- 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.
23+
- 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.
2424

2525
## Connection flow
2626
The Boss Room network connection flow is owned by the `GameNetPortal`:
27-
- The Host will invoke either `GameNetPortal.StartHost`, or `StartRelayHost` (if Photon relay is being used).
28-
- The client will invoke either `ClientGameNetPortal.StartClient`, or `StartClientRelayMode`.
29-
- 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).
27+
- The Host will invoke either `GameNetPortal.StartHost` or `StartUnityRelayHost` if Unity Relay is being used.
28+
- The client will invoke either `ClientGameNetPortal.StartClient` or `StartClientUnityRelayModeAsync`.
29+
- Boss Room's own connection validation logic is performed in `ServerGameNetPortal.ApprovalCheck`, which is plugged in to the `NetworkManager`'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).
3030

3131
## Data model
3232
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.
3333

3434
## Transports
35-
Currently two network transport mechanisms are supported:
35+
Currently three network transport mechanisms are supported:
3636
- IP based
37-
- Relay Based
37+
- Unity Relay Based
3838

39-
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.
39+
In the first, the clients connect directly 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.
4040

41-
In the latter, some setup is required. Please see our guide [here](Documentation/Photon-Realtime/Readme.md) on how to setup our current relay.
41+
For Unity Relay based multiplayer sessions, some setup is required. Please see our guide [here](Documentation/Unity-Relay/README.md).
4242

4343
Please see [Multiplayer over internet](README.md) section of our Readme for more information on using either one.
4444

45-
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:
46-
- **UNet(IP):** UNet is the default MLAPI transport and the default IP transport for Boss Room.
47-
- **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.
48-
- **Photon Realtime (Relay):** Photon Realtime is a relay transport using the [Photon Realtime Service](https://www.photonengine.com/Realtime).
45+
To allow for any of these options to be chosen at runtime we created `TransportPicker`. It allows one to choose 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:
46+
- **UNET(IP):** UNET is the default Netcode transport. However, it is not the default IP transport for Boss Room.
47+
- **UTP (IP):** Unity Transport Package is a network transport layer, packaged with network simulation tools which are useful for spotting networking issues early during development. This IP based protocol is the default IP transport for Boss Room. See the documentation on [Unity Transport Package](https://docs-multiplayer.unity3d.com/docs/transport-utp/about-transport-utp/#unity-transport-package-utp).
48+
- **Unity (Relay):** Unity Relay is a relay service provided by Unity services, supported by Unity Transport. Read more about [Unity Relay](https://docs-multiplayer.unity3d.com/docs/relay/relay).
4949

50-
To add new transport in the project parts of `GameNetPortal` and `ClientGameNetPortal` (transport switches) need to be extended.
50+
To add new transports in the project, parts of `GameNetPortal` and `ClientGameNetPortal` (transport switches) need to be extended.
5151

5252
## Game state / Scene flow
5353
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.
5454

55-
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.
55+
Because it is currently challenging to have a client be in a different scene than the server it's connected to, the options for Netcode 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 Netcode's networked scene management.
5656

5757
We chose the latter approach.
5858

59-
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.
59+
Each scene has exactly one `GameStateBehaviour` (a specialization of `Netcode.NetworkBehaviour`), that is responsible for running the global state logic for that scene. States are transitioned by triggered scene transitions.
6060

6161
## Important classes
6262

6363
**Shared**
64-
- `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.
64+
- `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 don’t do any logic internally.
6565

6666
**Server**
67-
- `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.
67+
- `ServerCharacterMovement` manages the movement Finite State Machine (FSM) on the server. Updates the NetworkVariables that synchronize position, rotation and movement speed of the entity on its FixedUpdate.
6868
- `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.
6969
- `AIBrain` contains main AI FSM.
7070
- `Action` is the abstract base class for all server actions
@@ -81,8 +81,8 @@ Each scene has exactly one `GameStateBehaviour` (a specialization of `MLAPI.Netw
8181
- Client->server RPC, containing target destination.
8282
- Anticipatory animation plays immediately on client.
8383
- Server performs pathfinding.
84-
- Once pathfinding is finished, server representation of entity starts updating it's NetworkVariables at 30fps.
85-
- Visuals GameObject never outpaces the simulation GameObject, always slightly behind and interpolating towards the networked position and rotation.
84+
- Once pathfinding is finished, server representation of entity starts updating its NetworkVariables at the same cadence as FixedUpdate.
85+
- Visuals GameObject never outpaces the simulation GameObject, and so is always slightly behind and interpolating towards the networked position and rotation.
8686

8787
## Navigation System
8888
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.
@@ -91,5 +91,25 @@ Each scene which uses navigation or dynamic navigation objects should have a `Na
9191
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-\<name-of-creating-object\.asset>"
9292

9393
### Dynamic Navigation Objects
94-
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.
94+
A dynamic navigation object is an object which affects the state of the navigation mesh such as a door which can be opened or closed.
9595
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.
96+
97+
## Player Hierarchy
98+
99+
The `Player Prefab` field inside of Boss Room's `NetworkManager` is populated with `PersistentPlayer` prefab. Netcode will spawn a PersistentPlayer per client connection, with the client designated as the owner of the prefab instance. All `Player Prefab` prefab instances will be migrated between scenes internally by Netcode's scene management, therefore it is not necessary to mark this object as a `DontDestroyOnLoad` object. This object is suitable for storing data, in some cases in the form of `NetworkVariable`s, that could be accessed across scenes (eg. name, avatar GUID, etc). PersistentPlayer's GameObject hierarchy is quite trivial as it is comprised of only one GameObject:
100+
101+
* PersistentPlayer: a `NetworkObject` that will not be destroyed between scenes
102+
103+
####CharSelect Scene
104+
Inside `CharSelect` scene, clients select from 8 possible avatar classes, and that selection is stored inside PersistentPlayer's `NetworkAvatarGuidState`.
105+
106+
####BossRoom Scene
107+
Inside `BossRoom` scene, `ServerBossRoomState` spawns a `PlayerAvatar` per PersistentPlayer present. This `PlayerAvatar` prefab instance, that is owned by the corresponding connected client, is destroyed by Netcode when a scene load occurs (either to `PostGame` scene, or back to `MainMenu` scene), or through client disconnection.
108+
109+
`ClientAvatarGuidHandler`, a `NetworkBehaviour` component residing on the `PlayerAvatar` prefab instance will fetch the validated avatar GUID from `NetworkAvatarGuidState`, and spawn a local, non-networked graphics GameObject corresponding to the avatar GUID. This GameObject is childed to PlayerAvatar's `PlayerGraphics` child GameObject.
110+
111+
Once initialized successfully, the in-game PlayerAvatar GameObject hierarchy inside `BossRoom` scene will look something like (in the case of a selected Archer Boy class):
112+
113+
* Player Avatar: a `NetworkObject` that *will* be destroyed when `BossRoom` scene is unloaded
114+
* Player Graphics: a child GameObject containing `NetworkAnimator` component responsible for replicating animations invoked on the server
115+
* PlayerGraphics_Archer_Boy: a purely graphical representation of the selected avatar class

Assets/BossRoom/Material/Characters/Hero_Eyes_sheet.mat

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ Material:
106106
- _UVSec: 0
107107
- _ZWrite: 0
108108
m_Colors:
109-
- _BaseColor: {r: 0.5019608, g: 0.5019608, b: 0.5019608, a: 1}
109+
- _BaseColor: {r: 0.248, g: 0.248, b: 0.248, a: 1}
110110
- _Color: {r: 0.5, g: 0.5, b: 0.5, a: 1}
111-
- _EmissionColor: {r: 0.27450982, g: 0.27450982, b: 0.27450982, a: 1}
111+
- _EmissionColor: {r: 0.135, g: 0.135, b: 0.135, a: 1}
112112
- _SpecColor: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
113113
m_BuildTextureStacks: []
114114
--- !u!114 &4340826014879142409

Assets/BossRoom/Material/Dungeon/env_deco.mat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Material:
2525
m_ShaderKeywords: _METALLICSPECGLOSSMAP _NORMALMAP
2626
m_LightmapFlags: 4
2727
m_EnableInstancingVariants: 1
28-
m_DoubleSidedGI: 0
28+
m_DoubleSidedGI: 1
2929
m_CustomRenderQueue: 2000
3030
stringTagMap:
3131
RenderType: Opaque

0 commit comments

Comments
 (0)