Skip to content

Commit

Permalink
Update Link, Add Instance, Update Using Examples (#25)
Browse files Browse the repository at this point in the history
* Update Link, Add Instance, Update Using Examples

Updated the link to Unity's NGO docs to be on version 1.5.2 since that's what the game uses;
Added an Instance field to the ExampleNetworkHandler;
Fixed Using Examples referencing NetworkHandler instead of ExampleNetworkHandler;
Added Using Example for calling the EventClientRpc

* Simplified Instance Variable
  • Loading branch information
Xilophor authored Dec 14, 2023
1 parent f4a4e51 commit d2a1db6
Showing 1 changed file with 43 additions and 5 deletions.
48 changes: 43 additions & 5 deletions docs/user-guide/modding/advanced/networking.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

!> **Warning: This is an advanced article. While this introduces some C# concepts, it is highly recommended to understand C# and the basics of modding this game <i>before</i> reading this article.**

?>**Note:** This is not a tutorial on how to use Unity's [Netcode for GameObjects](https://docs-multiplayer.unity3d.com/netcode/current/tutorials/get-started-ngo/) RPCs and Network Variables. This is only meant to be used to understand <i>how</i> to implement custom networking into the game.
?>**Note:** This is not a tutorial on how to use Unity's [Netcode for GameObjects](https://docs-multiplayer.unity3d.com/netcode/1.5.2/about/) RPCs and Network Variables. This is only meant to be used to understand <i>how</i> to implement custom networking into the game.

## Preface

Expand Down Expand Up @@ -89,10 +89,19 @@ namespace ExampleMod
public class ExampleNetworkHandler : NetworkBehaviour
{

public static ExampleNetworkHandler Instance { get; private set; }
}
}
```

We also add the one line of code to allow scripts to easily access any methods or variables, since in the case of our ExampleMod, there is only one version of this class. While you can just use:

```cs
public static ExampleNetworkHandler Instance;
```

Doing it with the aforementioned method will prevent any classes from overriding the Instance variable, ensuring it can always be referenced as long as the ExampleNetworkHandler exists.

### ClientRpc

We have our basic component! From here, we need to add the RPCs and a measure to avoid duplicate signals. Since the event info is only sent by the server, we do not have to deal with a ServerRpc and only need to set up the ClientRpc. This is what our example mod uses:
Expand Down Expand Up @@ -136,7 +145,7 @@ if (LevelEvent != null)

All this if statement checks is whether the event is not equal to null and calls the event if so. The event will be null *if there are no subscribers to the event.*

### Preventing Duplication of Events
### Preventing Duplication of Events and Instance

Since we are using `static` when defining our C# event, an edge case can occur. What happens if the event is not unsubscribed from, and the player joins a new server? Any code that unknowingly subscribes to the event a second time will run twice! How do we make sure this does not occur? We set the C# event to equal null. The best time to do so is when the NetworkHandler gets spawned in:

Expand All @@ -151,6 +160,21 @@ public override void OnNetworkSpawn()

This removes any subscribers and continues to call the base OnNetworkSpawn method to allow any code that runs in that method to still occur.

But what about our Instance variable? If we don't set it to anything, any scripts attempting to use this handler won't be able to use .Instance! Here, we can assign the `Instance` variable to be the current object. We also need to remove any previously existing GameObject with our `ExampleNetworkHandler` class, which can only be done via the server.

```cs
public override void OnNetworkSpawn()
{
LevelEvent = null;

if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer)
Instance?.gameObject.GetComponent<NetworkObject>().Despawn();
Instance = this;

base.OnNetworkSpawn();
}
```

### Finalized Network Handler

We finished! All that's left is to throw it all together into one script:
Expand All @@ -169,6 +193,10 @@ namespace ExampleMod
{
LevelEvent = null;

if (NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsServer)
Instance?.gameObject.GetComponent<NetworkObject>().Despawn();
Instance = this;

base.OnNetworkSpawn();
}

Expand All @@ -179,13 +207,15 @@ namespace ExampleMod
}

public static event Action<String> LevelEvent;

public static ExampleNetworkHandler Instance { get; private set; }
}
}
```

## Spawning the NetworkHandler

Before we can spawn the ExampleNetworkHandler, we must load it into the game. There are two ways we can do this: through LethalLib or loading it from an AssetBundle. In our case, we are going to load our handler from an AssetBundle.
Before we can spawn the ExampleNetworkHandler, we must load it into the game. To do so, we need to load our handler from an AssetBundle.

The Game Object we spawn as an asset requires a network object. We will use this prefab for our Network Handler:

Expand Down Expand Up @@ -343,19 +373,27 @@ Finally! The handler is in the game! Now we can utilize it. But how? Easy, we su
[HarmonyPostfix, HarmonyPatch(typeof(RoundManager), nameof(RoundManager.GenerateNewLevelClientRpc))]
static void SubscribeToHandler()
{
NetworkHandler.LevelEvent += ReceivedEventFromServer;
ExampleNetworkHandler.LevelEvent += ReceivedEventFromServer;
}

[HarmonyPostfix, HarmonyPatch(typeof(RoundManager), nameof(RoundManager.DespawnPropsAtEndOfRound))]
static void UnsubscribeFromHandler()
{
NetworkHandler.LevelEvent -= ReceivedEventFromServer;
ExampleNetworkHandler.LevelEvent -= ReceivedEventFromServer;
}

static void ReceivedEventFromServer(string eventName)
{
// Event Code Here
}

static void SendEventToClients(string eventName)
{
if (!(NetworkManager.Singleton.IsHost || NetworkManager.Singleton.IsHost))
return;

ExampleNetworkHandler.Instance.EventClientRpc(eventName);
}
```

What does this all do? Well, `NetworkHandler.LevelEvent += ReceivedEventFromServer` simply tells C# that we want `ReceivedEventFromServer(string eventName)` to run when the `LevelEvent` event is invoked. `NetworkHandler.LevelEvent -= Received` tells C# that we no longer want `ReceivedEventFromServer` to run when the event is invoked.
Expand Down

0 comments on commit d2a1db6

Please sign in to comment.