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.