/// <summary> /// Method invoked when new peer connects /// </summary> /// <param name="peer">Peer that has connected</param> public void OnPeerConnected(IPeerManager peer) { Debug.Assert(State == SimulationState.Initial); MasterPeer = peer; Log.Info($"Master {peer.PeerEndPoint} connected."); var info = new Commands.Info() { Version = "todo", UnityVersion = Application.unityVersion, OperatingSystem = SystemInfo.operatingSystemFamily.ToString(), }; State = SimulationState.Connected; if (Loader.Instance.LoaderUI != null) { Loader.Instance.LoaderUI.SetLoaderUIState(LoaderUI.LoaderUIStateType.PROGRESS); } var infoData = PacketsProcessor.Write(info); var message = MessagesPool.Instance.GetMessage(infoData.Length); message.AddressKey = Key; message.Content.PushBytes(infoData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(peer.PeerEndPoint, message); }
/// <summary> /// Method invoked when manager receives ping command /// </summary> /// <param name="ping">Ping command</param> private void OnPingCommand(Commands.Ping ping) { var stopData = PacketsProcessor.Write(new Commands.Pong() { Id = ping.Id }); var message = MessagesPool.Instance.GetMessage(stopData.Length); message.AddressKey = Key; message.Content.PushBytes(stopData); message.Type = DistributedMessageType.Unreliable; BroadcastMessage(message); }
/// <summary> /// Method invoked when manager receives ping command /// </summary> /// <param name="ping">Ping command</param> private void OnPingCommand(Commands.Ping ping) { var dataWriter = new NetDataWriter(); PacketsProcessor.Write(dataWriter, new Commands.Pong() { Id = ping.Id }); var message = MessagesPool.Instance.GetMessage(dataWriter.Length); message.AddressKey = Key; message.Content.PushBytes(dataWriter.CopyData()); message.Type = DistributedMessageType.Unreliable; BroadcastMessage(message); }
/// <summary> /// Sends loaded command to the master /// </summary> public void SendLoadedCommand() { if (State != SimulationState.Connected) { return; } State = SimulationState.Ready; var stopData = PacketsProcessor.Write(new Commands.Loaded()); var message = MessagesPool.Instance.GetMessage(stopData.Length); message.AddressKey = Key; message.Content.PushBytes(stopData); message.Type = DistributedMessageType.ReliableOrdered; BroadcastMessage(message); Log.Info($"{GetType().Name} loaded the simulation and has sent loaded command to the master."); }
/// <summary> /// Sends ready command to the master /// </summary> public void SendReadyCommand() { if (State != SimulationState.Connected) { return; } State = SimulationState.Ready; var dataWriter = new NetDataWriter(); PacketsProcessor.Write(dataWriter, new Commands.Ready()); var message = MessagesPool.Instance.GetMessage(dataWriter.Length); message.AddressKey = Key; message.Content.PushBytes(dataWriter.CopyData()); message.Type = DistributedMessageType.ReliableOrdered; BroadcastMessage(message); Log.Info($"{GetType().Name} is ready and has sent ready command to the master."); }
/// <summary> /// Broadcast the stop command to the master /// </summary> public void BroadcastStopCommand() { if (State == SimulationState.Stopping) { return; } Log.Info($"{GetType().Name} broadcasts the simulation stop command."); var stopData = PacketsProcessor.Write(new Commands.Stop()); var message = MessagesPool.Instance.GetMessage(stopData.Length); message.AddressKey = Key; message.Content.PushBytes(stopData); message.Type = DistributedMessageType.ReliableOrdered; BroadcastMessage(message); State = SimulationState.Stopping; }
/// <summary> /// Method invoked when new peer connects /// </summary> /// <param name="peer">Peer that has connected</param> public void OnPeerConnected(IPeerManager peer) { Debug.Assert(State == SimulationState.Initial); MasterPeer = peer; Debug.Log($"Master {peer.PeerEndPoint} connected."); var info = new Commands.Info() { Version = "todo", UnityVersion = Application.unityVersion, OperatingSystem = SystemInfo.operatingSystemFamily.ToString(), }; State = SimulationState.Connected; Loader.Instance.LoaderUI.SetLoaderUIState(LoaderUI.LoaderUIStateType.PROGRESS); UnicastMessage(peer.PeerEndPoint, new Message(Key, new BytesStack(PacketsProcessor.Write(info), false), MessageType.ReliableOrdered)); }
/// <summary> /// Broadcast the stop command to the master /// </summary> public void BroadcastStopCommand() { if (State == SimulationState.Stopping || Loader.Instance.Network.CurrentSimulation == null) { return; } Log.Info($"{GetType().Name} broadcasts the simulation stop command."); var dataWriter = new NetDataWriter(); PacketsProcessor.Write(dataWriter, new Commands.Stop { SimulationId = Loader.Instance.Network.CurrentSimulation.Id }); var message = MessagesPool.Instance.GetMessage(dataWriter.Length); message.AddressKey = Key; message.Content.PushBytes(dataWriter.CopyData()); message.Type = DistributedMessageType.ReliableOrdered; BroadcastMessage(message); State = SimulationState.Stopping; }
/// <summary> /// Download required for the simulation vehicle bundles from the server /// </summary> /// <param name="load">Load command from the server</param> /// <param name="mapBundlePath">Path where the map bundle will be saved</param> private void LoadMapBundle(Commands.Load load, string mapBundlePath) { var vehicleBundles = new List <string>(); DownloadVehicleBundles(load, vehicleBundles, () => { if (MasterPeer == null) { Debug.LogWarning("Master peer has disconnected while loading the simulation scene."); Loader.ResetLoaderScene(); return; } var zip = new ZipFile(mapBundlePath); { string manfile; ZipEntry entry = zip.GetEntry("manifest"); using (var ms = zip.GetInputStream(entry)) { int streamSize = (int)entry.Size; byte[] buffer = new byte[streamSize]; streamSize = ms.Read(buffer, 0, streamSize); manfile = Encoding.UTF8.GetString(buffer); } Manifest manifest = new Deserializer().Deserialize <Manifest>(manfile); AssetBundle textureBundle = null; if (zip.FindEntry(($"{manifest.assetGuid}_environment_textures"), false) != -1) { var texStream = zip.GetInputStream(zip.GetEntry($"{manifest.assetGuid}_environment_textures")); textureBundle = AssetBundle.LoadFromStream(texStream, 0, 1 << 20); } string platform = SystemInfo.operatingSystemFamily == OperatingSystemFamily.Windows ? "windows" : "linux"; var mapStream = zip.GetInputStream(zip.GetEntry($"{manifest.assetGuid}_environment_main_{platform}")); var mapBundle = AssetBundle.LoadFromStream(mapStream, 0, 1 << 20); if (mapBundle == null) { throw new Exception($"Failed to load environment from '{load.MapName}' asset bundle"); } textureBundle?.LoadAllAssets(); var scenes = mapBundle.GetAllScenePaths(); if (scenes.Length != 1) { throw new Exception( $"Unsupported environment in '{load.MapName}' asset bundle, only 1 scene expected"); } var sceneName = Path.GetFileNameWithoutExtension(scenes[0]); var loader = SceneManager.LoadSceneAsync(sceneName, LoadSceneMode.Additive); loader.completed += op => { if (op.isDone) { SceneManager.SetActiveScene(SceneManager.GetSceneByName(sceneName)); textureBundle?.Unload(false); mapBundle.Unload(false); zip.Close(); try { var prefabs = LoadVehicleBundles(vehicleBundles); Loader.Instance.SimConfig = new SimulationConfig() { Name = load.Name, ApiOnly = load.ApiOnly, Headless = load.Headless, Interactive = load.Interactive, TimeOfDay = DateTime.ParseExact(load.TimeOfDay, "o", CultureInfo.InvariantCulture), Rain = load.Rain, Fog = load.Fog, Wetness = load.Wetness, Cloudiness = load.Cloudiness, UseTraffic = load.UseTraffic, UsePedestrians = load.UsePedestrians, Agents = load.Agents.Zip(prefabs, (agent, prefab) => { var config = new AgentConfig() { Name = agent.Name, Prefab = prefab, Connection = agent.Connection, Sensors = agent.Sensors, }; if (!string.IsNullOrEmpty(agent.Bridge)) { config.Bridge = Web.Config.Bridges.Find(bridge => bridge.Name == agent.Bridge); if (config.Bridge == null) { throw new Exception($"Bridge {agent.Bridge} not found"); } } return(config); }).ToArray(), }; Loader.Instance.CurrentSimulation = CreateSimulationModel(Loader.Instance.SimConfig); Loader.Instance.CurrentSimulation.Status = "Running"; if (Loader.Instance.SimConfig.ApiOnly) { var api = Instantiate(Loader.Instance.ApiManagerPrefab); api.name = "ApiManager"; } var simulatorManager = Loader.CreateSimulatorManager(); if (load.UseSeed) { simulatorManager.Init(load.Seed); } else { simulatorManager.Init(); } InitializeSimulation(simulatorManager.gameObject); // Notify WebUI simulation is running NotificationManager.SendNotification("simulation", SimulationResponse.Create(Loader.Instance.CurrentSimulation), Loader.Instance.CurrentSimulation.Owner); Debug.Log($"Client ready to start"); var result = new Commands.LoadResult() { Success = true, }; Loader.Instance.LoaderUI.SetLoaderUIState(LoaderUI.LoaderUIStateType.READY); if (MasterPeer == null) { Debug.LogWarning("Master peer has disconnected while loading the simulation scene."); Loader.ResetLoaderScene(); return; } var resultData = PacketsProcessor.Write(result); var message = MessagesPool.Instance.GetMessage(resultData.Length); message.AddressKey = Key; message.Content.PushBytes(resultData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); State = SimulationState.Ready; } catch (Exception ex) { Debug.LogException(ex); var err = new Commands.LoadResult() { Success = false, ErrorMessage = ex.ToString(), }; var errData = PacketsProcessor.Write(err); var message = MessagesPool.Instance.GetMessage(errData.Length); message.AddressKey = Key; message.Content.PushBytes(errData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); Loader.ResetLoaderScene(); } } }; } }); }
/// <summary> /// Download required for the simulation vehicle bundles from the server /// </summary> /// <param name="load">Load command from the server</param> /// <param name="bundles">Paths where bundles will be saved</param> /// <param name="finished">Callback invoked when downloading is completed</param> private void DownloadVehicleBundles(Commands.Load load, List <string> bundles, Action finished) { try { int count = 0; var agents = load.Agents; var agentsToDownload = load.Agents.Length; if (agentsToDownload == 0) { finished(); return; } for (int i = 0; i < agentsToDownload; i++) { //Check if downloading is already being processed, if true this may be a quick rerun of the simulation if (processedDownloads.Contains(agents[i].Name)) { Interlocked.Increment(ref count); continue; } VehicleModel vehicleModel; using (var db = DatabaseManager.Open()) { var sql = Sql.Builder.Where("name = @0", agents[i].Name); vehicleModel = db.SingleOrDefault <VehicleModel>(sql); } if (vehicleModel == null) { Debug.Log($"Downloading {agents[i].Name} from {agents[i].Url}"); vehicleModel = new VehicleModel() { Name = agents[i].Name, Url = agents[i].Url, BridgeType = agents[i].Bridge, LocalPath = WebUtilities.GenerateLocalPath("Vehicles"), Sensors = agents[i].Sensors, }; bundles.Add(vehicleModel.LocalPath); processedDownloads.Add(vehicleModel.Name); DownloadManager.AddDownloadToQueue(new Uri(vehicleModel.Url), vehicleModel.LocalPath, null, (success, ex) => { //Check if downloaded vehicle model is still valid in current load command if (CurrentLoadCommand.Agents.All(loadAgent => loadAgent.Name != vehicleModel.Name)) { return; } processedDownloads.Remove(vehicleModel.Name); if (ex != null) { var err = new Commands.LoadResult() { Success = false, ErrorMessage = ex.ToString(), }; var errData = PacketsProcessor.Write(err); var message = MessagesPool.Instance.GetMessage(errData.Length); message.AddressKey = Key; message.Content.PushBytes(errData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); return; } using (var db = DatabaseManager.Open()) { db.Insert(vehicleModel); } if (Interlocked.Increment(ref count) == agentsToDownload) { finished(); } } ); } else { Debug.Log($"Vehicle {agents[i].Name} exists"); bundles.Add(vehicleModel.LocalPath); if (Interlocked.Increment(ref count) == agentsToDownload) { finished(); } } } } catch (Exception ex) { Debug.LogException(ex); var err = new Commands.LoadResult() { Success = false, ErrorMessage = ex.ToString(), }; var errData = PacketsProcessor.Write(err); var message = MessagesPool.Instance.GetMessage(errData.Length); message.AddressKey = Key; message.Content.PushBytes(errData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); Loader.ResetLoaderScene(); } }
/// <summary> /// Method invoked when manager receives load command /// </summary> /// <param name="load">Received load command</param> private void OnLoadCommand(Commands.Load load) { Debug.Assert(State == SimulationState.Connected); CurrentLoadCommand = load; State = SimulationState.Loading; Debug.Log("Preparing simulation"); try { //Check if downloading is already being processed, if true this may be a quick rerun of the simulation if (processedDownloads.Contains(load.MapUrl)) { return; } MapModel map; using (var db = DatabaseManager.Open()) { var sql = Sql.Builder.Where("name = @0", load.MapName); map = db.SingleOrDefault <MapModel>(sql); } if (map == null) { Debug.Log($"Downloading {load.MapName} from {load.MapUrl}"); map = new MapModel() { Name = load.MapName, Url = load.MapUrl, LocalPath = WebUtilities.GenerateLocalPath("Maps"), }; using (var db = DatabaseManager.Open()) { db.Insert(map); } processedDownloads.Add(map.Name); DownloadManager.AddDownloadToQueue(new Uri(map.Url), map.LocalPath, null, (success, ex) => { processedDownloads.Remove(map.Name); //Check if downloaded map is still valid in current load command if (CurrentLoadCommand.MapName != map.Name) { return; } if (ex != null) { map.Error = ex.Message; using (var db = DatabaseManager.Open()) { db.Update(map); } Debug.LogException(ex); } if (success) { LoadMapBundle(load, map.LocalPath); } else { var err = new Commands.LoadResult() { Success = false, ErrorMessage = ex.ToString(), }; var errData = PacketsProcessor.Write(err); var message = MessagesPool.Instance.GetMessage(errData.Length); message.AddressKey = Key; message.Content.PushBytes(errData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); } }); } else { Debug.Log($"Map {load.MapName} exists"); LoadMapBundle(load, map.LocalPath); } } catch (Exception ex) { Debug.LogException(ex); var err = new Commands.LoadResult() { Success = false, ErrorMessage = ex.ToString(), }; var errData = PacketsProcessor.Write(err); var message = MessagesPool.Instance.GetMessage(errData.Length); message.AddressKey = Key; message.Content.PushBytes(errData); message.Type = DistributedMessageType.ReliableOrdered; UnicastMessage(MasterPeer.PeerEndPoint, message); Loader.ResetLoaderScene(); } }