private async Task RestartServer() { // get new overlay data await OverlaysCustom.FetchOverlayData(); if (serverRestarting) { Logger.LogRow(Logger.LogType.Error, "Already restarting server. Cancelling this restart."); return; } // stop the server serverRestarting = true; // TODO this is race condition because it is started from somewhere else if (server != null) { await server.StopAsync(); } // restart the server server = WebHost .CreateDefaultBuilder() .UseKestrel(x => { x.ListenAnyIP(6724); }) .UseStartup <Routes>() .Build(); await server.RunAsync(); serverRestarting = false; }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseDefaultFiles(); //app.UseStaticFiles(); app.UseRouting(); app.UseEndpoints(endpoints => { SparkAPI.MapRoutes(endpoints); // OverlaysVRML.MapRoutes(endpoints); OverlaysCustom.MapRoutes(endpoints); endpoints.MapGet("/spark_info", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); await context.Response.WriteAsJsonAsync( new Dictionary <string, object> { { "version", Program.AppVersionString() }, { "windows_store", Program.IsWindowsStore() }, { "ess_version", Program.InstalledSpeakerSystemVersion }, }); }); endpoints.MapGet("/session", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); context.Response.Headers.Add("Content-Type", "application/json"); if (Program.InGame) { await context.Response.WriteAsync(Program.lastJSON); } else { context.Response.StatusCode = 404; await context.Response.WriteAsync(""); } }); endpoints.MapGet("/stats", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); Dictionary <string, object> response = GetStatsResponse(); await context.Response.WriteAsJsonAsync(response); }); // TODO in progress. This will have settings for the overlay replacement endpoints.MapGet("/overlay_info", async context => { try { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); Dictionary <string, object> response = new Dictionary <string, object>(); response["stats"] = GetStatsResponse(); response["visibility"] = new Dictionary <string, object>() { { "minimap", true }, { "main_banner", true }, { "neutral_jousts", true }, { "defensive_jousts", true }, { "event_log", true }, { "playspace", true }, { "player_speed", true }, { "disc_speed", true }, }; response["caster_prefs"] = SparkSettings.instance.casterPrefs; if (Program.InGame) { response["session"] = Program.lastJSON; } await context.Response.WriteAsJsonAsync(response); } catch (Exception e) { Logger.LogRow(Logger.LogType.Error, e.ToString()); } }); endpoints.MapGet("/midmatch_overlay", async context => { string file = ReadResource("midmatch_overlay.html"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/scoreboard", async context => { string file = ReadResource("default_scoreboard.html"); string[] columns = { "player_name", "points", "assists", "saves", "stuns", }; List <List <Dictionary <string, object> > > matchStats = GetMatchStats(); string overlayOrangeTeamName = ""; string overlayBlueTeamName = ""; switch (SparkSettings.instance.overlaysTeamSource) { case 0: overlayOrangeTeamName = SparkSettings.instance.overlaysManualTeamNameOrange; overlayBlueTeamName = SparkSettings.instance.overlaysManualTeamNameBlue; break; case 1: overlayOrangeTeamName = Program.CurrentRound.teams[Team.TeamColor.orange].vrmlTeamName; overlayBlueTeamName = Program.CurrentRound.teams[Team.TeamColor.blue].vrmlTeamName; break; } if (string.IsNullOrWhiteSpace(overlayOrangeTeamName)) { overlayOrangeTeamName = "ORANGE TEAM"; } if (string.IsNullOrWhiteSpace(overlayBlueTeamName)) { overlayBlueTeamName = "BLUE TEAM"; } string[] teamHTMLs = new string[2]; for (int i = 0; i < 2; i++) { StringBuilder html = new StringBuilder(); html.Append("<thead>"); foreach (string column in columns) { html.Append("<th>"); if (column == "player_name") { html.Append(i == 0 ? overlayBlueTeamName : overlayOrangeTeamName); } else { html.Append(column); } html.Append("</th>"); } html.Append("</thead>"); html.Append("<body>"); if (matchStats[i].Count >= 8) { Logger.LogRow(Logger.LogType.Error, "8 or more players on a team. Must have failed to split."); } // cap out at 8 players. Anything more breaks the layout for (int playerIndex = 0; playerIndex < matchStats[i].Count && playerIndex < 8; playerIndex++) { Dictionary <string, object> player = matchStats[i][playerIndex]; html.Append("<tr>"); foreach (string column in columns) { html.Append("<td>"); html.Append(player[column]); html.Append("</td>"); } html.Append("</tr>"); } html.Append("<body>"); teamHTMLs[i] = html.ToString(); } file = file.Replace("{{ BLUE_TEAM }}", teamHTMLs[0]); file = file.Replace("{{ ORANGE_TEAM }}", teamHTMLs[1]); await context.Response.WriteAsync(file); }); endpoints.MapGet("/disc_positions", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); List <Dictionary <string, float> > positions = await GetDiscPositions(); await context.Response.WriteAsJsonAsync(positions); }); endpoints.MapGet("/disc_position_heatmap", async context => { string file = ReadResource("disc_position_heatmap.html"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/get_player_speed", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); await context.Response.WriteAsJsonAsync( new Dictionary <string, object> { { "speed", Program.lastFrame?.GetPlayer(Program.lastFrame.client_name)?.velocity.ToVector3() .Length() ?? -1 }, }); }); endpoints.MapGet("/get_disc_speed", async context => { context.Response.Headers.Add("Access-Control-Allow-Origin", "*"); context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With"); await context.Response.WriteAsJsonAsync( new Dictionary <string, object> { { "speed", Program.lastFrame?.disc.velocity.ToVector3().Length() ?? -1 }, }); }); endpoints.MapGet("/speedometer/player", async context => { string file = ReadResource("speedometer.html"); file = file.Replace("FETCH_URL", "/get_player_speed"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/speedometer/lone_echo_1", async context => { string file = ReadResource("speedometer.html"); file = file.Replace("FETCH_URL", "http://127.0.0.1:6723/le1/speed/"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/speedometer/lone_echo_2", async context => { string file = ReadResource("speedometer.html"); file = file.Replace("FETCH_URL", "http://127.0.0.1:6723/le2/speed/"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/speedometer/disc", async context => { string file = ReadResource("speedometer.html"); file = file.Replace("FETCH_URL", "/get_disc_speed"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/minimap", async context => { string file = ReadResource("default_minimap.html"); await context.Response.WriteAsync(file); }); endpoints.MapGet("/full_overlay", async context => { string file = ReadResource("full_overlay.html"); await context.Response.WriteAsync(file); }); // add all the stuff in wwwroot // Determine path Assembly assembly = Assembly.GetExecutingAssembly(); string sparkPath = Path.GetDirectoryName(assembly.Location); if (sparkPath != null) { // Format: "{Namespace}.{Folder}.{filename}.{Extension}" foreach (string str in assembly.GetManifestResourceNames()) { string[] pieces = str.Split('.'); if (pieces.Length > 2 && pieces?[1] == "wwwroot_resources") { List <string> folderPieces = pieces.Skip(2).SkipLast(2).Append(string.Join('.', pieces.TakeLast(2))).ToList(); string url; if (folderPieces.Count > 1 && folderPieces[^ 1].Contains("min.")) { // combine the min into the filename folderPieces[^ 2] = string.Join('.', folderPieces.TakeLast(2));