New Documentation (#184)
* Documentation Outline * Spacing adjustments * Captured old wiki content * yml fix * Docs work * resize images * Replaced images * Removed md from links * Renamed Misty to Fizzy * Captured Unity docs * links cleanup * clear links * Cleanup and moved NetworkBehavior to Classes. * added slashes to yml paths * reverted slashes * Fixes bad link * Update Ignorance.md This should be enough documentation for now, yeah? * Localized images * Update Ignorance.md formatting updates * Lots of Cleanup * fix link * Formatting * fix code blocks * Lots of content and cleanup * fixed yml * Added blank line * Added spaces in titles * tightened bullets * Fixed bullet spacing * Fixed more bullets * unbolded content * Cleanup and removal of empty pages Updated README with links to docs pages * Restored prior version * Contributing * Improvements to content * lower case fix * fix link * renamed Contributions * fixed link * home page content * Fixed Encoding * Moved Why TCP * Replaced Unity with Mirror * Telepathy Description * changed to h2 * Moved Sample down * Removed dead links * Copied Contributions Added Test Fixed h3's * Fixed headings * added to Test * Fixed image alts and links * fixed last alt
105
CONTRIBUTING.md
@ -1,31 +1,103 @@
|
|||||||
# How to contribute
|
# Contribution Guide
|
||||||
|
|
||||||
We are really glad you're reading this, because we need volunteer developers to help this project improve.
|
## Introduction
|
||||||
|
|
||||||
|
First off, thank you for considering contributing to this project. It's people like you that make it such a great tool.
|
||||||
|
|
||||||
|
Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
|
||||||
|
|
||||||
|
This is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into the main project itself.
|
||||||
|
|
||||||
If you haven't already, come find us in [Discord](https://discord.gg/wvesC6). We want you working on things you're excited about, and we can give you instant feedback.
|
If you haven't already, come find us in [Discord](https://discord.gg/wvesC6). We want you working on things you're excited about, and we can give you instant feedback.
|
||||||
|
|
||||||
## Testing
|
### I don't want to read this whole thing I just have a question!!
|
||||||
|
|
||||||
|
We currently allow our users to use the issue tracker for support questions. But please be wary that maintaining an open source project can take a lot of time from the maintainers. If asking for a support question, state it clearly and take the time to explain your problem properly. Also, if your problem is not strictly related to this project we recommend you to use [Stack Overlow](https://stackoverflow.com) instead.
|
||||||
|
|
||||||
|
## How Can I Contribute?
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
We have a handful of unit tests, but most of our testbed consists of running it with existing projects.
|
We have a handful of unit tests, but most of our testbed consists of running it with existing projects.
|
||||||
Try our builds and pull requests in your own projects and let us know how it goes.
|
Try our builds and pull requests in your own projects and let us know how it goes.
|
||||||
|
|
||||||
## Submitting changes
|
### Reporting Bugs
|
||||||
|
|
||||||
Please send a [GitHub Pull Request](https://github.com/vis2k/HLAPI-Community-Edition/pull/new/improvements) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)).
|
Before creating bug reports, please check the existing bug reports as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible.
|
||||||
|
|
||||||
|
#### How Do I Submit A (Good) Bug Report?
|
||||||
|
|
||||||
|
[Create an issue](https://github.com/vis2k/Mirror/issues/new?template=bug_report.md) on the project's repository and provide the following information.
|
||||||
|
|
||||||
|
Explain the problem and include additional details to help maintainers reproduce the problem:
|
||||||
|
|
||||||
|
* **Use a clear and descriptive title** for the issue to identify the problem.
|
||||||
|
* **Provide a simplified project that reproduces the issue whenever possible.**
|
||||||
|
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you used the project. When listing steps, **don't just say what you did, but explain how you did it**.
|
||||||
|
* **Provide specific examples to demonstrate the steps**. It's always better to get more information. You can include links to files or GitHub projects, copy/pasteable snippets or even print screens or animated GIFS. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||||
|
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||||
|
* **Explain which behavior you expected to see instead and why.**
|
||||||
|
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
|
||||||
|
|
||||||
|
Provide more context by answering these questions:
|
||||||
|
|
||||||
|
* **Did the problem start happening recently** (e.g. after updating to a new version) or was this always a problem?
|
||||||
|
* If the problem started happening recently, **can you reproduce the problem in an older version?** What's the most recent version in which the problem doesn't happen?
|
||||||
|
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
|
||||||
|
|
||||||
|
Include details about your configuration and environment:
|
||||||
|
|
||||||
|
* **Which version of the project are you using?**
|
||||||
|
* **What's the name and version of the OS you're using**?
|
||||||
|
* **Any other information that could be useful about you environment**
|
||||||
|
|
||||||
|
### Suggesting Enhancements
|
||||||
|
|
||||||
|
This section guides you through submitting an enhancement suggestion for this project, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions.
|
||||||
|
|
||||||
|
Before creating enhancement suggestions, please check the list of enhancements suggestions in the issue tracker as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please include as many details as possible.
|
||||||
|
|
||||||
|
#### How Do I Submit A (Good) Enhancement Suggestion?
|
||||||
|
|
||||||
|
[Create an issue](https://github.com/vis2k/Mirror/issues/new?template=feature_request.md) on the project's repository and provide the following information:
|
||||||
|
|
||||||
|
* **Use a clear and descriptive title** for the issue to identify the suggestion.
|
||||||
|
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
|
||||||
|
* **Provide specific examples to demonstrate the steps**. It's always better to get more information. You can include links to files or GitHub projects, copy/pasteable snippets or even print screens or animated GIFS. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||||
|
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
|
||||||
|
* **List some other similar projects where this enhancement exists.**
|
||||||
|
* **Specify which version of the project you're using.**
|
||||||
|
* **Specify the current environment you're using.** if this is a useful information.
|
||||||
|
* **Provide a specific use case** - Often we get requests for a feature not realizing there is already a way to fulfill their use case. In other words, don't just give us a solution, give us a problem.
|
||||||
|
|
||||||
|
|
||||||
|
### Creating Pull Requests
|
||||||
|
|
||||||
|
#### How Do I Submit A (Good) Pull Request?
|
||||||
|
|
||||||
|
Please send a [GitHub Pull Request](https://github.com/vis2k/Mirror/compare) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)).
|
||||||
When you send a pull request, we will love you forever if you include unit tests.
|
When you send a pull request, we will love you forever if you include unit tests.
|
||||||
We can always use more test coverage.
|
We can always use more test coverage.
|
||||||
|
|
||||||
|
* **Use a clear and descriptive title** for the pull request to state the improvement you made to the code or the bug you solved.
|
||||||
|
* **Provide a link to the related issue** if the pull request is a follow up of an existing bug report or enhancement suggestion.
|
||||||
|
* **Comment why this pull request represents an enhancement** and give a rationale explaining why you did it that way and not another way.
|
||||||
|
* **Use the same coding style as the one used in this project**.
|
||||||
|
* **Welcome suggestions from the maintainers to improve your pull request**.
|
||||||
|
|
||||||
Please follow our coding conventions (below) and make sure all of your commits are atomic (one feature per commit). Rebase your pull requests if necessary.
|
Please follow our coding conventions (below) and make sure all of your commits are atomic (one feature per commit). Rebase your pull requests if necessary.
|
||||||
|
|
||||||
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
|
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
|
||||||
|
|
||||||
$ git commit -m "A brief summary of the commit
|
```sh
|
||||||
>
|
$ git commit -m "A brief summary of the commit""
|
||||||
> A paragraph describing what changed and its impact."
|
>
|
||||||
|
> A paragraph describing what changed and its impact.
|
||||||
|
```
|
||||||
|
|
||||||
Submit your pull requests to the right branch:
|
Submit your pull requests to the right branch:
|
||||||
* fixes for bug fixes
|
* Submit against the 2018 branch when the change **only** applies to Unity 2018.2+
|
||||||
* improvements for refactorings and cleanups, we love to delete code.
|
* Submit against the master branch in all other cases
|
||||||
* features for new shiny features you want in HLAPI.
|
|
||||||
|
|
||||||
If your pull request breaks any test, it has no hope of being merged.
|
If your pull request breaks any test, it has no hope of being merged.
|
||||||
|
|
||||||
@ -35,7 +107,14 @@ Start reading our code and you'll get the hang of it. We optimize for readabilit
|
|||||||
|
|
||||||
* We indent using 4 spaces (soft tabs)
|
* We indent using 4 spaces (soft tabs)
|
||||||
* We value simplicity. The code should be easy to read and avoid magic
|
* We value simplicity. The code should be easy to read and avoid magic
|
||||||
* We use default visual studio code formatting standard
|
* **KISS / Occam's Razor** - always use the most simple solution.
|
||||||
|
* **No Premature Optimizations**
|
||||||
|
MMOs need to run for weeks without issues or exploits.
|
||||||
|
If you want your code to run 1% faster, spend \$100 on a better CPU.
|
||||||
|
* **Curly Braces { }**
|
||||||
|
Always use braces even for one line if's. Unity did this everywhere, and there is value in not accidentally missing a line in an if statement because there were no braces.
|
||||||
|
* **Variable naming**
|
||||||
|
\`NetworkIdentity identity\`, not \`NetworkIdentity uv\` or similar. If the variable needs a comment the name needs to be changed. For example, `msg = ... // the message` use `message = ...` without a comment instead Please Avoid **var** where possible. My text editor doesn't show me the type, so it needs to be obvious, and having two different ways to do the same thing only creates unnecessary complexity and confusion.
|
||||||
* This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
|
* This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
|
||||||
|
|
||||||
Thanks.
|
Thanks.
|
||||||
|
@ -25,7 +25,7 @@ What previously required **10.000** lines of code, now takes **1.000** lines of
|
|||||||
_Note: Mirror is based on Unity's abandoned UNET Networking system. We fixed it up and pushed it to MMO Scale._
|
_Note: Mirror is based on Unity's abandoned UNET Networking system. We fixed it up and pushed it to MMO Scale._
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Check out our [Wiki](https://github.com/vis2k/Mirror/wiki) and read the [UNET Manual](https://docs.unity3d.com/Manual/UNet.html), Mirror is still similar enough.
|
Check out our [Documentation](https://github.com/vis2k/Mirror/docs) and read the [Wiki](https://github.com/vis2k/Mirror/wiki).
|
||||||
|
|
||||||
The main difference is that you have to use `using Mirror;` instead of `using UnityEngine.Networking;` at the top of your scripts.
|
The main difference is that you have to use `using Mirror;` instead of `using UnityEngine.Networking;` at the top of your scripts.
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ Alternatively, you can install it manually:
|
|||||||
4. Select Runtime-Editor/Mirror.Runtime.dll and tell Unity to **only Include** the Editor platform
|
4. Select Runtime-Editor/Mirror.Runtime.dll and tell Unity to **only Include** the Editor platform
|
||||||
|
|
||||||
## Migration Guide
|
## Migration Guide
|
||||||
If you are still using UNET and want to switch to Mirror, you should check out our [Migration Guide](migration.md). Don't panic, it's very easy and won't take more than 5 minutes.
|
If you are still using UNET and want to switch to Mirror, you should check out our [Migration Guide](docs/General/Migration). Don't panic, it's very easy and won't take more than 5 minutes.
|
||||||
|
|
||||||
## Example Projects
|
## Example Projects
|
||||||
Download Mirror from the [Asset Store](https://www.assetstore.unity3d.com/#!/content/129321), we have several small example projects included.
|
Download Mirror from the [Asset Store](https://www.assetstore.unity3d.com/#!/content/129321), we have several small example projects included.
|
||||||
@ -66,7 +66,7 @@ Building Mirror yourself is very easy. Simply download the project, open it in V
|
|||||||
* [uSurvival 122 CCU worst case test](https://docs.google.com/document/d/e/2PACX-1vT28FcGXYlbG8gwi8DhD914n7K-wCAE8qhfetPkSli96ikc1Td3zJO1IiwVhfPVtKUHF0l3N7ZkM5GU/pub#h.pwbvffnwcewe)
|
* [uSurvival 122 CCU worst case test](https://docs.google.com/document/d/e/2PACX-1vT28FcGXYlbG8gwi8DhD914n7K-wCAE8qhfetPkSli96ikc1Td3zJO1IiwVhfPVtKUHF0l3N7ZkM5GU/pub#h.pwbvffnwcewe)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
If you like to contribute, feel free to submit pull requests and visit our [Discord Server](https://discordapp.com/invite/N9QVxbM).
|
If you like to contribute, feel free to [submit pull requests](https://github.com/vis2k/Mirror/docs/General/Contributions) and visit our [Discord Server](https://discordapp.com/invite/N9QVxbM).
|
||||||
|
|
||||||
We follow the [KISS](https://en.wikipedia.org/wiki/KISS_principle) principle, so make sure that your Pull Requests contain no magic.
|
We follow the [KISS](https://en.wikipedia.org/wiki/KISS_principle) principle, so make sure that your Pull Requests contain no magic.
|
||||||
|
|
||||||
|
35
docs/Classes/Attributes.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Attributes Overview
|
||||||
|
|
||||||
|
Networking attributes are added to member functions of NetworkBehaviour scripts, to make them run on either the client or server.
|
||||||
|
|
||||||
|
These attributes can be used for Unity game loop methods like Start or Update, as well as other implemented methods.
|
||||||
|
|
||||||
|
- **NetworkSettings**
|
||||||
|
Something about this
|
||||||
|
- **Server**
|
||||||
|
means don't allow a client to call that method (throws a warning or an error when called on a client).
|
||||||
|
- **ServerCallback**
|
||||||
|
Something about this
|
||||||
|
- **Client**
|
||||||
|
means don't allow a server to call that method (throws a warning or an error when called on the server).
|
||||||
|
- **ClientRpc**
|
||||||
|
The server uses an Rpc to run that function on clients.
|
||||||
|
- **ClientCallback**
|
||||||
|
Something about this
|
||||||
|
- **TargetRpc**
|
||||||
|
Something about this
|
||||||
|
- **Command**
|
||||||
|
Call this from a client to run this function on the server. Make sure to validate input etc. It's not possible to call this from a server. Use this as a wrapper around another function, if you want to call it from the server too.
|
||||||
|
The allowed argument types are;
|
||||||
|
- Basic type (byte, int, float, string, UInt64, etc)
|
||||||
|
- Built-in Unity math type (Vector3, Quaternion, etc),
|
||||||
|
- Arrays of basic types
|
||||||
|
- Structs containing allowable types
|
||||||
|
- NetworkIdentity
|
||||||
|
- NetworkInstanceId
|
||||||
|
- NetworkHash128
|
||||||
|
- GameObject with a NetworkIdentity component attached.
|
||||||
|
- **SyncVar**
|
||||||
|
SyncVars are used to synchronize a variable from the server to all clients automatically. Don't assign them from a client, it's pointless. Don't let them be null, you will get errors. You can use int, long, float, string, Vector3 etc. (all simple types) and NetworkIdentity and GameObject if the GameObject has a NetworkIdentity attached to it. You can use hooks.
|
||||||
|
- **SyncEvent**
|
||||||
|
Something about this
|
337
docs/Classes/NetworkBehavior.md
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
# NetworkBehavior
|
||||||
|
|
||||||
|
NetworkBehaviour scripts work with GameObjects that have a NetworkIdentity component. These scripts can perform high-level API functions such as Commands, ClientRPCs, SyncEvents and SyncVars.
|
||||||
|
|
||||||
|
With the server-authoritative system of Mirror, the server must use the NetworkServer.Spawn function to spawn GameObjects with Network Identity components. Spawning them this way assigns them a NetworkInstanceId and creates them on clients connected to the server.
|
||||||
|
|
||||||
|
**Note:** This is not a component that you can add to a GameObject directly. Instead, you must create a script which inherits from `NetworkBehaviour` (instead of the default `MonoBehaviour`), then you can add your script as a component to a GameObject.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
- **isLocalPlayer**
|
||||||
|
Returns true if this GameObject is the one that represents the player on the local client.
|
||||||
|
- **isServer**
|
||||||
|
Returns true if this GameObject is running on the server, and has been spawned.
|
||||||
|
- **isClient**
|
||||||
|
Returns true if this GameObject is on the client and has been spawned by the server.
|
||||||
|
- **hasAuthority**
|
||||||
|
Returns true if this GameObject is the authoritative version of the GameObject, meaning it is the source for changes to be synchronized. For most GameObjects, this returns true on the server. However, if the localPlayerAuthority value on the NetworkIdentity is true, the authority rests with that player’s client, and this value is true on that client instead of on the server.
|
||||||
|
- **netId**
|
||||||
|
The unique network ID of this GameObject. The server assigns this at runtime. It is unique for all GameObjects in that network session.
|
||||||
|
- **playerControllerId**
|
||||||
|
The ID of the player associated with this NetworkBehaviour script. This is only valid if the object is a local player.
|
||||||
|
- **connectionToServer**
|
||||||
|
The NetworkConnection associated with the Network Identity component attached to this GameObject. This is only valid for **player objects** on the client.
|
||||||
|
- **connectionToClient**
|
||||||
|
The NetworkConnection associated with the Network Identity component attached to this GameObject. This is only valid for player GameObjects on the server.
|
||||||
|
- **localPlayerAuthority**
|
||||||
|
This value is set on the Network Identity component and is accessible from the NetworkBehaviour script for convenient access in scripts.
|
||||||
|
|
||||||
|
NetworkBehaviour scripts have the following features:
|
||||||
|
|
||||||
|
- Synchronized variables
|
||||||
|
- Network callbacks
|
||||||
|
- Server and client functions
|
||||||
|
- Sending commands
|
||||||
|
- Client RPC calls
|
||||||
|
- Networked events
|
||||||
|
|
||||||
|
![Data Flow Graph](UNetDirections.jpg)
|
||||||
|
|
||||||
|
## Synchronized variables
|
||||||
|
|
||||||
|
You can synchronize member variables of NetworkBehaviour scripts from the server to clients. The server is authoritative in this system, so synchronization only takes place in the direction of server to client.
|
||||||
|
|
||||||
|
Use the SyncVar attribute to tag member variables as synchronized. Synchronized variables can be any basic type (bool, byte, sbyte, char, decimal, double, float, int, uint, long, ulong, short, ushort, string), but not classes, lists, or other collections.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class SpaceShip : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[SyncVar]
|
||||||
|
public int health;
|
||||||
|
|
||||||
|
[SyncVar]
|
||||||
|
public string playerName;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When the value of a `SyncVar` changes on the server, the server automatically sends the new value to all ready clients in the game, and updates the corresponding `SyncVar` values on those clients. When GameObjects spawn, they are created on the client with the latest state of all `SyncVar` attributes from the server.
|
||||||
|
|
||||||
|
**Note:** To make a request from a client to the server, you need to use commands, not synchronized variables. See documentation on Sending commands for more information.
|
||||||
|
|
||||||
|
## Network callbacks
|
||||||
|
|
||||||
|
There are built-in callback functions which are invoked on NetworkBehaviour scripts for various network events. These are virtual functions on the base class, so you can override them in your own code like this:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class SpaceShip : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public override void OnStartServer()
|
||||||
|
{
|
||||||
|
// disable client stuff
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartClient()
|
||||||
|
{
|
||||||
|
// register client events, enable effects
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The built-in callbacks are:
|
||||||
|
|
||||||
|
- **OnStartServer**
|
||||||
|
called when a GameObject spawns on the server, or when the server is started for GameObjects in the Scene
|
||||||
|
- **OnStartClient**
|
||||||
|
called when the GameObject spawns on the client, or when the client connects to a server for GameObjects in the Scene
|
||||||
|
- **OnSerialize**
|
||||||
|
called to gather state to send from the server to clients
|
||||||
|
- **OnDeSerialize**
|
||||||
|
called to apply state to GameObjects on clients
|
||||||
|
- **OnNetworkDestroy**
|
||||||
|
called on clients when the server destroys the GameObject
|
||||||
|
- **OnStartLocalPlayer**
|
||||||
|
called on clients for player GameObjects on the local client (only)
|
||||||
|
- **OnRebuildObservers**
|
||||||
|
called on the server when the set of observers for a GameObjects is rebuilt
|
||||||
|
- **OnSetLocalVisibility**
|
||||||
|
called on the client and/or server when the visibility of a GameObject changes for the local client
|
||||||
|
- **OnCheckObserver**
|
||||||
|
called on the server to check visibility state for a new client
|
||||||
|
|
||||||
|
Note that in a peer-hosted setup, when one of the clients is acting as both host and client, both `OnStartServer` and `OnStartClient` are called on the same GameObject. Both these functions are useful for actions that are specific to either the client or server, such as suppressing effects on a server, or setting up client-side events.
|
||||||
|
|
||||||
|
## Server and Client functions
|
||||||
|
|
||||||
|
You can tag member functions in NetworkBehaviour scripts with custom attributes to designate them as server-only or client-only functions. For example:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class SimpleSpaceShip : NetworkBehaviour
|
||||||
|
{
|
||||||
|
int health;
|
||||||
|
|
||||||
|
[Server]
|
||||||
|
public void TakeDamage( int amount)
|
||||||
|
{
|
||||||
|
// will only work on server
|
||||||
|
health -= amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[ServerCallback]
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
// engine invoked callback - will only run on server
|
||||||
|
}
|
||||||
|
|
||||||
|
[Client]
|
||||||
|
void ShowExplosion()
|
||||||
|
{
|
||||||
|
// will only run on client
|
||||||
|
}
|
||||||
|
|
||||||
|
[ClientCallback]
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
// engine invoked callback - will only run on client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Server` and `[ServerCallback]` return immediately if the client is not active. Likewise, `Client` and `ClientCallback` return immediately if the server is not active.
|
||||||
|
|
||||||
|
The `[Server]` and `Client` attributes are for your own custom callback functions. They do not generate compile time errors, but they do emit a warning log message if called in the wrong scope.
|
||||||
|
|
||||||
|
The `ServerCallback` and `ClientCallback` attributes are for built-in callback functions that are called automatically by Mirror. These attributes do not cause a warning to be generated.
|
||||||
|
|
||||||
|
For more information, see API reference documentation on the attributes discussed:
|
||||||
|
|
||||||
|
- ClientAttribute
|
||||||
|
- ClientCallbackAttribute
|
||||||
|
- ServerAttribute
|
||||||
|
- ServerCallbackAttribute
|
||||||
|
|
||||||
|
## Sending commands
|
||||||
|
|
||||||
|
To execute code on the server, you must use commands. The high-level API is a server-authoritative system, so commands are the only way for a client to trigger some code on the server.
|
||||||
|
|
||||||
|
Only player GameObjects can send commands.
|
||||||
|
|
||||||
|
When client player GameObject sends a command, that command runs on the corresponding player GameObject on the server. This routing happens automatically, so it is impossible for a client to send a command for a different player.
|
||||||
|
|
||||||
|
To define a command in your code, you must write a function which has:
|
||||||
|
|
||||||
|
- A name that begins with `Cmd`
|
||||||
|
- The `Command` attribute
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class SpaceShip : NetworkBehaviour
|
||||||
|
{
|
||||||
|
bool alive;
|
||||||
|
float thrusting;
|
||||||
|
int spin;
|
||||||
|
|
||||||
|
[ClientCallback] // This code executes on the client, gathering input
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
int spin = 0;
|
||||||
|
|
||||||
|
if (Input.GetKey(KeyCode.LeftArrow))
|
||||||
|
spin += 1;
|
||||||
|
|
||||||
|
if (Input.GetKey(KeyCode.RightArrow))
|
||||||
|
spin -= 1;
|
||||||
|
|
||||||
|
// This line triggers the code to run on the server
|
||||||
|
CmdThrust(Input.GetAxis("Vertical"), spin);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command] // This code executes on the server after Update() is called from below.
|
||||||
|
public void CmdThrust(float thrusting, int spin)
|
||||||
|
{
|
||||||
|
if (!alive)
|
||||||
|
{
|
||||||
|
this.thrusting = 0;
|
||||||
|
this.spin = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.thrusting = thrusting;
|
||||||
|
this.spin = spin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Commands are called just by invoking the function normally on the client. Instead of the command function running on the client, it is automatically invoked on the corresponding player GameObject on the server.
|
||||||
|
|
||||||
|
Commands are type-safe, have built-in security and routing to the player, and use an efficient serialization mechanism for the arguments to make calling them fast.
|
||||||
|
|
||||||
|
## Client RPC calls
|
||||||
|
|
||||||
|
Client RPC calls are a way for server GameObjects to make things happen on client GameObjects.
|
||||||
|
|
||||||
|
Client RPC calls are not restricted to player GameObjects, and may be called on any GameObject with a Network Identity component.
|
||||||
|
|
||||||
|
To define a client RPC call in your code, you must write a function which:
|
||||||
|
|
||||||
|
- Has a name that begins with `Rpc`
|
||||||
|
- Has the `ClientRPC` attribute
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class SpaceShipRpc : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[ServerCallback] // This is code run on the server
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
int value = UnityEngine.Random.Range(0,100);
|
||||||
|
|
||||||
|
if (value < 10)
|
||||||
|
{
|
||||||
|
// This invokes the RpcDoOnClient function on all clients
|
||||||
|
RpcDoOnClient(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ClientRpc] // This code will run on all clients
|
||||||
|
public void RpcDoOnClient(int foo)
|
||||||
|
{
|
||||||
|
Debug.Log("OnClient " + foo);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Networked events
|
||||||
|
|
||||||
|
Networked events are like Client RPC calls, but instead of calling a function on the GameObject, they trigger Events instead.
|
||||||
|
|
||||||
|
This allows you to write scripts which can register for a callback when an event is triggered.
|
||||||
|
|
||||||
|
To define a Networked event in your code, you must write a function which both:
|
||||||
|
|
||||||
|
- Has a name that begins with `Event`
|
||||||
|
- Has the `SyncEvent` attribute
|
||||||
|
|
||||||
|
You can use events to build powerful networked game systems that can be extended by other scripts. This example shows how an effect script on the client can respond to events generated by a combat script on the server.
|
||||||
|
|
||||||
|
SyncEvent is the base class that Commands and ClientRPC calls are derived from. You can use the SyncEvent attribute on your own functions to make your own event-driven networked gameplay code. Using SyncEvent, you can extend Mirror’s Multiplayer features to better fit your own programming patterns. For example:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
// Server script
|
||||||
|
public class MyCombat : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public delegate void TakeDamageDelegate(int amount);
|
||||||
|
public delegate void DieDelegate();
|
||||||
|
public delegate void RespawnDelegate();
|
||||||
|
|
||||||
|
float deathTimer;
|
||||||
|
bool alive;
|
||||||
|
int health;
|
||||||
|
|
||||||
|
[SyncEvent(channel=1)]
|
||||||
|
public event TakeDamageDelegate EventTakeDamage;
|
||||||
|
|
||||||
|
[SyncEvent]
|
||||||
|
public event DieDelegate EventDie;
|
||||||
|
|
||||||
|
[SyncEvent]
|
||||||
|
public event RespawnDelegate EventRespawn;
|
||||||
|
|
||||||
|
[ServerCallback]
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
// Check if it is time to Respawn
|
||||||
|
if (!alive)
|
||||||
|
{
|
||||||
|
if (Time.time > deathTimer)
|
||||||
|
{
|
||||||
|
Respawn();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Server]
|
||||||
|
void Respawn()
|
||||||
|
{
|
||||||
|
alive = true;
|
||||||
|
|
||||||
|
// send respawn event to all clients from the Server
|
||||||
|
EventRespawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Server]
|
||||||
|
void EventTakeDamage(int amount)
|
||||||
|
{
|
||||||
|
if (!alive)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (health > amount)
|
||||||
|
health -= amount;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
health = 0;
|
||||||
|
alive = false;
|
||||||
|
|
||||||
|
// send die event to all clients
|
||||||
|
EventDie();
|
||||||
|
deathTimer = Time.time + 5.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
24
docs/Classes/NetworkClient.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# NetworkClient
|
||||||
|
|
||||||
|
`NetworkClient` is a high-level API class that manages a network connection from a client to a server, and can send and receive messages between the client and the server. The `NetworkClient` class also helps to manage spawned network GameObjects, and routing of RPC message and network events.
|
||||||
|
|
||||||
|
See the [NetworkClient](#networkclient) script reference for more information.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
- **serverIP**
|
||||||
|
The IP address of the server that this client is connected to.
|
||||||
|
- **serverPort**
|
||||||
|
The port of the server that this client is connected to.
|
||||||
|
- **connection**
|
||||||
|
The NetworkConnection GameObject this `NetworkClient` instance is using.
|
||||||
|
- **handlers**
|
||||||
|
The set of registered message handler functions.
|
||||||
|
- **numChannels**
|
||||||
|
The number of configured NetworkTransport QoS channels.
|
||||||
|
- **isConnected**
|
||||||
|
True if the client is connected to a server.
|
||||||
|
- **allClients**
|
||||||
|
List of active NetworkClients (static).
|
||||||
|
- **active**
|
||||||
|
True if any NetworkClients are active (static).
|
73
docs/Classes/NetworkConnection.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# NetworkConnection
|
||||||
|
|
||||||
|
NetworkConnection is a high-level API class that encapsulates a network connection. NetworkClient objects have a `NetworkConnection`, and NetworkServers have multiple connections - one from each client. NetworkConnections have the ability to send byte arrays, or serialized objects as network messages.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
- **hostId**
|
||||||
|
The [NetworkTransport] hostId for this connection.
|
||||||
|
- **connectionId**
|
||||||
|
- The `NetworkTransport` connectionId for this connection.
|
||||||
|
- **isReady**
|
||||||
|
Flag to control whether state updates are sent to this connection
|
||||||
|
- **lastMessageTime**
|
||||||
|
The last time that a message was received on this connection.
|
||||||
|
- **address**
|
||||||
|
The IP address of the end-point that this connection is connected to.
|
||||||
|
- **playerControllers**
|
||||||
|
The set of players that have been added with AddPlayer().
|
||||||
|
- **clientOwnedObjects**
|
||||||
|
The set of objects that this connection has authority over.
|
||||||
|
|
||||||
|
The NetworkConnection class has virtual functions that are called when data is sent to the transport layer or recieved from the transport layer. These functions allow specialized versions of NetworkConnection to inspect or modify this data, or even route it to different sources. These function are show below, including the default behaviour:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public virtual void TransportRecieve(byte[] bytes, int numBytes, int channelId)
|
||||||
|
{
|
||||||
|
HandleBytes(bytes, numBytes, channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error)
|
||||||
|
{
|
||||||
|
return NetworkTransport.Send(hostId, connectionId, channelId, bytes, numBytes, out error);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An example use of these function is to log the contents of incoming and outgoing packets. Below is an example of a DebugConnection class that is derived from NetworkConnection that logs the first 50 bytes of packets to the console. To use a class like this call the SetNetworkConnectionClass() function on a NetworkClient or NetworkServer.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
class DebugConnection : NetworkConnection
|
||||||
|
{
|
||||||
|
public override void TransportRecieve(byte[] bytes, int numBytes, int channelId)
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < numBytes; i++)
|
||||||
|
{
|
||||||
|
var s = String.Format("{0:X2}", bytes[i]);
|
||||||
|
msg.Append(s);
|
||||||
|
if (i > 50) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnityEngine.Debug.Log("TransportRecieve h:" + hostId + " con:" + connectionId + " bytes:" + numBytes + " " + msg);
|
||||||
|
|
||||||
|
HandleBytes(bytes, numBytes, channelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TransportSend(byte[] bytes, int numBytes, int channelId, out byte error)
|
||||||
|
{
|
||||||
|
StringBuilder msg = new StringBuilder();
|
||||||
|
|
||||||
|
for (int i = 0; i < numBytes; i++)
|
||||||
|
{
|
||||||
|
var s = String.Format("{0:X2}", bytes[i]);
|
||||||
|
msg.Append(s);
|
||||||
|
if (i > 50) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnityEngine.Debug.Log("TransportSend h:" + hostId + " con:" + connectionId + " bytes:" + numBytes + " " + msg);
|
||||||
|
|
||||||
|
return NetworkTransport.Send(hostId, connectionId, channelId, bytes, numBytes, out error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
34
docs/Classes/NetworkServer.md
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# NetworkServer
|
||||||
|
|
||||||
|
NetworkServer is a High-Level-API class that manages connections from multiple clients.
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
- **active**
|
||||||
|
Checks if the server has been started.
|
||||||
|
- **connections**
|
||||||
|
A list of all the current connections from clients.
|
||||||
|
- **dontListen**
|
||||||
|
If you enable this, the server will not listen for incoming connections on the regular network port.
|
||||||
|
- **handlers**
|
||||||
|
Dictionary of the message handlers registered with the server.
|
||||||
|
- **hostTopology**
|
||||||
|
The host topology that the server is using.
|
||||||
|
- **listenPort**
|
||||||
|
The port that the server is listening on.
|
||||||
|
- **localClientActive**
|
||||||
|
True if a local client is currently active on the server.
|
||||||
|
- **localConnections**
|
||||||
|
A list of local connections on the server.
|
||||||
|
- **maxDelay**
|
||||||
|
The maximum delay before sending packets on connections.
|
||||||
|
- **networkConnectionClass**
|
||||||
|
The class to be used when creating new network connections.
|
||||||
|
- **numChannels**
|
||||||
|
The number of channels the network is configure with.
|
||||||
|
- **objects**
|
||||||
|
This is a dictionary of networked objects that have been spawned on the server.
|
||||||
|
- **serverHostId**
|
||||||
|
The transport layer hostId used by this server.
|
||||||
|
- **useWebSockets**
|
||||||
|
This makes the server listen for WebSockets connections instead of normal transport layer connections.
|
42
docs/Classes/SyncLists.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# SyncLists Overview
|
||||||
|
|
||||||
|
There are some very important optimizations when it comes to bandwidth done in Mirror.
|
||||||
|
|
||||||
|
## Channels
|
||||||
|
|
||||||
|
There was a bug in HLAPI that caused syncvar to be sent to every channel when they changed. If you had 10 channels, then all the variables would be sent 10 times during the same frame, all as different packages.
|
||||||
|
|
||||||
|
## SyncLists
|
||||||
|
|
||||||
|
HLAPI SyncLists sent a message for every change immediately. They did not respect the SyncInterval. If you add 10 items to a list, it means sending 10 messages.
|
||||||
|
|
||||||
|
In Mirror SyncList were redesigned. The lists queue up their changes, and the changes are sent as part of the syncvar synchronization. If you add 10 items, then only 1 message is sent with all changes according to the next SyncInterval.
|
||||||
|
|
||||||
|
We also raised the limit from 32 SyncVars to 64 per NetworkBehavior.
|
||||||
|
|
||||||
|
A SyncList can only be of the following type
|
||||||
|
|
||||||
|
- Basic type (byte, int, float, string, UInt64, etc)
|
||||||
|
- Built-in Unity math type (Vector3, Quaternion, etc)
|
||||||
|
- NetworkIdentity
|
||||||
|
- NetworkInstanceId
|
||||||
|
- NetworkHash128
|
||||||
|
- GameObject with a NetworkIdentity component attached.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Don't modify them in Awake, use OnStartServer or Start. SyncListStructs use Structs. C\# structs are value types, just like int, float, Vector3 etc. You can't do synclist.value = newvalue; You have to copy the element, assign the new value, assign the new element to the synclist. You can use hooks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
// for the official things
|
||||||
|
[SyncListString(hook="MyHook")] SyncListString mylist;
|
||||||
|
void MyHook(SyncListString.Operation op, int index) {
|
||||||
|
// do things
|
||||||
|
}
|
||||||
|
|
||||||
|
// for custom structs
|
||||||
|
[SyncListString(hook="MyHook")] SyncListStructCustom mylist;
|
||||||
|
void MyHook(SyncListStructCustom.Operation op, int index) {
|
||||||
|
// do things
|
||||||
|
}
|
||||||
|
```
|
BIN
docs/Classes/UNetDirections.jpg
Normal file
After Width: | Height: | Size: 45 KiB |
21
docs/Classes/index.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Classes Overview
|
||||||
|
|
||||||
|
General description of Classes
|
||||||
|
|
||||||
|
- [NetworkServer](NetworkServer)
|
||||||
|
NetworkServer is a High-Level-API class that manages connections from multiple clients.
|
||||||
|
- [NetworkClient](NetworkClient)
|
||||||
|
NetworkClient is a high-level API class that manages a network connection from a client to a server, and can send and receive messages between the client and the server.
|
||||||
|
- [NetworkConnection](NetworkConnection)
|
||||||
|
NetworkConnection is a high-level API class that encapsulates a network connection.
|
||||||
|
- [NetworkBehavior](NetworkBehavior)
|
||||||
|
NetworkBehaviour scripts work with GameObjects that have a NetworkIdentity component. These scripts can perform high-level API functions such as Commands, ClientRPCs, SyncEvents and SyncVars.
|
||||||
|
- [Attributes](Attributes)
|
||||||
|
Networking attributes are added to member functions of NetworkBehaviour scripts, to make them run on either the client or server.
|
||||||
|
- [SyncLists](SyncLists)
|
||||||
|
SyncLists contain lists of values:
|
||||||
|
- SyncListString
|
||||||
|
- SyncListFloat
|
||||||
|
- SyncListInt
|
||||||
|
- SyncListUInt
|
||||||
|
- SyncListBool
|
BIN
docs/Components/NetworkAddressAndPortSettings.png
Normal file
After Width: | Height: | Size: 18 KiB |
21
docs/Components/NetworkAnimator.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# NetworkAnimator
|
||||||
|
|
||||||
|
The Network Animator component allows you to synchronize animation states for networked objects. It synchronizes state and parameters from an Animator Controller.
|
||||||
|
|
||||||
|
Note that if you create a Network Animator component on an empty GameObject, Mirror also creates a Network Identity component and an Animator component on that GameObject.
|
||||||
|
|
||||||
|
![The Network Animator component in the Inspector window](NetworkAnimatorComponent.png)
|
||||||
|
|
||||||
|
- **Animator**
|
||||||
|
Use this field to define the Animator component you want the Network Animator to synchronize with.
|
||||||
|
|
||||||
|
## Details
|
||||||
|
|
||||||
|
The Network Animator ensures the synchronization of GameObject animation across the network - meaning that all players see the animation happen at the same. There are two kinds of authority for networked animation (see documentation on Network system concepts for more information about authority)):
|
||||||
|
|
||||||
|
- If the GameObject has authority on the client, you should animate it locally on the client that owns the GameObject. That client sends the animation state information to the server, which broadcasts it to all the other clients. For example, this would be suitable for player characters.
|
||||||
|
- If the GameObject has authority on the server, then you should animate it on the server. The server then sends state information to all clients. This is common for animated GameObjects that are not related to a specific client, such as non-player characters.
|
||||||
|
|
||||||
|
The Network Animator synchronizes the animation parameters checked in the Inspector window. It does not automatically synchronize animation triggers. A GameObject with authority can use the SetTrigger function to fire an animation trigger on other clients.
|
||||||
|
|
||||||
|
The GetParameterAutoSend and SetParameterAutoSend functions can be used to control which individual animator parameters should be automatically synchronized.
|
BIN
docs/Components/NetworkAnimatorComponent.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
3
docs/Components/NetworkController.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# NetworkController
|
||||||
|
|
||||||
|
General description of NetworkController
|
BIN
docs/Components/NetworkGame3Instances.jpg
Normal file
After Width: | Height: | Size: 218 KiB |
BIN
docs/Components/NetworkIdentity.jpg
Normal file
After Width: | Height: | Size: 36 KiB |
63
docs/Components/NetworkIdentity.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# NetworkIdentity
|
||||||
|
|
||||||
|
The Network Identity component is at the heart of the Unity networking high-level API. It controls a GameObject’s unique identity on the network, and it uses that identity to make the networking system aware of the GameObject. It offers two different options for configuration and they are mutually exclusive, which means either one of the options or none can be checked.
|
||||||
|
|
||||||
|
- **Server Only**
|
||||||
|
Tick this checkbox to ensure that Unity only spawns the GameObject on the server, and not on clients.
|
||||||
|
- **Local Player Authority**
|
||||||
|
Tick this checkbox to give authoritative network control of this GameObject to the client that owns it. The player GameObject on that client has authority over it. Other components such as Network Transform use this to determine which client to treat as the source of authority.
|
||||||
|
|
||||||
|
If none of these options is checked, the server will have authority over the object. Changes made by clients (e.g. moving the object) are not allowed and will not be synchronized.
|
||||||
|
|
||||||
|
![Inspector](NetworkIdentity.jpg)
|
||||||
|
|
||||||
|
## Instantiated Network GameObjects
|
||||||
|
|
||||||
|
With the Unity’s server-authoritative networking system, the server must spawn networked GameObjects with network identities, using [NetworkServer.Spawn]. This automatically creates them on clients that are connected to the server, and assigns them a [NetworkInstanceId].
|
||||||
|
|
||||||
|
You must put a Network Identity component on any Prefabs that spawn at runtime for the network system to use them. See [Object Spawning] for more information.
|
||||||
|
|
||||||
|
## Scene-based Network GameObjects
|
||||||
|
|
||||||
|
You can also network GameObjects that are saved as part of your Scene (for example, environmental props). Networking GameObjects makes them behave slightly differently, because you need to have them spawn across the network.
|
||||||
|
|
||||||
|
When building your game, Unity disables all Scene-based GameObjects with Network Identity components. When a client connects to the server, the server sends spawn messages to tell the client which Scene GameObjects to enable and what their most up-to-date state information is. This ensures the client’s game does not contain GameObjects at incorrect locations when they start playing, or that Unity does not spawn and immediately destroy GameObjects on connection (for example, if an event removed the GameObject before that client connected). See [Networked Scene GameObjects] for more information.
|
||||||
|
|
||||||
|
## Preview Pane Information
|
||||||
|
|
||||||
|
This component contains network tracking information, and displays that information in the preview pane. For example, the scene ID, network ID and asset ID the object has been assigned. This allows you to inspect the information which can be useful for investigation and debugging.
|
||||||
|
|
||||||
|
![Preview](NetworkIdentityPreview.png)
|
||||||
|
|
||||||
|
At runtime there is more information to display here (a disabled NetworkBehaviour is displayed non-bold):
|
||||||
|
|
||||||
|
![Runtime Preview](NetworkIdentityPreviewRuntime.png)
|
||||||
|
|
||||||
|
- **assetId**
|
||||||
|
This identifies the prefab associated with this object (for spawning).
|
||||||
|
- **clientAuthorityOwner**
|
||||||
|
The client that has authority for this object. This will be null if no client has authority.
|
||||||
|
- **connectionToClient**
|
||||||
|
The NetworkConnection associated with this NetworkIdentity. This is only valid for player objects on the server.
|
||||||
|
- **connectionToServer**
|
||||||
|
The NetworkConnection associated with this NetworkIdentity. This is only valid for player objects on a local client.
|
||||||
|
- **hasAuthority**
|
||||||
|
True if this object is the authoritative version of the object. This would mean either on a the server for normal objects, or on the client with localPlayerAuthority.
|
||||||
|
- **isClient**
|
||||||
|
True if this object is running on a client.
|
||||||
|
- **isLocalPlayer**
|
||||||
|
This returns true if this object is the one that represents the player on the local machine.
|
||||||
|
- **isServer**
|
||||||
|
True if this object is running on the server, and has been spawned.
|
||||||
|
- **localPlayerAuthority**
|
||||||
|
True if this object is controlled by the client that owns it - the local player object on that client has authority over it. This is used by other components such as NetworkTransform.
|
||||||
|
- **netId**
|
||||||
|
A unique identifier for this network session, assigned when spawned.
|
||||||
|
- **observers**
|
||||||
|
The list of client NetworkConnections that are able to see this object. This is read-only.
|
||||||
|
- **playerControllerId**
|
||||||
|
The identifier of the controller associated with this object. Only valid for player objects.
|
||||||
|
- **SceneId**
|
||||||
|
A unique identifier for networked objects in a Scene. This is only populated in play-mode.
|
||||||
|
- **serverOnly**
|
||||||
|
A flag to make this object not be spawned on clients.
|
BIN
docs/Components/NetworkIdentityPreview.png
Normal file
After Width: | Height: | Size: 5.3 KiB |
BIN
docs/Components/NetworkIdentityPreviewRuntime.png
Normal file
After Width: | Height: | Size: 16 KiB |
330
docs/Components/NetworkManager.md
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
# NetworkManager
|
||||||
|
|
||||||
|
The Network Manager is a component for managing the networking aspects of a multiplayer game.
|
||||||
|
|
||||||
|
The Network Manager features include:
|
||||||
|
|
||||||
|
- Game state management
|
||||||
|
- Spawn management
|
||||||
|
- Scene management
|
||||||
|
- Debugging information
|
||||||
|
- Customization
|
||||||
|
|
||||||
|
## Getting Started with the Network Manager
|
||||||
|
|
||||||
|
The Network Manager is the core controlling component of a multiplayer game. To get started, create an empty GameObject in your starting Scene, and add the NetworkManager component. The newly added Network Manager component looks like this:
|
||||||
|
|
||||||
|
![The Network Manager as seen in the inspector window](NetworkManagerInspector.png)
|
||||||
|
|
||||||
|
The Inspector for the Network Manager in the Editor allows you to configure and control many things related to networking.
|
||||||
|
|
||||||
|
Note: You should only ever have one active Network Manager in each Scene. Do not place the Network Manager component on a networked GameObject (one which has a Network Identity component), because Mirror disables these when the Scene loads.
|
||||||
|
|
||||||
|
If you are already familiar with multiplayer game development, you might find it useful to know that the Network Manager component is implemented entirely using the API, so everything it does is also available to you through scripting. For advanced users, if you find that you need to expand on the Network Manager component’s features, you can use scripting to derive your own class from NetworkManager and customize its behaviour by overriding any of the virtual function hooks that it provides. However, the Network Manager component wraps up a lot of useful functionality into a single place, and makes creating, running and debugging multiplayer games as simple as possible.
|
||||||
|
|
||||||
|
## Game State Management
|
||||||
|
|
||||||
|
A Networking multiplayer game can run in three modes - as a client, as a dedicated server, or as a “Host” which is both a client and a server at the same time.
|
||||||
|
|
||||||
|
If you’re using the Network Manager HUD, it automatically tells the Network Manager which mode to start in, based on which options the player selects. If you’re writing your own UI that allows the player to start the game, you’ll need to call these from your own code. These methods are:
|
||||||
|
|
||||||
|
- NetworkManager.StartClient
|
||||||
|
- NetworkManager.StartServer
|
||||||
|
- NetworkManager.StartHost
|
||||||
|
|
||||||
|
![The network address and port settings in the Network Manager component](NetworkAddressAndPortSettings.png)
|
||||||
|
|
||||||
|
Whichever mode the game starts in (client, server, or host), the Network Address and Network Port properties are used. In client mode, the game attempts to connect to the address and port specified. In server or host mode, the game listens for incoming connections on the port specified.
|
||||||
|
|
||||||
|
## Spawn Management
|
||||||
|
|
||||||
|
Use the Network Manager to manage the spawning (networked instantiation) of networked GameObjects from Prefabs.
|
||||||
|
|
||||||
|
![The “Spawn Info” section of the Network Manager component](NetworkManagerSpawnInfo.png)
|
||||||
|
|
||||||
|
Most games have a Prefab which represents the player, so the Network Manager has a Player Prefab slot. You should assign this slot with your player Prefab. When you have a player Prefab set, a player GameObject is automatically spawned from that Prefab for each user in the game. This applies to the local player on a hosted server, and remote players on remote clients. You must attach a Network Identity component to the Player Prefab.
|
||||||
|
|
||||||
|
Once you have assigned a player Prefab, you can start the game as a host and see the player GameObject spawn. Stopping the game destroys the player GameObject. If you build and run another copy of the game and connect it as a client to *localhost*, the Network Manager makes another player GameObject appear. When you stop that client, it destroys that player’s GameObject.
|
||||||
|
|
||||||
|
In addition to the player Prefab, you must also register other Prefabs that you want to dynamically spawn during gameplay with the Network Manager.
|
||||||
|
|
||||||
|
You can add Prefabs to the list shown in the inspector labelled Registered Spawnable Prefabs. You can also can register prefabs via code, with the ClientScene.RegisterPrefab method.
|
||||||
|
|
||||||
|
If you have only one Network Manager, you need to register to it all prefabs which might be spawned in any Scene. If you have a separate Network Manager in each Scene, you only need to register the prefabs relevant for that Scene.
|
||||||
|
|
||||||
|
## Customizing Player Instantiation
|
||||||
|
|
||||||
|
The Network Manager spawns player GameObjects using its implementation of NetworkManager.OnServerAddPlayer. If you want to customize the way player GameObjects are created, you can override that virtual function. This code shows an example of the default implementation:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
|
||||||
|
{
|
||||||
|
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
|
||||||
|
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: If you are implementing a custom version of OnServerAddPlayer, the method NetworkServer.AddPlayerForConnection must be called for the newly created player GameObject, so that it is spawned and associated with the client’s connection. AddPlayerForConnection spawns the GameObject, so you do not need to use NetworkServer.Spawn.
|
||||||
|
|
||||||
|
## Start Positions
|
||||||
|
|
||||||
|
To control where players are spawned, you can use the Network Start Position component. To use these, attach a Network Start Position component to a GameObject in the Scene, and position the GameObject where you would like one of the players to start. You can add as many start positions to your Scene as you like. The Network Manager detects all start positions in your Scene, and when it spawns each player instance, it uses the position and orientation of one of them.
|
||||||
|
|
||||||
|
The Network Manager has a Player Spawn Method property, which allows you to configure how start positions are chosen.
|
||||||
|
|
||||||
|
- Choose Random to spawn players at randomly chosen startPosition options.
|
||||||
|
- Choose Round Robin to cycle through startPosition options in a set list.
|
||||||
|
|
||||||
|
If the Random or Round Robin modes don’t suit your game, you can customize how the start positions are selected by using code. You can access the available Network Start Position components by the list NetworkManager.startPositions, and you can use the helper method GetStartPosition on the Network Manager that can be used in implementation of OnServerAddPlayer to find a start position.
|
||||||
|
|
||||||
|
## Scene Management
|
||||||
|
|
||||||
|
Most games have more than one Scene. At the very least, there is usually a title screen or starting menu Scene in addition to the Scene where the game is actually played. The Network Manager is designed to automatically manage Scene state and Scene transitions in a way that works for a multiplayer game.
|
||||||
|
|
||||||
|
There are two slots on the NetworkManager Inspector for scenes: the Offline Scene and the Online Scene. Dragging Scene assets into these slots activates networked Scene management.
|
||||||
|
|
||||||
|
When a server or host is started, the Online Scene is loaded. This then becomes the current network Scene. Any clients that connect to that server are instructed to also load that Scene. The name of this Scene is stored in the networkSceneName property.
|
||||||
|
|
||||||
|
When the network is stopped, by stopping the server or host or by a client disconnecting, the offline Scene is loaded. This allows the game to automatically return to a menu Scene when disconnected from a multiplayer game.
|
||||||
|
|
||||||
|
You can also change Scenes while the game is active by calling NetworkManager.ServerChangeScene. This makes all the currently connected clients change Scene too, and updates networkSceneName so that new clients also load the new Scene.
|
||||||
|
|
||||||
|
While networked Scene management is active, any calls to game state management functions such NetworkManager.StartHost() or NetworkManager.StopClient() can cause Scene changes. This applies to the runtime control UI. By setting up Scenes and calling these methods, you can control the flow of your multiplayer game.
|
||||||
|
|
||||||
|
Note that Scene changes cause all the GameObjects in the previous Scene to be destroyed.
|
||||||
|
|
||||||
|
You should normally make sure the Network Manager persists between Scenes, otherwise the network connection is broken upon a Scene change. To do this, ensure the Don’t Destroy On Load box is checked in the Inspector. However it is also possible to have a separate Network Manager in each Scene with different settings, which may be helpful if you wish to control incremental Prefab loading, or different Scene transitions.
|
||||||
|
|
||||||
|
## Customization
|
||||||
|
|
||||||
|
There are virtual functions on the NetworkManager class that you can customize by creating your own derived class that inherits from NetworkManager. When implementing these functions, be sure to take care of the functionality that the default implementations provide. For example, in OnServerAddPlayer(), the function NetworkServer.AddPlayer must be called to activate the player GameObject for the connection.
|
||||||
|
|
||||||
|
These are all the callbacks that can happen for host/server and clients, in some cases it’s important to invoke the base class function to maintain default behaviour. To see the implementation itself you can view it in the source code.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class CustomManager : NetworkManager {
|
||||||
|
// Server callbacks
|
||||||
|
public override void OnServerConnect(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
Debug.Log("A client connected to the server: " + conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnServerDisconnect(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
NetworkServer.DestroyPlayersForConnection(conn);
|
||||||
|
|
||||||
|
if (conn.lastError != NetworkError.Ok)
|
||||||
|
{
|
||||||
|
if (LogFilter.logError)
|
||||||
|
Debug.LogError("ServerDisconnected due to error: " + conn.lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("A client disconnected from the server: " + conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnServerReady(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
NetworkServer.SetClientReady(conn);
|
||||||
|
|
||||||
|
Debug.Log("Client is set to the ready state (ready to receive state updates): " + conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
|
||||||
|
{
|
||||||
|
var player = (GameObject)GameObject.Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
|
||||||
|
|
||||||
|
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
|
||||||
|
|
||||||
|
Debug.Log("Client has requested to get his player added to the game");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnServerRemovePlayer(NetworkConnection conn, PlayerController player)
|
||||||
|
{
|
||||||
|
if (player.gameObject != null)
|
||||||
|
NetworkServer.Destroy(player.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnServerError(NetworkConnection conn, int errorCode)
|
||||||
|
{
|
||||||
|
Debug.Log("Server network error occurred: " + (NetworkError)errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartHost()
|
||||||
|
{
|
||||||
|
Debug.Log("Host has started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartServer()
|
||||||
|
{
|
||||||
|
Debug.Log("Server has started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStopServer()
|
||||||
|
{
|
||||||
|
Debug.Log("Server has stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStopHost()
|
||||||
|
{
|
||||||
|
Debug.Log("Host has stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client callbacks
|
||||||
|
public override void OnClientConnect(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
base.OnClientConnect(conn);
|
||||||
|
|
||||||
|
Debug.Log("Connected successfully to server, now to set up other stuff for the client...");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClientDisconnect(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
StopClient();
|
||||||
|
|
||||||
|
if (conn.lastError != NetworkError.Ok)
|
||||||
|
{
|
||||||
|
if (LogFilter.logError)
|
||||||
|
Debug.LogError("ClientDisconnected due to error: " + conn.lastError);
|
||||||
|
}
|
||||||
|
|
||||||
|
Debug.Log("Client disconnected from server: " + conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClientError(NetworkConnection conn, int errorCode)
|
||||||
|
{
|
||||||
|
Debug.Log("Client network error occurred: " + (NetworkError)errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClientNotReady(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
Debug.Log("Server has set client to be not-ready (stop getting state updates)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartClient(NetworkClient client)
|
||||||
|
{
|
||||||
|
Debug.Log("Client has started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStopClient() {
|
||||||
|
Debug.Log("Client has stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnClientSceneChanged(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
base.OnClientSceneChanged(conn);
|
||||||
|
|
||||||
|
Debug.Log("Server triggered scene change and we've done the same, do any extra work here for the client...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The inspector for the NetworkManager provides the ability to change some connection parameters and timeouts. Some parameters have not been exposed here but can be changed through code.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class CustomManager : NetworkManager {
|
||||||
|
|
||||||
|
// Set custom connection parameters early, so they are not too late to be enforced
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
customConfig = true;
|
||||||
|
connectionConfig.MaxCombinedReliableMessageCount = 40;
|
||||||
|
connectionConfig.MaxCombinedReliableMessageSize = 800;
|
||||||
|
connectionConfig.MaxSentMessageQueueSize = 2048;
|
||||||
|
connectionConfig.IsAcksLong = true;
|
||||||
|
globalConfig.ThreadAwakeTimeout = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The Network Manager component allows you to control the state of a networked game. It provides an interface in the Editor for you to configure the network, the Prefabs you use for spawning GameObjects, and the Scenesyou use for different game states.
|
||||||
|
|
||||||
|
For more details on implementing the Network Manager in your game, see documentation on Using the Network Manager.
|
||||||
|
|
||||||
|
![The Network Manager component in the Inspector window](NetworkManagerUNetComponent.png)
|
||||||
|
|
||||||
|
- **Dont Destroy On Load**
|
||||||
|
Use this property to control whether or not Mirror should destroy the GameObject with the Network Manager when the Scene changes. Tick this checkbox to ensure Mirror does not destroy your Network Manager GameObject when the Scene changes in your game. Untick the checkbox if you want Mirror to destroy the GameObject when the Scene it exists in is no longer the active Scene. This is useful if you want to manage multiple, separate Network Manager GameObjects in each of your Scenes. This checkbox is ticked by default.
|
||||||
|
- **Run In Background**
|
||||||
|
Use this property to control whether the networked game runs when the window it is running in is not focused. Tick the checkbox if you want it to run; untick it if you want the game to stop running when the window is not focused. This checkbox is ticked by default. You need to enable this property if you want to run multiple instances of a program on the same machine, such as when testing using localhost. You should disable it when deploying to mobile platforms. When enabled, it sets Application.runInBackground to true when the Network Manager starts up. You can also set this property from the Unity menu: Edit \> Project Settings, then select the Player category, and navigate to the Resolution and Presentation panel.
|
||||||
|
- **Log Level**
|
||||||
|
Use this property to control the amount of information Mirror outputs to the console window. A low level results in more information; a high level results in less information. Each level includes message from all the levels higher than itself (for example, if you select “Warn”, the console also prints outputs all “Error” and “Fatal” log messages). The drop-down lists the levels from low to high. This property is set to Info by default. You can set Log Level to Set in Scripting to prevent the Network Manager from setting the log level at all. This means you can control the level from your own scripts instead.
|
||||||
|
- **Offline Scene**
|
||||||
|
If you assign a Scene to this field, the Network Manager automatically switches to the specified Scene when a network session stops - for example, when the client disconnects, or when the server shuts down.
|
||||||
|
- **Online Scene**
|
||||||
|
If you assign a Scene to this field, the Network Manager automatically switches to the specified Scene when a network session starts - for example, when the client connects to a server, or when the server starts listening for connections.
|
||||||
|
- **Network Info**
|
||||||
|
You can expand this section of the inspector to access network-related settings, listed below
|
||||||
|
- **Use Web Sockets**
|
||||||
|
When running as a host, enable this setting to make the host listen for Web Socket connections instead of normal transport layer connections, so that WebGL clients can connect to it (if you build your game for the WebGL platform). These WebGL instances of your game cannot act as a host (in either peer-hosted or server-only mode). Therefore, for WebGL instances of your multiplayer game to be able to find each other and play together, you must host a server-only instance of your game running in LAN mode, with a publicly reachable IP address, and it must have this option enabled. This checkbox is unticked by default.
|
||||||
|
- **Network Address**
|
||||||
|
The network address currently in use. For clients, this is the address of the server that is connected to. For servers, this is the local address. This is set to ‘localhost’ by default.
|
||||||
|
- **Network Port**
|
||||||
|
The network port currently in use. For clients, this is the port of the server connected to. For servers, this is the listen port. This is set to 7777 by default.
|
||||||
|
- **Server Bind To IP**
|
||||||
|
Allows you to tell the server whether to bind to a specific IP address. If this checkbox is not ticked, then there is no specific IP address bound to (IP_ANY). This checkbox is unticked by default. Use this if your server has multiple network addresses (eg, internal LAN, external internet, VPN) and you want to specific the IP address to serve your game on.
|
||||||
|
- **Server Bind Address**
|
||||||
|
This field is only visible when the Server Bind To IP checkbox is ticked. Use this to enter the specific IP address that the server should bind to.
|
||||||
|
- **Script CRC Check**
|
||||||
|
When this is enabled, Mirror checks that the clients and the server are using matching scripts. This is useful to make sure outdated versions of your client are not connecting to the latest (updated) version of your server. This checkbox is ticked by default. It does this by performing a ([CRC check](https://en.wikipedia.org/wiki/Cyclic_redundancy_check)) between the server and client that ensures the NetworkBehaviour scripts match. This may not be appropriate in some cases, such as when you are intentionally using different Unity projects for the client and server. In most other cases however, you should leave it enabled.
|
||||||
|
- **Max Delay**
|
||||||
|
The maximum time in seconds to delay buffered messages. The default of 0.01 seconds means packets are delayed at most by 10 milliseconds. Setting this to zero disables connection buffering. This is set to 0.01 by default.
|
||||||
|
- **Max Buffered Packets**
|
||||||
|
The maximum number of packets that a NetworkConnection can buffer for each channel. This corresponds to the ChannelOption.MaxPendingBuffers channel option. This is set to 16 by default.
|
||||||
|
- **Packet Fragmentation**
|
||||||
|
This allows the `NetworkConnection` instances to fragment packets that are larger than `maxPacketSize` to up a maximum of 64K. This can cause delays in sending large packets. This checkbox is ticked by default.
|
||||||
|
- **SpawnInfo**
|
||||||
|
You can expand this section of the inspector to access spawn-related settings, listed below
|
||||||
|
- **Player Prefab**
|
||||||
|
Define the default prefab Mirror should use to create player GameObjects on the server. Mirror creates Player GameObjects in the default handler for AddPlayer on the server. Implement OnServerAddPlayer to override this behavior.
|
||||||
|
- **Auto Create Player**
|
||||||
|
Tick this checkbox if you want Mirror to automatically create player GameObjects on connect, and when the Scene changes. This checkbox is ticked by default. Note that if you are using the MigrationManager and you do not enable Auto Create Player, you need to call ClientScene.SendReconnectMessage when your client reconnects.
|
||||||
|
- **Player Spawn Method**
|
||||||
|
Define how Mirror should decide where to spawn new player GameObjects. This is set to Random by default.
|
||||||
|
- **Random**
|
||||||
|
Choose Random to spawn players at randomly chosen startPositions.
|
||||||
|
- **Round Robin**
|
||||||
|
Choose Round Robin to cycle through startPositions in a set list.
|
||||||
|
- **Registered Spawnable Prefabs**
|
||||||
|
Use this list to add prefabs that you want the Network Manager to be aware of, so that it can spawn them. You can also add and remove them via scripting.
|
||||||
|
- **Advanced Configuration**
|
||||||
|
Tick this checkbox to reveal advanced configuration options in the Network Manager Inspector window.
|
||||||
|
- **Max Connections**
|
||||||
|
Define the maximum number of concurrent network connections to support. This is set to 4 by default.
|
||||||
|
- **Qos Channels**
|
||||||
|
A list containing the different communication channels the current Network Manager has, and the Quality Of Service (QoS) setting for each channel. Use this list to add or remove channels, and adjust their QoS setting. You can also configure the channels via scripting. For the descriptions of each QoS option, see QosType.
|
||||||
|
- **Timeouts**
|
||||||
|
- **Min Update Timeout**
|
||||||
|
Set the minimum time (in milliseconds) the network thread waits between sending network messages. The network thread doesn’t send multiplayer network messages immediately. Instead, it check each connection periodically at a fixed rate to see if it has something to send. This is set to 10ms by default. See API reference documentation on MinUpdateTimeout for more information.
|
||||||
|
- **Connect Timeout**
|
||||||
|
Define the amount of time (in milliseconds) Mirror should wait while trying to connect before attempting the connection again. This is set to 2000ms by default. See API reference documentation on ConnectTimeout for more information.
|
||||||
|
- **Disconnect Timeout**
|
||||||
|
The amount of time (in milliseconds) before Mirror considers a connection to be disconnected. This is set to 2000ms by default. See API reference documentation on DisconnectTimeout for more information.
|
||||||
|
- **Ping Timeout**
|
||||||
|
The amount of time (in milliseconds) between sending pings (also known as “keep-alive” packets). The ping timeout duration should be approximately one-third to one-quarter of the Disconnect Timeout duration, so that Mirror doesn’t assume that clients are disconnected until the server has failed to receive at least three pings from the client. This is set to 500ms by default. See API reference documentation on ConnectionConfig.PingTimeout for more information.
|
||||||
|
- **Global Config**
|
||||||
|
These settings relate to the Reactor. The Reactor is the part of the multiplayer system which receives network packets from the underlying operating system, and passes them into the multiplayer system for processing.
|
||||||
|
- **Thread Awake Timeout**
|
||||||
|
The timeout duration in milliseconds, used by the Reactor. How the Reactor uses this value depends on which Reactor Model you select (see below). This is set to 1ms by default.
|
||||||
|
- **Reactor Model**
|
||||||
|
Choose which type of reactor to use. The reactor model defines how Mirror reads incoming packets. For most games and applications, the default Select reactor is appropriate. If you want to trade a small delay in the processing of network messages for lower CPU usage and improved battery life, use the Fix Rate reactor.
|
||||||
|
- **Select Reactor**
|
||||||
|
This model uses the `select()` API which means that the network thread “awakens” (becomes active) as soon as a packet is available. Using this method means your game gets the data as fast as possible. This is the default Reactor Model setting.
|
||||||
|
- **Fix Rate Reactor**
|
||||||
|
This model lets the network thread sleep manually for a given amount of time (defined by the value in Thread Awake Timeout) before checking whether there are incoming packets waiting to be processed.
|
||||||
|
- **Reactor Max Recv Messages**
|
||||||
|
Set the maximum number of messages stored in the receive queue. This is set to 1024 messages by default.
|
||||||
|
- **Reactor Max Sent Messages**
|
||||||
|
Set the maximum number of messages stored in the send queue. This is set to 1024 messages by default.
|
||||||
|
- **Use Network Simulator**
|
||||||
|
Tick this checkbox to enable the usage of the network simulator. The network simulator introduces simulated latency and packet loss based on the following settings:
|
||||||
|
- **Simulated Average Latency**
|
||||||
|
The amount of delay in milliseconds to simulate.
|
||||||
|
- **Simulated Packet Loss**
|
||||||
|
The amount of packet loss to simulate in percent.
|
70
docs/Components/NetworkManagerHUD.md
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# NetworkManagerHUD
|
||||||
|
|
||||||
|
The Network Manager HUD is a quick-start tool to help you start building your multiplayer game straight away, without first having to build a user interface for game creation/connection/joining. It allows you to jump straight into your gameplay programming, and means you can build your own version of these controls later in your development schedule.
|
||||||
|
|
||||||
|
It is not, however, intended to be included in finished games. The idea is that these controls are useful to get you started, but you should create your own UI later on, to allow your players to find and join games in a way that suits your game. For example, you might want to stylize the design of the screens, buttons and list of available games to match the overall style of your game.
|
||||||
|
|
||||||
|
To start using the Network Manager HUD, create an empty GameObject in your Scene (menu: GameObject \> Create Empty) and add the Network Manager HUD component to the new GameObject.
|
||||||
|
|
||||||
|
![The Network Manager HUD component, as viewed in the inspector](NetworkManagerHUDComponent.png)
|
||||||
|
|
||||||
|
- **Show Runtime GUI**
|
||||||
|
Tick this checkbox to show the **Network Manager** HUD GUI at run time. This allows you to reveal or hide it for quick debugging.
|
||||||
|
- **GUI Horizontal Offset**
|
||||||
|
Set the horizontal **pixel** offset of the HUD, measured from the left edge of the screen.
|
||||||
|
- **GUI Vertical Offset**
|
||||||
|
Set the vertical pixel offset of the HUD, measured from the top edge of the screen.
|
||||||
|
|
||||||
|
The Network Manager HUD (“heads-up display”) provides the basic functions so that people playing your game can start hosting a networked game, or find and join an existing networked game. Unity displays the Network Manager HUD as a collection of simple UI buttons in the Game view.
|
||||||
|
|
||||||
|
![The Network Manager HUD UI, as viewed in the Game view](NetworkManagerHUDUI.png)
|
||||||
|
|
||||||
|
## Using the HUD
|
||||||
|
|
||||||
|
The Network Manager HUD has two basic modes: LAN (Local Area Network) mode and Matchmaker mode. These modes match the two common types of multiplayer games. LAN mode is for creating or joining games hosted on a local area network (that is, multiple computers connected to the same network), and Matchmaker mode is for creating, finding and joining games across the internet (multiple computers connected to separate networks).
|
||||||
|
|
||||||
|
The Network Manager HUD starts in LAN mode, and displays buttons relating to hosting and joining a LAN-based multiplayer game. To switch the HUD to Matchmaker mode, click the Enable Match Maker (M) button.
|
||||||
|
|
||||||
|
Note: Remember that the Network Manager HUD feature is a temporary aid to development. It allows you to get your multiplayer game running quickly, but you should replace it with your own UI controls when you are ready.
|
||||||
|
|
||||||
|
### LAN Host
|
||||||
|
|
||||||
|
Click the LAN Host button to start a game as a host on the local network. This client is both the host *and* one of the players in the game. It uses the information from the Network Info section in the inspector to host the game.
|
||||||
|
|
||||||
|
When you click this button, the HUD switches to a simple display of network details, and a Stop (X) button which allows you to stop hosting the game and return to the main LAN menu.
|
||||||
|
|
||||||
|
![The Network Manager HUD when hosting a LAN game.](NetworkManagerHUDHostingLAN.png)
|
||||||
|
|
||||||
|
When you have started a game as a host, other players of the game can then connect to the host to join the game.
|
||||||
|
|
||||||
|
Click the Stop (X) button to disconnect any players that are connected to the host player. Clicking Stop (X) also returns the HUD to the LAN menu.
|
||||||
|
|
||||||
|
### LAN Client
|
||||||
|
|
||||||
|
To connect to a host on the local network, use the text field to the right of the LAN Client button to specify the address of the host. The default host address is “localhost”, which means the client looks on its own computer for the game host. Click LAN Client (C) to attempt to connect to the host address you have specified.
|
||||||
|
|
||||||
|
Use the default “localhost” in this field if you are running multiple instances of your game on one computer, to test multiplayer interactivity. To do this, you can create a standalone build of your game, and then launch it multiple times on your computer. This is a common way to quickly test that your networked game interactions are functioning as you expect, without you needing to deploy your game to multiple computers or devices.
|
||||||
|
|
||||||
|
![An example of three instances of a networked game running on the same desktop PC. This is useful for quick tests to ensure networked interactions are behaving as you intended. One is running as LAN Host, and two are running as LAN Client.](NetworkGame3Instances.jpg)
|
||||||
|
|
||||||
|
When you want to test your game on multiple machines within the same network (that is, on a LAN), you need to put the address of the person acting as host into the "localhost**"** text field.
|
||||||
|
|
||||||
|
The person acting as the host needs to tell their IP address to everyone running LAN clients, so that you can type this into the box.
|
||||||
|
|
||||||
|
Enter the IP address (or leave it as “localhost” if you are testing it on your own machine), then click LAN Client to attempt to connect to the host.
|
||||||
|
|
||||||
|
When the client is attempting to connect, the HUD displays a Cancel Connection Attempt button. Click this if you want to stop trying to connect to the host.
|
||||||
|
|
||||||
|
![The HUD while attempting a connection](NetworkManagerHUDConnectionAttempt.png)
|
||||||
|
|
||||||
|
If the connection is successful, the HUD displays the Stop (X) button. Click this if you want to stop the game on the client and disconnect from the host:
|
||||||
|
|
||||||
|
![The HUD after a successful connection](NetworkManagerHUDConnected.png)
|
||||||
|
|
||||||
|
### LAN Server Only
|
||||||
|
|
||||||
|
Click LAN Server Only to start a game which acts as a server that other clients can connect to, but which does not act as a client to the game itself. This type of game is often called a “dedicated server”. A user cannot play the game on this particular instance of your game. All players must connect as clients, and nobody plays on the instance that is running as the server.
|
||||||
|
|
||||||
|
A dedicated server on a LAN results in better performance for all connected players, because the server doesn’t need to process a local player’s gameplay in addition to acting as server.
|
||||||
|
|
||||||
|
You might also choose this option if you want to host a game that can be played over the internet (rather than just within a local network), but want to maintain control of the server yourself - for example, to prevent cheating by one of the clients, because only the server has authority over the game. To do this, you would need to run the game in Server Only mode on a computer with a public IP address.
|
BIN
docs/Components/NetworkManagerHUDComponent.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
docs/Components/NetworkManagerHUDConnected.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
docs/Components/NetworkManagerHUDConnectionAttempt.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
docs/Components/NetworkManagerHUDHostingLAN.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
docs/Components/NetworkManagerHUDUI.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
docs/Components/NetworkManagerInspector.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/Components/NetworkManagerSpawnInfo.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
BIN
docs/Components/NetworkManagerUNetComponent.png
Normal file
After Width: | Height: | Size: 41 KiB |
3
docs/Components/NetworkNavMeshAgent.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# NetworkNavMeshAgent
|
||||||
|
|
||||||
|
General description of NetworkNavMeshAgent
|
BIN
docs/Components/NetworkProximityCheck.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
20
docs/Components/NetworkProximityChecker.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# NetworkProximityChecker
|
||||||
|
|
||||||
|
The Network Proximity Checker component controls the visibility of GameObjects for network clients, based on proximity to players.
|
||||||
|
|
||||||
|
![The Network Proximity Checker component](NetworkProximityCheck.png)
|
||||||
|
|
||||||
|
- **Vis Range**
|
||||||
|
Define the range that the GameObject should be visible in.
|
||||||
|
- **Vis Update Interval**
|
||||||
|
Define how often (in seconds) the GameObject should check for players entering its visible range.
|
||||||
|
- **Check Method**
|
||||||
|
Define which type of physics (2D or 3D) to use for proximity checking.
|
||||||
|
- **Force Hidden**
|
||||||
|
Tick this checkbox to hide this object from all players.
|
||||||
|
|
||||||
|
With the Network Proximity Checker, a game running on a client doesn’t have information about GameObjects that are not visible. This has two main benefits: it reduces the amount of data sent across the network, and it makes your game more secure against hacking.
|
||||||
|
|
||||||
|
This component relies on physics to calculate visibility, so the GameObject must also have a collider component on it.
|
||||||
|
|
||||||
|
A GameObject with a Network Proximity Checker component must also have a [Network Identity] component. When you create a Network Proximity Checker component on a GameObject, Mirror also creates a Network Identity component on that GameObject if it does not already have one.
|
3
docs/Components/NetworkRigidbody.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# NetworkRigidbody
|
||||||
|
|
||||||
|
General description of NetworkRigidbody
|
BIN
docs/Components/NetworkStartPosition.jpg
Normal file
After Width: | Height: | Size: 42 KiB |
9
docs/Components/NetworkStartPosition.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# NetworkStartPosition
|
||||||
|
|
||||||
|
NetworkStartPosition is used by the NetworkManager when creating player objects. The position and rotation of the NetworkStartPosition are used to place the newly created player object.
|
||||||
|
|
||||||
|
The NetworkManager will spawn players at (0, 0, 0) by default. Adding this component to a gameobject will automatically register/unregister its gameobjects transform at the NetworkManager as available spawning position.
|
||||||
|
|
||||||
|
It's possible to have multiple starting positions within one scene. Depending on the NetworkManager setting Spawn Info/Player Spawn Method the spawning is either Random (possible that the same spawn position will be used by two or more players) or Round Robin (use every available position, until there are more clients than spawn points).
|
||||||
|
|
||||||
|
![Inspector](NetworkStartPosition.jpg)
|
46
docs/Components/NetworkTransform.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# NetworkTransform
|
||||||
|
|
||||||
|
The Network Transform component synchronizes the movement and rotation of GameObjects across the network. Note that the network Transform component only synchronizes spawned networked GameObjects.
|
||||||
|
|
||||||
|
![The Network Transform component](NetworkTransform.png)
|
||||||
|
|
||||||
|
- **Network Send Rate (seconds)**
|
||||||
|
Set the number of network updates per second. You can set this slider to 0 for GameObjects that do not need to update after being created, like non-interactive effects generated by a player (for example, a dust cloud left behind that the player cannot interact with).
|
||||||
|
- **Transform Sync Mode**
|
||||||
|
Select what type of synchronization should occur on this GameObject.
|
||||||
|
- **Sync None**
|
||||||
|
Don’t synchronize.
|
||||||
|
- **Sync Transform**
|
||||||
|
Use the GameObject’s Transform for synchronization. Use this if the physics system does not control this GameObject (that is, if you are moving it via scripting or animation). This is the default option.
|
||||||
|
- **Sync Rigidbody 2D**
|
||||||
|
Use the Rigidbody2D component for synchronization. Use this if the 2D physics system controls this GameObject.
|
||||||
|
- **Sync Rigidbody 3D**
|
||||||
|
Use the Rigidbody component for synchronization. Use this if the 3D physics system controls this GameObject.
|
||||||
|
- **Sync Character Controller**
|
||||||
|
Use the Character Controller component for synchronization. Only select this if you’re using a Character Controller.
|
||||||
|
- **Movement:**
|
||||||
|
- **Movement Threshold**
|
||||||
|
Set the distance that a GameObject can move without sending a movement synchronization update.
|
||||||
|
- **Snap Threshold**
|
||||||
|
Set the threshold at which, if a movement update puts a GameObject further from its current position than this, the GameObject snaps to the position instead of moving smoothly.
|
||||||
|
- **Interpolate Movement Factor**
|
||||||
|
Use this to enable and control interpolation of the synchronized movement. The larger this number is, the faster the GameObject interpolates to the target position. If this is set to 0, the GameObject snaps to the new position.
|
||||||
|
- **Rotation:**
|
||||||
|
- **Rotation Axis**
|
||||||
|
Define which rotation axis or axes should synchronize. This is set to XYZ (full 3D) by default.
|
||||||
|
- **Interpolate Rotation Factor**
|
||||||
|
Use this to enable and control interpolation of the synchronized rotation. The larger this number is, the faster the GameObject interpolates to the target rotation. If this is set to 0, the GameObject snaps to the new rotation.
|
||||||
|
- **Compress Rotation**
|
||||||
|
If you compress rotation data, the amount of data sent is lower, and the accuracy of the rotation synchronization is lower.
|
||||||
|
- **None**
|
||||||
|
Choose this to apply no compression to the rotation synchronization. This is the default option.
|
||||||
|
- **Low**
|
||||||
|
Choose this to apply a low amount of compression to the rotation synchronization. This option lessens the amount of information sent for rotation data.
|
||||||
|
- **High**
|
||||||
|
Choose this to apply a high amount of compression to the rotation synchronization. This option sends the least amount of information possible for rotation data.
|
||||||
|
- **Sync Angular Velocity**
|
||||||
|
Tick this checkbox to synchronize the angular velocity of the attached Rigidbody component.
|
||||||
|
|
||||||
|
This component takes authority into account, so local player GameObjects (which have local authority) synchronize their position from the client to server, then out to other clients. Other GameObjects (with server authority) synchronize their position from the server to clients.
|
||||||
|
|
||||||
|
A GameObject with a Network Transform component must also have a Network Identity component. When you create a Network Transform component on a GameObject, Mirror also creates a Network Identity component on that GameObject if it does not already have one.
|
BIN
docs/Components/NetworkTransform.png
Normal file
After Width: | Height: | Size: 20 KiB |
29
docs/Components/NetworkTransformChild.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# NetworkTransformChild
|
||||||
|
|
||||||
|
The Network Transform Child component synchronizes the position and rotation of the child GameObject of a GameObject with a Network Transform component. You should use this component in situations where you need to synchronize an independently-moving child object of a Networked GameObject.
|
||||||
|
|
||||||
|
To use the Network Transform Child component, attach it to the same parent GameObject as the Network Transform, and use the Target field to define which child GameObject to apply the component settings to. You can have multiple Network Transform Child components on one parent GameObject.
|
||||||
|
|
||||||
|
![The Network Transform Child component](NetworkTransformChild.png)
|
||||||
|
|
||||||
|
- **Network Send Rate (seconds)**
|
||||||
|
Set the number of network updates per second. You can set this slider to 0 for GameObjects that do not need to update after being created, like non-interactive effects generated by a player (for example, a dust cloud left behind that the player cannot interact with).
|
||||||
|
- Child transform to be synchronized. (Remember, this component goes on the *parent*, not the child - so you specify the child object using this field).
|
||||||
|
- **Movement Threshold**
|
||||||
|
Set the distance that a GameObject can move without sending a movement synchronization update.
|
||||||
|
- Use this to enable and control **interpolation** of the synchronized movement. The larger this number is, the faster the GameObject interpolates to the **target position**. If this is set to 0, the GameObject snaps to the new position.
|
||||||
|
- **Interpolate Rotation Factor**
|
||||||
|
Use this to enable and control interpolation of the synchronized rotation. The larger this number is, the faster the GameObject interpolates to the target rotation. If this is set to 0, the GameObject snaps to the new rotation.
|
||||||
|
- Define which rotation axis or axes should synchronize. This is set to XYZ (full 3D) by default.
|
||||||
|
- **Compress Rotation**
|
||||||
|
If you compress rotation data, the amount of data sent is lower, and the accuracy of the rotation synchronization is lower.
|
||||||
|
- **None**
|
||||||
|
Choose this to apply no compression to the rotation synchronization. This is the default option.
|
||||||
|
- **Low**
|
||||||
|
Choose this to apply a low amount of compression to the rotation synchronization. This option lessens the amount of information sent for rotation data.
|
||||||
|
- **High**
|
||||||
|
Choose this to apply a high amount of compression to the rotation synchronization. This option sends the least amount of information possible for rotation data.
|
||||||
|
|
||||||
|
This component does not use physics; it synchronizes the position and rotation of the child GameObject, and interpolates towards updated values. Use Interpolate Movement Factor and Interpolate Rotation Factor to customize the rate of interpolation.
|
||||||
|
|
||||||
|
A GameObject with a Network Transform Child component must also have a Network Identity component. When you create a Network Transform Child component on a GameObject, Mirror also creates a Network Identity component on that GameObject if it does not already have one.
|
BIN
docs/Components/NetworkTransformChild.png
Normal file
After Width: | Height: | Size: 14 KiB |
28
docs/Components/index.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Components Overview
|
||||||
|
|
||||||
|
General description of Components
|
||||||
|
|
||||||
|
- [NetworkManager](NetworkManager)
|
||||||
|
The Network Manager is a component for managing the networking aspects of a multiplayer game.
|
||||||
|
- [NetworkManagerHUD](NetworkManagerHUD)
|
||||||
|
The Network Manager HUD is a quick-start tool to help you start building your multiplayer game straight away, without first having to build a user interface for game creation/connection/joining. It allows you to jump straight into your gameplay programming, and means you can build your own version of these controls later in your development schedule.
|
||||||
|
- [NetworkIdentity](NetworkIdentity)
|
||||||
|
The Network Identity component is at the heart of the Mirror networking high-level API. It controls a GameObject’s unique identity on the network, and it uses that identity to make the networking system aware of the GameObject. It offers two different options for configuration and they are mutually exclusive, which means either one of the options or none can be checked.
|
||||||
|
- [NetworkStartPosition](NetworkStartPosition)
|
||||||
|
Network Start Position is used by the Network Manager when creating player objects. The position and rotation of the Network Start Position are used to place the newly created player object.
|
||||||
|
- [NetworkProximityChecker](NetworkProximityChecker)
|
||||||
|
The Network Proximity Checker component controls the visibility of GameObjects for network clients, based on proximity to players.
|
||||||
|
- [NetworkTransform](NetworkTransform)
|
||||||
|
The Network Transform component synchronizes the movement and rotation of GameObjects across the network. Note that the network Transform component only synchronizes spawned networked GameObjects.
|
||||||
|
- [NetworkTransformChild](NetworkTransformChild)
|
||||||
|
The Network Transform Child component synchronizes the position and rotation of the child GameObject of a GameObject with a Network Transform component.
|
||||||
|
- [NetworkAnimator](NetworkAnimator)
|
||||||
|
The Network Animator component allows you to synchronize animation states for networked objects. It synchronizes state and parameters from an Animator Controller.
|
||||||
|
- [NetworkNavMeshAgent](NetworkNavMeshAgent)
|
||||||
|
Coming Soon
|
||||||
|
- [NetworkController](NetworkController)
|
||||||
|
Coming Soon
|
||||||
|
- [NetworkRigidbody](NetworkRigidbody)
|
||||||
|
Coming Soon
|
||||||
|
|
||||||
|
|
51
docs/Concepts/Authority.md
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
# Network Authority
|
||||||
|
|
||||||
|
Servers and clients can both manage a GameObject’s behavior. The concept of “authority” refers to how and where a GameObject is managed.
|
||||||
|
|
||||||
|
## Server Authority
|
||||||
|
|
||||||
|
The default state of authority in networked games using Mirror is that the Server has authority over all GameObjects which do not represent players. This means - for example - the server would manage control of all collectable items, moving platforms, NPCs - and any other parts of your game that players can interac and player GameObjects have authority on their owner’s client (meaning the client manages their behavior).
|
||||||
|
|
||||||
|
## Local Authority
|
||||||
|
|
||||||
|
Local authority (sometimes referred to as client authority) means the local client has authoritative control over a particular networked GameObject. This is in contrast to the default state which is that the server has authoritative control over networked GameObjects.
|
||||||
|
|
||||||
|
In addition to `isLocalPlayer`, you can choose to make the player GameObjects have “local authority”. This means that the player GameObject on its owner’s client is responsible for (or has authority over) itself. This is particularly useful for controlling movement; it means that each client has authority over how their own player GameObject is being controlled.
|
||||||
|
|
||||||
|
To enable local player authority on a GameObject, tick the Network Identity component’s Local Player Authority checkbox. The Network Transform component uses this “authority” setting, and sends movement information from the client to the other clients if this is set.
|
||||||
|
|
||||||
|
See Scripting API Reference documentation on NetworkIdentity and localPlayerAuthority for information on implementing local player authority via script.
|
||||||
|
|
||||||
|
![This image shows the Enemy object under server authority. The enemy appears on Client 1 and Client 2, but the server is in charge of its position, movement, and behavior](NetworkAuthority.png)
|
||||||
|
|
||||||
|
Use the NetworkIdentity.hasAuthority property to find out whether a GameObject has local authority (also accessible on `NetworkBehaviour` for convenience). Non-player GameObjects have authority on the server, and player GameObjects with localPlayerAuthority set have authority on their owner’s client.
|
||||||
|
|
||||||
|
## Local (Client) Authority for Non-Player GameObjects
|
||||||
|
|
||||||
|
It is possible to have client authority over non-player GameObjects. There are two ways to do this. One is to spawn the GameObject using NetworkServer.SpawnWithClientAuthority, and pass the network connection of the client to take ownership. The other is to use NetworkIdentity.AssignClientAuthority with the network connection of the client to take ownership.
|
||||||
|
|
||||||
|
Assigning authority to a client causes Mirror to call OnStartAuthority() on each `NetworkBehaviour` on the GameObject, and sets the `hasAuthority` property to true. On other clients, the `hasAuthority` property remains false. Non-player GameObjects with client authority can send commands, just like players can. These commands are run on the server instance of the GameObject, not on the player associated with the connection.
|
||||||
|
|
||||||
|
If you want non-player GameObjects to have client authority, you must enable localPlayerAuthority on their Network Identity component. The example below spawns a GameObject and assigns authority to the client of the player that spawned it.
|
||||||
|
|
||||||
|
```
|
||||||
|
[Command]
|
||||||
|
void CmdSpawn()
|
||||||
|
{
|
||||||
|
var go = (GameObject)Instantiate(otherPrefab, transform.position + new Vector3(0,1,0), Quaternion.identity);
|
||||||
|
NetworkServer.SpawnWithClientAuthority(go, connectionToClient);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Network Context Properties
|
||||||
|
|
||||||
|
The `NetworkBehaviour` class contains properties that allow scripts
|
||||||
|
to know what the context of a networked GameObject is at any time.
|
||||||
|
|
||||||
|
- isServer - true if the GameObject is on a server (or host) and has been spawned.
|
||||||
|
- isClient - true if the GameObject is on a client, and was created by the server.
|
||||||
|
- isLocalPlayer - true if the GameObject is a player GameObject for this client.
|
||||||
|
- hasAuthority - true if the GameObject is owned by the local process
|
||||||
|
|
||||||
|
To see these properties, select the GameObject you want to inspect, and in the Inspector window, view the preview window for the NetworkBehaviour scripting components. You can use the value of these properties to execute code based on the context in which the script is running.
|
86
docs/Concepts/ClientsServers.md
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
# Network Clients and Servers
|
||||||
|
|
||||||
|
Many multiplayer games can use the Network Manager to manage connections, but you can also use the lower-level NetworkServer and NetworkClient classes directly.
|
||||||
|
|
||||||
|
When using the High-Level API, every game must have a host server to connect to. Each participant in a multiplayer game can be a client, a dedicated server, or a combination of server and client at the same time. This combination role is the common case of a multiplayer game with no dedicated server.
|
||||||
|
|
||||||
|
For multiplayer games with no dedicated server, one of the players running the game acts as the server for that game. That player’s instance of the game runs a “local client” instead of a normal remote client. The local client uses the same Scenes and GameObjects as the server, and communicates internally using message queues instead of sending messages across the network. To Mirror code and systems, the local client is just another client, so almost all user code is the same, whether a client is local or remote. This makes it easy to make a game that works in both multiplayer and standalone mode with the same code.
|
||||||
|
|
||||||
|
A common pattern for multiplayer games is to have a GameObject that manages the network state of the game. Below is the start of a NetworkManager script. This script would be attached to a GameObject that is in the start-up Scene of the game. It has a simple UI and keyboard handling functions that allow the game to be started in different network modes. Before you release your game you should create a more visually appealing menu, with options such as “Start single player game” and “Start multiplayer game”.
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class MyNetworkManager : MonoBehaviour {
|
||||||
|
public bool isAtStartup = true;
|
||||||
|
NetworkClient myClient;
|
||||||
|
|
||||||
|
void Update ()
|
||||||
|
{
|
||||||
|
if (isAtStartup)
|
||||||
|
{
|
||||||
|
if (Input.GetKeyDown(KeyCode.S))
|
||||||
|
SetupServer();
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.C))
|
||||||
|
SetupClient();
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.B))
|
||||||
|
{
|
||||||
|
SetupServer();
|
||||||
|
SetupLocalClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void OnGUI()
|
||||||
|
{
|
||||||
|
if (isAtStartup)
|
||||||
|
{
|
||||||
|
GUI.Label(new Rect(2, 10, 150, 100), "Press S for server");
|
||||||
|
GUI.Label(new Rect(2, 30, 150, 100), "Press B for both");
|
||||||
|
GUI.Label(new Rect(2, 50, 150, 100), "Press C for client");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This basic code calls setup functions to get things going. Below are the simple setup functions for each of the scenarios. These functions create a server, or the right kind of client for each scenario. Note that the remote client assumes the server is on the same machine (127.0.0.1). For a finished game this would be an internet address, or something supplied by the Matchmaking system.
|
||||||
|
|
||||||
|
```
|
||||||
|
// Create a server and listen on a port
|
||||||
|
public void SetupServer()
|
||||||
|
{
|
||||||
|
NetworkServer.Listen(4444);
|
||||||
|
isAtStartup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a client and connect to the server port
|
||||||
|
public void SetupClient()
|
||||||
|
{
|
||||||
|
myClient = new NetworkClient();
|
||||||
|
myClient.RegisterHandler(MsgType.Connect, OnConnected);
|
||||||
|
myClient.Connect("127.0.0.1", 4444);
|
||||||
|
isAtStartup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a local client and connect to the local server
|
||||||
|
public void SetupLocalClient()
|
||||||
|
{
|
||||||
|
myClient = ClientScene.ConnectLocalServer();
|
||||||
|
myClient.RegisterHandler(MsgType.Connect, OnConnected);
|
||||||
|
isAtStartup = false;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The clients in this code register a callback function for the connection event [MsgType.Connect](https://docs.unity3d.com/ScriptReference/Networking.MsgType.Connect.html). This is a built-in message of Mirror that the script invokes when a client connects to a server. In this case, the code for the handler on the client is:
|
||||||
|
|
||||||
|
```
|
||||||
|
// client function
|
||||||
|
public void OnConnected(NetworkMessage netMsg)
|
||||||
|
{
|
||||||
|
Debug.Log("Connected to server");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is enough to get a multiplayer application up and running. With this script you can then send network messages using NetworkClient.Send and NetworkServer.SendToAll. Note that sending messages is a low level way of interacting with the system.
|
48
docs/Concepts/ClockSync.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Clock Synchronization
|
||||||
|
|
||||||
|
For many algorithms you need the clock to be synchronized between the client and
|
||||||
|
the server. Mirror does that automatically for you.
|
||||||
|
|
||||||
|
To get the current time use this code:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
double now = NetworkTime.time;
|
||||||
|
```
|
||||||
|
|
||||||
|
It will return the same value in the client and the servers. It starts at 0 when
|
||||||
|
the server starts. Note the time is a double and should never be casted to a
|
||||||
|
float. Casting this down to a float means the clock will lose precision after
|
||||||
|
some time:
|
||||||
|
|
||||||
|
- after 1 day, accuracy goes down to 8 ms
|
||||||
|
- after 10 days, accuracy is 62 ms
|
||||||
|
- after 30 days , accuracy is 250 ms
|
||||||
|
- after 60 days, accuracy is 500 ms
|
||||||
|
|
||||||
|
Mirror will also calculate the RTT time as seen by the application:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
double rtt = NetworkTime.rtt;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can measure accuracy.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
double time_standard_deviation = NetworkTime.timeSd;
|
||||||
|
```
|
||||||
|
|
||||||
|
for example, if this returns 0.2, it means the time measurements swing up and
|
||||||
|
down roughly 0.2 s
|
||||||
|
|
||||||
|
Network hickups are compensated against by smoothing out the values using EMA.
|
||||||
|
You can configure how often you want the the ping to be sent:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
NetworkTime.PingFrequency = 2.0f;
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also configure how many ping results are used in the calculation:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
NetworkTime.PingWindowSize = 10;
|
||||||
|
```
|
40
docs/Concepts/Communications/NetworkBehavior.md
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# NetworkBehaviour Callbacks
|
||||||
|
|
||||||
|
Like the Network Manager callbacks, there are a number of events relating to network behaviours that can occur over the course of a normal multiplayer game. These include events such as the host starting up, a player joining, or a player leaving. Each of these possible events has an associated **callback** that you can implement in your own code to take action when the event occurs.
|
||||||
|
|
||||||
|
When you create a script which **inherits** from NetworkBehaviour, you can write your own implementation of what should happen when these events occur. To do this, you override the virtual methods on the `NetworkBehaviour` class with your own implementation of what should happen when the given event occurs.
|
||||||
|
|
||||||
|
This page lists all the virtual methods (the callbacks) that you can implement on Network Behaviour, and when they occur. A game can run in one of three modes, **host**, **client**, or **server-only**. The callbacks for each mode are listed below:
|
||||||
|
|
||||||
|
## Callbacks in server mode
|
||||||
|
|
||||||
|
**When a client connects:**
|
||||||
|
|
||||||
|
- `OnStartServer`
|
||||||
|
- `OnRebuildObservers`
|
||||||
|
- `Start()` function is called
|
||||||
|
|
||||||
|
## Callbacks in client mode
|
||||||
|
|
||||||
|
**When a client connects:**
|
||||||
|
|
||||||
|
- `OnStartClient`
|
||||||
|
- `OnStartLocalPlayer`
|
||||||
|
- `OnStartAuthority`
|
||||||
|
- `Start()` function is called
|
||||||
|
|
||||||
|
## Callbacks in host mode
|
||||||
|
|
||||||
|
These are only called on the **Player GameObjects** when a client connects:
|
||||||
|
|
||||||
|
- `OnStartServer`
|
||||||
|
- `OnStartClient`
|
||||||
|
- `OnRebuildObservers`
|
||||||
|
- `OnStartAuthority`
|
||||||
|
- `OnStartLocalPlayer`
|
||||||
|
- `Start()` function is called
|
||||||
|
- `OnSetLocalVisibility`
|
||||||
|
|
||||||
|
**On any remaining clients, when a client disconnects:**
|
||||||
|
|
||||||
|
- `OnNetworkDestroy`
|
78
docs/Concepts/Communications/NetworkManager.md
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Network Manager Callbacks
|
||||||
|
|
||||||
|
There are a number of events that can occur over the course of the normal operation of a multiplayer game, such as the host starting up, a player joining, or a player leaving. Each of these possible events has an associated callback that you can implement in your own code to take action when the event occurs.
|
||||||
|
|
||||||
|
To do this for the Network Manager, you need to create your own script which inherits from NetworkManager. You can then override the virtual methods on NetworkManager with your own implementation of what should happen when the given event occurs.
|
||||||
|
|
||||||
|
This page lists all the virtual methods (the callbacks) that you can implement on the Network Manager, and when they occur. The callbacks that occur, and the order they occur, vary slightly depending on whether your game is running in LAN mode, so each mode’s callbacks are listed separately below.
|
||||||
|
|
||||||
|
## LAN Callbacks
|
||||||
|
|
||||||
|
These are the callbacks that occur when the game is running on a Local Area Connection (LAN). A game can be running in one of three modes, host, client, or server-only. The callbacks for each mode are listed below:
|
||||||
|
|
||||||
|
### LAN callbacks in host mode:
|
||||||
|
|
||||||
|
When the host is started:
|
||||||
|
|
||||||
|
- `Start()` function is called
|
||||||
|
- `OnStartHost`
|
||||||
|
- `OnStartServer`
|
||||||
|
- `OnServerConnect`
|
||||||
|
- `OnStartClient`
|
||||||
|
- `OnClientConnect`
|
||||||
|
- `OnServerSceneChanged`
|
||||||
|
- `OnServerReady`
|
||||||
|
- `OnServerAddPlayer`
|
||||||
|
- `OnClientSceneChanged`
|
||||||
|
|
||||||
|
When a client connects:
|
||||||
|
|
||||||
|
- `OnServerConnect`
|
||||||
|
- `OnServerReady`
|
||||||
|
- `OnServerAddPlayer`
|
||||||
|
|
||||||
|
When a client disconnects:
|
||||||
|
|
||||||
|
- `OnServerDisconnect`
|
||||||
|
|
||||||
|
When the host is stopped:
|
||||||
|
|
||||||
|
- `OnStopHost`
|
||||||
|
- `OnStopServer`
|
||||||
|
- `OnStopClient`
|
||||||
|
|
||||||
|
### LAN callbacks in client mode
|
||||||
|
|
||||||
|
When the client starts:
|
||||||
|
|
||||||
|
- `Start()` function is called
|
||||||
|
- `OnStartClient`
|
||||||
|
- `OnClientConnect`
|
||||||
|
- `OnClientSceneChanged`
|
||||||
|
|
||||||
|
When the client stops:
|
||||||
|
|
||||||
|
- `OnStopClient`
|
||||||
|
- `OnClientDisconnect`
|
||||||
|
|
||||||
|
### LAN callbacks in server mode
|
||||||
|
|
||||||
|
When the server starts:
|
||||||
|
|
||||||
|
- `Start()` function is called
|
||||||
|
- `OnStartServer`
|
||||||
|
- `OnServerSceneChanged`
|
||||||
|
|
||||||
|
When a client connects:
|
||||||
|
|
||||||
|
- `OnServerConnect`
|
||||||
|
- `OnServerReady`
|
||||||
|
- `OnServerAddPlayer`
|
||||||
|
|
||||||
|
When a client disconnects:
|
||||||
|
|
||||||
|
- `OnServerDisconnect`
|
||||||
|
|
||||||
|
When the server stops:
|
||||||
|
|
||||||
|
- `OnStopServer`
|
138
docs/Concepts/Communications/NetworkMessages.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Network Messages
|
||||||
|
|
||||||
|
In addition to “high-level” Commands and RPC calls, you can also send raw network messages.
|
||||||
|
|
||||||
|
There is a class called MessageBase that you can extend to make serializable network message classes. This class has Serialize and Deserialize functions that take writer and reader objects. You can implement these functions yourself, or you can rely on code-generated implementations that are automatically created by the networking
|
||||||
|
system. The base class looks like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
public abstract class MessageBase
|
||||||
|
{
|
||||||
|
// De-serialize the contents of the reader into this message
|
||||||
|
public virtual void Deserialize(NetworkReader reader) {}
|
||||||
|
|
||||||
|
// Serialize the contents of this message into the writer
|
||||||
|
public virtual void Serialize(NetworkWriter writer) {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Message classes can contain members that are basic types, structs, and arrays, including most of the common Unity Engine types (such as Vector3). They cannot contain members that are complex classes or generic containers. Remember that if you want to rely on the code-generated implementations, you must make sure your types are publicly visible.
|
||||||
|
|
||||||
|
There are built-in message classes for common types of network messages:
|
||||||
|
|
||||||
|
- EmptyMessage
|
||||||
|
- StringMessage
|
||||||
|
- IntegerMessage
|
||||||
|
|
||||||
|
To send a message, use the `Send()` method on the NetworkClient, NetworkServer, and NetworkConnection classes which work the same way. It takes a message ID, and a message object that is derived from MessageBase. The code below demonstrates how to send and handle a message using one of the built-in message classes:
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Begin : NetworkBehaviour
|
||||||
|
{
|
||||||
|
const short MyBeginMsg = 1002;
|
||||||
|
|
||||||
|
NetworkClient m_client;
|
||||||
|
|
||||||
|
public void SendReadyToBeginMessage(int myId)
|
||||||
|
{
|
||||||
|
var msg = new IntegerMessage(myId);
|
||||||
|
m_client.Send(MyBeginMsg, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Init(NetworkClient client)
|
||||||
|
{
|
||||||
|
m_client = client;
|
||||||
|
NetworkServer.RegisterHandler(MyBeginMsg, OnServerReadyToBeginMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerReadyToBeginMessage(NetworkMessage netMsg)
|
||||||
|
{
|
||||||
|
var beginMessage = netMsg.ReadMessage<IntegerMessage>();
|
||||||
|
Debug.Log("received OnServerReadyToBeginMessage " + beginMessage.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To declare a custom network message class and use it:
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Scores : MonoBehaviour
|
||||||
|
{
|
||||||
|
NetworkClient myClient;
|
||||||
|
|
||||||
|
public class MyMsgType
|
||||||
|
{
|
||||||
|
public static short Score = MsgType.Highest + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
public class ScoreMessage : MessageBase
|
||||||
|
{
|
||||||
|
public int score;
|
||||||
|
public Vector3 scorePos;
|
||||||
|
public int lives;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendScore(int score, Vector3 scorePos, int lives)
|
||||||
|
{
|
||||||
|
ScoreMessage msg = new ScoreMessage();
|
||||||
|
msg.score = score;
|
||||||
|
msg.scorePos = scorePos;
|
||||||
|
msg.lives = lives;
|
||||||
|
|
||||||
|
NetworkServer.SendToAll(MyMsgType.Score, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a client and connect to the server port
|
||||||
|
public void SetupClient()
|
||||||
|
{
|
||||||
|
myClient = new NetworkClient();
|
||||||
|
myClient.RegisterHandler(MsgType.Connect, OnConnected);
|
||||||
|
myClient.RegisterHandler(MyMsgType.Score, OnScore);
|
||||||
|
myClient.Connect("127.0.0.1", 4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnScore(NetworkMessage netMsg)
|
||||||
|
{
|
||||||
|
ScoreMessage msg = netMsg.ReadMessage<ScoreMessage>();
|
||||||
|
Debug.Log("OnScoreMessage " + msg.score);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnConnected(NetworkMessage netMsg)
|
||||||
|
{
|
||||||
|
Debug.Log("Connected to server");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that there is no serialization code for the `ScoreMessage` class in this source code example. The body of the serialization functions is automatically generated for this class by Mirror.
|
||||||
|
|
||||||
|
## ErrorMessage Class
|
||||||
|
|
||||||
|
There is also an ErrorMessage class that is derived from `MessageBase`. This class is passed to error callbacks on clients and servers.
|
||||||
|
|
||||||
|
The errorCode in the ErrorMessage class corresponds to the Networking.NetworkError enumeration.
|
||||||
|
|
||||||
|
```
|
||||||
|
class MyClient
|
||||||
|
{
|
||||||
|
NetworkClient client;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
client = new NetworkClient();
|
||||||
|
client.RegisterHandler(MsgType.Error, OnError);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnError(NetworkMessage netMsg)
|
||||||
|
{
|
||||||
|
var errorMsg = netMsg.ReadMessage<ErrorMessage>();
|
||||||
|
Debug.Log("Error:" + errorMsg.errorCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
95
docs/Concepts/Communications/RemoteActions.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Remote Actions
|
||||||
|
|
||||||
|
The network system has ways to perform actions across the network. These type of actions are sometimes called Remote Procedure Calls. There are two types of RPCs in the network system, Commands - which are called from the client and run on the server; and ClientRpc calls - which are called on the server and run on clients.
|
||||||
|
|
||||||
|
The diagram below shows the directions that remote actions take:
|
||||||
|
|
||||||
|
![Data Flow Graph](UNetDirections.jpg)
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
Commands are sent from player objects on the client to player objects on the server. For security, Commands can only be sent from YOUR player object, so you cannot control the objects of other players. To make a function into a command, add the [Command] custom attribute to it, and add the “Cmd” prefix. This function will now be run on the server when it is called on the client. Any arguments will automatically be passed to the server with the command.
|
||||||
|
|
||||||
|
Commands functions must have the prefix “Cmd”. This is a hint when reading code that calls the command - this function is special and is not invoked locally like a normal function.
|
||||||
|
|
||||||
|
```
|
||||||
|
class Player : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public GameObject bulletPrefab;
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
void CmdDoFire(float lifeTime)
|
||||||
|
{
|
||||||
|
GameObject bullet = (GameObject)Instantiate(
|
||||||
|
bulletPrefab,
|
||||||
|
transform.position + transform.right,
|
||||||
|
Quaternion.identity);
|
||||||
|
|
||||||
|
var bullet2D = bullet.GetComponent<Rigidbody2D>();
|
||||||
|
bullet2D.velocity = transform.right * bulletSpeed;
|
||||||
|
Destroy(bullet, lifeTime);
|
||||||
|
|
||||||
|
NetworkServer.Spawn(bullet);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (!isLocalPlayer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.Space))
|
||||||
|
CmdDoFire(3.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Be careful of sending commands from the client every frame! This can cause a lot of network traffic.
|
||||||
|
|
||||||
|
It is possible to send commands from non-player objects that have client authority. These objects must have been spawned with NetworkServer.SpawnWithClientAuthority or have authority set with NetworkIdentity.AssignClientAuthority. Commands sent from these object are run on the server instance of the object, not on the associated player object for the client.
|
||||||
|
|
||||||
|
## ClientRpc Calls
|
||||||
|
|
||||||
|
ClientRpc calls are sent from objects on the server to objects on clients. They can be sent from any server object with a NetworkIdentity that has been spawned. Since the server has authority, then there no security issues with server objects being able to send these calls. To make a function into a ClientRpc call, add the [ClientRpc] custom attribute to it, and add the “Rpc” prefix. This function will now be run on clients when it is called on the server. Any arguments will automatically be passed to the clients with the ClientRpc call..
|
||||||
|
|
||||||
|
ClientRpc functions must have the prefix “Rpc”. This is a hint when reading code that calls the method - this function is special and is not invoked locally like a normal function.
|
||||||
|
|
||||||
|
```
|
||||||
|
class Player : NetworkBehaviour
|
||||||
|
{
|
||||||
|
|
||||||
|
[SyncVar]
|
||||||
|
int health;
|
||||||
|
|
||||||
|
[ClientRpc]
|
||||||
|
void RpcDamage(int amount)
|
||||||
|
{
|
||||||
|
Debug.Log("Took damage:" + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeDamage(int amount)
|
||||||
|
{
|
||||||
|
if (!isServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
health -= amount;
|
||||||
|
RpcDamage(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When running a game as a host with a LocalClient, ClientRpc calls will be invoked on the LocalClient - even though it is in the same process as the server. So the behaviour of LocalClients and RemoteClients is the same for ClientRpc calls.
|
||||||
|
|
||||||
|
## Arguments to Remote Actions
|
||||||
|
|
||||||
|
The arguments passed to commands and ClientRpc calls are serialized and sent over the network. These arguments can be:
|
||||||
|
|
||||||
|
- basic types (byte, int, float, string, UInt64, etc)
|
||||||
|
- arrays of basic types
|
||||||
|
- structs containing allowable types
|
||||||
|
- built-in unity math types (Vector3, Quaternion, etc)
|
||||||
|
- NetworkIdentity
|
||||||
|
- NetworkInstanceId
|
||||||
|
- NetworkHash128
|
||||||
|
- GameObject with a NetworkIdentity component attached
|
||||||
|
|
||||||
|
Arguments to remote actions cannot be subcomponents of GameObjects, such as script instances or Transforms. They cannot be other types that cannot be serialized across the network.
|
BIN
docs/Concepts/Communications/UNetDirections.jpg
Normal file
After Width: | Height: | Size: 45 KiB |
20
docs/Concepts/Communications/index.md
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Actions and Communication
|
||||||
|
|
||||||
|
When you are making a multiplayer game, In addition to synchronizing the properties of networked GameObjects, you are likely to need to send, receive, and react to other pieces of information - such as when the match starts, when a player joins or leaves the match, or other information specific to your type of game, for example a notification to all players that a flag has been captured in a “capture-the-flag” style game.
|
||||||
|
|
||||||
|
Within the Mirror networking High-Level API there are three main ways to communicate this type of information.
|
||||||
|
|
||||||
|
## Remote Actions
|
||||||
|
|
||||||
|
Remote actions allow you to call a method in your script across the network. You can make the server call methods on all clients or individual clients specifically. You can also make clients call methods on the server. Using remote actions, you can pass data as parameters to your methods in a very similar way to how you call methods in local (non-multiplayer) projects.
|
||||||
|
|
||||||
|
## Networking Callbacks
|
||||||
|
|
||||||
|
Networking callbacks allow you to hook into built-in Mirror events which occur during the course of the game, such as when players join or leave, when GameObjects are created or destroyed, or when a new Scene is loaded. There are two types of networking callbacks that you can implement:
|
||||||
|
|
||||||
|
- Network manager callbacks, for callbacks relating to the network manager itself (such as when clients connect or disconnect)
|
||||||
|
- Network behaviour callbacks, for callbacks relating to individual networked GameObjects (such as when its Start function is called, or what this particular GameObject should do if a new player joins the game)
|
||||||
|
|
||||||
|
## Network Messages
|
||||||
|
|
||||||
|
Network messages are a “lower level” approach to sending messages (although they are still classed as part of the networking “High level API”). They allow you to send data directly between clients and the server using scripting. You can send basic types of data (int, string, etc) as well as most common Unity types (such as Vector3). Since you implement this yourself, these messages are not associated directly with any particular GameObjects or Unity events - it is up to you do decide their purpose and implement them!
|
92
docs/Concepts/Conversion.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Converting a Single-Player Game to Mirror
|
||||||
|
|
||||||
|
This document describes steps to converting a single player game to a multiplayer game, using Mirror. The process described here is a simplified, higher level version of the actual process for a real game; it doesn’t always work exactly like this, but it provides a basic recipe for the process.
|
||||||
|
|
||||||
|
## NetworkManager set-up
|
||||||
|
|
||||||
|
- Add a new GameObject and rename it “NetworkManager”.
|
||||||
|
- Add the NetworkManager component to the “NetworkManager” GameObject.
|
||||||
|
- Add the NetworkManagerHUD component to the GameObject. This provides the default UI for managing the network game state.
|
||||||
|
|
||||||
|
See Using the NetworkManager.
|
||||||
|
|
||||||
|
## Player Prefab Setup
|
||||||
|
|
||||||
|
- Find the Prefab for the player GameObject in the game, or create a Prefab from the player GameObject
|
||||||
|
- Add the NetworkIdentity component to the player Prefab
|
||||||
|
- Check the LocalPlayerAuthority box on the NetworkIdentity
|
||||||
|
- Set the `playerPrefab` in the NetworkManager’s Spawn Info section to the player Prefab
|
||||||
|
- Remove the player GameObject instance from the Scene if it exists in the Scene
|
||||||
|
|
||||||
|
See Player Objects for more information.
|
||||||
|
|
||||||
|
## Player Movement
|
||||||
|
|
||||||
|
- Add a NetworkTransform component to the player Prefab
|
||||||
|
- Update input and control scripts to respect `isLocalPlayer`
|
||||||
|
- Fix Camera to use spawned player and `isLocalPlayer`
|
||||||
|
|
||||||
|
For example, this script only processes input for the local player:
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Controls : NetworkBehaviour
|
||||||
|
{
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (!isLocalPlayer)
|
||||||
|
{
|
||||||
|
// exit from update if this is not the local player
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle player input for movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic Player Game State
|
||||||
|
|
||||||
|
- Make scripts that contain important data into NetworkBehaviours instead of MonoBehaviours
|
||||||
|
- Make important member variables into SyncVars
|
||||||
|
|
||||||
|
See State Synchronization.
|
||||||
|
|
||||||
|
## Networked Actions
|
||||||
|
|
||||||
|
- Make scripts that perform important actions into NetworkBehaviours instead of MonoBehaviours
|
||||||
|
- Update functions that perform important player actions to be commands
|
||||||
|
|
||||||
|
See Networked Actions.
|
||||||
|
|
||||||
|
## Non-player GameObjects
|
||||||
|
|
||||||
|
Fix non-player prefabs such as enemies:
|
||||||
|
|
||||||
|
- Add the NetworkIdentify component
|
||||||
|
- Add the NetworkTransform component
|
||||||
|
- Register spawnable Prefabs with the NetworkManager
|
||||||
|
- Update scripts with game state and actions
|
||||||
|
|
||||||
|
## Spawners
|
||||||
|
|
||||||
|
- Potentially change spawner scripts to be NetworkBehaviours
|
||||||
|
- Modify spawners to only run on the server (use isServer property or the `OnStartServer()` function)
|
||||||
|
- Call `NetworkServer.Spawn()` for created GameObjects
|
||||||
|
|
||||||
|
## Spawn Positions for Players
|
||||||
|
|
||||||
|
- Add a new GameObject and place it at player’s start location
|
||||||
|
- Add the NetworkStartPosition component to the new GameObject
|
||||||
|
|
||||||
|
## Lobby
|
||||||
|
|
||||||
|
- Create Lobby Scene
|
||||||
|
- Add a new GameObject to the Scene and rename it to “NetworkLobbyManager”.
|
||||||
|
- Add the NetworkLobbyManager component to the new GameObject.
|
||||||
|
- Configure the Manager:
|
||||||
|
- Scenes
|
||||||
|
- Prefabs
|
||||||
|
- Spawners
|
16
docs/Concepts/Debugging.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Debugging Information
|
||||||
|
|
||||||
|
Mirror provides tools to get information about your game at run time. This information is useful for testing your multiplayer game.
|
||||||
|
|
||||||
|
When your game is running in the Editor in Play mode, the Network Manager shows additional information about the state of the network at runtime. This includes:
|
||||||
|
|
||||||
|
- Network connections
|
||||||
|
- Active GameObjects on the server which have a Network Identity component
|
||||||
|
- Active GameObjects on the client which have a Network Identity component
|
||||||
|
- Client peers
|
||||||
|
|
||||||
|
![In Play Mode, the Network Manager HUD component displays additional information about the state of the game and the GameObjects that have spawned.](NetworkManagerHUDDebugging1.jpg)
|
||||||
|
|
||||||
|
Additionally, the Network Manager preview pane (at the bottom of the Inspector window) lists the registered message callback handlers.
|
||||||
|
|
||||||
|
![The Network Manager HUD component preview pane, showing registered callback handlers.](NetworkManagerHUDDebugging2.jpg)
|
BIN
docs/Concepts/GameObjects/NetworkLocalPlayers.png
Normal file
After Width: | Height: | Size: 44 KiB |
24
docs/Concepts/GameObjects/SceneObjects.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Scene GameObjects
|
||||||
|
|
||||||
|
There are two types of networked GameObjects in Mirror’s multiplayer system:
|
||||||
|
|
||||||
|
- Those that are created dynamically at runtime
|
||||||
|
- Those that are saved as part of a Scene
|
||||||
|
|
||||||
|
GameObjects that are created dynamically at runtime use the multiplayer Spawning system, and the prefabs they are instantiated from must be registered in the Network Manager’s list of networked GameObject prefabs.
|
||||||
|
|
||||||
|
However, networked GameObjects that you save as part of a Scene (and therefore already exist in the Scene when it is loaded) are handled differently. These GameObjects are loaded as part of the Scene on both the client and server, and exist at runtime before any spawn messages are sent by the multiplayer system.
|
||||||
|
|
||||||
|
When the Scene is loaded, all networked GameObjects in the Scene are disabled on both the client and the server. Then, when the Scene is fully loaded, the Network Manager automatically processes the Scene’s networked GameObjects, registering them all (and therefore causing them to be synchronized across clients), and enabling them, as if they were spawned at runtime.
|
||||||
|
|
||||||
|
Saving networked GameObjects in your Scene (rather than dynamically spawning them after the scene has loaded) has some benefits:
|
||||||
|
|
||||||
|
- They are loaded with the level, so there will be no pause at runtime.
|
||||||
|
- They can have specific modifications that differ from prefabs
|
||||||
|
- Other GameObject instances in the Scene can reference them, which can avoid you having to use code to finding the GameObjects and make references to them up at runtime.
|
||||||
|
|
||||||
|
When the Network Manager spawns the networked Scene GameObjects, those GameObjects behave like dynamically spawned GameObjects. Mirror sends them updates and ClientRPC calls.
|
||||||
|
|
||||||
|
If a Scene GameObject is destroyed on the server before a client joins the game, then it is never enabled on new clients that join.
|
||||||
|
|
||||||
|
When a client connects, the client is sent an ObjectSpawnScene spawn message for each of the Scene GameObjects that exist on the server, that are visible to that client. This message causes the GameObject on the client to be enabled, and has the latest state of that GameObject from the server in it. This means that only GameObjects that are visible to the client, and not destroyed on the server, are activated on the client. Like regular non-Scene GameObjects, these Scene GameObjects are started with the latest state when the client joins the game.
|
212
docs/Concepts/GameObjects/SpawnObject.md
Normal file
@ -0,0 +1,212 @@
|
|||||||
|
# Spawning GameObjects
|
||||||
|
|
||||||
|
In Mirror, you usually “spawn” (that is, create) new GameObjects with `Instantiate`. However, in the multiplayer High Level API, the word “spawn” means something more specific. In the server-authoritative model of the HLAPI, to “spawn” a GameObject on the server means that the GameObject is created on clients connected to the server, and is managed by the spawning system.
|
||||||
|
|
||||||
|
Once the GameObject is spawned using this system, state updates are sent to clients whenever the GameObject changes on the server. When Mirror destroys the GameObject on the server, it also destroys it on the clients. The server manages spawned GameObjects alongside all other networked GameObjects, so that if another client joins the game later, the server can spawn the GameObjects on that client. These spawned GameObjects have a unique network instance ID called “netId” that is the same on the server and clients for each GameObject. The unique network instance ID is used to route messages set across the network to GameObjects, and to identify GameObjects.
|
||||||
|
|
||||||
|
When the server spawns a GameObject with a Network Identity component, the GameObject spawned on the client has the same “state”. This means it is identical to the GameObject on the server; it has the same Transform, movement state, and (if NetworkTransform and SyncVars are used) synchronized variables. Therefore, client GameObjects are always up-to-date when Mirror creates them. This avoids issues such as GameObjects spawning at the wrong initial location, then reappearing at their correct position when a state update arrives.
|
||||||
|
|
||||||
|
The Network Manager before trying to register it with the Network Manager.
|
||||||
|
|
||||||
|
To register a Prefab with the Network Manager in the Editor, select the Network Manager GameObject, and in the Inspector, navigate to the Network Manager component. Click the triangle next to Spawn Info to open the settings, then under Registered Spawnable Prefabs, click the plus (+) button. Drag and drop Prefabs into the empty field to assign them to the list.
|
||||||
|
|
||||||
|
![The Network Manager Inspector with the Spawn Info foldout expanded, displaying three assigned Prefabs under Registered Spawnable Prefabs](UNetManagerSpawnInfo.png)
|
||||||
|
|
||||||
|
## Spawning Without Network Manager
|
||||||
|
|
||||||
|
For more advanced users, you may find that you want to register Prefabs and spawn GameObjects without using the NetworkManager component.
|
||||||
|
|
||||||
|
To spawn GameObjects without using the Network Manager, you can handle the Prefab registration yourself via script. Use the ClientScene.RegisterPrefab method to register Prefabs to the Network Manager.
|
||||||
|
|
||||||
|
### Example: MyNetworkManager
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class MyNetworkManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public GameObject treePrefab;
|
||||||
|
NetworkClient myClient;
|
||||||
|
|
||||||
|
// Create a client and connect to the server port
|
||||||
|
public void ClientConnect() {
|
||||||
|
ClientScene.RegisterPrefab(treePrefab);
|
||||||
|
myClient = new NetworkClient();
|
||||||
|
myClient.RegisterHandler(MsgType.Connect, OnClientConnect);
|
||||||
|
myClient.Connect("127.0.0.1", 4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnClientConnect(NetworkMessage msg) {
|
||||||
|
Debug.Log("Connected to server: " + msg.conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, you create an empty GameObject to act as the Network Manager, then create and attach the `MyNetworkManager` script (above) to that GameObject. Create a Prefab that has a Network Identity component attached to it, and drag that onto the treePrefab slot on the MyNetworkManager component in the Inspector. This ensures that when the server spawns the tree GameObject, it also creates the same kind of GameObject on the clients.
|
||||||
|
|
||||||
|
Registering Prefabs ensures that the Asset, so that there is no stalling or loading time for creating the Asset.
|
||||||
|
|
||||||
|
However, for the script to work, you also need to add code for the server. Add this to the MyNetworkManager script:
|
||||||
|
|
||||||
|
```
|
||||||
|
public void ServerListen()
|
||||||
|
{
|
||||||
|
NetworkServer.RegisterHandler(MsgType.Connect, OnServerConnect);
|
||||||
|
NetworkServer.RegisterHandler(MsgType.Ready, OnClientReady);
|
||||||
|
|
||||||
|
if (NetworkServer.Listen(4444))
|
||||||
|
Debug.Log("Server started listening on port 4444");
|
||||||
|
}
|
||||||
|
|
||||||
|
// When client is ready spawn a few trees
|
||||||
|
void OnClientReady(NetworkMessage msg)
|
||||||
|
{
|
||||||
|
Debug.Log("Client is ready to start: " + msg.conn);
|
||||||
|
NetworkServer.SetClientReady(msg.conn);
|
||||||
|
SpawnTrees();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpawnTrees()
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
var treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
|
||||||
|
NetworkServer.Spawn(treeGo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnServerConnect(NetworkMessage msg)
|
||||||
|
{
|
||||||
|
Debug.Log("New client connected: " + msg.conn);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The server does not need to register anything, as it knows what GameObject is being spawned (and the asset ID is sent in the spawn message). The client needs to be able to look up the GameObject, so it must be registered on the client.
|
||||||
|
|
||||||
|
When writing your own network manager, it’s important to make the client ready to receive state updates before calling the spawn command on the server, otherwise they won’t be sent. If you’re using Mirror’s built-in Network Manager component, this happens automatically.
|
||||||
|
|
||||||
|
For more advanced uses, such as object pools or dynamically created Assets, you can use the ClientScene.RegisterSpawnHandler method, which allows callback functions to be registered for client-side spawning. See documentation on Custom Spawn Functions for an example of this.
|
||||||
|
|
||||||
|
If the GameObject has a network state like synchronized variables, then that state is synchronized with the spawn message. In the following example, this script is attached to the tree Prefab:
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
class Tree : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[SyncVar]
|
||||||
|
public int numLeaves;
|
||||||
|
|
||||||
|
public override void OnStartClient()
|
||||||
|
{
|
||||||
|
Debug.Log("Tree spawned with leaf count " + numLeaves);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
With this script attached, you can change the `numLeaves` variable and modify the `SpawnTrees` function to see it accurately reflected on the client:
|
||||||
|
|
||||||
|
```
|
||||||
|
void SpawnTrees()
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
gameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
|
||||||
|
Tree tree = treeGo.GetComponent<Tree>();
|
||||||
|
tree.numLeaves = Random.Range(10,200);
|
||||||
|
Debug.Log("Spawning leaf with leaf count " + tree.numLeaves);
|
||||||
|
NetworkServer.Spawn(treeGo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Attach the `Tree` script to the `treePrefab` script created earlier to see this in action.
|
||||||
|
|
||||||
|
### Constraints
|
||||||
|
|
||||||
|
- A NetworkIdentity must be on the root GameObject of a spawnable Prefab. Without this, the Network Manager can’t register the Prefab.
|
||||||
|
- NetworkBehaviour scripts must be on the same GameObject as the NetworkIdentity, not on child GameObjects
|
||||||
|
|
||||||
|
## GameObject Creation Flow
|
||||||
|
|
||||||
|
The actual flow of internal operations that takes place for spawning GameObjects is:
|
||||||
|
|
||||||
|
- Prefab with Network Identity component is registered as spawnable.
|
||||||
|
- GameObject is instantiated from the Prefab on the server.
|
||||||
|
- Game code sets initial values on the instance (note that 3D physics forces applied here do not take effect immediately).
|
||||||
|
- `NetworkServer.Spawn` is called with the instance.
|
||||||
|
- The state of the SyncVars on the instance on the server are collected by calling `OnSerialize` on [Network Behaviour] components.
|
||||||
|
- A network message of type `MsgType.ObjectSpawn` is sent to connected clients that includes the SyncVar data.
|
||||||
|
- `OnStartServer` is called on the instance on the server, and `isServer` is set to `true`
|
||||||
|
- Clients receive the `ObjectSpawn` message and create a new instance from the registered Prefab.
|
||||||
|
- The SyncVar data is applied to the new instance on the client by calling OnDeserialize on Network Behaviour components.
|
||||||
|
- `OnStartClient` is called on the instance on each client, and `isClient` is set to `true`
|
||||||
|
- As gameplay progresses, changes to SyncVar values are automatically synchronized to clients. This continues until game ends.
|
||||||
|
- `NetworkServer.Destroy` is called on the instance on the server.
|
||||||
|
- A network message of type `MsgType.ObjectDestroy` is sent to clients.
|
||||||
|
- `OnNetworkDestroy` is called on the instance on clients, then the instance is destroyed.
|
||||||
|
|
||||||
|
### Player GameObjects
|
||||||
|
|
||||||
|
Player GameObjects in the HLAPI work slightly differently to non-player GameObjects. The flow for spawning player GameObjects with the Network Manager is:
|
||||||
|
|
||||||
|
- Prefab with `NetworkIdentity` is registered as the `PlayerPrefab`
|
||||||
|
- Client connects to the server
|
||||||
|
- Client calls `AddPlayer`, network message of type `MsgType.AddPlayer` is sent to the server
|
||||||
|
- Server receives message and calls `NetworkManager.OnServerAddPlayer`
|
||||||
|
- GameObject is instantiated from the PlayerPrefab on the server
|
||||||
|
- `NetworkManager.AddPlayerForConnection` is called with the new player instance on the server
|
||||||
|
- The player instance is spawned - you do not have to call `NetworkServer.Spawn` for the player instance. The spawn message is sent to all clients like on a normal spawn.
|
||||||
|
- A network message of type `MsgType.Owner` is sent to the client that added the player (only that client!)
|
||||||
|
- The original client receives the network message
|
||||||
|
- `OnStartLocalPlayer` is called on the player instance on the original client, and `isLocalPlayer` is set to true
|
||||||
|
|
||||||
|
Note that `OnStartLocalPlayer` is called after `OnStartClient`, because it only happens when the ownership message arrives from the server after the player GameObject is spawned, so `isLocalPlayer` is not set in `OnStartClient`.
|
||||||
|
|
||||||
|
Because `OnStartLocalPlayer` is only called for the client’s local player GameObject, it is a good place to perform initialization that should only be done for the local player. This could include enabling input processing, and enabling camera tracking for the player GameObject.
|
||||||
|
|
||||||
|
## Spawning GameObjects with Client Authority
|
||||||
|
|
||||||
|
To spawn GameObjects and assign authority of those GameObjects to a particular client, use NetworkServer.SpawnWithClientAuthority, which takes as an argument the `NetworkConnection` of the client that is to be made the authority.
|
||||||
|
|
||||||
|
For these GameObjects, the property `hasAuthority` is true on the client with authority, and `OnStartAuthority` is called on the client with authority. That client can issue commands for that GameObject. On other clients (and on the host), `hasAuthority` is false.
|
||||||
|
|
||||||
|
Objects spawned with client authority must have `LocalPlayerAuthority` set in their `NetworkIdentity`.
|
||||||
|
|
||||||
|
For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in a NetworkConnection GameObject for the owning client’s connection):
|
||||||
|
|
||||||
|
```
|
||||||
|
void SpawnTrees(NetworkConnection conn)
|
||||||
|
{
|
||||||
|
int x = 0;
|
||||||
|
for (int i = 0; i < 5; ++i)
|
||||||
|
{
|
||||||
|
var treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
|
||||||
|
var tree = treeGo.GetComponent<Tree>();
|
||||||
|
tree.numLeaves = Random.Range(10,200);
|
||||||
|
Debug.Log("Spawning leaf with leaf count " + tree.numLeaves);
|
||||||
|
NetworkServer.SpawnWithClientAuthority(treeGo, conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The Tree script can now be modified to send a command to the server:
|
||||||
|
|
||||||
|
```
|
||||||
|
public override void OnStartAuthority()
|
||||||
|
{
|
||||||
|
CmdMessageFromTree("Tree with " + numLeaves + " reporting in");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
void CmdMessageFromTree(string msg)
|
||||||
|
{
|
||||||
|
Debug.Log("Client sent a tree message: " + msg);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that you can’t just add the `CmdMessageFromTree` call into `OnStartClient`, because at that point the authority has not been set yet, so the call would fail.
|
176
docs/Concepts/GameObjects/SpawnObjectCustom.md
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
# Custom Spawn Functions
|
||||||
|
|
||||||
|
You can use spawn handler functions to customize the default behavior when creating spawned GameObjects on the client. Spawn handler functions ensure you have full control of how you spawn the GameObject, as well as how you destroy it.
|
||||||
|
|
||||||
|
Use ClientScene.RegisterSpawnHandler to register functions to spawn and destroy client GameObjects. The server creates GameObjects directly, and then spawns them on the clients through this functionality. This function takes the asset ID of the GameObject and two function delegates: one to handle creating GameObjects on the client, and one to handle destroying GameObjects on the client. The asset ID can be a dynamic one, or just the asset ID found on the prefab GameObject you want to spawn (if you have one).
|
||||||
|
|
||||||
|
The spawn / un-spawner need to have this GameObject signature. This is defined in the high level API.
|
||||||
|
|
||||||
|
```
|
||||||
|
// Handles requests to spawn GameObjects on the client
|
||||||
|
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
|
||||||
|
|
||||||
|
// Handles requests to unspawn GameObjects on the client
|
||||||
|
public delegate void UnSpawnDelegate(GameObject spawned);
|
||||||
|
```
|
||||||
|
|
||||||
|
The asset ID passed to the spawn function can be found on NetworkIdentity.assetId for prefabs, where it is populated automatically. The registration for a dynamic asset ID is handled like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
// generate a new unique assetId
|
||||||
|
NetworkHash128 creatureAssetId = NetworkHash128.Parse("e2656f");
|
||||||
|
|
||||||
|
// register handlers for the new assetId
|
||||||
|
ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);
|
||||||
|
|
||||||
|
// get assetId on an existing prefab
|
||||||
|
NetworkHash128 coinAssetId = coinPrefab.GetComponent<NetworkIdentity>().assetId;
|
||||||
|
|
||||||
|
// register handlers for an existing prefab you'd like to custom spawn
|
||||||
|
ClientScene.RegisterSpawnHandler(coinAssetId, SpawnCoin, UnSpawnCoin);
|
||||||
|
|
||||||
|
// spawn a coin - SpawnCoin is called on client
|
||||||
|
NetworkServer.Spawn(gameObject, coinAssetId);
|
||||||
|
```
|
||||||
|
|
||||||
|
The spawn functions themselves are implemented with the delegate signature. Here is the coin spawner. The SpawnCreature would look the same, but have different spawn logic:
|
||||||
|
|
||||||
|
```
|
||||||
|
public GameObject SpawnCoin(Vector3 position, NetworkHash128 assetId)
|
||||||
|
{
|
||||||
|
return (GameObject)Instantiate(m_CoinPrefab, position, Quaternion.identity);
|
||||||
|
}
|
||||||
|
public void UnSpawnCoin(GameObject spawned)
|
||||||
|
{
|
||||||
|
Destroy(spawned);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When using custom spawn functions, it is sometimes useful to be able to unspawn GameObjects without destroying them. This can be done by calling [NetworkServer.UnSpawn]. This causes a message to be sent to clients to un-spawn the GameObject, so that the custom un-spawn function will be called on the clients. The GameObject is not destroyed when this function is called.
|
||||||
|
|
||||||
|
Note that on the host, GameObjects are not spawned for the local client, because they already exist on the server. This also means that no spawn handler functions are called.
|
||||||
|
|
||||||
|
## Setting Up a GameObject Pool with Custom Spawn Handlers
|
||||||
|
|
||||||
|
Here is an example of how you might set up a very simple GameObject pooling system with custom spawn handlers. Spawning and unspawning then puts GameObjects in or out of the pool.
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
public class SpawnManager : MonoBehaviour
|
||||||
|
{
|
||||||
|
public int m_ObjectPoolSize = 5;
|
||||||
|
public GameObject m_Prefab;
|
||||||
|
public GameObject[] m_Pool;
|
||||||
|
|
||||||
|
public NetworkHash128 assetId { get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
|
||||||
|
public delegate void UnSpawnDelegate(GameObject spawned);
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
assetId = m_Prefab.GetComponent<NetworkIdentity> ().assetId;
|
||||||
|
m_Pool = new GameObject[m_ObjectPoolSize];
|
||||||
|
for (int i = 0; i < m_ObjectPoolSize; ++i)
|
||||||
|
{
|
||||||
|
m_Pool[i] = (GameObject)Instantiate(m_Prefab, Vector3.zero, Quaternion.identity);
|
||||||
|
m_Pool[i].name = "PoolObject" + i;
|
||||||
|
m_Pool[i].SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientScene.RegisterSpawnHandler(assetId, SpawnObject, UnSpawnObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject GetFromPool(Vector3 position)
|
||||||
|
{
|
||||||
|
foreach (var obj in m_Pool)
|
||||||
|
{
|
||||||
|
if (!obj.activeInHierarchy)
|
||||||
|
{
|
||||||
|
Debug.Log("Activating GameObject " + obj.name + " at " + position);
|
||||||
|
obj.transform.position = position;
|
||||||
|
obj.SetActive (true);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Debug.LogError ("Could not grab GameObject from pool, nothing available");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject SpawnObject(Vector3 position, NetworkHash128 assetId)
|
||||||
|
{
|
||||||
|
return GetFromPool(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnSpawnObject(GameObject spawned)
|
||||||
|
{
|
||||||
|
Debug.Log ("Re-pooling GameObject " + spawned.name);
|
||||||
|
spawned.SetActive (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To use this manager, create a new empty GameObject and name it “SpawnManager”. Create a new script called *SpawnManager,* copy in the code sample above, and attach it to the\* \*new SpawnManager GameObject. Next, drag a prefab you want to spawn multiple times to the Prefab field, and set the Object Pool Size (default is 5).
|
||||||
|
|
||||||
|
Finally, set up a reference to the SpawnManager in the script you are using for player movement:
|
||||||
|
|
||||||
|
```
|
||||||
|
SpawnManager spawnManager;
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Your player logic might contain something like this, which moves and fires coins:
|
||||||
|
|
||||||
|
```
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (!isLocalPlayer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var x = Input.GetAxis("Horizontal")*0.1f;
|
||||||
|
var z = Input.GetAxis("Vertical")*0.1f;
|
||||||
|
|
||||||
|
transform.Translate(x, 0, z);
|
||||||
|
|
||||||
|
if (Input.GetKeyDown(KeyCode.Space))
|
||||||
|
{
|
||||||
|
// Command function is called on the client, but invoked on the server
|
||||||
|
CmdFire();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the fire logic on the player, make it use the GameObject pool:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Command]
|
||||||
|
void CmdFire()
|
||||||
|
{
|
||||||
|
// Set up coin on server
|
||||||
|
var coin = spawnManager.GetFromPool(transform.position + transform.forward);
|
||||||
|
coin.GetComponent<Rigidbody>().velocity = transform.forward*4;
|
||||||
|
|
||||||
|
// spawn coin on client, custom spawn handler is called
|
||||||
|
NetworkServer.Spawn(coin, spawnManager.assetId);
|
||||||
|
|
||||||
|
// when the coin is destroyed on the server, it is automatically destroyed on clients
|
||||||
|
StartCoroutine (Destroy (coin, 2.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator Destroy(GameObject go, float timer)
|
||||||
|
{
|
||||||
|
yield return new WaitForSeconds (timer);
|
||||||
|
spawnManager.UnSpawnObject(go);
|
||||||
|
NetworkServer.UnSpawn(go);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The automatic destruction shows how the GameObjects are returned to the pool and re-used when you fire again.
|
13
docs/Concepts/GameObjects/SpawnPlayer.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Player GameObjects
|
||||||
|
|
||||||
|
Mirror’s multiplayer HLAPI system handles player GameObjects differently to non-player GameObjects. When a new player joins the game (when a new client connects to the server), that player’s GameObject becomes a “local player” GameObject on the client of that player, and Unity associates the player’s connection with the player’s GameObject. Unity associates one player GameObject for each person playing the game, and routes networking commands to that individual GameObject. A player cannot invoke a command on another player’s GameObject, only their own.
|
||||||
|
|
||||||
|
The NetworkBehaviour class (which you derive from to create your network scripts) has a property called isLocalPlayer. On each client’s player GameObject, Mirror sets that property to true on the NetworkBehaviour script, and invokes the OnStartLocalPlayer() callback. This means each client has a different GameObject set up like this, because on each client a different GameObject is the one that represents the local player. The diagram below shows two clients and their local players.
|
||||||
|
|
||||||
|
![In this diagram, the circles represent the player GameObjects marked as the local player on each client](NetworkLocalPlayers.png)
|
||||||
|
|
||||||
|
Only the player GameObject that is “yours” (from your point of view as the player) has the `isLocalPlayer` flag set. Usually you should set this flag in script to determine whether to process input, whether to make the camera track the GameObject, or do any other client-side things that should only occur for the player belonging to that client.
|
||||||
|
|
||||||
|
Player GameObjects represent the player (that is, the person playing the game) on the server, and have the ability to run commands from the player’s client. These commands are secure client-to-server remote procedure calls. In this server-authoritative system, other non-player server-side GameObjects cannot receive commands directly from client-side GameObjects. This is both for security, and to reduce the complexity of building your game. By routing all incoming commands from users through the player GameObject, you can ensure that these messages come from the right place, the right client, and can be handled in a central location.
|
||||||
|
|
||||||
|
The Network Manager adds a player every time a client connects to the server. In some situations though, you might want it not to add players until an input event happens - such as a user pressing a “start” button on the controller. To disable automatic player creation, navigate to the Network Manager component’s Inspector and untick the Auto Create Player checkbox.
|
75
docs/Concepts/GameObjects/SpawnPlayerCustom.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# Custom Player Spawning
|
||||||
|
|
||||||
|
The Network Manager offers a built-in simple player spawning feature, however you may want to customize the player spawning process - for example to assign a color to each new player spawned.
|
||||||
|
|
||||||
|
To do this you need to override the default behavior of the Network Manager with your own script.
|
||||||
|
|
||||||
|
When the Network Manager adds a player, it also instantiates a GameObject from the Player Prefab and associates it with the connection. To do this, the Network Manager calls NetworkServer.AddPlayerForConnection. You can modify this behavior by overriding NetworkManager.OnServerAddPlayer. The default implementation of `OnServerAddPlayer` instantiates a new player instance from the player Prefab and calls NetworkServer.AddPlayerForConnection to spawn the new player instance. Your custom implementation of `OnServerAddPlayer` must also call `NetworkServer.AddPlayerForConnection`, but your are free to perform any other initialization you require in that method too.
|
||||||
|
|
||||||
|
The example below customizes the color of a player. First, add the color script to the player prefab:
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
class Player : NetworkBehaviour
|
||||||
|
{
|
||||||
|
SyncVar
|
||||||
|
public Color color;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, create a NetworkManager to handle spawning.
|
||||||
|
|
||||||
|
```
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class MyNetworkManager : NetworkManager
|
||||||
|
{
|
||||||
|
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
|
||||||
|
{
|
||||||
|
GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
|
||||||
|
player.GetComponent<Player>().color = Color.red;
|
||||||
|
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The function `NetworkServer.AddPlayerForConnection` does not have to be called from within `OnServerAddPlayer`. As long as the correct connection object and `playerControllerId` are passed in, it can be called after `OnServerAddPlayer` has returned. This allows asynchronous steps to happen in between, such as loading player data from a remote data source.
|
||||||
|
|
||||||
|
Although in most multiplayer games, you typically want one player for each client, the HLAPI, you might need multiple player GameObjects for a single connection. When there are multiple players on one connection, you should use the `playerControllerId` property to tell them apart. This identifier is scoped to the connection, so that it maps to the ID of the controller associated with the player on that client.
|
||||||
|
|
||||||
|
The system automatically spawns the player GameObject passed to `NetworkServer.AddPlayerForConnection` on the server, so you don’t need to call NetworkServer.Spawn for the player. Once a player is ready, the active networked GameObjects (that is, GameObjects with an associated NetworkIdentity) in the Scene spawn on the player’s client. All networked GameObjects in the game are created on that client with their latest state, so they are in sync with the other participants of the game.
|
||||||
|
|
||||||
|
You don’t need to use playerPrefab on the `NetworkManager` to create player GameObjects. You could use different methods of creating different players.
|
||||||
|
|
||||||
|
## Ready State
|
||||||
|
|
||||||
|
In addition to players, client connections also have a “ready” state. The host sends clients that are ready information about spawned GameObjects and state synchronization updates; clients which are not ready are not sent these updates. When a client initially connects to a server, it is not ready. While in this non-ready state, the client can do things that don’t require real-time interactions with the game state on the server, such as loading Scenes, allowing the player to choose an avatar, or fill in log-in boxes. Once a client has completed all its pre-game work, and all its Assets are loaded, it can call ClientScene.Ready to enter the “ready” state. The simple example above demonstrates implementation of ready states; because adding a player with `NetworkServer.AddPlayerForConnection` also puts the client into the ready state if it is not already in that state.
|
||||||
|
|
||||||
|
Clients can send and receive network messages without being ready, which also means they can do so without having an active player GameObject. So a client at a menu or selection screen can connect to the game and interact with it, even though they have no player GameObject. See documentation on Network messages for more details about sending messages without using commands and RPC calls.
|
||||||
|
|
||||||
|
## Switching Players
|
||||||
|
|
||||||
|
To replace the player GameObject for a connection, use NetworkServer.ReplacePlayerForConnection. This is useful for restricting the commands that players can issue at certain times, such as in a pre-game lobby screen. This function takes the same arguments as `AddPlayerForConnection`, but allows there to already be a player for that connection. The old player GameObject does not have to be destroyed. The NetworkLobbyManager uses this technique to switch from the NetworkLobbyPlayer GameObject to a gameplay player GameObject when all the players in the lobby are ready.
|
||||||
|
|
||||||
|
You can also use `ReplacePlayerForConnection` to respawn a player after their GameObject is destroyed. In some cases it is better to just disable a GameObject and reset its game attributes on respawn. The following code sample demonstrates how to actually replace the destroyed GameObject with a new GameObject:
|
||||||
|
|
||||||
|
```
|
||||||
|
class GameManager
|
||||||
|
{
|
||||||
|
public void PlayerWasKilled(Player player)
|
||||||
|
{
|
||||||
|
var conn = player.connectionToClient;
|
||||||
|
var newPlayer = Instantiate<GameObject>(playerPrefab);
|
||||||
|
Destroy(player.gameObject);
|
||||||
|
NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the player GameObject for a connection is destroyed, then that client cannot execute Commands. They can, however, still send network messages.
|
||||||
|
|
||||||
|
To use `ReplacePlayerForConnection` you must have the NetworkConnection GameObject for the player’s client to establish the relationship between the GameObject and the client. This is usually the property connectionToClient on the NetworkBehaviour class, but if the old player has already been destroyed, then that might not be readily available.
|
||||||
|
|
||||||
|
To find the connection, there are some lists available. If using the `NetworkLobbyManager`, then the lobby players are available in lobbySlots. The NetworkServer also has lists of connections and localConnections.
|
BIN
docs/Concepts/GameObjects/UNetManagerSpawnInfo.png
Normal file
After Width: | Height: | Size: 64 KiB |
17
docs/Concepts/GameObjects/index.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Networked GameObjects
|
||||||
|
|
||||||
|
Networked GameObjects are GameObjects which are controlled and synchronized by Mirror’s networking system. Using synchronized networked GameObjects, you can create a shared experience for all the players who are playing an instance of your game. They see and hear the same events and actions - even though that may be from their own unique viewpoints within your game.
|
||||||
|
|
||||||
|
Multiplayer games in Mirror are typically built using Scenes that contain a mix of networked GameObjects and regular (non-networked) GameObjects. The networked GameObjects are those which move or change during gameplay in a way that needs to be synchronized across all users who are playing the game together. Non-networked GameObjects are those which either don’t move or change at all during gameplay (for example, static obstacles like rocks or fences), or GameObjects which have movement or changes that don’t need to be synchronized across players (for example, a gently swaying tree or clouds passing by in the background of your game).
|
||||||
|
|
||||||
|
A networked GameObject is one which has a Network Identity component attached. However, a Network Identity component alone is not enough for your GameObject to be functional and active in your multiplayer game. The Network Identity component is the starting point for synchronization, and it allows the Network Manager to synchronize the creation and destruction of the GameObject, but other than that, it does not specify *which properties* of your GameObject should be synchronized.
|
||||||
|
|
||||||
|
What exactly should be synchronized on each networked GameObject depends on the type of game you are making, and what each GameObject’s purpose is. Some examples of what you might want to synchronize are:
|
||||||
|
|
||||||
|
- The position and rotation of moving GameObjects such as the players and non-player characters.
|
||||||
|
- The animation state of an animated GameObject
|
||||||
|
- The value of a variable, for example how much time is left in the current round of a game, or how much energy a player has.
|
||||||
|
|
||||||
|
Some of these things can be automatically synchronized by Mirror. The synchronized creation and destruction of networked GameObjects is managed by the NetworkManager, and is known as Spawning. You can use the Network Transform component to synchronize the position and rotation of a GameObject, and you can use the Network Animator component to synchronize the animation of a GameObject.
|
||||||
|
|
||||||
|
To synchronize other properties of a networked GameObject, you need to use scripting. See State Synchronization for more information about this.
|
59
docs/Concepts/HLAPI.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# The Multiplayer High Level API
|
||||||
|
|
||||||
|
Mirror’s multiplayer High Level API (HLAPI) is a system for building multiplayer capabilities for Unity games. It is built on top of the lower level transport real-time communication layer, and handles many of the common tasks that are required for multiplayer games. While the transport layer supports any kind of network topology, the HLAPI is a server authoritative system; although it allows one of the participants to be a client and the server at the same time, so no dedicated server process is required. Working in conjunction with the internet services, this allows multiplayer games to be played over the internet with little work from developers.
|
||||||
|
|
||||||
|
The HLAPI is focused on ease of use and iterative development and provides useful functionality for multiplayer games, such as:
|
||||||
|
|
||||||
|
- Message handlers
|
||||||
|
- General purpose high performance serialization
|
||||||
|
- Distributed object management
|
||||||
|
- State synchronization
|
||||||
|
- Network classes: Server, Client, Connection, etc
|
||||||
|
|
||||||
|
The HLAPI is built from a series of layers that add functionality:
|
||||||
|
|
||||||
|
![Network Layers](NetworkLayers.jpg)
|
||||||
|
|
||||||
|
## Server and Host
|
||||||
|
|
||||||
|
In Mirror’s High Level API (HLAPI) system, multiplayer games include:
|
||||||
|
|
||||||
|
- **Server**
|
||||||
|
A server is an instance of the game which all other players connect to when they want to play together. A server often manages various aspects of the game, such as keeping score, and transmit that data back to the client.
|
||||||
|
|
||||||
|
- **Clients**
|
||||||
|
Clients are instances of the game that usually connect from different computers to the server. Clients can connect over a local network, or online.
|
||||||
|
|
||||||
|
A client is an instance of the game that connects to the server, so that the person playing it can play the game with other people, who connect on their own clients.
|
||||||
|
|
||||||
|
The server might be either a “dedicated server”, or a “host server”.
|
||||||
|
|
||||||
|
- **Dedicated server**
|
||||||
|
This is an instance of the game that only runs to act as a server.
|
||||||
|
|
||||||
|
- **Host server**
|
||||||
|
When there is no dedicated server, one of the clients also plays the role of the server. This client is the “host server”. The host server creates a single instance of the game (called the host), which acts as both server and client.
|
||||||
|
|
||||||
|
The diagram below represents three players in a multiplayer game. In this game, one client is also acting as host, which means the client itself is the “local client”. The local client connects to the host server, and both run on the same computer. The other two players are remote clients - that is, they are on different computers, connected to the host server.
|
||||||
|
|
||||||
|
![This diagram shows two remote clients connected to a host.](NetworkHost.png)
|
||||||
|
|
||||||
|
The host is a single instance of your game, acting as both server and client at the same time. The host uses a special kind of internal client for local client communication, while other clients are remote clients. The local client communicates with the server through direct function calls and message queues, because it is in the same process. It actually shares the Scene with the server. Remote clients communicate with the server over a regular network connection. When you use Mirror’s HLAPI, this is all handled automatically for you.
|
||||||
|
|
||||||
|
One of the aims of the multiplayer system is for the code for local clients and remote clients to be the same, so that you only have to think about one type of client most of the time when developing your game. In most cases, Mirror handles this difference automatically, so you should rarely need to think about the difference between your code running on a local client or a remote client.
|
||||||
|
|
||||||
|
## Instantiate and Spawn
|
||||||
|
|
||||||
|
When you make a single player game In Mirror, you usually use the `GameObject.Instantiate` method to create new GameObjects at runtime. However, with a multiplayer system, the server itself must “spawn” GameObjects in order for them to be active within the networked game. When the server spawns GameObjects, it triggers the creation of GameObjects on connected clients. The spawning system manages the lifecycle of the GameObject, and synchronizes the state of the GameObject based on how you set the GameObject up.
|
||||||
|
|
||||||
|
For more details about networked instantiating and spawning, see documentation on Spawning GameObjects.
|
||||||
|
|
||||||
|
## Players and Local Players
|
||||||
|
|
||||||
|
Mirror’s multiplayer HLAPI system handles player GameObjects differently to non-player GameObjects. When a new player joins the game (when a new client connects to the server), that player’s GameObject becomes a “local player” GameObject on the client of that player, and Mirror associates the player’s connection with the player’s GameObject. Mirror associates one player GameObject for each person playing the game, and routes networking commands to that individual GameObject. A player cannot invoke a command on another player’s GameObject, only their own.
|
||||||
|
|
||||||
|
For more details, see documentation on Player GameObjects.
|
||||||
|
|
||||||
|
## Authority
|
||||||
|
|
||||||
|
Servers and clients can both manage a GameObject’s behavior. The concept of “authority” refers to how and where a GameObject is managed. Mirror’s HLAPI is based around “server authority” as the default state, where the Server (the host) has authority over all GameObjects which do not represent players. Player GameObjects are a special case and treated as having “local authority”. You may want to build your game using a different system of authority - for more details, see Network Authority.
|
17
docs/Concepts/IDs.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Got ID?
|
||||||
|
|
||||||
|
## Asset Id
|
||||||
|
|
||||||
|
Mirror uses GUID for Asset Ids. Every prefab with a NetworkIdentity component has an Asset Id, which is simply Unity's AssetDatabase.AssetPathToGUID converted to 16 bytes. Mirror needs that to know which prefabs to spawn.
|
||||||
|
|
||||||
|
## Scene Id
|
||||||
|
|
||||||
|
Mirror uses uint for Scene Ids. Every GameObject with a NetworkIdentity in the scene (hierarchy) is assigned a scene id in OnPostProcessScene. Mirror needs that to distinguish scene objects from each other, because Unity has no unique id for different GameObjects in the scene.
|
||||||
|
|
||||||
|
## Network Instance Id (a.k.a. NetId)
|
||||||
|
|
||||||
|
Mirror uses uint for NetId. Every NetworkIdentity is assigned a NetId in NetworkIdentity.OnStartServer, or after spawning it. Mirror uses the id when passing messages between client and server to tell which object is the recipient of the message.
|
||||||
|
|
||||||
|
## Connection Id
|
||||||
|
|
||||||
|
Every network connection has a connection id, which is assigned by the low level Transport layer. Connection id 0 is reserved for the local connection when the server is also a client (host)
|
9
docs/Concepts/MobileTips.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Networking Tips for Mobile devices.
|
||||||
|
|
||||||
|
Mirror is fully compatible across Desktop, iOS and Android devices, players on all these platforms can play your multiplayer game together.
|
||||||
|
|
||||||
|
You might want to implement special measures if your game is mainly to be used with Wi-Fi or cellular networks. On some mobile devices, the networking chip might be a performance bottleneck, because pings between mobile devices (or between mobile devices and desktops) can be about 40–60ms, even on high-performance Wi-Fi network, and you may observe some delays of over 200ms, despite a low average ping on high performance wifi.
|
||||||
|
|
||||||
|
For players to play your game simultaneously from both desktop and mobile platforms (over Wi-Fi or cellular networks), your game server should have a public IP address accessible through the internet, or use the Matchmaker and Relay services.
|
||||||
|
|
||||||
|
Note: EDGE / 3G data connections go to sleep very quickly when no data is sent. Therefore, you might sometimes need to “wake” the connection. If you are using the WWW class make sure you connect to your site (and yield until it finishes) before making the networking connection.
|
BIN
docs/Concepts/NetworkAuthority.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/Concepts/NetworkHost.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
docs/Concepts/NetworkLayers.jpg
Normal file
After Width: | Height: | Size: 163 KiB |
BIN
docs/Concepts/NetworkManagerHUD-MatchMakerMode.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
docs/Concepts/NetworkManagerHUDDebugging1.jpg
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
docs/Concepts/NetworkManagerHUDDebugging2.jpg
Normal file
After Width: | Height: | Size: 232 KiB |
BIN
docs/Concepts/NetworkManagerInspector.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
docs/Concepts/NetworkManagerWithPlayerPrefab.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
docs/Concepts/NetworkProximityCheck.png
Normal file
After Width: | Height: | Size: 8.0 KiB |
63
docs/Concepts/Setup.md
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
# Setting up a multiplayer project
|
||||||
|
|
||||||
|
This page contains an overview of the most basic and common things you need when setting up a multiplayer project. In terms of what you require in your project, these are:
|
||||||
|
|
||||||
|
- A Network Manager
|
||||||
|
- A user interface (for players to find and join games)
|
||||||
|
- Networked Player Prefabs (for players to control)
|
||||||
|
- Scripts which are multiplayer-aware
|
||||||
|
|
||||||
|
There are variations on this list; for example, in a multiplayer chess game, or a real-time strategy (RTS) game, you don’t need a visible GameObject to represent the player. However, you might still want an invisible empty GameObject to represent the player, and attach scripts to it which relate to what the player is able to do.
|
||||||
|
|
||||||
|
This introductory page contains a brief description of each of the items listed above. However, each section links to more detailed documentation, which you need to continue reading to fully understand them.
|
||||||
|
|
||||||
|
There are also some important concepts that you need to understand and make choices about when building your game. These concepts can broadly be summarised as:
|
||||||
|
|
||||||
|
- The relationship between a client, a server, and a host
|
||||||
|
- The idea of authority over GameObjects and actions
|
||||||
|
|
||||||
|
To learn about these concepts, see documentation on Network System Concepts.
|
||||||
|
|
||||||
|
## The Network Manager
|
||||||
|
|
||||||
|
The Network Manager is responsible for managing the networking at a time.
|
||||||
|
|
||||||
|
![The Network Manager Component](NetworkManagerInspector.png)
|
||||||
|
|
||||||
|
Mirror’s built-in Network Manager component wraps up all of the features for managing your multiplayer game into one single component. If you have custom requirements which aren’t covered by this component, you can write your own network manager in script instead of using this component. If you’re just starting out with multiplayer games, you should use this component.
|
||||||
|
|
||||||
|
To learn more, see documentation on the Network Manager.
|
||||||
|
|
||||||
|
Mirror has an extremely basic built-in version of such an interface, called the NetworkManagerHUD. However, it is very basic in both functionality and visual design, so you should replace this with your own UI before you finish your project.
|
||||||
|
|
||||||
|
![Mirror’s built-in Network Manager HUD, shown in MatchMaker mode.](NetworkManagerHUD-MatchMakerMode.png)
|
||||||
|
|
||||||
|
To learn more, see documentation on the Network Manager HUD.
|
||||||
|
|
||||||
|
## Networked player GameObjects
|
||||||
|
|
||||||
|
Most multiplayer games feature some kind of object that a player can control, like a character, a car, or something else. Some multiplayer games don’t feature a single visible player object, and attach all the scripts to it which control what the player can do in your game.
|
||||||
|
|
||||||
|
If you are using Mirror’s Network Manager component (see *The Network Manager*, above), assign the Prefab to the Player Prefab field.
|
||||||
|
|
||||||
|
![The network manager with a “Player Car” prefab assigned to the Player Prefab field.](NetworkManagerWithPlayerPrefab.png)
|
||||||
|
|
||||||
|
When the game is running, the Network Manager creates a copy (an “instance”) of your player Prefab for each player that connects to the match.
|
||||||
|
|
||||||
|
However - and this is where it can get confusing for people new to multiplayer programming - you need to make sure the scripts on your player Prefab instance are “aware” of whether the player controlling the instance is using the host computer (the computer that is managing the game) or a client computer (a different computer to the one that is managing the game).
|
||||||
|
|
||||||
|
This is because both situations will be occurring at the same time.
|
||||||
|
|
||||||
|
## Multiplayer-aware Scripts
|
||||||
|
|
||||||
|
Writing scripts for a multiplayer game is different to writing scripts for a single-player game. This is because when you write a script for a multiplayer game, you need to think about the different contexts that the scripts run in. To learn about the networking concepts discussed here, see documentation on Network System Concepts.
|
||||||
|
|
||||||
|
For example, the scripts you place on your player Prefab should allow the “owner” of that player instance to control it, but it should not allow other people to control it.
|
||||||
|
|
||||||
|
You need to think about whether the server or the client has authority over what the script does. Sometimes, you want the script to run on both the server and the clients. Other times, you only want the script to run on the server, and you only want the clients to replicate how the GameObjects are moving (for example, in a game in which players pick up collectible GameObjects, the script should only run on the server so that the server can be the authority on the number of GameObjects collected).
|
||||||
|
|
||||||
|
Depending on what your script does, you need to decide which parts of your script should be active in which situations.
|
||||||
|
|
||||||
|
For player GameObjects, each person usually has active control over their own player instance. This means each client has local authority over its own player, and the server accepts what the client tells it about what the player is doing.
|
||||||
|
|
||||||
|
For non-player GameObjects, the server usually has authority over what happens (such as whether an item has been collected), and all clients accept what the server tells them about what has happened to that GameObject.
|
73
docs/Concepts/StateSync.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# State synchronization
|
||||||
|
|
||||||
|
State synchronization refers to the synchronization of values such as integers, floating point numbers, strings and boolean values belonging to scripts.
|
||||||
|
|
||||||
|
State synchronization is done from the Server to remote clients. The local client does not have data serialized to it. It does not need it, because it shares the Scene with the server. However, SyncVar hooks are called on local clients.
|
||||||
|
|
||||||
|
Data is not synchronized in the opposite direction - from remote clients to the server. To do this, you need to use Commands.
|
||||||
|
|
||||||
|
## SyncVars
|
||||||
|
|
||||||
|
SyncVars are variables of scripts that inherit from NetworkBehaviour, which are synchronized from the server to clients. When a GameObject is spawned, or a new player joins a game in progress, they are sent the latest state of all SyncVars on networked objects that are visible to them. Use the `SyncVar` custom attribute to specify which variables in your script you want to synchronize, like this:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
class Player : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[SyncVar]
|
||||||
|
int health;
|
||||||
|
|
||||||
|
public void TakeDamage(int amount)
|
||||||
|
{
|
||||||
|
if (!isServer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
health -= amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The state of SyncVars is applied to GameObjects on clients before OnStartClient() is called, so the state of the object is always up-to-date inside OnStartClient().
|
||||||
|
|
||||||
|
SyncVars can be basic types such as integers, strings and floats. They can also be Unity types such as Vector3 and user-defined structs, but updates for struct SyncVars are sent as monolithic updates, not incremental changes if fields within a struct change. You can have up to 32 SyncVars on a single NetworkBehaviour script, including SyncLists (see next section, below).
|
||||||
|
|
||||||
|
The server automatically sends SyncVar updates when the value of a SyncVar changes, so you do not need to track when they change or send information about the changes yourself.
|
||||||
|
|
||||||
|
## SyncLists
|
||||||
|
|
||||||
|
While SyncVars contain values, SyncLists contain lists of values. SyncList contents are included in initial state updates along with SyncVar states. Since SyncList is a class which synchronises its own contents, SyncLists do not require the SyncVar attribute. The following types of SyncList are available for basic types:
|
||||||
|
|
||||||
|
- SyncListString
|
||||||
|
- SyncListFloat
|
||||||
|
- SyncListInt
|
||||||
|
- SyncListUInt
|
||||||
|
- SyncListBool
|
||||||
|
|
||||||
|
There is also SyncListStruct, which you can use to synchronize lists of your own struct types. When using SyncListStruct, the struct type that you choose to use can contain members of basic types, arrays, and common Unity types. They cannot contain complex classes or generic containers, and only public variables in these structs are serialized.
|
||||||
|
|
||||||
|
SyncLists have a SyncListChanged delegate named Callback that allows clients to be notified when the contents of the list change. This delegate is called with the type of operation that occurred, and the index of the item that the operation was for.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class MyScript : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public struct Buf
|
||||||
|
{
|
||||||
|
public int id;
|
||||||
|
public string name;
|
||||||
|
public float timer;
|
||||||
|
};
|
||||||
|
|
||||||
|
public class TestBufs : SyncListStruct<Buf> {}
|
||||||
|
|
||||||
|
TestBufs m_bufs = new TestBufs();
|
||||||
|
|
||||||
|
void BufChanged(Operation op, int itemIndex)
|
||||||
|
{
|
||||||
|
Debug.Log("buf changed:" + op);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Start()
|
||||||
|
{
|
||||||
|
m_bufs.Callback = BufChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
156
docs/Concepts/StateSyncAdvanced.md
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
# Advanced State Synchronization
|
||||||
|
|
||||||
|
In most cases, the use of SyncVars is enough for your game scripts to serialize their state to clients. However in some cases you might require more complex serialization code. This page is only relevant for advanced developers who need customized synchronization solutions that go beyond Mirror’s normal SyncVar feature.
|
||||||
|
|
||||||
|
## Custom Serialization Functions
|
||||||
|
|
||||||
|
To perform your own custom serialization, you can implement virtual functions on NetworkBehaviour to be used for SyncVar serialization. These functions are:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public virtual bool OnSerialize(NetworkWriter writer, bool initialState);
|
||||||
|
```
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public virtual void OnDeSerialize(NetworkReader reader, bool initialState);
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the `initialState` flag to differentiate between the first time a GameObject[](ttps://docs.unity3d.com/Manual/class-GameObject.htmlhttps://docs.unity3d.com/Manual/Glossary.html#GameObject) is serialized and when incremental updates can be sent. The first time a GameObject is sent to a client, it must include a full state snapshot, but subsequent updates can save on bandwidth by including only incremental changes. Note that SyncVar hook functions are not called when `initialState` is true; they are only called for incremental updates.
|
||||||
|
|
||||||
|
If a class has SyncVars, then implementations of these functions are added automatically to the class, meaning that a class that has SyncVars cannot also have custom serialization functions.
|
||||||
|
|
||||||
|
The `OnSerialize` function should return true to indicate that an update should be sent. If it returns true, the dirty bits for that script are set to zero. If it returns false, the dirty bits are not changed. This allows multiple changes to a script to be accumulated over time and sent when the system is ready, instead of every frame.
|
||||||
|
|
||||||
|
## Serialization Flow
|
||||||
|
|
||||||
|
GameObjects with the Network Identity component attached can have multiple scripts derived from `NetworkBehaviour`. The flow for serializing these GameObjects is:
|
||||||
|
|
||||||
|
On the server:
|
||||||
|
|
||||||
|
- Each `NetworkBehaviour` has a dirty mask. This mask is available inside `OnSerialize` as `syncVarDirtyBits`
|
||||||
|
- Each SyncVar in a `NetworkBehaviour` script is assigned a bit in the dirty mask.
|
||||||
|
- Changing the value of SyncVars causes the bit for that SyncVar to be set in the dirty mask
|
||||||
|
- Alternatively, calling `SetDirtyBit` writes directly to the dirty mask
|
||||||
|
- NetworkIdentity GameObjects are checked on the server as part of it’s update loop
|
||||||
|
- If any `NetworkBehaviours` on a `NetworkIdentity` are dirty, then an `UpdateVars` packet is created for that GameObject
|
||||||
|
- The `UpdateVars` packet is populated by calling `OnSerialize` on each `NetworkBehaviour` on the GameObject
|
||||||
|
- `NetworkBehaviours` that are not dirty write a zero to the packet for their dirty bits
|
||||||
|
- `NetworkBehaviours` that are dirty write their dirty mask, then the values for the SyncVars that have changed
|
||||||
|
- If `OnSerialize` returns true for a `NetworkBehaviour`, the dirty mask is reset for that `NetworkBehaviour` so it does not send again until its value changes.
|
||||||
|
- The `UpdateVars` packet is sent to ready clients that are observing the GameObject
|
||||||
|
|
||||||
|
On the client:
|
||||||
|
|
||||||
|
- an `UpdateVars packet` is received for a GameObject
|
||||||
|
- The `OnDeserialize` function is called for each `NetworkBehaviour` script on the GameObject
|
||||||
|
- Each `NetworkBehaviour` script on the GameObject reads a dirty mask.
|
||||||
|
- If the dirty mask for a `NetworkBehaviour` is zero, the `OnDeserialize` function returns without reading any more
|
||||||
|
- If the dirty mask is non-zero value, then the `OnDeserialize` function reads the values for the SyncVars that correspond to the dirty bits that are set
|
||||||
|
- If there are SyncVar hook functions, those are invoked with the value read from the stream.
|
||||||
|
|
||||||
|
So for this script:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class data : NetworkBehaviour
|
||||||
|
{
|
||||||
|
[SyncVar]
|
||||||
|
public int int1 = 66;
|
||||||
|
|
||||||
|
[SyncVar]
|
||||||
|
public int int2 = 23487;
|
||||||
|
|
||||||
|
[SyncVar]
|
||||||
|
public string MyString = "Example string";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The following code sample demonstrates the generated `OnSerialize` function:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public override bool OnSerialize(NetworkWriter writer, bool forceAll)
|
||||||
|
{
|
||||||
|
if (forceAll)
|
||||||
|
{
|
||||||
|
// The first time a GameObject is sent to a client, send all the data (and no dirty bits)
|
||||||
|
writer.WritePackedUInt32((uint)this.int1);
|
||||||
|
writer.WritePackedUInt32((uint)this.int2);
|
||||||
|
writer.Write(this.MyString);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool wroteSyncVar = false;
|
||||||
|
if ((base.get_syncVarDirtyBits() & 1u) != 0u)
|
||||||
|
{
|
||||||
|
if (!wroteSyncVar)
|
||||||
|
{
|
||||||
|
// Write dirty bits if this is the first SyncVar written
|
||||||
|
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
|
||||||
|
wroteSyncVar = true;
|
||||||
|
}
|
||||||
|
writer.WritePackedUInt32((uint)this.int1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((base.get_syncVarDirtyBits() & 2u) != 0u)
|
||||||
|
{
|
||||||
|
if (!wroteSyncVar)
|
||||||
|
{
|
||||||
|
// Write dirty bits if this is the first SyncVar written
|
||||||
|
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
|
||||||
|
wroteSyncVar = true;
|
||||||
|
}
|
||||||
|
writer.WritePackedUInt32((uint)this.int2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((base.get_syncVarDirtyBits() & 4u) != 0u)
|
||||||
|
{
|
||||||
|
if (!wroteSyncVar)
|
||||||
|
{
|
||||||
|
// Write dirty bits if this is the first SyncVar written
|
||||||
|
writer.WritePackedUInt32(base.get_syncVarDirtyBits());
|
||||||
|
wroteSyncVar = true;
|
||||||
|
}
|
||||||
|
writer.Write(this.MyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wroteSyncVar)
|
||||||
|
{
|
||||||
|
// Write zero dirty bits if no SyncVars were written
|
||||||
|
writer.WritePackedUInt32(0);
|
||||||
|
}
|
||||||
|
return wroteSyncVar;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The following code sample demonstrates the `OnDeserialize` function:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public override void OnDeserialize(NetworkReader reader, bool initialState)
|
||||||
|
{
|
||||||
|
if (initialState)
|
||||||
|
{
|
||||||
|
this.int1 = (int)reader.ReadPackedUInt32();
|
||||||
|
this.int2 = (int)reader.ReadPackedUInt32();
|
||||||
|
this.MyString = reader.ReadString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int num = (int)reader.ReadPackedUInt32();
|
||||||
|
if ((num & 1) != 0)
|
||||||
|
{
|
||||||
|
this.int1 = (int)reader.ReadPackedUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((num & 2) != 0)
|
||||||
|
{
|
||||||
|
this.int2 = (int)reader.ReadPackedUInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((num & 4) != 0)
|
||||||
|
{
|
||||||
|
this.MyString = reader.ReadString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If a `NetworkBehaviour` has a base class that also has serialization functions, the base class functions should also be called.
|
||||||
|
|
||||||
|
Note that the `UpdateVar` packets created for GameObject state updates may be aggregated in buffers before being sent to the client, so a single transport layer packet may contain updates for multiple GameObjects.
|
43
docs/Concepts/Visibility.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Network Visibility
|
||||||
|
|
||||||
|
Multiplayer games use the concept of network visibility to determine which players can see which GameObjects at any given time during gameplay. In a game that has a moving viewpoint and moving GameObjects, it’s common that players cannot see everything that is happening in the game at once.
|
||||||
|
|
||||||
|
If a particular player, at a certain point in time during gameplay, cannot see most of the other players, non-player characters, or other moving or interactive things in your game, there is usually no need for the host to send information about those things to the player’s client.
|
||||||
|
|
||||||
|
This can benefit your game in two ways:
|
||||||
|
|
||||||
|
- It reduces the amount of data sent across the network between players. This can help improve the responsiveness of your game, and reduce bandwidth use. The bigger and more complex your multiplayer game, the more important this issue is.
|
||||||
|
- It also helps prevent hacking. Since a player client does not have information about things that can’t be seen, a hack on that player’s computer cannot reveal the information.
|
||||||
|
|
||||||
|
The idea of “visibility” in the context of networking doesn’t necessarily relate to whether GameObjects are directly visible on-screen. Instead, it relates to whether data should or shouldn’t be sent about the GameObject in question to a particular client. Put simply, if a client can’t ‘see’ an GameObject, it does not need to be sent information about that GameObject across the network. Ideally you want to limit the amount of data you are sending across the network to only what is necessary, because sending large amounts of unnecessary data across the network can cause network performance problems.
|
||||||
|
|
||||||
|
However, it can be also be resource intensive or complex to determine accurately whether a GameObject truly visible to a given player, so it’s often a good idea to use a more simple calculation for the purposes of determining whether a player should be sent networked data about it - i.e. whether it is ‘Network Visible’. The balance you want to achieve when considering this is between the cost of the complexity of the calculation for determining the visibility, and the cost of sending more information than necessary over the network. A very simple way to calculate this is a distance (proximity) check, and Mirror provides a built-in component for this purpose.
|
||||||
|
|
||||||
|
## Network Proximity Checker Component
|
||||||
|
|
||||||
|
Mirror’s Network Proximity Checker component is simplest way to implement network visibility for players. It works in conjunction with the physics system to determine whether GameObjects are close enough (that is, “visible” for the purposes of sending network messages in your multiplayer game).
|
||||||
|
|
||||||
|
![The Network Proximity Checker component](NetworkProximityCheck.png)
|
||||||
|
|
||||||
|
To use this component, add it to the Prefab of the networked GameObject for which you want to limit network visibility.
|
||||||
|
|
||||||
|
The Network Proximity Checker has two configurable visibility parameters:
|
||||||
|
|
||||||
|
- Vis Range controls the distance threshold within which the network should consider a GameObject visible to a player.
|
||||||
|
- Vis Update Interval controls how often the distance test is performed. The value is the delay in seconds between checks. This allows you to optimise the check to perform as few tests as possible. The lower the number, the more frequently the updates occur. For slow-moving GameObjects you can set this interval to higher values. For fast-moving GameObjects, you should set it to lower values.
|
||||||
|
|
||||||
|
You must attach a Collider component to any GameObjects you want to use with the Network Proximity Checker.
|
||||||
|
|
||||||
|
## Network Visibility on Remote Clients
|
||||||
|
|
||||||
|
When a player on a remote client joins a networked game, only GameObjects that are network-visible to the player will be spawned on that remote client. This means that even if the player enters a large world with many networked GameObjects, the game can start quickly because it does not need to spawn every GameObject that exists in the world. Note that this applies to networked GameObjects in your Scene, but does not affect the loading of Assets. Unity still takes time to load the Assets for registered Prefabs and Scene GameObjects.
|
||||||
|
|
||||||
|
When a player moves within the world, the set of network-visible GameObjects changes. The player’s client is told about these changes as they happen. The ObjectHide message is sent to clients when a GameObject becomes no longer network-visible. By default, Mirror destroys the GameObject when it receives this message. When a GameObject becomes visible, the client receives an ObjectSpawn message, as if Mirror has spawned the GameObject for the first time. By default, the GameObject is instantiated like any other spawned GameObject.
|
||||||
|
|
||||||
|
## Network Visibility on the Host
|
||||||
|
|
||||||
|
The host shares the same Scene as the server, because it acts as both the server and the client to the player hosting the game. For this reason, it cannot destroy GameObjects that are not visible to the local player.
|
||||||
|
|
||||||
|
Instead, there is the virtual method OnSetLocalVisibility on the NetworkBehaviour class that is invoked. This method is invoked on all `NetworkBehaviour` scripts on GameObjects that change visibility state on the host.
|
||||||
|
|
||||||
|
The default implementation of `OnSetLocalVisibility` disables or enables all Renderer components on the GameObject. If you want to customize this implementation, you can override the method in your script, and provide a new behavior for how the host (and therefore the local client) should respond when a GameObject becomes network-visible or invisible (such as disabling HUD elements or renderers).
|
31
docs/Concepts/VisibilityCustom.md
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Customizing Network Visibility
|
||||||
|
|
||||||
|
The built-in Network Proximity Checker component is the built-in default component for determining a GameObject’s network visibility. However, this only provides you with a distance-based check. Sometimes you might want to use other kinds of visibility check, such as grid-based rules, line-of-sight tests, navigation path tests, or any other type of test that suits your game.
|
||||||
|
|
||||||
|
To do this, you can implement your own custom equivalent of the Network Proximity Checker. To do that, you need to understand how the Network Proximity Checker works. See documentation on the in-editor Network Proximity Checker component, and the NetworkProximityChecker API.
|
||||||
|
|
||||||
|
The Network Proximity Checker is implemented using the public visibility interface of Mirror’s HLAPI. Using this same interface, you can implement any kind of visibility rules you desire. Each NetworkIdentity
|
||||||
|
keeps track of the set of players that it is visible to. The players that a NetworkIdentity GameObject is visible to are called the “observers” of the NetworkIdentity.
|
||||||
|
|
||||||
|
The Network Proximity Checker calls the RebuildObservers method on the Network Identity component at a fixed interval (set using the “Vis Update Interval” value in the inspector), so that the set of network-visible GameObjects for each player is updated as they move around.
|
||||||
|
|
||||||
|
On the `NetworkBehaviour`class (which your networked scripts inherit from), there are some virtual functions for determining visibility. These are:
|
||||||
|
|
||||||
|
- **OnCheckObserver**
|
||||||
|
This method is called on the server, on each networked GameObject when a new player enters the game. If it returns true, that player is added to the object’s observers. The `NetworkProximityChecker` method does a simple distance check in its implementation of this function, and uses `Physics.OverlapSphere()` to find the players that are within the visibility distance for this object.
|
||||||
|
|
||||||
|
- **OnRebuildObservers**
|
||||||
|
This method is called on the server when `RebuildObservers` is invoked. This method expects the set of observers to be populated with the players that can see the object. The NetworkServer then handles sending `ObjectHide` and `ObjectSpawn` messages based on the differences between the old and new visibility sets.
|
||||||
|
|
||||||
|
You can check whether any given networked GameObject is a player by checking if its `NetworkIdentity` has a valid connectionToClient. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
var hits = Physics.OverlapSphere(transform.position, visRange);
|
||||||
|
foreach (var hit in hits)
|
||||||
|
{
|
||||||
|
// (if a GameObject has a connectionToClient, it is a player)
|
||||||
|
var uv = hit.GetComponent<NetworkIdentity>();
|
||||||
|
if (uv != null && uv.connectionToClient != null)
|
||||||
|
observers.Add(uv.connectionToClient);
|
||||||
|
}
|
||||||
|
```
|
24
docs/Concepts/index.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Networking Concepts Overview
|
||||||
|
|
||||||
|
## High level scripting API
|
||||||
|
|
||||||
|
Mirror’s networking has a “high-level” scripting API (which we’ll refer to as the HLAPI). Using this means you get access to commands which cover most of the common requirements for multiuser games without needing to worry about the “lower level” implementation details. The HLAPI allows you to:
|
||||||
|
|
||||||
|
- Control the networked state of the game using a “Network Manager”.
|
||||||
|
- Operate “client hosted” games, where the host is also a player client.
|
||||||
|
- Serialize data using a general-purpose serializer.
|
||||||
|
- Send and receive network messages.
|
||||||
|
- Send networked commands from clients to servers.
|
||||||
|
- Make remote procedure calls (RPCs) from servers to clients.
|
||||||
|
- Send networked events from servers to clients.
|
||||||
|
|
||||||
|
## Engine and Editor integration
|
||||||
|
|
||||||
|
Mirror’s networking is integrated into the engine and the editor, allowing you to work with components and visual aids to build your multiplayer game. It provides:
|
||||||
|
|
||||||
|
- A NetworkIdentity component for networked objects.
|
||||||
|
- A NetworkBehaviour for networked scripts.
|
||||||
|
- Configurable automatic synchronization of object transforms.
|
||||||
|
- Automatic synchronization of script variables.
|
||||||
|
- Support for placing networked objects in Unity scenes.
|
||||||
|
- Network components
|
18
docs/Events/Application.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Application Events Overview
|
||||||
|
|
||||||
|
General description of Application Events
|
||||||
|
|
||||||
|
- **OnStartHost**
|
||||||
|
Something about this
|
||||||
|
- **OnStartServer**
|
||||||
|
Something about this
|
||||||
|
- **OnStartClient**
|
||||||
|
Something about this
|
||||||
|
- **OnStopServer**
|
||||||
|
Something about this
|
||||||
|
- **OnStopClient**
|
||||||
|
Something about this
|
||||||
|
- **OnStopHost**
|
||||||
|
Something about this
|
||||||
|
- **OnApplicationQuit**
|
||||||
|
Something about this
|
14
docs/Events/Client.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Client Events Overview
|
||||||
|
|
||||||
|
General description of Client Events
|
||||||
|
|
||||||
|
- **OnClientConnect**
|
||||||
|
Something about this
|
||||||
|
- **OnClientDisconnect**
|
||||||
|
Something about this
|
||||||
|
- **OnClientNotReady**
|
||||||
|
Something about this
|
||||||
|
- **OnClientSceneChanged**
|
||||||
|
Something about this
|
||||||
|
- **OnClientError**
|
||||||
|
Something about this
|
8
docs/Events/Other.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Other Events Overview
|
||||||
|
|
||||||
|
General description of Other Events
|
||||||
|
|
||||||
|
- **OnValidate**
|
||||||
|
Something about this
|
||||||
|
- **OnDestroy**
|
||||||
|
Something about this
|
16
docs/Events/Server.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Server Events Overview
|
||||||
|
|
||||||
|
General description of Server Events
|
||||||
|
|
||||||
|
- **OnServerConnect**
|
||||||
|
Something about this
|
||||||
|
- **OnServerDisconnect**
|
||||||
|
Something about this
|
||||||
|
- **OnServerAddPlayer**
|
||||||
|
Something about this
|
||||||
|
- **OnServerRemovePlayer**
|
||||||
|
Something about this
|
||||||
|
- **OnServerSceneChanged**
|
||||||
|
Something about this
|
||||||
|
- **OnServerError**
|
||||||
|
Something about this
|
@ -8,23 +8,31 @@ Following these guidelines helps to communicate that you respect the time of the
|
|||||||
|
|
||||||
This is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into the main project itself.
|
This is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into the main project itself.
|
||||||
|
|
||||||
### I don't want to read this whole thing I just have a question!!!
|
If you haven't already, come find us in [Discord](https://discord.gg/wvesC6). We want you working on things you're excited about, and we can give you instant feedback.
|
||||||
|
|
||||||
We currently allow our users to use the issue tracker for support questions. But please be wary that maintaining an open source project can take a lot of time from the maintainers. If asking for a support question, state it clearly and take the time to explain your problem properly. Also, if your problem is not strictly related to this project we recommend you to use Stack Overlow instead.
|
### I don't want to read this whole thing I just have a question!!
|
||||||
|
|
||||||
|
We currently allow our users to use the issue tracker for support questions. But please be wary that maintaining an open source project can take a lot of time from the maintainers. If asking for a support question, state it clearly and take the time to explain your problem properly. Also, if your problem is not strictly related to this project we recommend you to use [Stack Overlow](https://stackoverflow.com) instead.
|
||||||
|
|
||||||
## How Can I Contribute?
|
## How Can I Contribute?
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
We have a handful of unit tests, but most of our testbed consists of running it with existing projects.
|
||||||
|
Try our builds and pull requests in your own projects and let us know how it goes.
|
||||||
|
|
||||||
### Reporting Bugs
|
### Reporting Bugs
|
||||||
|
|
||||||
Before creating bug reports, please check the existing bug reports as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible.
|
Before creating bug reports, please check the existing bug reports as you might find out that you don't need to create one. When you are creating a bug report, please include as many details as possible.
|
||||||
|
|
||||||
#### How Do I Submit A (Good) Bug Report?
|
#### How Do I Submit A (Good) Bug Report?
|
||||||
|
|
||||||
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue on the project's repository and provide the following information.
|
[Create an issue](https://github.com/vis2k/Mirror/issues/new?template=bug_report.md) on the project's repository and provide the following information.
|
||||||
|
|
||||||
Explain the problem and include additional details to help maintainers reproduce the problem:
|
Explain the problem and include additional details to help maintainers reproduce the problem:
|
||||||
|
|
||||||
* **Use a clear and descriptive title** for the issue to identify the problem.
|
* **Use a clear and descriptive title** for the issue to identify the problem.
|
||||||
|
* **Provide a simplified project that reproduces the issue whenever possible.**
|
||||||
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you used the project. When listing steps, **don't just say what you did, but explain how you did it**.
|
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you used the project. When listing steps, **don't just say what you did, but explain how you did it**.
|
||||||
* **Provide specific examples to demonstrate the steps**. It's always better to get more information. You can include links to files or GitHub projects, copy/pasteable snippets or even print screens or animated GIFS. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
* **Provide specific examples to demonstrate the steps**. It's always better to get more information. You can include links to files or GitHub projects, copy/pasteable snippets or even print screens or animated GIFS. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
|
||||||
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
|
||||||
@ -51,7 +59,7 @@ Before creating enhancement suggestions, please check the list of enhancements s
|
|||||||
|
|
||||||
#### How Do I Submit A (Good) Enhancement Suggestion?
|
#### How Do I Submit A (Good) Enhancement Suggestion?
|
||||||
|
|
||||||
Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue on the project's repository and provide the following information:
|
[Create an issue](https://github.com/vis2k/Mirror/issues/new?template=feature_request.md) on the project's repository and provide the following information:
|
||||||
|
|
||||||
* **Use a clear and descriptive title** for the issue to identify the suggestion.
|
* **Use a clear and descriptive title** for the issue to identify the suggestion.
|
||||||
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
|
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
|
||||||
@ -60,13 +68,53 @@ Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com
|
|||||||
* **List some other similar projects where this enhancement exists.**
|
* **List some other similar projects where this enhancement exists.**
|
||||||
* **Specify which version of the project you're using.**
|
* **Specify which version of the project you're using.**
|
||||||
* **Specify the current environment you're using.** if this is a useful information.
|
* **Specify the current environment you're using.** if this is a useful information.
|
||||||
|
* **Provide a specific use case** - Often we get requests for a feature not realizing there is already a way to fulfill their use case. In other words, don't just give us a solution, give us a problem.
|
||||||
|
|
||||||
|
|
||||||
### Creating Pull Requests
|
### Creating Pull Requests
|
||||||
|
|
||||||
#### How Do I Submit A (Good) Pull Request?
|
#### How Do I Submit A (Good) Pull Request?
|
||||||
|
|
||||||
|
Please send a [GitHub Pull Request](https://github.com/vis2k/Mirror/compare) with a clear list of what you've done (read more about [pull requests](https://help.github.com/articles/about-pull-requests)).
|
||||||
|
When you send a pull request, we will love you forever if you include unit tests.
|
||||||
|
We can always use more test coverage.
|
||||||
|
|
||||||
* **Use a clear and descriptive title** for the pull request to state the improvement you made to the code or the bug you solved.
|
* **Use a clear and descriptive title** for the pull request to state the improvement you made to the code or the bug you solved.
|
||||||
* **Provide a link to the related issue** if the pull request is a follow up of an existing bug report or enhancement suggestion.
|
* **Provide a link to the related issue** if the pull request is a follow up of an existing bug report or enhancement suggestion.
|
||||||
* **Comment why this pull request represents an enhancement** and give a rationale explaining why you did it that way and not another way.
|
* **Comment why this pull request represents an enhancement** and give a rationale explaining why you did it that way and not another way.
|
||||||
* **Use the same coding style than the one used in this project**.
|
* **Use the same coding style as the one used in this project**.
|
||||||
* **Welcome suggestions from the maintainers to improve your pull request**.
|
* **Welcome suggestions from the maintainers to improve your pull request**.
|
||||||
|
|
||||||
|
Please follow our coding conventions (below) and make sure all of your commits are atomic (one feature per commit). Rebase your pull requests if necessary.
|
||||||
|
|
||||||
|
Always write a clear log message for your commits. One-line messages are fine for small changes, but bigger changes should look like this:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git commit -m "A brief summary of the commit""
|
||||||
|
>
|
||||||
|
> A paragraph describing what changed and its impact.
|
||||||
|
```
|
||||||
|
|
||||||
|
Submit your pull requests to the right branch:
|
||||||
|
* Submit against the 2018 branch when the change **only** applies to Unity 2018.2+
|
||||||
|
* Submit against the master branch in all other cases
|
||||||
|
|
||||||
|
If your pull request breaks any test, it has no hope of being merged.
|
||||||
|
|
||||||
|
## Coding conventions
|
||||||
|
|
||||||
|
Start reading our code and you'll get the hang of it. We optimize for readability:
|
||||||
|
|
||||||
|
* We indent using 4 spaces (soft tabs)
|
||||||
|
* We value simplicity. The code should be easy to read and avoid magic
|
||||||
|
* **KISS / Occam's Razor** - always use the most simple solution.
|
||||||
|
* **No Premature Optimizations**
|
||||||
|
MMOs need to run for weeks without issues or exploits.
|
||||||
|
If you want your code to run 1% faster, spend \$100 on a better CPU.
|
||||||
|
* **Curly Braces { }**
|
||||||
|
Always use braces even for one line if's. Unity did this everywhere, and there is value in not accidentally missing a line in an if statement because there were no braces.
|
||||||
|
* **Variable naming**
|
||||||
|
\`NetworkIdentity identity\`, not \`NetworkIdentity uv\` or similar. If the variable needs a comment the name needs to be changed. For example, `msg = ... // the message` use `message = ...` without a comment instead Please Avoid **var** where possible. My text editor doesn't show me the type, so it needs to be obvious, and having two different ways to do the same thing only creates unnecessary complexity and confusion.
|
||||||
|
* This is open source software. Consider the people who will read your code, and make it look nice for them. It's sort of like driving a car: Perhaps you love doing donuts when you're alone, but with passengers the goal is to make the ride as smooth as possible.
|
||||||
|
|
||||||
|
Thanks.
|
8
docs/General/Deprecations.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Deprecations
|
||||||
|
|
||||||
|
Certain features of Unity Networking were removed from Mirror for various reasons.
|
||||||
|
|
||||||
|
This page will identify all removed features, the reason for removal, and possible alternatives.
|
||||||
|
|
||||||
|
- Couch Co-Op
|
||||||
|
-
|
210
docs/General/Migration.md
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
# Migration Guide
|
||||||
|
|
||||||
|
## Migrating a project from UNet (HLAPI)
|
||||||
|
|
||||||
|
This guide gives you a step by step instruction for migrating your project from HLAP to Mirror. Mirror is a fork of HLAPI. As such the migration is straight forward for most projects.
|
||||||
|
|
||||||
|
There's also a Migration Tool you can try:
|
||||||
|
<https://gist.github.com/Lymdun/dae130c5a69d69ab202bd621af2de1ad>
|
||||||
|
|
||||||
|
### 1. BACKUP
|
||||||
|
|
||||||
|
You have been warned.
|
||||||
|
|
||||||
|
### 2. Install Mirror
|
||||||
|
|
||||||
|
Get Mirror from the asset store and import it in your project (link to be provided)
|
||||||
|
|
||||||
|
### 3. Replace namespace
|
||||||
|
|
||||||
|
Replace `UnityEngine.Networking` for `Mirror` everywhere in your project. For example, if you have this:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
public class Player : NetworkBehaviour {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
replace it with:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Player : NetworkBehaviour {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
At this point, you might get some compilation errors. Don't panic, these are easy to fix. Keep going
|
||||||
|
|
||||||
|
### 4. Remove channels from NetworkSettings
|
||||||
|
|
||||||
|
NetworkSettings in HLAPI have channels, but this is flat out broken. Rather than ignoring your settings we removed channels from NetworkSettings.
|
||||||
|
|
||||||
|
For example, if you have this code:
|
||||||
|
|
||||||
|
```
|
||||||
|
[NetworkSettings(channel=1,sendInterval=0.05f)]
|
||||||
|
public class NetStreamer : NetworkBehaviour
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
replace it with:
|
||||||
|
|
||||||
|
```
|
||||||
|
[NetworkSettings(sendInterval=0.05f)]
|
||||||
|
public class NetStreamer : NetworkBehaviour
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that the default transport (Telepathy), completely ignores channels, all messages are reliable, sequenced and fragmented. They just work with no fuss. If you want to take advantage of unreliable channels use LLAPITransport instead.
|
||||||
|
|
||||||
|
### 5. Rename SyncListStruct to SyncListSTRUCT
|
||||||
|
|
||||||
|
There is a bug in the original UNET Weaver that makes it mess with our `Mirror.SyncListStruct` without checking the namespace. Until Unity officially removes UNET in 2019.1, we will have to use the name `SyncListSTRUCT` instead.
|
||||||
|
|
||||||
|
For example, if you have definitions like:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class SyncListQuest : SyncListStruct<Quest> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
replace them with:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public class SyncListQuest : SyncListSTRUCT<Quest> {}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Replace NetworkHash128 and NetworkInstanceId
|
||||||
|
|
||||||
|
These have been changed to System.Guid and uint, respectively.
|
||||||
|
|
||||||
|
For example, if you have something like this:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public sealed class SpawnItemMessage : MessageBase
|
||||||
|
{
|
||||||
|
public NetworkHash128 assetID;
|
||||||
|
public NetworkInstanceId networkInstanceID;
|
||||||
|
public Vector3 position;
|
||||||
|
public Quaternion rotation;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
replace with:
|
||||||
|
|
||||||
|
```
|
||||||
|
public sealed class SpawnItemMessage : MessageBase
|
||||||
|
{
|
||||||
|
public System.Guid assetID;
|
||||||
|
public uint networkInstanceID;
|
||||||
|
public Vector3 position;
|
||||||
|
public Quaternion rotation;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Update your synclist callbacks
|
||||||
|
|
||||||
|
In HLAPI SyncLists have a callback delegate that gets called in the client whenever the list is updated We have changed the callback to be a C\# event instead and we also pass the item that was updated/removed
|
||||||
|
|
||||||
|
For example, if you have this code:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
public class MyBehaviour : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public SyncListInt m_ints = new SyncListInt();
|
||||||
|
|
||||||
|
private void OnIntChanged(SyncListInt.Operation op, int index)
|
||||||
|
{
|
||||||
|
Debug.Log("list changed " + op);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartClient()
|
||||||
|
{
|
||||||
|
m_ints.Callback = OnIntChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
replace it with:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class MyBehaviour : NetworkBehaviour
|
||||||
|
{
|
||||||
|
public SyncListInt m_ints = new SyncListInt();
|
||||||
|
|
||||||
|
private void OnIntChanged(SyncListInt.Operation op, int index, int item)
|
||||||
|
{
|
||||||
|
Debug.Log("list changed " + op + " item " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnStartClient()
|
||||||
|
{
|
||||||
|
m_ints.Callback += OnIntChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice the callback will also work in the server in Mirror.
|
||||||
|
|
||||||
|
### 8. Replace Components
|
||||||
|
|
||||||
|
Every networked prefab and scene object needs to be adjusted. They will be using `NetworkIdentity` from Unet, and you need to replace that componenent with `NetworkIdentity` from Mirror. You may be using other network components, such as `NetworkAnimator` or `NetworkTransform`. All components from Unet should be replaced with their corresponding component from Mirror.
|
||||||
|
|
||||||
|
Note that if you remove and add a NetworkIdentity, you will need to reassign it in any component that was referencing it.
|
||||||
|
|
||||||
|
### 9. Update Extended Components
|
||||||
|
|
||||||
|
Some commonly extended components, such as NetworkManager, have changed method parameters in Mirror. A commonly used override is OnServerAddPlayer. Using the original HLAPI, your override may have looked like this:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId, NetworkReader extraMessageReader)
|
||||||
|
{
|
||||||
|
base.OnServerAddPlayer(conn, playerControllerId, extraMessageReader);
|
||||||
|
// your code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In your newly Mirror-capable NetworkManager, if you are using the OnServerAddPlayer override, remove the "playerControllerId" parameter from your override and the base call:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
public override void OnServerAddPlayer(NetworkConnection conn, NetworkReader extraMessageReader)
|
||||||
|
{
|
||||||
|
base.OnServerAddPlayer(conn, extraMessageReader);
|
||||||
|
// your code
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that in both HLAPI and Mirror the parameter "extraMessageReader" is optional.
|
||||||
|
|
||||||
|
### 10. Update your firewall and router
|
||||||
|
|
||||||
|
LLAPI uses UDP. Mirror uses TCP by default. This means you may need to change your router port forwarding and firewall rules in your machine to expose the TCP port instead of UDP. This highly depends on your router and operating system.
|
||||||
|
|
||||||
|
## Video version
|
||||||
|
|
||||||
|
See for yourself how uMMORPG was migrated to Mirror
|
||||||
|
|
||||||
|
[![Manually upgrading uMMORPG V1.130 to V1.131 (Mirror)](MigrationVideo.jpg)](http://www.youtube.com/watch?v=LF9rTSS3rlI)
|
||||||
|
|
||||||
|
## Possible Error Messages
|
||||||
|
|
||||||
|
- TypeLoadException: A type load exception has occurred. - happens if you still have SyncListStruct instead of SyncListSTRUCT in your project.
|
||||||
|
- NullPointerException: The most likely cause is that you replaced NetworkIdentities or other components but you had them assigned somewhere. Reassign those references.
|
||||||
|
- `error CS0246: The type or namespace name 'UnityWebRequest' could not be found. Are you missing 'UnityEngine.Networking' using directive?`
|
||||||
|
|
||||||
|
Add this to the top of your script:
|
||||||
|
`using UnityWebRequest = UnityEngine.Networking.UnityWebRequest;`
|
||||||
|
`UnityWebRequest` is not part of UNet or Mirror, but it is in the same namespace as UNet. Changing the namespace to Mirror caused your script not to find UnityWebRequest. The same applies for `WWW` and all `UnityWebRequest` related classes.
|
BIN
docs/General/MigrationVideo.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
81
docs/General/Start.md
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
This document describes steps to converting a single player game to a multiplayer game, using the new Unity Multiplayer networking system. The process described here is a simplified, higher level version of the actual process for a real game; it doesn’t always work exactly like this, but it provides a basic recipe for the process.
|
||||||
|
|
||||||
|
## NetworkManager set-up
|
||||||
|
|
||||||
|
- Add a new GameObject to the Scene and rename it “NetworkManager”.
|
||||||
|
- Add the NetworkManager component to the “NetworkManager” GameObject.
|
||||||
|
- Add the NetworkManagerHUD component to the GameObject. This provides the default UI for managing the network game state.
|
||||||
|
See [Using the NetworkManager].
|
||||||
|
|
||||||
|
## Player Prefab
|
||||||
|
|
||||||
|
- Find the Prefab for the player GameObject in the game, or create a Prefab from the player GameObject
|
||||||
|
- Add the NetworkIdentity component to the player Prefab
|
||||||
|
- Check the LocalPlayerAuthority box on the NetworkIdentity
|
||||||
|
- Set the `playerPrefab` in the NetworkManager’s Spawn Info section to the player Prefab
|
||||||
|
- Remove the player GameObject instance from the Scene if it exists in the Scene
|
||||||
|
|
||||||
|
See [Player Objects] for more information.
|
||||||
|
|
||||||
|
## Player movement
|
||||||
|
|
||||||
|
- Add a NetworkTransform component to the player Prefab
|
||||||
|
- Update input and control scripts to respect `isLocalPlayer`
|
||||||
|
- Fix Camera to use spawned player and `isLocalPlayer`
|
||||||
|
|
||||||
|
For example, this script only processes input for the local player:
|
||||||
|
|
||||||
|
``` cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Controls : NetworkBehaviour
|
||||||
|
{
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if (!isLocalPlayer)
|
||||||
|
{
|
||||||
|
// exit from update if this is not the local player
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle player input for movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Basic player game state
|
||||||
|
|
||||||
|
- Make scripts that contain important data into NetworkBehaviours instead of MonoBehaviours
|
||||||
|
- Make important member variables into SyncVars
|
||||||
|
|
||||||
|
See [State Synchronization].
|
||||||
|
|
||||||
|
## Networked actions
|
||||||
|
|
||||||
|
- Make scripts that perform important actions into NetworkBehaviours instead of MonoBehaviours
|
||||||
|
- Update functions that perform important player actions to be commands
|
||||||
|
|
||||||
|
See [Networked Actions](#networked-actions).
|
||||||
|
|
||||||
|
## Non-player GameObjects
|
||||||
|
|
||||||
|
Fix non-player prefabs such as enemies:
|
||||||
|
|
||||||
|
- Add the NetworkIdentify component
|
||||||
|
- Add the NetworkTransform component
|
||||||
|
- Register spawnable Prefabs with the NetworkManager
|
||||||
|
- Update scripts with game state and actions
|
||||||
|
|
||||||
|
## Spawners
|
||||||
|
|
||||||
|
- Potentially change spawner scripts to be NetworkBehaviours
|
||||||
|
- Modify spawners to only run on the server (use isServer property or the `OnStartServer()` function)
|
||||||
|
- Call `NetworkServer.Spawn()` for created GameObjects
|
||||||
|
|
||||||
|
## Spawn positions for players
|
||||||
|
|
||||||
|
- Add a new GameObject and place it at player’s start location
|
||||||
|
- Add the NetworkStartPosition component to the new GameObject
|
10
docs/General/Support.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Support
|
||||||
|
|
||||||
|
## Discord
|
||||||
|
|
||||||
|
- You can find us on [Discord](https://discord.gg/2BvnM4R).
|
||||||
|
|
||||||
|
## GitHub
|
||||||
|
|
||||||
|
- You can create an issue in [GitHub](https://github.com/vis2k/Mirror/issues)
|
||||||
|
- You can also contribute with Pull Requests...see [Contributions](Contributions)
|
74
docs/General/WhyTCP.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Why TCP by default and not UDP?
|
||||||
|
|
||||||
|
## The same old Discussion
|
||||||
|
|
||||||
|
It's the year 2018 and every game developer swears by UDP. Yet we chose TCP as default for Mirror. Why is that?
|
||||||
|
|
||||||
|
UDP vs. TCP, the technical aspects
|
||||||
|
|
||||||
|
First of all, a quick word about the major differences between UDP and TCP.
|
||||||
|
|
||||||
|
- UDP has lower latency and is unreliable by default and hard to use correctly
|
||||||
|
- TCP has higher latency and is reliable by default and easy to use
|
||||||
|
|
||||||
|
Now instead of having yet another technical UDP vs. TCP discussion, let's take a look at a real world example to see why we chose TCP over UDP.
|
||||||
|
|
||||||
|
## That Game again
|
||||||
|
|
||||||
|
Back in 2011, some guy named Markus Persson (aka Notch) created arguably the biggest multiplayer game of all time. The game is called Minecraft.
|
||||||
|
|
||||||
|
Minecraft uses TCP, but why is that? Nobody knows, except Markus Persson.
|
||||||
|
|
||||||
|
But we can make an educated guess.
|
||||||
|
|
||||||
|
## On Java vs. C++
|
||||||
|
|
||||||
|
But wait, let's go back a bit further. Minecraft was written in Java, which is outrageous given that back in 2011 every game developer used to swear by C++.
|
||||||
|
|
||||||
|
Here are the major differences between C++ and Java:
|
||||||
|
|
||||||
|
- C++ has a lower footprint, it's faster and it's hard to use correctly
|
||||||
|
- Java is slow, uses ungodly amounts of memory and is easy to use
|
||||||
|
|
||||||
|
That discussion sounds oddly familiar. Speed vs. ease of use, just like UDP vs. TCP.
|
||||||
|
|
||||||
|
## Why?
|
||||||
|
|
||||||
|
Okay so, why did Notch chose Java instead of C++ and TCP instead of UDP, given that they are both so much slower than their counter parts?
|
||||||
|
|
||||||
|
Well, obviously because Notch is an idiot and knows nothing about game development.
|
||||||
|
|
||||||
|
But wait, something doesn't add up. Every kid on the planet plays Minecraft and right now there are thousands of people who are having a blast building shelters, surviving zombies at night, making friends and occasionally falling in love.
|
||||||
|
|
||||||
|
Oh, and Notch is a billionaire now.
|
||||||
|
|
||||||
|
All the evidence points us to the fact that back in 2011, Notch knew something that others didn't.
|
||||||
|
|
||||||
|
## The Riddle
|
||||||
|
|
||||||
|
The answer to the riddle is the question about optimization.
|
||||||
|
|
||||||
|
Notch never optimized for performance. That's why he didn't care about C++ or UDP.
|
||||||
|
|
||||||
|
Notch optimized for probability of success. That's why he chose Java and TCP.
|
||||||
|
|
||||||
|
And it worked. What good would it be if Minecraft ran at twice the framerate and half the latency without ever seeing the light of day?
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
Back in 2015 when we started uMMORPG and Cubica, we originally used Unity's built in Networking system aka UNET. UNET used UDP and avoided garbage collection at all costs.
|
||||||
|
|
||||||
|
What sounds good in theory, was terrible in practice. We spent about half our work hours from 2015 to 2018 dealing with UNET bugs. There was packet loss. Highly complex code due to GC avoidance. Synchronization issues. Random errors. And most importantly, no decent way to debug any of it.
|
||||||
|
|
||||||
|
If a monster didn't spawn on a client, we wouldn't know what caused it.
|
||||||
|
|
||||||
|
- Was the packet dropped by UDP?
|
||||||
|
- Was it a bug in the highly complex UNET source code?
|
||||||
|
- Was the reliable layer on top of UDP not working as intended?
|
||||||
|
- Was the reliable layer actually fully reliable?
|
||||||
|
- Did we use the right networking config for the host that we tested it on?
|
||||||
|
- Or was it a bug in our own project?
|
||||||
|
|
||||||
|
After 3 years in UDP hell, we realized what Notch had realized a long time ago: if we ever wanted to finish our games, we would need a networking layer that just works.
|
||||||
|
|
||||||
|
That's why we made Telepathy and Mirror. **Life is short. We just need the damn thing to work.**
|
24
docs/General/index.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# General Overview
|
||||||
|
|
||||||
|
## High level scripting API
|
||||||
|
|
||||||
|
Mirror’s networking has a “high-level” scripting API (which we’ll refer to as the HLAPI). Using this means you get access to commands which cover most of the common requirements for multiuser games without needing to worry about the “lower level” implementation details. The HLAPI allows you to:
|
||||||
|
|
||||||
|
- Control the networked state of the game using a “Network Manager".
|
||||||
|
- Operate “client hosted” games, where the host is also a player client.
|
||||||
|
- Serialize data using a general-purpose serializer.
|
||||||
|
- Send and receive network messages.
|
||||||
|
- Send networked commands from clients to servers.
|
||||||
|
- Make remote procedure calls (RPCs) from servers to clients.
|
||||||
|
- Send networked events from servers to clients.
|
||||||
|
|
||||||
|
## Engine and Editor integration
|
||||||
|
|
||||||
|
Mirror’s networking is integrated into the engine and the editor, allowing you to work with components and visual aids to build your multiplayer game. It provides:
|
||||||
|
|
||||||
|
- A NetworkIdentity component for networked objects.
|
||||||
|
- A NetworkBehaviour for networked scripts
|
||||||
|
- Configurable automatic synchronization of object transforms.
|
||||||
|
- Automatic synchronization of script variables.
|
||||||
|
- Support for placing networked objects in Unity scenes.
|
||||||
|
- Network components
|
77
docs/Messages/index.md
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# Messages Overview
|
||||||
|
|
||||||
|
General description of Messages
|
||||||
|
|
||||||
|
- **AddPlayerMessage**
|
||||||
|
This is passed to handler functions registered for the AddPlayer built-in message.
|
||||||
|
- **EmptyMessage**
|
||||||
|
A utility class to send a network message with no contents.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Test
|
||||||
|
{
|
||||||
|
void SendNotification()
|
||||||
|
{
|
||||||
|
var msg = new EmptyMessage();
|
||||||
|
NetworkServer.SendToAll(667, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **ErrorMessage**
|
||||||
|
This is passed to handler functions registered for the SYSTEM_ERROR built-in message.
|
||||||
|
- **IntegerMessage**
|
||||||
|
A utility class to send simple network messages that only contain an integer.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Test
|
||||||
|
{
|
||||||
|
void SendValue(int value)
|
||||||
|
{
|
||||||
|
var msg = new IntegerMessage(value);
|
||||||
|
NetworkServer.SendToAll(MsgType.Scene, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- **NotReadyMessage**
|
||||||
|
This is passed to handler functions registered for the SYSTEM_NOT_READY built-in message.
|
||||||
|
- **PeerAuthorityMessage**
|
||||||
|
Information about a change in authority of a non-player in the same network game.
|
||||||
|
This information is cached by clients and used during host-migration.
|
||||||
|
- **PeerInfoMessage**
|
||||||
|
Information about another participant in the same network game.
|
||||||
|
This information is cached by clients and used during host-migration.
|
||||||
|
- **PeerInfoPlayer**
|
||||||
|
A structure used to identify player object on other peers for host migration.
|
||||||
|
- **PeerListMessage**
|
||||||
|
Internal UNET message for sending information about network peers to clients.
|
||||||
|
- **ReadyMessage**
|
||||||
|
This is passed to handler functions registered for the SYSTEM_READY built-in message.
|
||||||
|
- **ReconnectMessage**
|
||||||
|
This network message is used when a client reconnect to the new host of a game.
|
||||||
|
- **RemovePlayerMessage**
|
||||||
|
This is passed to handler funtions registered for the SYSTEM_REMOVE_PLAYER built-in message.
|
||||||
|
- **StringMessage**
|
||||||
|
This is a utility class for simple network messages that contain only a string.
|
||||||
|
This example sends a message with the name of the Scene.
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using UnityEngine;
|
||||||
|
using Mirror;
|
||||||
|
|
||||||
|
public class Test
|
||||||
|
{
|
||||||
|
void SendSceneName(string sceneName)
|
||||||
|
{
|
||||||
|
var msg = new StringMessage(sceneName);
|
||||||
|
NetworkServer.SendToAll(MsgType.Scene, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
# Boostrap 4 Github Pages
|
|
||||||
|
|
||||||
[![Build Status](https://travis-ci.org/nicolas-van/bootstrap-4-github-pages.svg?branch=master)](https://travis-ci.org/nicolas-van/bootstrap-4-github-pages)
|
|
||||||
|
|
||||||
A [Bootstrap 4](https://getbootstrap.com/) start up project for [Github Pages](https://pages.github.com/) and [Jekyll](https://jekyllrb.com/).
|
|
||||||
|
|
||||||
* A full Bootstrap 4 theme usable both on Github Pages and with a standalone Jekyll.
|
|
||||||
* Recompiles Bootstrap from SCSS files, which allows to customize Bootstrap's variables and use Bootstrap themes.
|
|
||||||
* Full support of Bootstrap's JavaScript plugins.
|
|
||||||
* Supports all features of Github Pages and Jekyll.
|
|
||||||
|
|
||||||
[See the website for demonstration and documentation](https://nicolas-van.github.io/bootstrap-4-github-pages/).
|
|
||||||
|
|
||||||
## Contribution
|
|
||||||
|
|
||||||
[See the contribution guide.](./CONTRIBUTING.md)
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[See the license file.](./LICENSE.md)
|
|
59
docs/Samples/Pong.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Pong
|
||||||
|
|
||||||
|
A simple example for "How to built a multiplayer game with Mirror" is Pong,
|
||||||
|
which is included in the AssetStore package of Mirror. It illustrates the usage
|
||||||
|
of NetworkManager, NetworkManagerHUD, NetworkBehaviour, NetworkIdentity,
|
||||||
|
NetworkTransform, NetworkStartPosition and NetworkingAttributes.
|
||||||
|
|
||||||
|
![Pong](Pong1.jpg)
|
||||||
|
|
||||||
|
## Setting the number of players
|
||||||
|
|
||||||
|
First of all, let's have a look at the NetworkManager object in the main scene.
|
||||||
|
When adding the NetworkManager component to a gameobject, a few default settings
|
||||||
|
are already set (**Don't destroy on Load**, **Run in Background**, ...) For
|
||||||
|
playing Pong the maximum number of players is 2, so the setting **Network
|
||||||
|
Info/Max connections** will also be 2. As there are no other scenes (lobby,
|
||||||
|
online or offline scene) in this sample the properties for **Offline Scene** and
|
||||||
|
**Online Scene** will stay empty.
|
||||||
|
|
||||||
|
## Creating the player
|
||||||
|
|
||||||
|
Furthermore every player needs a racket to play with. Each player who joins the
|
||||||
|
game will have his own controllable object, which represents him within the
|
||||||
|
game. This gameobject is called *PlayerObject*. For spawning the *PlayerObject*
|
||||||
|
a prefab has to be created, containing at least a NetworkIdentity component with
|
||||||
|
**Local Player Authority** checked. The **Local Player Authority** allows the
|
||||||
|
player to control and modify the gameobjects properties (e.g. for movement). The
|
||||||
|
NetworkManager needs a reference to this prefab, which is located in **Spawn
|
||||||
|
Info/Player Prefab**. To have the player movement synchronized over the network,
|
||||||
|
the player prefab also contains a NetworkTransform.
|
||||||
|
|
||||||
|
![NetworkManagerSettings](Pong2.jpg)
|
||||||
|
|
||||||
|
## Player start position
|
||||||
|
|
||||||
|
The main scene contains 2 gameobjects with only a NetworkStartPosition component
|
||||||
|
(gameobjects RacketSpawnLeft, RacketSpawnRight in the scene). These transforms
|
||||||
|
will be automatically registered by the NetworkManager as spawning positions.
|
||||||
|
|
||||||
|
![NetworkStartPositions](Pong3.jpg)
|
||||||
|
|
||||||
|
## Setting up the network
|
||||||
|
|
||||||
|
A very convenient component for establish/testing connections is the
|
||||||
|
NetworkManagerHUD. It provides basic functionality for start a game as LAN
|
||||||
|
client, LAN server or host (LAN client and LAN server at the same time). It
|
||||||
|
requires the NetworkManager component.
|
||||||
|
|
||||||
|
![NetworkManagerHUD](Pong4.jpg)
|
||||||
|
|
||||||
|
## The ball of Pong
|
||||||
|
|
||||||
|
The ball is the main focus of Pong, as this is the object needed to score
|
||||||
|
points. Its NetworkIdentity component has neither **Server Only** nor **Local
|
||||||
|
Player Authority** checked, as it's moved by the server physics engine and can
|
||||||
|
be influenced by the players. As with the *PlayerObject* the position is
|
||||||
|
synchronized via NetworkTransform. When having multiple scenes, the ball can be
|
||||||
|
spawned by the NetworkManager, but to keep this sample simple, it's placed
|
||||||
|
directly within the main scene.
|
BIN
docs/Samples/Pong1.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
docs/Samples/Pong2.jpg
Normal file
After Width: | Height: | Size: 229 KiB |
BIN
docs/Samples/Pong3.jpg
Normal file
After Width: | Height: | Size: 162 KiB |
BIN
docs/Samples/Pong4.jpg
Normal file
After Width: | Height: | Size: 9.7 KiB |
10
docs/Samples/index.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Samples Overview
|
||||||
|
|
||||||
|
General description of Samples
|
||||||
|
|
||||||
|
- [Pong](Pong)
|
||||||
|
A simple example for "How to built a multiplayer game with Mirror" is Pong,
|
||||||
|
which is included in the AssetStore package of Mirror. It illustrates the
|
||||||
|
usage of NetworkManager, NetworkManagerHUD, NetworkBehaviour,
|
||||||
|
NetworkIdentity, NetworkTransform, NetworkStartPosition and
|
||||||
|
NetworkingAttributes.
|
3
docs/Services/Chat.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Chat Service
|
||||||
|
|
||||||
|
General description of Chat Service
|
3
docs/Services/Match.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Match Service
|
||||||
|
|
||||||
|
General description of Match Service
|
3
docs/Services/NAT.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# NAT Service
|
||||||
|
|
||||||
|
General description of NAT Service
|