4 using System.Collections.Generic;
 
   18     private class WorldServerInfo
 
   20         public NetConnection Connection;
 
   23     private class ServerInfo
 
   25         public NetConnection Connection;
 
   30     private class ClientInfo
 
   32         public int PeerIndex = -1;
 
   33         public string PlayerID;
 
   37     public delegate 
void ConnectedToWorldServerHandler ();
 
   38     public delegate 
void ConnectionToWorldServerFailedHandler ();
 
   39     public delegate 
void DisconnectedFromWorldServerHandler ();
 
   40     public delegate 
void ConnectedToServerHandler ();
 
   41     public delegate 
void ConnectionToServerFailedHandler ();
 
   42     public delegate 
void PreDisconnectFromServerHandler ();
 
   43     public delegate 
void DisconnectedFromServerHandler ();
 
   44     public delegate 
void ServersFoundHandler (GameServerType serverType, 
string mapId, IPEndPoint[] results);
 
   45     public delegate 
void ServerSearchFailedHandler (GameServerType serverType, 
string mapId);
 
   46     public delegate 
void ServerSummaryReceivedHandler (
GameServerSummary serverSummary);
 
   47     public delegate 
void ServerSpawnedHandler (GameServerType serverType, 
string mapId, IPEndPoint endPoint);
 
   48     public delegate 
void ServerSpawnFailedHandler (GameServerType serverType, 
string mapId);
 
   49     public delegate 
void PlayerFoundHandler (
string playerId, GameServerType serverType, 
string mapId, IPEndPoint serverEndPoint);
 
   50     public delegate 
void PlayerNotFoundHandler (
string playerId);
 
   51     public delegate 
void PlayerJoinedServerHandler (
int peerIndex, 
PlayerProfile playerProfile);
 
   52     public delegate 
void PlayerLeftServerHandler (
int peerIndex, 
PlayerProfile playerProfile);
 
   53     public delegate 
void PlayerJoinedWorldServerHandler (
string playerId, 
string playerNickname);
 
   54     public delegate 
void PlayerLeftWorldServerHandler (
string playerId, 
string playerNickname);
 
   55     public delegate 
void WorldDataReceivedHandler (
UJeli worldDataPage);
 
   57 #pragma warning disable 0067 
  130 #pragma warning restore 0067 
  137         get { 
return _instance; }
 
  145         get { 
return _configBase; }
 
  153         get { 
return _logsBase; }
 
  161         get { 
return _screenshotsBase; }
 
  169         get { 
return _studyLogsBase; }
 
  177         get { 
return _config; }
 
  185         get { 
return _isConnectedToWorldServer; }
 
  193         get { 
return _isConnectedToServer; }
 
  204                 return _server.ServerType;
 
  205             else return GameServerType.None;
 
  217                 return _server.MapID;
 
  227         get { 
return _mapSettings; }
 
  235         get { 
return _clientsById.Count; }
 
  243         get { 
return _clientsById.Keys.ToArray(); }
 
  251         get { 
return _clientsByPeerIndex.Keys.ToArray(); }
 
  262                 from client in _clientsById.Values
 
  263                 where client.PlayerProfile != null
 
  264                 select client.PlayerProfile).ToArray();
 
  276                 from client in _clientsById.Values
 
  277                 where client.PlayerProfile != null
 
  278                 select client.PlayerProfile.Basic.Nickname).ToArray();
 
  287         get { 
return (_localClient != null) ? _localClient.PlayerID : _config.Network.PlayerID; }
 
  295         get { 
return (_localClient != null) ? _localClient.PlayerProfile : null; }
 
  300     static Dictionary<NetDeliveryMethod, DeliveryMode> _netToTimelineDeliveryModes
 
  301         = 
new Dictionary<NetDeliveryMethod, DeliveryMode>()
 
  303         { NetDeliveryMethod.ReliableOrdered, DeliveryMode.ReliableOrdered },
 
  304         { NetDeliveryMethod.ReliableUnordered, DeliveryMode.ReliableUnordered },
 
  305         { NetDeliveryMethod.Unreliable, DeliveryMode.Unreliable },
 
  308     static Dictionary<DeliveryMode, NetDeliveryMethod> _timelineToNetDeliveryModes
 
  309         = 
new Dictionary<DeliveryMode, NetDeliveryMethod>()
 
  311         { DeliveryMode.ReliableOrdered, NetDeliveryMethod.ReliableOrdered },
 
  312         { DeliveryMode.ReliableUnordered, NetDeliveryMethod.ReliableUnordered },
 
  313         { DeliveryMode.Unreliable, NetDeliveryMethod.Unreliable },
 
  323     string _screenshotsBase;
 
  324     string _studyLogsBase;
 
  326     StreamWriter _logWriter;
 
  328     WorldServerInfo _worldServer;
 
  329     bool _isConnectedToWorldServer;
 
  330     bool _isServerSearchCrucial;
 
  332     bool _isServerConnectionCrucial;
 
  333     bool _isConnectedToServer;
 
  335     ClientInfo _localClient;
 
  336     Dictionary<int, ClientInfo> _clientsByPeerIndex;
 
  337     Dictionary<string, ClientInfo> _clientsById;
 
  338     Dictionary<string, List<WorldDataReceivedHandler>> _worldDataRequests;
 
  339     Dictionary<string, UJeli> _worldDataCache;
 
  340     Dictionary<int, DateTime> _serverSummaryRequestTimes;
 
  341     int _nextServerSummaryRequestIndex;
 
  343     List<ExtendedSpawnInfo> _postSyncObjects = 
