7 using System.Collections.Generic;
12 using Lidgren.Network;
21 private class WorldServerInfo
23 public NetConnection Connection;
26 private class ClientInfo
28 public NetConnection Connection;
29 public string PlayerID;
32 public ClientSyncJob SyncJob;
35 private class ClientSyncJob
37 public ClientInfo Client;
38 public List<int> ExistingObjectIndices =
new List<int>();
41 public delegate
void ConnectedToWorldServerHandler ();
42 public delegate
void PlayerJoinedServerHandler (
int peerIndex,
string playerId,
PlayerProfile playerProfile);
43 public delegate
void PlayerSyncedHandler (
int peerIndex,
string playerId,
PlayerProfile playerProfile);
44 public delegate
void PlayerLeftServerHandler (
int peerIndex,
string playerId,
PlayerProfile playerProfile);
45 public delegate
void PlayerJoinedWorldServerHandler (
string playerId,
string playerNickname);
46 public delegate
void PlayerLeftWorldServerHandler (
string playerId,
string playerNickname);
47 public delegate
void WorldDataReceivedHandler (
UJeli worldDataPage);
48 public delegate
void ServerSpawnedHandler (GameServerType serverType,
string mapId, IPEndPoint endPoint);
49 public delegate
void ServerSpawnFailedHandler (GameServerType serverType,
string mapId);
50 public delegate
void ServerSyncFinishedHandler ();
52 #pragma warning disable 0067
89 #pragma warning restore 0067
96 get {
return _instance; }
104 get {
return _serverPoolIndex; }
112 get {
return _serverPoolIndex != -1; }
120 get {
return _mapSettings; }
128 get {
return _clientsById.Count; }
136 get {
return _clientsById.Keys.ToArray(); }
144 get {
return _clientsByPeerIndex.Keys.ToArray(); }
155 from client in _clientsById.Values
156 where client.PlayerProfile != null
157 select client.PlayerProfile).ToArray();
169 from client in _clientsById.Values
170 where client.PlayerProfile != null
171 select client.PlayerProfile.Basic.Nickname).ToArray();
180 get {
return _isConnectedToWorldServer; }
185 static Dictionary<NetDeliveryMethod, DeliveryMode> _netToTimelineDeliveryModes
186 =
new Dictionary<NetDeliveryMethod, DeliveryMode>()
188 { NetDeliveryMethod.ReliableOrdered, DeliveryMode.ReliableOrdered },
189 { NetDeliveryMethod.ReliableUnordered, DeliveryMode.ReliableUnordered },
190 { NetDeliveryMethod.Unreliable, DeliveryMode.Unreliable },
193 static Dictionary<DeliveryMode, NetDeliveryMethod> _timelineToNetDeliveryModes
194 =
new Dictionary<DeliveryMode, NetDeliveryMethod>()
196 { DeliveryMode.ReliableOrdered, NetDeliveryMethod.ReliableOrdered },
197 { DeliveryMode.ReliableUnordered, NetDeliveryMethod.ReliableUnordered },
198 { DeliveryMode.Unreliable, NetDeliveryMethod.Unreliable },
203 public string WorldID;
204 public GameServerType ServerType;
208 StreamWriter _logWriter;
209 int _serverPoolIndex;
212 WorldServerInfo _worldServer;
213 bool _isConnectedToWorldServer;
215 Dictionary<int, ClientInfo> _clientsByPeerIndex;
216 Dictionary<string, ClientInfo> _clientsById;
217 int _nextClientPeerIndex;
218 Dictionary<string, List<WorldDataReceivedHandler>> _worldDataRequests;
219 Dictionary<string, UJeli> _worldDataCache;
221 List<string> _availableZoneMaps;
222 List<string> _availableMinigameMaps;
229 DontDestroyOnLoad(
this);
231 if (Application.platform == RuntimePlatform.OSXPlayer)
233 _logsBase = Path.GetFullPath(Application.dataPath +
"/Logs/");
237 _logsBase = Path.GetFullPath(
"Logs/");
240 var flags = CommandLineUtils.GetCommandLineFlags();
242 if (flags.ContainsKey(
"zone"))
244 ServerType = GameServerType.Zone;
245 MapID = flags[
"zone"];
247 else if (flags.ContainsKey(
"minigame"))
249 ServerType = GameServerType.Minigame;
250 MapID = flags[
"minigame"];
253 if (flags.ContainsKey(
"world"))
254 WorldID = flags[
"world"];
256 _serverPoolIndex = -1;
259 if (flags.ContainsKey(
"pool"))
261 try { _serverPoolIndex =
int.Parse(flags[
"pool"]); }
264 if (flags.ContainsKey(
"request"))
266 try { _requestIndex =
int.Parse(flags[
"request"]); }
271 var netPeerConfig =
new NetPeerConfiguration(
"Liberi");
272 netPeerConfig.AcceptIncomingConnections =
true;
273 netPeerConfig.MaximumConnections = 32;
274 netPeerConfig.EnableMessageType(NetIncomingMessageType.ConnectionApproval);
275 netPeerConfig.EnableMessageType(NetIncomingMessageType.UnconnectedData);
277 if (flags.ContainsKey(
"port"))
279 try { netPeerConfig.Port =
int.Parse(flags[
"port"]); }
280 catch { netPeerConfig.Port = WorldNetUtils.GetFirstFreePort(34444); }
282 else netPeerConfig.Port = WorldNetUtils.GetFirstFreePort(34444);
284 if (flags.ContainsKey(
"maxplayers"))
286 try { netPeerConfig.MaximumConnections =
int.Parse(flags[
"maxplayers"]); }
290 _netPeer =
new NetPeer(netPeerConfig);
292 _clientsByPeerIndex =
new Dictionary<int, ClientInfo>();
293 _clientsById =
new Dictionary<string, ClientInfo>();
295 _worldDataRequests =
new Dictionary<string, List<WorldDataReceivedHandler>>();
296 _worldDataCache =
new Dictionary<string,UJeli>();
298 TimelineUtils.SetDefaultTimelineFunctions();
299 UTimelineUtils.SetDefautTimelineFunctions();
301 Sync.PropagateSpawnDown += OnPropagateSpawnDown;
302 Sync.PropagateDespawnDown += OnPropagateDespawnDown;
308 _availableZoneMaps =
new List<string>();
309 _availableMinigameMaps =
new List<string>();
311 foreach (var scene
in EditorBuildSettings.scenes)
315 if (scene.path.Contains(
"/Zones/"))
316 _availableZoneMaps.Add(Path.GetFileNameWithoutExtension(scene.path));
317 else if (scene.path.Contains(
"/Minigames/"))
318 _availableMinigameMaps.Add(Path.GetFileNameWithoutExtension(scene.path));
327 if (_isConnectedToWorldServer)
330 GUILayout.BeginArea(
new Rect(5, 5, 200, Screen.height - 10),
"Zones:", GUI.skin.box);
334 foreach (var map
in _availableZoneMaps)
336 if (GUILayout.Button(map))
338 ServerType = GameServerType.Zone;
347 GUILayout.BeginArea(
new Rect(210, 5, 200, Screen.height - 10),
"Minigames:", GUI.skin.box);
351 foreach (var map
in _availableMinigameMaps)
353 if (GUILayout.Button(map))
355 ServerType = GameServerType.Minigame;
368 Screen.SetResolution(Width, Height,
false);
369 Screen.showCursor =
true;
371 Directory.CreateDirectory(_logsBase);
373 _logWriter = File.CreateText(Path.GetFullPath(
374 string.Format(
"{0}LiberiServer_{1:yyyy-MM-dd_HH-mm-ss}.log", _logsBase, DateTime.Now)));
375 _logWriter.AutoFlush =
true;
384 Log(
string.Format(
"Server initializing... Type: {0}, Map: {1}, Pooled: {2}",
385 ServerType, MapID,
IsPooled ?
"Yes" :
"No"));
387 ConnectToWorldServer();
394 public void Log (
string line)
396 if (_logWriter != null)
397 _logWriter.WriteLine(
string.Format(
"[{0:HH:mm:ss}] {1}", DateTime.Now, line));
400 private ClientInfo GetClient (
int peerIndex)
402 ClientInfo client = null;
403 _clientsByPeerIndex.TryGetValue(peerIndex, out client);
407 private ClientInfo GetClient (
string playerId)
409 ClientInfo client = null;
410 _clientsById.TryGetValue(playerId, out client);
419 var client = GetClient(playerId);
422 return client.PeerIndex;
431 var client = GetClient(peerIndex);
434 return client.PlayerID;
443 var client = GetClient(playerId);
446 return client.PlayerProfile;
455 var client = GetClient(peerIndex);
458 return client.PlayerProfile;
469 var requestMsg = _netPeer.CreateMessage();
470 requestMsg.Write((byte)WorldServerMessageType.SpawnServer);
471 requestMsg.Write(serverType.ToString().ToLower());
472 requestMsg.Write(mapId);
474 _worldServer.Connection.SendMessage(requestMsg, NetDeliveryMethod.ReliableOrdered, 0);
477 private void ConnectToWorldServer ()
479 Log(
"Starting network...");
481 try { _netPeer.Start(); }
484 if (_netPeer.Status != NetPeerStatus.Running)
486 Log(
"Failed to start network.");
491 Log(
"Network started. Port: " + _netPeer.Configuration.Port);
493 IPEndPoint worldEndPoint = null;
495 if (
string.IsNullOrEmpty(WorldID))
496 WorldID =
"localhost";
498 IPAddress ipAddress = null;
499 IPAddress.TryParse(WorldID, out ipAddress);
501 Log(
"Searching for world server: " + WorldID);
503 if (WorldID ==
"localhost")
504 worldEndPoint =
new IPEndPoint(IPAddress.Parse(
"127.0.0.1"), 34443);
505 else if (ipAddress != null)
506 worldEndPoint =
new IPEndPoint(ipAddress, 34443);
507 else worldEndPoint = WorldNetUtils.GetWorldEndPoint(WorldID);
509 if (worldEndPoint == null)
511 Log(
"Failed to find world server.");
516 Log(
"World server found.");
518 var localHail = _netPeer.CreateMessage();
519 localHail.Write((byte)WorldServerMessageType.RegisterServer);
520 localHail.Write(ServerType.ToString().ToLower());
521 localHail.Write(MapID);
522 localHail.Write(_netPeer.Configuration.MaximumConnections);
523 localHail.Write(_serverPoolIndex);
525 Log(
"Connecting to world server at: " + worldEndPoint);
527 _worldServer =
new WorldServerInfo();
528 _worldServer.Connection = _netPeer.Connect(worldEndPoint, localHail);
529 _worldServer.Connection.Tag = _worldServer;
532 private void NotifyWorldServerReady ()
534 var readyMsg = _netPeer.CreateMessage();
535 readyMsg.Write((byte)WorldServerMessageType.ServerReady);
536 readyMsg.Write(_serverPoolIndex);
539 readyMsg.Write(_requestIndex);
541 _worldServer.Connection.SendMessage(readyMsg, NetDeliveryMethod.ReliableOrdered, 0);
544 private IEnumerator LoadAndSyncMapAsync ()
546 Log(
"Pulling map settings...");
550 float settingsWaitTime = 0f;
552 while (_mapSettings == null)
554 settingsWaitTime += Time.deltaTime;
556 if (settingsWaitTime >= 10f)
558 Abort(
"Failed to load map settings.");
562 yield
return new WaitForSeconds(.1f);
565 Log(
"Map settings received.");
566 Log(
"Loading map: " + MapID);
568 yield
return Application.LoadLevelAsync(MapID);
579 _nextClientPeerIndex = 1;
581 NotifyWorldServerReady();
592 public void PullWorldData (
string pageName, WorldDataReceivedHandler callback,
bool useCache =
false)
598 if (cachedDataPage != null && callback != null)
600 callback(cachedDataPage);
605 Log(
"Pulling world data page: " + pageName);
607 List<WorldDataReceivedHandler> callbacksByPage = null;
609 if (!_worldDataRequests.TryGetValue(pageName, out callbacksByPage))
611 callbacksByPage =
new List<WorldDataReceivedHandler>();
612 _worldDataRequests.Add(pageName, callbacksByPage);
615 callbacksByPage.Add(callback);
617 var worldDataPullMsg = _netPeer.CreateMessage();
618 worldDataPullMsg.Write((byte)WorldServerMessageType.PullWorldData);
619 worldDataPullMsg.Write(pageName);
621 _worldServer.Connection.SendMessage(worldDataPullMsg, NetDeliveryMethod.ReliableOrdered, 0);
630 UJeli dataPage = null;
631 _worldDataCache.TryGetValue(pageName, out dataPage);
642 Log(
"Pushing world data page: " + pageName);
644 _worldDataCache[pageName] = pageContents;
646 var pushMsg = _netPeer.CreateMessage();
647 pushMsg.Write((byte)WorldServerMessageType.PushWorldData);
648 pushMsg.Write(pageName);
649 pushMsg.Write(pageContents.ToString());
651 _worldServer.Connection.SendMessage(pushMsg, NetDeliveryMethod.ReliableOrdered, 0);
661 public void PushPlayerData (
string playerid,
string tablePath,
string propertyName,
string propertyValue)
666 TablePath = tablePath,
667 PropertyName = propertyName,
668 PropertyValue = propertyValue
678 var pushMsg = _netPeer.CreateMessage();
679 pushMsg.Write((byte)WorldServerMessageType.PushPlayerData);
680 pushMsg.Write(changes.Length);
682 foreach (var change
in changes)
684 pushMsg.Write(change.PlayerID);
685 pushMsg.Write(change.TablePath);
686 pushMsg.Write(change.PropertyName);
687 pushMsg.Write(change.PropertyValue);
690 _worldServer.Connection.SendMessage(pushMsg, NetDeliveryMethod.ReliableOrdered, 0);
695 ClientInfo client = null;
697 if (!_clientsById.TryGetValue(profile.PlayerID, out client))
700 var opMsg = _netPeer.CreateMessage();
701 opMsg.Write((byte)ClientMessageType.Custom);
702 opMsg.Write((byte)CustomClientMessageType.PlayerProfileOperation);
703 opMsg.Write(client.PeerIndex);
704 opMsg.Write(op.Data);
706 if (sendToAllClients)
708 var recipients = _clientsByPeerIndex.Values.Select(c => c.Connection).ToList();
710 if (recipients.Count > 0)
711 _netPeer.SendMessage(opMsg, recipients, NetDeliveryMethod.ReliableOrdered, 0);
713 else client.Connection.SendMessage(opMsg, NetDeliveryMethod.ReliableOrdered, 0);
716 private void AddClient (ClientInfo client)
718 _clientsByPeerIndex[client.PeerIndex] = client;
719 _clientsById[client.PlayerID] = client;
721 Log(
string.Format(
"Client added. Index: {0}, ID: {1}", client.PeerIndex, client.PlayerID));
724 private void RemoveClient (ClientInfo client)
726 _clientsByPeerIndex.Remove(client.PeerIndex);
727 _clientsById.Remove(client.PlayerID);
729 Log(
"Client " + client.PeerIndex +
" removed.");
737 if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Q))
740 UnityEditor.EditorApplication.isPlaying =
false;
745 private void UpdateNetwork ()
747 if (_netPeer.Status != NetPeerStatus.Running)
754 foreach (var client
in _clientsByPeerIndex.Values)
758 var timelineNetMsg = _netPeer.CreateMessage();
759 timelineNetMsg.Write((byte)timelineMsg.MessageType);
760 timelineNetMsg.Write(timelineMsg.Data);
762 client.Connection.SendMessage(
764 _timelineToNetDeliveryModes[timelineMsg.DeliveryMode],
770 var messages =
new List<NetIncomingMessage>();
771 _netPeer.ReadMessages(messages);
773 foreach (var msg
in messages)
779 ProcessNetMessage(msg);
784 Log(
string.Format(
"Network message exception. Message Type: {0}, Exception Type: {1}",
785 msg.MessageType, e.GetType()));
790 _netPeer.Recycle(messages);
793 private void ProcessNetMessage (NetIncomingMessage msg)
795 if (msg.MessageType == NetIncomingMessageType.ConnectionApproval)
797 Log(
"Client connecting from: " + msg.SenderConnection.RemoteEndPoint);
799 ClientInfo client =
new ClientInfo()
801 Connection = msg.SenderConnection,
802 PlayerID = msg.ReadString(),
803 PeerIndex = _nextClientPeerIndex
806 if (_clientsById.ContainsKey(client.PlayerID))
808 Log(
"Duplicate client kicked. ID: " + client.PlayerID);
809 msg.SenderConnection.Deny(
"error.duplicate_player");
815 var localHail = _netPeer.CreateMessage();
816 localHail.Write(client.PeerIndex);
818 msg.SenderConnection.Tag = client;
819 msg.SenderConnection.Approve(localHail);
821 var worldNotification = _netPeer.CreateMessage();
822 worldNotification.Write((byte)WorldServerMessageType.PlayerJoinedServer);
823 worldNotification.Write(client.PlayerID);
825 _worldServer.Connection.SendMessage(worldNotification, NetDeliveryMethod.ReliableOrdered, 0);
827 _nextClientPeerIndex++;
829 else if (msg.MessageType == NetIncomingMessageType.StatusChanged)
831 var newStatus = (NetConnectionStatus)msg.ReadByte();
833 if (newStatus == NetConnectionStatus.Connected)
835 var remoteHail = msg.SenderConnection.RemoteHailMessage;
837 var serverMessageType = (ServerMessageType)remoteHail.ReadByte();
839 if (serverMessageType == ServerMessageType.ServerInitialization)
841 if (msg.SenderConnection.Tag == _worldServer && _worldServer != null)
843 _isConnectedToWorldServer =
true;
845 if (ConnectedToWorldServer != null)
848 Log(
"Connected to world server.");
850 StartCoroutine(LoadAndSyncMapAsync());
854 else if (newStatus == NetConnectionStatus.Disconnected)
856 if (msg.SenderConnection.Tag == _worldServer && _worldServer != null)
858 if (_isConnectedToWorldServer)
859 Abort(
"Disconnected from world server.");
860 else Abort(
"Failed to connect to world server.");
862 else if (msg.SenderConnection.Tag is ClientInfo)
864 var client = (ClientInfo)msg.SenderConnection.Tag;
866 Sync.UnregisterClient(client.PeerIndex);
868 RemoveClient(client);
870 var worldNotification = _netPeer.CreateMessage();
871 worldNotification.Write((byte)WorldServerMessageType.PlayerLeftServer);
872 worldNotification.Write(client.PlayerID);
874 _worldServer.Connection.SendMessage(worldNotification, NetDeliveryMethod.ReliableOrdered, 0);
876 if (PlayerLeftServer != null)
879 var otherClientConnections =
new List<NetConnection>();
881 foreach (var otherClient
in _clientsByPeerIndex.Values)
883 if (otherClient != client)
884 otherClientConnections.Add(otherClient.Connection);
887 if (otherClientConnections.Count > 0)
889 var clientNotification = _netPeer.CreateMessage();
890 clientNotification.Write((byte)ClientMessageType.Custom);
891 clientNotification.Write((byte)CustomClientMessageType.PlayerLeftServer);
892 clientNotification.Write(client.PeerIndex);
894 _netPeer.SendMessage(clientNotification, otherClientConnections, NetDeliveryMethod.ReliableOrdered, 0);
897 if (ServerType == GameServerType.Minigame && _clientsById.Count == 0)
900 UnityEditor.EditorApplication.isPlaying =
false;
908 else if (msg.MessageType == NetIncomingMessageType.Data)
910 byte messageTypeByte = msg.ReadByte();
912 if (messageTypeByte >= (byte)TimelineMessageType.SyncerMessageBase)
914 var timelineMessageType = (TimelineMessageType)messageTypeByte;
916 int peerIndex = ((ClientInfo)msg.SenderConnection.Tag).PeerIndex;
918 var timelineMsg =
new TimelineMessage(
920 msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes),
921 _netToTimelineDeliveryModes[msg.DeliveryMethod]);
927 var serverMessageType = (ServerMessageType)messageTypeByte;
929 if (serverMessageType == ServerMessageType.PlayerData)
931 string playerId = msg.ReadString();
933 var client = _clientsById[playerId];
934 client.PlayerProfile = playerProfile;
936 Log(
string.Format(
"Received player profile. ID: {0}, Nickname: {1}",
937 playerId, playerProfile.Basic.Nickname));
939 if (PlayerJoinedServer != null)
942 var otherClientConnections =
new List<NetConnection>();
944 foreach (var otherClient
in _clientsByPeerIndex.Values)
946 if (otherClient != client)
947 otherClientConnections.Add(otherClient.Connection);
950 if (otherClientConnections.Count > 0)
952 var clientNotification = _netPeer.CreateMessage();
953 clientNotification.Write((byte)ClientMessageType.Custom);
954 clientNotification.Write((byte)CustomClientMessageType.PlayerJoinedServer);
955 clientNotification.Write(client.PeerIndex);
956 clientNotification.Write(client.PlayerID);
958 clientNotification.Write(playerProfile.ToString());
960 _netPeer.SendMessage(clientNotification, otherClientConnections, NetDeliveryMethod.ReliableOrdered, 0);
963 else if (serverMessageType == ServerMessageType.PlayerJoinedWorldServer)
967 else if (serverMessageType == ServerMessageType.PlayerLeftWorldServer)
971 else if (serverMessageType == ServerMessageType.ServerSpawned)
973 GameServerType serverType = (GameServerType)Enum.Parse(typeof(GameServerType), msg.ReadString(),
true);
974 string mapId = msg.ReadString();
975 IPEndPoint endPoint = msg.ReadIPEndPoint();
977 if (ServerSpawned != null)
980 else if (serverMessageType == ServerMessageType.ServerSpawnFailed)
982 GameServerType serverType = (GameServerType)msg.ReadByte();
983 string mapId = msg.ReadString();
985 if (ServerSpawnFailed != null)
988 else if (serverMessageType == ServerMessageType.WorldData)
990 string pageName = msg.ReadString();
991 var worldDataPage =
UJeli.Parse(msg.ReadString());
992 worldDataPage.Name = pageName;
994 Log(
"Received world data page: " + pageName);
996 _worldDataCache[pageName] = worldDataPage;
998 List<WorldDataReceivedHandler> callbacksByPage = null;
1000 if (_worldDataRequests.TryGetValue(pageName, out callbacksByPage))
1002 while (callbacksByPage.Count > 0)
1004 callbacksByPage[0](worldDataPage);
1005 callbacksByPage.RemoveAt(0);
1009 else if (serverMessageType == ServerMessageType.Custom)
1011 var customServerMessageType = (CustomServerMessageType)msg.ReadByte();
1013 if (customServerMessageType == CustomServerMessageType.SyncWithClient)
1015 var client = (ClientInfo)msg.SenderConnection.Tag;
1017 var syncJob =
new ClientSyncJob()
1022 client.SyncJob = syncJob;
1024 Sync.RegisterClient(client.PeerIndex, msg.SenderConnection.AverageRoundtripTime);
1026 StartCoroutine(SyncWithClientAsync(syncJob));
1028 else if (customServerMessageType == CustomServerMessageType.PlayerProfileOperation)
1030 int peerIndex = msg.ReadInt32();
1034 if (profile == null)
1038 profileOperation.Write(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes));
1040 profile.ProcessIncomingOperation(profileOperation);
1042 else if (customServerMessageType == CustomServerMessageType.SpawnObject)
1044 int requesterIndex = ((ClientInfo)msg.SenderConnection.Tag).PeerIndex;
1045 int requestIndex = msg.ReadInt32();
1047 spawnInfo.ReadFrom(msg);
1049 Sync.ProcessPropagatedUpSpawn(requesterIndex, requestIndex, spawnInfo);
1051 else if (customServerMessageType == CustomServerMessageType.DespawnObject)
1053 int requesterIndex = ((ClientInfo)msg.SenderConnection.Tag).PeerIndex;
1054 int objectIndex = msg.ReadInt32();
1056 Sync.ProcessPropagatedUpDespawn(requesterIndex, objectIndex);
1058 else if (customServerMessageType == CustomServerMessageType.ClientFinishedSyncing)
1060 var client = (ClientInfo)msg.SenderConnection.Tag;
1062 Log(
"Client " + client.PeerIndex +
" synced.");
1064 if (PlayerSynced != null)
1065 PlayerSynced(client.PeerIndex, client.PlayerID, client.PlayerProfile);
1070 else if (msg.MessageType == NetIncomingMessageType.UnconnectedData)
1072 var serverMessageType = (ServerMessageType)msg.ReadByte();
1074 if (serverMessageType == ServerMessageType.Custom)
1076 var customServerMessageType = (CustomServerMessageType)msg.ReadByte();
1078 if (customServerMessageType == CustomServerMessageType.SummaryRequest)
1080 int requestIndex = msg.ReadInt32();
1082 var summaryMsg = _netPeer.CreateMessage();
1084 summaryMsg.Write((byte)ClientMessageType.Custom);
1085 summaryMsg.Write((byte)CustomClientMessageType.ServerSummary);
1086 summaryMsg.Write(requestIndex);
1087 summaryMsg.Write((byte)ServerType);
1088 summaryMsg.Write(MapID);
1090 if (ServerType == GameServerType.Minigame)
1092 else summaryMsg.Write(_netPeer.Configuration.MaximumConnections);
1094 var playerClients = _clientsById.Values.Where(
1096 c.PlayerProfile != null &&
1097 !c.PlayerProfile.Basic.IsMonitor)
1100 summaryMsg.Write(playerClients.Length);
1102 foreach (var client
in playerClients)
1104 summaryMsg.Write(client.PlayerID);
1105 summaryMsg.Write(client.PlayerProfile.Basic.ToString());
1108 _netPeer.SendUnconnectedMessage(summaryMsg, msg.SenderEndPoint);
1114 private IEnumerator SyncWithClientAsync (ClientSyncJob syncJob)
1116 ClientInfo client = syncJob.Client;
1118 Log(
"Syncing client " + client.PeerIndex +
"...");
1120 if (_clientsByPeerIndex.Count > 1)
1122 var existingPlayersMsg = _netPeer.CreateMessage();
1123 existingPlayersMsg.Write((byte)ClientMessageType.Custom);
1124 existingPlayersMsg.Write((byte)CustomClientMessageType.ExistingPlayers);
1125 existingPlayersMsg.Write(_clientsByPeerIndex.Count - 1);
1127 foreach (var otherClient
in _clientsByPeerIndex.Values)
1129 if (otherClient == client)
1132 existingPlayersMsg.Write(otherClient.PeerIndex);
1133 existingPlayersMsg.Write(otherClient.PlayerID);
1134 existingPlayersMsg.Write(otherClient.PlayerProfile.ToString());
1137 client.Connection.SendMessage(existingPlayersMsg, NetDeliveryMethod.ReliableOrdered, 0);
1144 while (syncJob.ExistingObjectIndices.Count > 0)
1146 if (client.Connection.Status != NetConnectionStatus.Connected)
1149 int objectIndex = syncJob.ExistingObjectIndices[0];
1150 syncJob.ExistingObjectIndices.RemoveAt(0);
1154 if (existingObject == null)
1159 var spawnMsg = _netPeer.CreateMessage();
1160 spawnMsg.Write((byte)ClientMessageType.Custom);
1161 spawnMsg.Write((byte)CustomClientMessageType.SpawnExistingObject);
1162 spawnMsg.Write(spawnInfo != null);
1164 if (spawnInfo != null)
1165 spawnInfo.WriteTo(spawnMsg);
1167 spawnMsg.Write(objectIndex);
1169 client.Connection.SendMessage(spawnMsg, NetDeliveryMethod.ReliableOrdered, 0);
1174 client.SyncJob = null;
1176 if (client.Connection.Status == NetConnectionStatus.Connected)
1178 var syncFinishedMsg = _netPeer.CreateMessage();
1179 syncFinishedMsg.Write((byte)ClientMessageType.Custom);
1180 syncFinishedMsg.Write((byte)CustomClientMessageType.SyncedToServer);
1182 client.Connection.SendMessage(syncFinishedMsg, NetDeliveryMethod.ReliableOrdered, 0);
1197 UnityEditor.EditorApplication.isPlaying =
false;
1203 private void OnMapSettingsReceived (
UJeli mapSettings)
1205 _mapSettings = mapSettings;
1207 if (_mapSettings.HasChild(
"SpawnDetailsTemplates"))
1209 var spawnDetailsTemplatesJeli = _mapSettings[
"SpawnDetailsTemplates"];
1211 foreach (var templateJeli
in spawnDetailsTemplatesJeli.Children)
1213 Sync.SetSpawnDetailsTemplate(templateJeli.Name, templateJeli);
1218 void OnPropagateSpawnDown (
int requesterIndex,
int requestIndex,
int objectIndex,
SpawnInfo spawnInfo)
1220 var responseMsg = _netPeer.CreateMessage();
1221 responseMsg.Write((byte)ClientMessageType.Custom);
1222 responseMsg.Write((byte)CustomClientMessageType.ObjectSpawnRequestResponse);
1223 responseMsg.Write(requestIndex);
1224 responseMsg.Write(objectIndex);
1226 var spawnMsg = _netPeer.CreateMessage();
1227 spawnMsg.Write((byte)ClientMessageType.Custom);
1228 spawnMsg.Write((byte)CustomClientMessageType.SpawnObject);
1229 spawnInfo.WriteTo(spawnMsg);
1230 spawnMsg.Write(objectIndex);
1232 NetConnection requesterConnection = null;
1234 if (requesterIndex > 0)
1235 requesterConnection = _clientsByPeerIndex[requesterIndex].Connection;
1237 List<NetConnection> otherConnections =
new List<NetConnection>();
1239 foreach (var client
in _clientsByPeerIndex.Values)
1241 if (client.Connection != requesterConnection)
1242 otherConnections.Add(client.Connection);
1245 if (requesterConnection != null)
1246 _netPeer.SendMessage(responseMsg, requesterConnection, NetDeliveryMethod.ReliableOrdered, 0);
1248 if (otherConnections.Count > 0)
1249 _netPeer.SendMessage(spawnMsg, otherConnections, NetDeliveryMethod.ReliableOrdered, 0);
1252 private void OnPropagateDespawnDown (
int requesterIndex,
int objectIndex)
1254 StartCoroutine(PropagateDespawnDownAsync(requesterIndex, objectIndex));
1257 private IEnumerator PropagateDespawnDownAsync (
int requesterIndex,
int objectIndex)
1262 var despawnMsg = _netPeer.CreateMessage();
1263 despawnMsg.Write((byte)ClientMessageType.Custom);
1264 despawnMsg.Write((byte)CustomClientMessageType.DespawnObject);
1265 despawnMsg.Write(objectIndex);
1267 NetConnection requesterConnection = null;
1269 if (requesterIndex > 0)
1270 requesterConnection = _clientsByPeerIndex[requesterIndex].Connection;
1272 List<NetConnection> otherConnections =
new List<NetConnection>();
1274 foreach (var client
in _clientsByPeerIndex.Values)
1276 if (client.Connection != requesterConnection)
1277 otherConnections.Add(client.Connection);
1280 if (otherConnections.Count > 0)
1281 _netPeer.SendMessage(despawnMsg, otherConnections, NetDeliveryMethod.ReliableOrdered, 0);
1289 _isConnectedToWorldServer =
false;
1291 if (_netPeer.Status != NetPeerStatus.NotRunning && _netPeer.Status != NetPeerStatus.ShutdownRequested)
1293 Log(
"Stopping network...");
1295 _netPeer.Shutdown(
"");
1297 Log(
"Network stopped.");
1305 Log(
"Server shutting down...");
1307 _logWriter.Dispose();
static int[] ObjectIndices
Gets an array containing the object indices of every synchronized object.
UJeli GetCachedWorldData(string pageName)
Returns a cached copy of a previously fetched world data page.
static GameServer Instance
Gets the sole instance of this game server.
void PullWorldData(string pageName, WorldDataReceivedHandler callback, bool useCache=false)
Requests a specific page of world data from the world server.
PlayerLeftWorldServerHandler PlayerLeftWorldServer
Event fired when a player leaves the world server.
void PushPlayerData(params PlayerDataChange[] changes)
Pushes an arbitrary number of changes to a player's persistent data.
static Minigame Instance
Gets the sole instance of this minigame.
Describes a single change to a player's persistent data.
ServerSpawnedHandler ServerSpawned
Event fired when a requested server spawn is successful.
void Log(string line)
Write one line to the server log. Log entries are automatically timestamped.
bool IsConnectedToWorldServer
Whether or not this server is connected to the world server.
static SpawnInfo GetSpawnInfo(GameObject go)
Gets the spawn-time information of a given object.
void RequestServerSpawn(GameServerType serverType, string mapId)
Sends a server spawn request to the world server.
ServerSpawnFailedHandler ServerSpawnFailed
Event fired when a requested server spawn fails.
int ServerPoolIndex
Gets the index of the server pool which created this server. -1 if this server is not pooled...
static bool IsMapSynced
Gets whether or not the map on this peer is synced to the canonical version on the network...
string GetPlayerID(int peerIndex)
Get the ID of the player with the given peer index.
ServerSyncFinishedHandler ServerSyncFinished
Event fired when server finsihes syncing (all map objects have had their OnSpawn called).
PlayerProfile GetPlayerProfile(int peerIndex)
Get the profile of the player with the given peer index.
PlayerJoinedWorldServerHandler PlayerJoinedWorldServer
Event fired when a player joins the world server.
void PushWorldData(string pageName, UJeli pageContents)
Pushes a world data page to the world server.
string[] PlayerNicknames
Gets an array containing the nicknames of all connected players.
void PushPlayerData(string playerid, string tablePath, string propertyName, string propertyValue)
Pushes a single change to a player's persistent data.
static TimelineSynchronizer TimelineSynchronizer
Gets the Janus timeline synchronizer if this is the server.
void DisconnectFromWorldServer()
Disconnect from the world server.
int[] PlayerPeerIndices
Gets an array containing the peer indices of all connected players.
PlayerJoinedServerHandler PlayerJoinedServer
Event fired when a player joins this server.
int MaxPlayers
Gets the maximum player capacity of this minigame.
PlayerProfile[] PlayerProfiles
Gets an array containing the profiles of all connected players.
bool IsPooled
Gets whether or not this server was created by a server pool.
UJeli MapSettings
Settings for this map, pulled from the world server.
PlayerProfile GetPlayerProfile(string playerId)
Get the profile of the player with the given ID.
PlayerSyncedHandler PlayerSynced
Event fired when a player is fully synchronized with this server.
Stores data related to the player profile. Loads from a World Data file and provides runtime operatio...
A class for serializing game data into a stream for propagation between objects and peers...
Class for general minigame management.
static GameObject GetObject(int objectIndex)
Get an object by its object index.
PlayerLeftServerHandler PlayerLeftServer
Event fired when a player leaves this server.
Unity version of Jeli markup class.
int GetPlayerPeerIndex(string playerId)
Get the peer index of the player with the given ID.
int NumPlayers
Gets the number of connected players.
ConnectedToWorldServerHandler ConnectedToWorldServer
Event fired when this server connects to the world server.
This class server two main functions: 1) As a MonoBehaviour, it allows for network synchronization of...
The spawn-time information of a spawned object.
string[] PlayerIDs
Gets an array containing the IDs of all connected players.
void Abort(string error)
Abort the server process with a given error.