-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathCharacterSpawner.cs
More file actions
199 lines (174 loc) · 7.28 KB
/
Copy pathCharacterSpawner.cs
File metadata and controls
199 lines (174 loc) · 7.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
using System;
using System.Collections.Generic;
using Mirage.Logging;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Mirage
{
/// <summary>
/// Spawns a player as soon as the connection is authenticated
/// </summary>
[HelpURL("https://miragenet.github.io/Mirage/docs/guides/game-objects/spawn-player/")]
[AddComponentMenu("Network/CharacterSpawner")]
public class CharacterSpawner : MonoBehaviour
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(CharacterSpawner));
[Header("References")]
public NetworkClient Client;
public NetworkServer Server;
public NetworkSceneManager SceneManager;
public ClientObjectManager ClientObjectManager;
public ServerObjectManager ServerObjectManager;
[Header("Spawn")]
public NetworkIdentity PlayerPrefab;
[Tooltip("Whether to span the player upon connection automatically")]
public bool AutoSpawn = true;
[Tooltip("Should the characters gameObject name be set when it is spawned")]
public bool SetName = true;
[Header("Location")]
public int startPositionIndex;
/// <summary>
/// List of transforms where players can be spawned
/// </summary>
public List<Transform> startPositions = new List<Transform>();
/// <summary>
/// Enumeration of methods of where to spawn player objects in multiplayer games.
/// </summary>
public enum PlayerSpawnMethod { Random, RoundRobin }
/// <summary>
/// The current method of spawning players used by the CharacterSpawner.
/// </summary>
[Tooltip("Round Robin or Random order of Start Position selection")]
public PlayerSpawnMethod playerSpawnMethod;
// Start is called before the first frame update
protected internal virtual void Awake()
{
if (PlayerPrefab == null)
{
throw new InvalidOperationException("Assign a player in the CharacterSpawner");
}
if (Client != null)
{
if (SceneManager != null)
{
SceneManager.OnClientFinishedSceneChange.AddListener(OnClientFinishedSceneChange);
}
else
{
Client.Authenticated.AddListener(OnClientAuthenticated);
Client.Connected.AddListener(OnClientConnected);
}
}
if (Server != null)
{
Server.Started.AddListener(OnServerStarted);
if (ServerObjectManager == null)
{
throw new InvalidOperationException("Assign a ServerObjectManager");
}
}
}
protected virtual void OnDestroy()
{
if (Client != null && SceneManager != null)
{
SceneManager.OnClientFinishedSceneChange.RemoveListener(OnClientFinishedSceneChange);
Client.Authenticated.RemoveListener(OnClientAuthenticated);
}
if (Server != null)
{
Server.Started.RemoveListener(OnServerStarted);
}
}
internal void OnClientConnected(INetworkPlayer player)
{
if (ClientObjectManager != null)
{
ClientObjectManager.RegisterPrefab(PlayerPrefab);
}
else
{
throw new InvalidOperationException("Assign a ClientObjectManager");
}
}
private void OnClientAuthenticated(INetworkPlayer _)
{
Client.Send(new AddCharacterMessage());
}
private void OnServerStarted()
{
Server.MessageHandler.RegisterHandler<AddCharacterMessage>(OnServerAddPlayerInternal);
}
/// <summary>
/// Called on the client when a normal scene change happens.
/// <para>The default implementation of this function sets the client as ready and adds a player. Override the function to dictate what happens when the client connects.</para>
/// </summary>
/// <param name="scene"></param>
/// <param name="sceneOperation">The type of scene load that happened.</param>
public virtual void OnClientFinishedSceneChange(Scene scene, SceneOperation sceneOperation)
{
if (AutoSpawn && sceneOperation == SceneOperation.Normal)
RequestServerSpawnPlayer();
}
public virtual void RequestServerSpawnPlayer()
{
Client.Send(new AddCharacterMessage());
}
private void OnServerAddPlayerInternal(INetworkPlayer player, AddCharacterMessage msg)
{
logger.Log("CharacterSpawner.OnServerAddPlayer");
if (player.HasCharacter)
{
// player already has character on server, but client asked for it
// so we respawn it here so that client recieves it again
// this can happen when client loads normally, but server addititively
ServerObjectManager.Spawn(player.Identity);
}
else
{
OnServerAddPlayer(player);
}
}
/// <summary>
/// Called on the server when a client adds a new player with ClientScene.AddPlayer.
/// <para>The default implementation for this function creates a new player object from the playerPrefab.</para>
/// </summary>
/// <param name="player">Connection from client.</param>
public virtual void OnServerAddPlayer(INetworkPlayer player)
{
var startPos = GetStartPosition();
var character = startPos != null
? Instantiate(PlayerPrefab, startPos.position, startPos.rotation)
: Instantiate(PlayerPrefab);
if (SetName)
SetCharacterName(player, character);
ServerObjectManager.AddCharacter(player, character.gameObject);
}
protected virtual void SetCharacterName(INetworkPlayer player, NetworkIdentity character)
{
// When spawning a player game object, Unity defaults to something like "MyPlayerObject(clone)"
// which sucks... So let's override it and make it easier to debug. Credit to Mirror for the nice touch.
character.name = $"{PlayerPrefab.name} {player.Address}";
}
/// <summary>
/// This finds a spawn position based on start position objects in the scene.
/// <para>This is used by the default implementation of OnServerAddPlayer.</para>
/// </summary>
/// <returns>Returns the transform to spawn a player at, or null.</returns>
public virtual Transform GetStartPosition()
{
if (startPositions.Count == 0)
return null;
if (playerSpawnMethod == PlayerSpawnMethod.Random)
{
return startPositions[UnityEngine.Random.Range(0, startPositions.Count)];
}
else
{
var startPosition = startPositions[startPositionIndex];
startPositionIndex = (startPositionIndex + 1) % startPositions.Count;
return startPosition;
}
}
}
}