new List<ExtendedSpawnInfo>();
 
  348         public int ObjectIndex;
 
  363         DontDestroyOnLoad(
this);
 
  365         _configBase = Path.GetFullPath(Application.dataPath + 
"/../Config/");
 
  366         _logsBase = Path.GetFullPath(Application.dataPath + 
"/../Logs/");
 
  367         _screenshotsBase = Path.GetFullPath(Application.dataPath + 
"/../Screenshots/");
 
  368         _studyLogsBase = Path.GetFullPath(Application.dataPath + 
"/../Study Logs/");
 
  372         var netPeerConfig = 
new NetPeerConfiguration(
"Liberi");
 
  373         netPeerConfig.AcceptIncomingConnections = 
false;
 
  374         netPeerConfig.EnableMessageType(NetIncomingMessageType.UnconnectedData);
 
  376         _netPeer = 
new NetPeer(netPeerConfig);
 
  378         _clientsByPeerIndex = 
new Dictionary<int, ClientInfo>();
 
  379         _clientsById = 
new Dictionary<string, ClientInfo>();
 
  381         _worldDataRequests = 
new Dictionary<string, List<WorldDataReceivedHandler>>();
 
  382         _worldDataCache = 
new Dictionary<string, UJeli>();
 
  384         _serverSummaryRequestTimes = 
new Dictionary<int, DateTime>();
 
  386         TimelineUtils.SetDefaultTimelineFunctions();
 
  387         UTimelineUtils.SetDefautTimelineFunctions();
 
  389         Sync.PropagateSpawnUp += OnPropagateSpawnUp;
 
  390         Sync.PropagateDespawnUp += OnPropagateDespawnUp;
 
  397         Directory.CreateDirectory(_configBase);
 
  398         Directory.CreateDirectory(_logsBase);
 
  399         Directory.CreateDirectory(_screenshotsBase);
 
  401         _logWriter = File.CreateText(Path.GetFullPath(
 
  402             string.Format(
"{0}Liberi_{1:yyyy-MM-dd_HH-mm-ss}.log", _logsBase, DateTime.Now)));
 
  403         _logWriter.AutoFlush = 
true;
 
  405         Log(
"Client initializing...");
 
  409         foreach (var deviceState 
in _config.Devices.States)
 
  411             if (deviceState.Value)
 
  418         var flags = CommandLineUtils.GetCommandLineFlags();
 
  420         if (flags.ContainsKey(
"playerid"))
 
  421             _config.Network.PlayerID = flags[
"playerid"];
 
  425         StartCoroutine(ReportLossyBytes());
 
  431     public void Log (
string line)
 
  433         if (_logWriter != null)
 
  434             _logWriter.WriteLine(
string.Format(
"[{0:HH:mm:ss}] {1}", DateTime.Now, line));
 
  437     private ClientInfo GetClient (
int peerIndex)
 
  439         ClientInfo client = null;
 
  440         _clientsByPeerIndex.TryGetValue(peerIndex, out client);
 
  444     private ClientInfo GetClient (
string playerId)
 
  446         ClientInfo client = null;
 
  447         _clientsById.TryGetValue(playerId, out client);
 
  456         var client = GetClient(playerId);
 
  459             return client.PeerIndex;
 
  468         var client = GetClient(peerIndex);
 
  471             return client.PlayerID;
 
  480         var client = GetClient(playerId);
 
  483             return client.PlayerProfile;
 
  492         var client = GetClient(peerIndex);
 
  495             return client.PlayerProfile;
 
  506         Application.LoadLevel(
"launch");
 
  514         Log(
"Starting network...");
 
  516         try { _netPeer.Start(); }
 
  519         if (_netPeer.Status != NetPeerStatus.Running)
 
  521             Log(
"Failed to start network.");
 
  526         Log(
"Network started.");
 
  530         IPEndPoint worldEndPoint = null;
 
  532         if (
string.IsNullOrEmpty(_config.Network.WorldID))
 
  534             _config.Network.WorldID = 
"localhost";
 
  535             _config.Network.Save();
 
  538         IPAddress ipAddress = null; 
 
  539         IPAddress.TryParse(_config.Network.WorldID, out ipAddress);
 
  541         Log(
"Searching for world server: " + _config.Network.WorldID);
 
  543         if (_config.Network.WorldID == 
"localhost")
 
  544             worldEndPoint = 
new IPEndPoint(IPAddress.Parse(
"127.0.0.1"), 34443);
 
  545         else if (ipAddress != null)
 
  546             worldEndPoint = 
new IPEndPoint(ipAddress, 34443);
 
  547         else worldEndPoint = WorldNetUtils.GetWorldEndPoint(_config.Network.WorldID);
 
  549         if (worldEndPoint == null)
 
  551             Log(
"Failed to find world server.");
 
  556         Log(
"World server found.");
 
  558         var localHail = _netPeer.CreateMessage();
 
  559         localHail.Write((byte)WorldServerMessageType.RegisterClient);
 
  560         localHail.Write(_config.Network.PlayerID);
 
  564         Log(
"Connecting to world server at: " + worldEndPoint);
 
  566         _worldServer = 
new WorldServerInfo();
 
  567         _worldServer.Connection = _netPeer.Connect(worldEndPoint, localHail);
 
  568         _worldServer.Connection.Tag = _worldServer;
 
  577         if (_isConnectedToServer)
 
  581         _isConnectedToWorldServer = 
false;
 
  584         if (_netPeer.Status != NetPeerStatus.NotRunning && _netPeer.Status != NetPeerStatus.ShutdownRequested)
 
  586             Log(
"Stopping network...");
 
  588             _netPeer.Shutdown(error);
 
  590             Log(
"Network stopped.");
 
  607     public void FindServers (GameServerType serverType, 
string mapId, 
bool isCrucial)
 
  609         Log(
"Searching for servers. Map: " + mapId);
 
  613         var searchMsg = _netPeer.CreateMessage();
 
  614         searchMsg.Write((byte)WorldServerMessageType.FindServers);
 
  615         searchMsg.Write(serverType.ToString().ToLower());
 
  616         searchMsg.Write(mapId);
 
  618         _isServerSearchCrucial = isCrucial;
 
  620         _worldServer.Connection.SendMessage(searchMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
  628         var requestMsg = _netPeer.CreateMessage();
 
  629         requestMsg.Write((byte)ServerMessageType.Custom);
 
  630         requestMsg.Write((byte)CustomServerMessageType.SummaryRequest);
 
  631         requestMsg.Write(_nextServerSummaryRequestIndex);
 
  633         _netPeer.SendUnconnectedMessage(requestMsg, serverEndPoint);
 
  635         _serverSummaryRequestTimes[_nextServerSummaryRequestIndex] = DateTime.Now;
 
  636         _nextServerSummaryRequestIndex++;
 
  646         var requestMsg = _netPeer.CreateMessage();
 
  647         requestMsg.Write((byte)WorldServerMessageType.SpawnServer);
 
  648         requestMsg.Write(serverType.ToString().ToLower());
 
  649         requestMsg.Write(mapId);
 
  651         _worldServer.Connection.SendMessage(requestMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
  663     public void ConnectToServer (IPEndPoint serverEndPoint, GameServerType serverType, 
string mapId, 
bool isCrucial)
 
  665         if (_isConnectedToServer)
 
  668         var localHail = _netPeer.CreateMessage();
 
  669         localHail.Write(_localClient.PlayerProfile.PlayerID);
 
  671         _server = 
new ServerInfo()
 
  677         _isServerConnectionCrucial = isCrucial;
 
  679         Log(
"Connecting to server at: " + serverEndPoint);
 
  683         _server.Connection = _netPeer.Connect(serverEndPoint, localHail);
 
  684         _server.Connection.Tag = _server;
 
  695         if (PreDisconnectFromServer != null)
 
  698         Log(
"Unsyncing from server...");
 
  702         Log(
"Unsynced from server.");
 
  704         if (_server.Connection.Status == NetConnectionStatus.Connected)
 
  706             Log(
"Disconnecting from server...");
 
  708             _server.Connection.Disconnect(
"");
 
  710             Log(
"Disconnected from server.");
 
  713         _clientsByPeerIndex.Clear();
 
  714         _clientsById.Clear();
 
  716         _isConnectedToServer = 
false;
 
  726     public void PullWorldData (
string pageName, WorldDataReceivedHandler callback, 
bool useCache = 
false)
 
  732             if (cachedDataPage != null && callback != null)
 
  734                 callback(cachedDataPage);
 
  739         Log(
"Pulling world data page: " + pageName);
 
  741         List<WorldDataReceivedHandler> callbacksByPage = null;
 
  743         if (!_worldDataRequests.TryGetValue(pageName, out callbacksByPage))
 
  745             callbacksByPage = 
new List<WorldDataReceivedHandler>();
 
  746             _worldDataRequests.Add(pageName, callbacksByPage);
 
  749         callbacksByPage.Add(callback);
 
  751         var worldDataPullMsg = _netPeer.CreateMessage();
 
  752         worldDataPullMsg.Write((byte)WorldServerMessageType.PullWorldData);
 
  753         worldDataPullMsg.Write(pageName);
 
  755         _worldServer.Connection.SendMessage(worldDataPullMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
  760         if (actionType == ControlsAction.Back)
 
  777         if (DisconnectedFromServer != null)
 
  780         if (_isServerConnectionCrucial)
 
  783             UnityEditor.EditorApplication.isPlaying = 
false;
 
  788         else FindServers(GameServerType.Zone, _localClient.PlayerProfile.Location.LastZoneMapID, 
true);
 
  797         UJeli dataPage = null;
 
  798         _worldDataCache.TryGetValue(pageName, out dataPage);
 
  808         bool newFullScreen = !_config.Video.FullScreen;
 
  809         Screen.SetResolution(_config.Video.Width, _config.Video.Height, newFullScreen);
 
  811         _config.Video.FullScreen = newFullScreen;
 
  812         _config.Video.Save();
 
  814         return newFullScreen;
 
  822         int numScreenshotsThisSecond = 1;
 
  823         string screenshotPath = null;
 
  827             screenshotPath = 
string.Format(
"{0}Screenshot_{1:yyyy-MM-dd_HH-mm-ss}_{2:00}.png",
 
  828                 _screenshotsBase, DateTime.Now, numScreenshotsThisSecond);
 
  830             numScreenshotsThisSecond++;
 
  832         while (File.Exists(screenshotPath));
 
  834         Application.CaptureScreenshot(screenshotPath);
 
  837     private void LoadAndSyncMap (
string mapId, 
int peerIndex)
 
  839         StopCoroutine(
"LoadAndSyncMapAsync");
 
  840         StartCoroutine(LoadAndSyncMapAsync(mapId, peerIndex));
 
  843     private IEnumerator LoadAndSyncMapAsync (
string mapId, 
int peerIndex)
 
  845         Log(
"Pulling map settings...");
 
  849         float settingsWaitTime = 0f;
 
  851         while (_mapSettings == null)
 
  853             settingsWaitTime += .1f;
 
  855             if (settingsWaitTime >= 10f)
 
  861             yield 
return new WaitForSeconds(.1f);
 
  864         Log(
"Map settings received.");
 
  865         Log(
"Loading map: " + mapId);
 
  869         yield 
return Application.LoadLevelAsync(mapId);
 
  873         if (_server == null || _server.Connection.Status != NetConnectionStatus.Connected)
 
  878         Log(
"Syncing to server...");
 
  880         Sync.BeginSyncClient(peerIndex);
 
  882         var syncMessage = _netPeer.CreateMessage();
 
  883         syncMessage.Write((byte)ServerMessageType.Custom);
 
  884         syncMessage.Write((byte)CustomServerMessageType.SyncWithClient);
 
  886         _server.Connection.SendMessage(syncMessage, NetDeliveryMethod.ReliableOrdered, 0);
 
  890             if (_server == null || _server.Connection.Status != NetConnectionStatus.Connected)
 
  896         foreach (var obj 
in _postSyncObjects)
 
  900                 try {
Sync.ProcessPropagatedDownSpawn(obj.Info, obj.ObjectIndex, obj.IsNew);}
 
  902                 catch (ArgumentException e)
 
  904                     Debug.LogWarning(e.Message + 
"\n" + obj.Info.Prefab.name + 
", " + obj.ObjectIndex +
 
  905                     " was skipped when spawning postSyncObjects due to it haveing already been spawned in an edge case.");
 
  910         _postSyncObjects.Clear();
 
  912         Log(
"Synced to server.");
 
  919     internal void SendPlayerProfileOperation (
GameMessage op)
 
  921         var opMsg = _netPeer.CreateMessage();
 
  922         opMsg.Write((byte)CustomServerMessageType.PlayerProfileOperation);
 
  923         opMsg.Write(Game.LocalPeerIndex);
 
  924         opMsg.Write(op.Data);
 
  926         _worldServer.Connection.SendMessage(opMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
  935         if (Input.GetKey(KeyCode.LeftControl) && Input.GetKeyDown(KeyCode.Q))
 
  938             UnityEditor.EditorApplication.isPlaying = 
false;
 
  948     private void UpdateScreenshot ()
 
  950         if (Input.GetKeyDown(_config.Controls.TakeScreenshot))
 
  956     IEnumerator ReportLossyBytes ()
 
  962             yield 
return new WaitForSeconds(1.0f);
 
  965             DebugManager.Log((
float)_lossyBytes/t + 
" bytes/sec of lossy data.");
 
  969     private void UpdateNetwork ()
 
  971         if (_netPeer.Status != NetPeerStatus.Running)
 
  980                 var timelineNetMsg = _netPeer.CreateMessage();
 
  981                 timelineNetMsg.Write((byte)timelineMsg.MessageType);
 
  982                 timelineNetMsg.Write(timelineMsg.Data);
 
  984                 _server.Connection.SendMessage(
 
  986                     _timelineToNetDeliveryModes[timelineMsg.DeliveryMode],
 
  991         var messages = 
new List<NetIncomingMessage>();
 
  992         _netPeer.ReadMessages(messages);
 
  994         foreach (var msg 
in messages)
 
 1000                 if (msg.DeliveryMethod == NetDeliveryMethod.Unreliable)
 
 1001                     _lossyBytes += msg.LengthBytes;
 
 1003                 ProcessNetMessage(msg);
 
 1008                 Log(
string.Format(
"Network message exception. Message Type: {0}, Exception Type: {1}",
 
 1009                     msg.MessageType, e.GetType()));
 
 1014         _netPeer.Recycle(messages);
 
 1017     private void ProcessNetMessage (NetIncomingMessage msg)
 
 1019         if (msg.MessageType == NetIncomingMessageType.StatusChanged)
 
 1021             var newStatus = (NetConnectionStatus)msg.ReadByte();
 
 1023             if (newStatus == NetConnectionStatus.Connected)
 
 1025                 if (msg.SenderConnection.Tag == _server && _server != null)
 
 1027                     var remoteHail = msg.SenderConnection.RemoteHailMessage;
 
 1029                     _localClient.PeerIndex = remoteHail.ReadInt32();
 
 1031                     _clientsByPeerIndex.Add(_localClient.PeerIndex, _localClient);
 
 1032                     _clientsById.Add(_localClient.PlayerID, _localClient);
 
 1034                     _isConnectedToServer = 
true;
 
 1036                     Log(
string.Format(
"Connected to server. Type: {0}, Map: {1}",
 
 1037                         _server.ServerType, _server.MapID));
 
 1039                     Log(
"Peer index assigned: " + _localClient.PeerIndex);
 
 1041                     if (ConnectedToServer != null)
 
 1044                     LoadAndSyncMap(_server.MapID, _localClient.PeerIndex);
 
 1047             else if (newStatus == NetConnectionStatus.Disconnected)
 
 1049                 string reason = msg.ReadString();
 
 1051                 if (msg.SenderConnection.Tag == _worldServer && _worldServer != null)
 
 1053                     if (_isConnectedToWorldServer)
 
 1055                         Log(
"Disconnected from world server.");
 
 1058                         if (DisconnectedFromWorldServer != null)
 
 1063                         Log(
"Failed to connect to world server.");
 
 1065                         if (reason == 
"error.duplicate_player" ||
 
 1066                             reason == 
"error.daily_limit_reached")
 
 1072                         if (ConnectionToWorldServerFailed != null)
 
 1076                 else if (msg.SenderConnection.Tag == _server && _server != null)
 
 1078                     if (_isConnectedToServer)
 
 1080                         Log(
"Disconnected from server.");
 
 1083                         if (DisconnectedFromServer != null)
 
 1086                         if (_isServerConnectionCrucial)
 
 1090                         else FindServers(GameServerType.Zone, _localClient.PlayerProfile.Location.LastZoneMapID, 
true);
 
 1094                         Log(
"Failed to connect to server.");
 
 1097                         if (ConnectionToServerFailed != null)
 
 1100                         if (_isServerConnectionCrucial)
 
 1102                             if (reason == 
"error.duplicate_player")
 
 1106                         else FindServers(GameServerType.Zone, _localClient.PlayerProfile.Location.LastZoneMapID, 
true);
 
 1111         else if (msg.MessageType == NetIncomingMessageType.Data)
 
 1113             byte messageTypeByte = msg.ReadByte();
 
 1115             if (messageTypeByte >= (byte)TimelineMessageType.ManagerMessageBase)
 
 1117                 if (!_isConnectedToServer)
 
 1120                 var timelineMessageType = (TimelineMessageType)messageTypeByte;
 
 1121                 var timelineMsg = 
new TimelineMessage(
 
 1122                     timelineMessageType,
 
 1123                     msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes),
 
 1124                     _netToTimelineDeliveryModes[msg.DeliveryMethod]);
 
 1130                 var clientMessageType = (ClientMessageType)messageTypeByte;
 
 1132                 if (clientMessageType == ClientMessageType.ClientInitialization)
 
 1134                     _isConnectedToWorldServer = 
true;
 
 1136                     Log(
"Connected to world server.");
 
 1138                     UJeli profileJeli = 
UJeli.Parse(msg.ReadString());
 
 1139                     var localPlayerProfile = 
new PlayerProfile(_config.Network.PlayerID, profileJeli);
 
 1141                     _localClient = 
new ClientInfo()
 
 1143                         PlayerID = _config.Network.PlayerID,
 
 1147                     Log(
string.Format(
"Received player profile. ID: {0}, Nickname: {1}",
 
 1148                         _localClient.PlayerID, localPlayerProfile.Basic.Nickname));
 
 1150                     if (ConnectedToWorldServer != null)
 
 1153                     if (!
string.IsNullOrEmpty(_localClient.PlayerProfile.Location.LastZoneMapID))
 
 1154                         FindServers(GameServerType.Zone, _localClient.PlayerProfile.Location.LastZoneMapID, 
true);
 
 1156                 else if (clientMessageType == ClientMessageType.ServersFound)
 
 1158                     GameServerType serverType = (GameServerType)Enum.Parse(typeof(GameServerType), msg.ReadString(), 
true);
 
 1159                     string mapId = msg.ReadString();
 
 1160                     int numResults = msg.ReadInt32();
 
 1162                     Log(
string.Format(
"{0} server(s) found.", numResults));
 
 1164                     if (numResults != 0)
 
 1166                         IPEndPoint[] results = 
new IPEndPoint[numResults];
 
 1168                         for (
int i = 0; i < numResults; i++)
 
 1170                             results[i] = msg.ReadIPEndPoint();
 
 1173                         if (_server != null)
 
 1175                             if (_server.ServerType == GameServerType.Minigame && serverType == GameServerType.Zone)
 
 1186                             if (serverType == GameServerType.Zone)
 
 1195                         if (ServersFound != null)
 
 1200                         if (ServerSearchFailed != null)
 
 1203                         if (_isServerSearchCrucial)
 
 1207                 else if (clientMessageType == ClientMessageType.ServerSpawned)
 
 1209                     GameServerType serverType = (GameServerType)Enum.Parse(typeof(GameServerType), msg.ReadString(), 
true);
 
 1210                     string mapId = msg.ReadString();
 
 1211                     IPEndPoint endPoint = msg.ReadIPEndPoint();
 
 1213                     if (ServerSpawned != null)
 
 1216                 else if (clientMessageType == ClientMessageType.ServerSpawnFailed)
 
 1218                     GameServerType serverType = (GameServerType)msg.ReadByte();
 
 1219                     string mapId = msg.ReadString();
 
 1221                     if (ServerSpawnFailed != null)
 
 1224                 else if (clientMessageType == ClientMessageType.PlayerFound)
 
 1226                     string playerId = msg.ReadString();
 
 1227                     bool found = msg.ReadBoolean();
 
 1231                         GameServerType serverType = (GameServerType)msg.ReadByte();
 
 1232                         string mapId = msg.ReadString();
 
 1233                         IPEndPoint serverEndPoint = msg.ReadIPEndPoint();
 
 1235                         if (PlayerFound != null)
 
 1236                             PlayerFound(playerId, serverType, mapId, serverEndPoint);
 
 1240                         if (PlayerNotFound != null)
 
 1244                 else if (clientMessageType == ClientMessageType.Custom)
 
 1246                     CustomClientMessageType customClientMessageType = (CustomClientMessageType)msg.ReadByte();
 
 1248                     if (customClientMessageType == CustomClientMessageType.SyncedToServer)
 
 1250                         Sync.FinishSyncClient();
 
 1253                         var syncFinishedMsg = _netPeer.CreateMessage();
 
 1254                         syncFinishedMsg.Write((byte)ServerMessageType.Custom);
 
 1255                         syncFinishedMsg.Write((byte)CustomServerMessageType.ClientFinishedSyncing);
 
 1257                         _server.Connection.SendMessage(syncFinishedMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
 1259                     else if (customClientMessageType == CustomClientMessageType.ExistingPlayers)
 
 1261                         int numExistingPlayers = msg.ReadInt32();
 
 1263                         for (
int i = 0; i < numExistingPlayers; i++)
 
 1265                             int peerIndex = msg.ReadInt32();
 
 1266                             string playerId = msg.ReadString();
 
 1269                             if (_clientsByPeerIndex.ContainsKey(peerIndex))
 
 1272                             var otherClient = 
new ClientInfo()
 
 1274                                 PeerIndex = peerIndex,
 
 1275                                 PlayerID = playerId,
 
 1279                             _clientsByPeerIndex.Add(peerIndex, otherClient);
 
 1280                             _clientsById.Add(playerId, otherClient);
 
 1282                             Log(
string.Format(
"Received existing player info. Index: {0}, ID: {1}, Nickname: {2}",
 
 1283                                 peerIndex, playerId, playerProfile.Basic.Nickname));
 
 1286                     else if (customClientMessageType == CustomClientMessageType.PlayerProfileOperation)
 
 1288                         int peerIndex = msg.ReadInt32();
 
 1292                         if (profile == null)
 
 1296                         profileOperation.Write(msg.ReadBytes(msg.LengthBytes - msg.PositionInBytes));
 
 1298                         profile.ProcessIncomingOperation(profileOperation);
 
 1300                     else if (customClientMessageType == CustomClientMessageType.SpawnObject)
 
 1303                         spawnInfo.ReadFrom(msg);
 
 1304                         int objectIndex = msg.ReadInt32();
 
 1308                             _postSyncObjects.Add(
new ExtendedSpawnInfo(spawnInfo, objectIndex, 
true));
 
 1312                         try {
Sync.ProcessPropagatedDownSpawn(spawnInfo, objectIndex, 
true);}
 
 1314                         catch (ArgumentException e)
 
 1316                             Debug.LogError(e.Message);
 
 1317                             Debug.Log(spawnInfo.Prefab.name + 
", " + objectIndex);
 
 1320                     else if (customClientMessageType == CustomClientMessageType.SpawnExistingObject)
 
 1322                         bool hasSpawnInfo = msg.ReadBoolean();
 
 1328                             spawnInfo.ReadFrom(msg);
 
 1331                         int objectIndex = msg.ReadInt32();
 
 1333                         try {
Sync.ProcessPropagatedDownSpawn(spawnInfo, objectIndex, 
false);}
 
 1335                         catch (ArgumentException e)
 
 1337                             Debug.LogWarning(e.Message);
 
 1338                             Debug.Log(spawnInfo + 
", " + objectIndex);
 
 1341                     else if (customClientMessageType == CustomClientMessageType.ObjectSpawnRequestResponse)
 
 1343                         int requestIndex = msg.ReadInt32();
 
 1344                         int objectIndex = msg.ReadInt32();
 
 1346                         Sync.ProcessSpawnRequestResponse(requestIndex, objectIndex);
 
 1348                     else if (customClientMessageType == CustomClientMessageType.DespawnObject)
 
 1350                         int objectIndex = msg.ReadInt32();
 
 1352                         Sync.ProcessPropagatedDownDespawn(objectIndex);
 
 1354                     else if (customClientMessageType == CustomClientMessageType.PlayerJoinedServer)
 
 1356                         int peerIndex = msg.ReadInt32();
 
 1357                         string playerId = msg.ReadString();
 
 1360                         var client = 
new ClientInfo()
 
 1362                             PeerIndex = peerIndex,
 
 1363                             PlayerID = playerId,
 
 1367                         _clientsByPeerIndex.Add(peerIndex, client);
 
 1368                         _clientsById.Add(playerId, client);
 
 1370                         if (OtherPlayerJoinedServer != null)
 
 1373                     else if (customClientMessageType == CustomClientMessageType.PlayerLeftServer)
 
 1375                         int peerIndex = msg.ReadInt32();
 
 1377                         ClientInfo client = null;
 
 1378                         _clientsByPeerIndex.TryGetValue(peerIndex, out client);
 
 1382                             _clientsByPeerIndex.Remove(peerIndex);
 
 1383                             _clientsById.Remove(client.PlayerID);
 
 1386                         if (OtherPlayerLeftServer != null)
 
 1390                 else if (clientMessageType == ClientMessageType.WorldData)
 
 1392                     string pageName = msg.ReadString();
 
 1393                     var worldDataPage = 
UJeli.Parse(msg.ReadString());
 
 1394                     worldDataPage.Name = pageName;
 
 1396                     Log(
"Received world data page: " + pageName);
 
 1398                     _worldDataCache[pageName] = worldDataPage;
 
 1400                     List<WorldDataReceivedHandler> callbacksByPage = null;
 
 1402                     if (_worldDataRequests.TryGetValue(pageName, out callbacksByPage))
 
 1404                         if (callbacksByPage.Count > 0)
 
 1406                             callbacksByPage[0](worldDataPage);
 
 1407                             callbacksByPage.RemoveAt(0);
 
 1413         else if (msg.MessageType == NetIncomingMessageType.UnconnectedData)
 
 1415             var clientMessageType = (ClientMessageType)msg.ReadByte();
 
 1417             if (clientMessageType == ClientMessageType.Custom)
 
 1419                 var customClientMessageType = (CustomClientMessageType)msg.ReadByte();
 
 1421                 if (customClientMessageType == CustomClientMessageType.ServerSummary)
 
 1425                     int requestIndex = msg.ReadInt32();
 
 1427                     if (!_serverSummaryRequestTimes.ContainsKey(requestIndex))
 
 1430                     serverSummary.EndPoint = msg.SenderEndPoint;
 
 1431                     serverSummary.RTT = (float)DateTime.Now.Subtract(_serverSummaryRequestTimes[requestIndex]).TotalSeconds;
 
 1432                     serverSummary.ServerType = (GameServerType)msg.ReadByte();
 
 1433                     serverSummary.MapID = msg.ReadString();
 
 1434                     serverSummary.MaxPlayers = msg.ReadInt32();
 
 1435                     serverSummary.PlayerProfiles = 
new PlayerProfile[msg.ReadInt32()];
 
 1437                     for (
int i = 0; i < serverSummary.PlayerProfiles.Length; i++)
 
 1440                         serverSummary.PlayerProfiles[i] = 
new PlayerProfile(msg.ReadString(), 
UJeli.Parse(msg.ReadString()));
 
 1443                     if (ServerSummaryReceived != null)
 
 1446                     _serverSummaryRequestTimes.Remove(requestIndex);
 
 1452     private void OnMapSettingsReceived (
UJeli mapSettings)
 
 1454         _mapSettings = mapSettings;
 
 1456         if (_mapSettings.HasChild(
"SpawnDetailsTemplates"))
 
 1458             var spawnDetailsTemplatesJeli = _mapSettings[
"SpawnDetailsTemplates"];
 
 1460             foreach (var templateJeli 
in spawnDetailsTemplatesJeli.Children)
 
 1462                 Sync.SetSpawnDetailsTemplate(templateJeli.Name, templateJeli);
 
 1467     private void OnPropagateSpawnUp (
int requestIndex, 
SpawnInfo spawnInfo)
 
 1469         var spawnMsg = _netPeer.CreateMessage();
 
 1470         spawnMsg.Write((byte)ServerMessageType.Custom);
 
 1471         spawnMsg.Write((byte)CustomServerMessageType.SpawnObject);
 
 1472         spawnMsg.Write(requestIndex);
 
 1473         spawnInfo.WriteTo(spawnMsg);
 
 1475         _server.Connection.SendMessage(spawnMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
 1478     private void OnPropagateDespawnUp (
int objectIndex)
 
 1480         StartCoroutine(PropagateDespawnUpAsync(objectIndex));
 
 1483     private IEnumerator PropagateDespawnUpAsync (
int objectIndex)
 
 1488         var despawnMsg = _netPeer.CreateMessage();
 
 1489         despawnMsg.Write((byte)ServerMessageType.Custom);
 
 1490         despawnMsg.Write((byte)CustomServerMessageType.DespawnObject);
 
 1491         despawnMsg.Write(objectIndex);
 
 1493         _server.Connection.SendMessage(despawnMsg, NetDeliveryMethod.ReliableOrdered, 0);
 
 1501         Log(
"Client shutting down...");
 
 1503         _logWriter.Dispose();
 
PlayerProfile[] PlayerProfiles
Gets an array containing the profiles of all players connected to the current server. 
 
static string GetString(string key, SystemLanguage language=SystemLanguage.Unknown)
Gets the localized string for the given string key. 
 
PlayerLeftWorldServerHandler OtherPlayerLeftWorldServer
Event fired when another player leaves the world server. 
 
PlayerProfile GetPlayerProfile(string playerId)
Get the profile of the player with the given ID. 
 
ServerSpawnFailedHandler ServerSpawnFailed
Event fired when a requested server spawn fails. 
 
void DisconnectFromWorldServer(string error="")
Disconnect from the world server. 
 
ServersFoundHandler ServersFound
Event fired when a server search returns results. 
 
static void ShowError(string error)
Show the overlay with a given error message. 
 
void PullWorldData(string pageName, WorldDataReceivedHandler callback, bool useCache=false)
Requests a specific page of world data from the world server. 
 
bool IsConnectedToWorldServer
Gets whether or not this client is connected to the world server. 
 
string[] PlayerNicknames
Gets an array containing the nicknames of all connected players. 
 
Game client launch screen. 
 
ServerSearchFailedHandler ServerSearchFailed
Event fired when a server search fails. 
 
PlayerNotFoundHandler PlayerNotFound
Event fired when a player search fails. 
 
void RequestServerSpawn(GameServerType serverType, string mapId)
Sends a server spawn request to the world server. 
 
string[] PlayerIDs
Gets an array containing the IDs of all players connected to the current server. 
 
ConnectedToServerHandler ConnectedToServer
Event fired when the game client successfully connects to a server. 
 
PlayerFoundHandler PlayerFound
Event fired when a player search is successful. 
 
bool IsActionEnabled(ControlsAction actionType)
Gets whether or not the given action is enabled. 
 
GameServerType ServerType
Gets the type of server we are connected to, if any. 
 
string GetActionCaption(ControlsAction actionType)
Gets the caption to use for the given action. 
 
int NumPlayers
Gets the number of players connected to the current server. 
 
float StatsReportInterval
The interval at which to report local player statistics to the world server. 
 
UJeli GetCachedWorldData(string pageName)
Returns a cached copy of a previously fetched world data page. 
 
int[] PlayerPeerIndices
Gets an array containing the peer indices of all players connected to the current server...
 
PreDisconnectFromServerHandler PreDisconnectFromServer
Event fired before the server disconnection code is run (before profiles are remove/unsync occurs)...
 
string ScreenshotsBase
Gets the base path for screenshots. 
 
static float GetActionHoldTime(ControlsAction action)
Returns the amount of time the given action has been held down. 
 
An overlay for displaying game status on the game client. 
 
static DeviceManager Instance
Gets the sole instance of the device manager. 
 
void Log(string line)
Write one line to the client log. Log entries are automatically timestamped. 
 
void ConnectToWorldServer()
Connect to the world server specified in the network configuration. 
 
static bool IsMapSynced
Gets whether or not the map on this peer is synced to the canonical version on the network...
 
void RequestServerSummary(IPEndPoint serverEndPoint)
Requests a summary from the server at the given end point. 
 
DisconnectedFromWorldServerHandler DisconnectedFromWorldServer
Event fired when the game client is disconnected from the world server. 
 
static void ShowStatus(string status)
Show the overlay with a given status message. 
 
string LocalPlayerID
Gets the player ID of the local client. 
 
ServerSummaryReceivedHandler ServerSummaryReceived
Event fired when a response is received for a server summary request. 
 
string LogsBase
Gets the base path for log files. 
 
bool IsConnectedToServer
Gets whether or not this client is connected to a server. 
 
static void Hide()
Hide the overlay. 
 
bool ToggleFullScreen()
Toggles the full screen state of the game window. 
 
void DisableDevice(string deviceId)
Disables a device by ID. 
 
A summary for a game server. 
 
void GoToLaunchScreen(bool upToDate)
Go to the client launch screen. 
 
ConnectionToServerFailedHandler ConnectionToServerFailed
Event fired when the game client fails to connect to a server. 
 
static bool UpToDate
Whether or not the game is up to date. 
 
ServerSpawnedHandler ServerSpawned
Event fired when a requested server spawn is successful. 
 
void DisconnectFromServer()
Disconnect from the current server. 
 
ConnectedToWorldServerHandler ConnectedToWorldServer
Event fired when the game client successfully connects to the world server. 
 
Configuration class for game client. 
 
int GetPlayerPeerIndex(string playerId)
Get the peer index of the player with the given ID. 
 
void FindServers(GameServerType serverType, string mapId, bool isCrucial)
Initiates a server search. 
 
Interface for classes which provide information for an action. 
 
string ConfigBase
Gets the base path for configuration files. 
 
DisconnectedFromServerHandler DisconnectedFromServer
Event fired when the game client is disconnected from a server. 
 
string GetPlayerID(int peerIndex)
Get the ID of the player with the given peer index. 
 
string StudyLogsBase
Gets the base path for study logs. 
 
void SaveScreenshot()
Saves a timestamped screenshot into the screenshots folder. 
 
PlayerJoinedWorldServerHandler OtherPlayerJoinedWorldServer
Event fired when another player joins the world server. 
 
void EnableDevice(string deviceId)
Enables a device by ID. 
 
PlayerProfile GetPlayerProfile(int peerIndex)
Get the profile of the player with the given peer index. 
 
void ConnectToServer(IPEndPoint serverEndPoint, GameServerType serverType, string mapId, bool isCrucial)
Connect to a server. 
 
ClientConfig Config
Gets the configuration for this client. 
 
static GameClient Instance
Gets the sole instance of the game client. 
 
Stores data related to the player profile. Loads from a World Data file and provides runtime operatio...
 
PlayerLeftServerHandler OtherPlayerLeftServer
Event fired when another player leaves the current server. 
 
A class for serializing game data into a stream for propagation between objects and peers...
 
PlayerJoinedServerHandler OtherPlayerJoinedServer
Event fired when another player joins the current server. 
 
static GameObject GetObject(int objectIndex)
Get an object by its object index. 
 
ConnectionToWorldServerFailedHandler ConnectionToWorldServerFailed
Event fired when the game client fails to connect to the world server. 
 
static void RegisterActionInfoProvider(ControlsAction action, IActionInfoProvider actionInfoProvider)
Register an action info provider. 
 
UJeli MapSettings
Settings for this map, pulled from the world server. 
 
A class that manages the DebugLogs. Creates a file for logging Exceptions thrown by Unity as well as ...
 
string MapID
Gets the ID of the currently loaded map. 
 
Unity version of Jeli markup class. 
 
static TimelineManager TimelineManager
Gets the Janus timeline manager on this peer. 
 
PlayerProfile LocalPlayerProfile
Gets the player profile of the local client. 
 
This class server two main functions: 1) As a MonoBehaviour, it allows for network synchronization of...
 
The spawn-time information of a spawned object. 
 
Controls class. Allows for controls queries and controls injection from devices. 
 
static void ResetHoldTime(ControlsAction action)
Resets the Hold Time of an action. Used to "consume" a held input, such as the 3 second hold for quit...