mirror of
https://github.com/CommonLoon102/NBloodServerSupervisor.git
synced 2024-12-22 18:52:44 +01:00
Add project files.
This commit is contained in:
parent
6971176d7d
commit
0ac65ffd08
7
Common/Common.csproj
Normal file
7
Common/Common.csproj
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
26
Common/PortUtils.cs
Normal file
26
Common/PortUtils.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Common
|
||||||
|
{
|
||||||
|
public class PortUtils
|
||||||
|
{
|
||||||
|
public static int GetPort()
|
||||||
|
{
|
||||||
|
IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
|
||||||
|
var usedPorts = ipGlobalProperties.GetActiveTcpConnections().Select(c => c.LocalEndPoint.Port).ToList();
|
||||||
|
usedPorts.AddRange(ipGlobalProperties.GetActiveTcpListeners().Select(c => c.Port).ToList());
|
||||||
|
usedPorts.AddRange(ipGlobalProperties.GetActiveUdpListeners().Select(c => c.Port).ToList());
|
||||||
|
|
||||||
|
var availablePorts = Enumerable.Range(1025, ushort.MaxValue).ToList().Except(usedPorts).ToList();
|
||||||
|
|
||||||
|
Random rnd = new Random();
|
||||||
|
int index = rnd.Next(0, availablePorts.Count() - 1);
|
||||||
|
int port = availablePorts[index];
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
Model/Model.csproj
Normal file
7
Model/Model.csproj
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
13
Model/Player.cs
Normal file
13
Model/Player.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Model
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class Player
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Score { get; set; }
|
||||||
|
}
|
||||||
|
}
|
20
Model/Server.cs
Normal file
20
Model/Server.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Model
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class Server
|
||||||
|
{
|
||||||
|
public DateTime SpawnedAtUtc { get; set; } = DateTime.UtcNow;
|
||||||
|
public int ProcessId { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public bool IsStarted { get; set; }
|
||||||
|
public bool IsPrivate { get; set; }
|
||||||
|
public int CurrentPlayers { get; set; }
|
||||||
|
public int MaximumPlayers { get; set; }
|
||||||
|
public string GameType { get; set; }
|
||||||
|
public IList<Player> Players { get; set; } = new List<Player>();
|
||||||
|
}
|
||||||
|
}
|
12
Model/State.cs
Normal file
12
Model/State.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Model
|
||||||
|
{
|
||||||
|
public class State
|
||||||
|
{
|
||||||
|
public ConcurrentDictionary<int, Server> Servers { get; } = new ConcurrentDictionary<int, Server>();
|
||||||
|
}
|
||||||
|
}
|
12
Model/StateResponse.cs
Normal file
12
Model/StateResponse.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Model
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
public class StateResponse
|
||||||
|
{
|
||||||
|
public IList<Server> Servers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
43
NBloodServerSupervisor.sln
Normal file
43
NBloodServerSupervisor.sln
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 16
|
||||||
|
VisualStudioVersion = 16.0.29709.97
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Supervisor", "Supervisor\Supervisor.csproj", "{BBE1C869-D87B-4174-8EC2-0E095488E99A}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "Model\Model.csproj", "{940B1560-86F4-4233-B59E-B502B0B5C2AF}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Common", "Common\Common.csproj", "{0BD5EB76-F38C-4C22-9213-6846251D18B8}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebInterface", "WebInterface\WebInterface.csproj", "{7DC8478B-FF2E-4CA5-BF4B-23DBC3E80D09}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{BBE1C869-D87B-4174-8EC2-0E095488E99A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BBE1C869-D87B-4174-8EC2-0E095488E99A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BBE1C869-D87B-4174-8EC2-0E095488E99A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BBE1C869-D87B-4174-8EC2-0E095488E99A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{940B1560-86F4-4233-B59E-B502B0B5C2AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{940B1560-86F4-4233-B59E-B502B0B5C2AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{940B1560-86F4-4233-B59E-B502B0B5C2AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{940B1560-86F4-4233-B59E-B502B0B5C2AF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0BD5EB76-F38C-4C22-9213-6846251D18B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0BD5EB76-F38C-4C22-9213-6846251D18B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0BD5EB76-F38C-4C22-9213-6846251D18B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0BD5EB76-F38C-4C22-9213-6846251D18B8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{7DC8478B-FF2E-4CA5-BF4B-23DBC3E80D09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{7DC8478B-FF2E-4CA5-BF4B-23DBC3E80D09}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{7DC8478B-FF2E-4CA5-BF4B-23DBC3E80D09}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{7DC8478B-FF2E-4CA5-BF4B-23DBC3E80D09}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {A47D63A4-8A9E-49E2-8DC8-C96C042534D4}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
17
Supervisor/Frags.cs
Normal file
17
Supervisor/Frags.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
class Frags : PacketData
|
||||||
|
{
|
||||||
|
public IList<int> Scores { get; set; }
|
||||||
|
public int GameType { get; set; }
|
||||||
|
|
||||||
|
public Frags()
|
||||||
|
{
|
||||||
|
IsStarted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
166
Supervisor/NBloodServerListener.cs
Normal file
166
Supervisor/NBloodServerListener.cs
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
using Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
static class NBloodServerListener
|
||||||
|
{
|
||||||
|
private const int listenPort = 11027;
|
||||||
|
private static IPEndPoint remoteIP = new IPEndPoint(IPAddress.Loopback, listenPort);
|
||||||
|
private static UdpClient udpClient = new UdpClient(remoteIP);
|
||||||
|
|
||||||
|
public static async void StartListening()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
UdpReceiveResult rst = await udpClient.ReceiveAsync();
|
||||||
|
ProcessPacket(rst.Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessPacket(byte[] buffer)
|
||||||
|
{
|
||||||
|
string message = Encoding.ASCII.GetString(buffer);
|
||||||
|
switch (message[0])
|
||||||
|
{
|
||||||
|
case 'A':
|
||||||
|
ProcessPlayerCountsPacket(message);
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
ProcessPlayerNamesPacket(message);
|
||||||
|
break;
|
||||||
|
case 'C':
|
||||||
|
ProcessFragsPacket(buffer);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessPlayerCountsPacket(string message)
|
||||||
|
{
|
||||||
|
string[] splitMessage = message.SplitMessage();
|
||||||
|
var packetData = new PlayerCounts()
|
||||||
|
{
|
||||||
|
Port = int.Parse(splitMessage[0]),
|
||||||
|
IsStarted = int.Parse(splitMessage[1]) != 0,
|
||||||
|
CurrentPlayers = int.Parse(splitMessage[2]),
|
||||||
|
MaximumPlayers = int.Parse(splitMessage[3])
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateState(packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateState(PlayerCounts packetData)
|
||||||
|
{
|
||||||
|
Program.State.Servers.AddOrUpdate(packetData.Port,
|
||||||
|
new Server()
|
||||||
|
{
|
||||||
|
Port = packetData.Port,
|
||||||
|
IsStarted = packetData.IsStarted,
|
||||||
|
IsPrivate = packetData.IsPrivate,
|
||||||
|
CurrentPlayers = packetData.CurrentPlayers,
|
||||||
|
MaximumPlayers = packetData.MaximumPlayers
|
||||||
|
},
|
||||||
|
(port, server) =>
|
||||||
|
{
|
||||||
|
server.IsStarted = packetData.IsStarted;
|
||||||
|
server.IsPrivate = packetData.IsPrivate;
|
||||||
|
server.CurrentPlayers = packetData.CurrentPlayers;
|
||||||
|
server.MaximumPlayers = packetData.MaximumPlayers;
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessPlayerNamesPacket(string message)
|
||||||
|
{
|
||||||
|
string[] splitMessage = message.SplitMessage();
|
||||||
|
var packetData = new PlayerNames()
|
||||||
|
{
|
||||||
|
Port = int.Parse(splitMessage[0]),
|
||||||
|
Names = splitMessage.Skip(1).ToList(),
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateState(packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateState(PlayerNames packetData)
|
||||||
|
{
|
||||||
|
Program.State.Servers.AddOrUpdate(packetData.Port,
|
||||||
|
new Server()
|
||||||
|
{
|
||||||
|
Port = packetData.Port,
|
||||||
|
IsStarted = packetData.IsStarted,
|
||||||
|
Players = packetData.Names.Select(name => new Player() { Name = name }).ToList()
|
||||||
|
},
|
||||||
|
(port, server) =>
|
||||||
|
{
|
||||||
|
server.IsStarted = packetData.IsStarted;
|
||||||
|
if (server.Players.Count == 0 || server.CurrentPlayers != packetData.Names.Count)
|
||||||
|
{
|
||||||
|
server.CurrentPlayers = packetData.Names.Count;
|
||||||
|
server.Players = packetData.Names.Select(name => new Player()
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Score = 0
|
||||||
|
}).ToList();
|
||||||
|
}
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessFragsPacket(byte[] buffer)
|
||||||
|
{
|
||||||
|
int port = BitConverter.ToInt32(buffer, 4);
|
||||||
|
int gameType = BitConverter.ToInt32(buffer, 8);
|
||||||
|
const int maxPlayers = 8;
|
||||||
|
int[] scores = new int[maxPlayers];
|
||||||
|
for (int i = 0; i < scores.Length; i++)
|
||||||
|
scores[i] = BitConverter.ToInt32(buffer, 12 + i * sizeof(int));
|
||||||
|
|
||||||
|
var packetData = new Frags()
|
||||||
|
{
|
||||||
|
Port = port,
|
||||||
|
GameType = gameType,
|
||||||
|
Scores = scores
|
||||||
|
};
|
||||||
|
|
||||||
|
UpdateState(packetData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void UpdateState(Frags packetData)
|
||||||
|
{
|
||||||
|
Program.State.Servers.AddOrUpdate(packetData.Port,
|
||||||
|
new Server()
|
||||||
|
{
|
||||||
|
Port = packetData.Port,
|
||||||
|
IsStarted = packetData.IsStarted,
|
||||||
|
GameType = GetGameType(packetData.GameType)
|
||||||
|
},
|
||||||
|
(port, server) =>
|
||||||
|
{
|
||||||
|
server.IsStarted = packetData.IsStarted;
|
||||||
|
server.GameType = GetGameType(packetData.GameType);
|
||||||
|
for (int i = 0; i < server.CurrentPlayers; i++)
|
||||||
|
server.Players[i].Score = packetData.Scores[i];
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetGameType(int gameType)
|
||||||
|
{
|
||||||
|
return gameType switch
|
||||||
|
{
|
||||||
|
1 => "Cooperative",
|
||||||
|
2 => "Bloodbath",
|
||||||
|
3 => "Capture The Flag",
|
||||||
|
_ => "Unknown",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
Supervisor/PacketData.cs
Normal file
13
Supervisor/PacketData.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
abstract class PacketData
|
||||||
|
{
|
||||||
|
public int Port { get; set; }
|
||||||
|
public bool IsStarted { get; set; }
|
||||||
|
public bool IsPrivate { get; set; }
|
||||||
|
}
|
||||||
|
}
|
17
Supervisor/PlayerCounts.cs
Normal file
17
Supervisor/PlayerCounts.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
class PlayerCounts : PacketData
|
||||||
|
{
|
||||||
|
public int CurrentPlayers { get; set; }
|
||||||
|
public int MaximumPlayers { get; set; }
|
||||||
|
|
||||||
|
public PlayerCounts()
|
||||||
|
{
|
||||||
|
IsStarted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
Supervisor/PlayerNames.cs
Normal file
16
Supervisor/PlayerNames.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
class PlayerNames : PacketData
|
||||||
|
{
|
||||||
|
public IList<string> Names { get; set; }
|
||||||
|
|
||||||
|
public PlayerNames()
|
||||||
|
{
|
||||||
|
IsStarted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
Supervisor/PrivateServerManager.cs
Normal file
39
Supervisor/PrivateServerManager.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
static class PrivateServerManager
|
||||||
|
{
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
KillUnusedServers();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void KillUnusedServers()
|
||||||
|
{
|
||||||
|
var killables = Program.State.Servers.Values.Where(s =>
|
||||||
|
s.IsPrivate
|
||||||
|
&& !s.IsStarted
|
||||||
|
&& s.CurrentPlayers < 2
|
||||||
|
&& (DateTime.UtcNow - s.SpawnedAtUtc) > TimeSpan.FromMinutes(10));
|
||||||
|
|
||||||
|
foreach (var server in killables)
|
||||||
|
{
|
||||||
|
Process.GetProcessById(server.ProcessId).Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
Supervisor/Program.cs
Normal file
27
Supervisor/Program.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using Model;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static readonly State State = new State();
|
||||||
|
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
NBloodServerListener.StartListening();
|
||||||
|
WebApiListener.StartListening();
|
||||||
|
|
||||||
|
if (args.Length > 0)
|
||||||
|
PublicServerManager.Start(args[0]);
|
||||||
|
|
||||||
|
PrivateServerManager.Start();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
7
Supervisor/Properties/launchSettings.json
Normal file
7
Supervisor/Properties/launchSettings.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Supervisor": {
|
||||||
|
"commandName": "Project"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
Supervisor/PublicServerManager.cs
Normal file
74
Supervisor/PublicServerManager.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using Common;
|
||||||
|
using Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
class PublicServerManager
|
||||||
|
{
|
||||||
|
public static void Start(string nbloodPath)
|
||||||
|
{
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
KillOrphanedServers();
|
||||||
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
LaunchNewServersWhenNeeded(nbloodPath);
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void KillOrphanedServers()
|
||||||
|
{
|
||||||
|
foreach (var process in Process.GetProcessesByName("nblood_server"))
|
||||||
|
{
|
||||||
|
if (!Program.State.Servers.Values.Any(s => s.ProcessId == process.Id))
|
||||||
|
{
|
||||||
|
process.Kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void LaunchNewServersWhenNeeded(string nbloodPath)
|
||||||
|
{
|
||||||
|
const int maxPlayers = 8;
|
||||||
|
|
||||||
|
for (int i = 3; i <= maxPlayers; i++)
|
||||||
|
{
|
||||||
|
if (IsNewServerNeeded(i))
|
||||||
|
{
|
||||||
|
int port = PortUtils.GetPort();
|
||||||
|
var process = Process.Start(nbloodPath, $"-server {i} -port {port}");
|
||||||
|
Program.State.Servers.AddOrUpdate(port, new Server()
|
||||||
|
{
|
||||||
|
Port = port,
|
||||||
|
ProcessId = process.Id,
|
||||||
|
MaximumPlayers = i,
|
||||||
|
CurrentPlayers = 1,
|
||||||
|
},
|
||||||
|
(prt, server) =>
|
||||||
|
{
|
||||||
|
server.ProcessId = process.Id;
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsNewServerNeeded(int i)
|
||||||
|
{
|
||||||
|
return !Program.State.Servers.Values.Any(s =>
|
||||||
|
!s.IsPrivate && s.MaximumPlayers == i && s.CurrentPlayers < s.MaximumPlayers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
Supervisor/StringUtils.cs
Normal file
19
Supervisor/StringUtils.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
static class StringUtils
|
||||||
|
{
|
||||||
|
public static string[] SplitMessage(this string message)
|
||||||
|
{
|
||||||
|
return message.Substring(1)
|
||||||
|
.Split('\t', StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.Select(e => e.Trim('\0'))
|
||||||
|
.Where(e => !string.IsNullOrWhiteSpace(e))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
Supervisor/Supervisor.csproj
Normal file
13
Supervisor/Supervisor.csproj
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Model\Model.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
85
Supervisor/WebApiListener.cs
Normal file
85
Supervisor/WebApiListener.cs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
using Model;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Supervisor
|
||||||
|
{
|
||||||
|
static class WebApiListener
|
||||||
|
{
|
||||||
|
private const int listenPort = 11028;
|
||||||
|
private static readonly IPEndPoint remoteIP = new IPEndPoint(IPAddress.Loopback, listenPort);
|
||||||
|
private static readonly UdpClient udpClient = new UdpClient(remoteIP);
|
||||||
|
|
||||||
|
private static readonly Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
private static readonly IPEndPoint webApiEndPoint = new IPEndPoint(IPAddress.Loopback, 11029);
|
||||||
|
|
||||||
|
public static async void StartListening()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
UdpReceiveResult rst = await udpClient.ReceiveAsync();
|
||||||
|
ProcessWebApiMessage(rst.Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessWebApiMessage(byte[] buffer)
|
||||||
|
{
|
||||||
|
string message = Encoding.ASCII.GetString(buffer);
|
||||||
|
switch (message[0])
|
||||||
|
{
|
||||||
|
case 'A':
|
||||||
|
ProcessGetCurrentStateRequest();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
StorePrivateServerInfo(message);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ProcessGetCurrentStateRequest()
|
||||||
|
{
|
||||||
|
var response = new StateResponse();
|
||||||
|
response.Servers = Program.State.Servers.Values.ToList();
|
||||||
|
|
||||||
|
byte[] serializedResponse = ObjectToByteArray(response);
|
||||||
|
socket.SendTo(serializedResponse, webApiEndPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void StorePrivateServerInfo(string message)
|
||||||
|
{
|
||||||
|
string[] split = message.SplitMessage();
|
||||||
|
int port = int.Parse(split[0]);
|
||||||
|
int processId = int.Parse(split[1]);
|
||||||
|
Program.State.Servers.AddOrUpdate(port, new Server()
|
||||||
|
{
|
||||||
|
ProcessId = processId,
|
||||||
|
IsPrivate = true
|
||||||
|
},
|
||||||
|
(prt, server) =>
|
||||||
|
{
|
||||||
|
server.ProcessId = processId;
|
||||||
|
server.IsPrivate = true;
|
||||||
|
return server;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] ObjectToByteArray(object obj)
|
||||||
|
{
|
||||||
|
BinaryFormatter bf = new BinaryFormatter();
|
||||||
|
using (var ms = new MemoryStream())
|
||||||
|
{
|
||||||
|
bf.Serialize(ms, obj);
|
||||||
|
return ms.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
172
WebInterface/Controllers/NBloodController.cs
Normal file
172
WebInterface/Controllers/NBloodController.cs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Runtime.Serialization.Formatters.Binary;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Common;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Model;
|
||||||
|
|
||||||
|
namespace WebInterface.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
public class NBloodController : ControllerBase
|
||||||
|
{
|
||||||
|
private static bool _isBusy = false;
|
||||||
|
private static DateTime _lastRefresh = DateTime.MinValue;
|
||||||
|
private static ListServersResponse _lastServerList = null;
|
||||||
|
private static readonly object _locker = new object();
|
||||||
|
|
||||||
|
private readonly ILogger<NBloodController> _logger;
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
|
||||||
|
private const int listenPort = 11029;
|
||||||
|
private static readonly IPEndPoint remoteIP = new IPEndPoint(IPAddress.Loopback, listenPort);
|
||||||
|
private static readonly UdpClient udpClient = new UdpClient(remoteIP);
|
||||||
|
|
||||||
|
private static readonly Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
||||||
|
private static readonly IPEndPoint webApiListenerEndPoint = new IPEndPoint(IPAddress.Loopback, 11028);
|
||||||
|
|
||||||
|
public NBloodController(ILogger<NBloodController> logger, IConfiguration config)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("[controller]/api/startserver")]
|
||||||
|
public StartServerResponse StartServer([FromQuery] ServerParameters parameters)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
|
while (_isBusy)
|
||||||
|
{
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
if (sw.Elapsed.TotalSeconds > 5)
|
||||||
|
throw new Exception("Request timeout: the previous request hasn't finished yet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_isBusy = true;
|
||||||
|
|
||||||
|
if (parameters.Players < 2)
|
||||||
|
parameters.Players = 2;
|
||||||
|
|
||||||
|
if (parameters.ApiKey != _config.GetValue<string>("ApiKey"))
|
||||||
|
return new StartServerResponse("Invalid ApiKey.");
|
||||||
|
|
||||||
|
string nbloodPath = _config.GetValue<string>("NBloodPath");
|
||||||
|
if (!System.IO.File.Exists(nbloodPath))
|
||||||
|
throw new Exception($"The configured path for the nblood executable is invalid.");
|
||||||
|
|
||||||
|
string processName = Path.GetFileNameWithoutExtension(nbloodPath);
|
||||||
|
int serversRunning = Process.GetProcessesByName(processName).Count();
|
||||||
|
if (serversRunning >= _config.GetValue<int>("MaximumServers"))
|
||||||
|
return new StartServerResponse("The maximum number of servers are already running.");
|
||||||
|
|
||||||
|
int port = PortUtils.GetPort();
|
||||||
|
|
||||||
|
string args = BuildArgs(parameters, port);
|
||||||
|
var process = Process.Start(nbloodPath, args);
|
||||||
|
byte[] payload = Encoding.ASCII.GetBytes($"B{port}\t{process.Id}\0");
|
||||||
|
socket.SendTo(payload, webApiListenerEndPoint);
|
||||||
|
|
||||||
|
_logger.LogInformation("Server started waiting for {0} players on port {1}.",
|
||||||
|
parameters.Players, port);
|
||||||
|
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(2));
|
||||||
|
return new StartServerResponse(port) { CommandLine = GetCommandLine(port) };
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex.ToString());
|
||||||
|
return new StartServerResponse("Unhandled exception has been occured. Check the logs for details.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_isBusy = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildArgs(ServerParameters parameters, int port)
|
||||||
|
{
|
||||||
|
string args = $"-server {parameters.Players} -port {port}";
|
||||||
|
if (parameters.IsBroadcast)
|
||||||
|
{
|
||||||
|
args += " -broadcast";
|
||||||
|
}
|
||||||
|
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[Route("[controller]/api/listservers")]
|
||||||
|
public ListServersResponse ListServers()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (DateTime.UtcNow - _lastRefresh > TimeSpan.FromSeconds(5)
|
||||||
|
|| _lastServerList == null)
|
||||||
|
{
|
||||||
|
byte[] payload = Encoding.ASCII.GetBytes($"A");
|
||||||
|
byte[] response;
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
socket.SendTo(payload, webApiListenerEndPoint);
|
||||||
|
response = udpClient.ReceiveAsync().Result.Buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
StateResponse stateResponse = (StateResponse)ByteArrayToObject(response);
|
||||||
|
var webResponse = new ListServersResponse();
|
||||||
|
webResponse.Servers = stateResponse.Servers.Where(s => !s.IsPrivate).Select(s => new Server()
|
||||||
|
{
|
||||||
|
Port = s.Port,
|
||||||
|
IsStarted = s.IsStarted,
|
||||||
|
CommandLine = s.CurrentPlayers == s.MaximumPlayers ? "Sorry, the game is already started." : GetCommandLine(s.Port),
|
||||||
|
GameType = s.GameType,
|
||||||
|
CurrentPlayers = s.CurrentPlayers,
|
||||||
|
MaximumPlayers = s.MaximumPlayers,
|
||||||
|
Players = s.Players.Select(p => new Player() { Name = p.Name, Score = p.Score }).ToList(),
|
||||||
|
SpawnedAtUtc = s.SpawnedAtUtc
|
||||||
|
}).OrderBy(s => s.MaximumPlayers).ToList();
|
||||||
|
|
||||||
|
_lastServerList = webResponse;
|
||||||
|
_lastRefresh = DateTime.UtcNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lastServerList;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex.ToString());
|
||||||
|
return new ListServersResponse("Unhandled exception has been occured. Check the logs for details.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ByteArrayToObject(byte[] arrBytes)
|
||||||
|
{
|
||||||
|
using (var memStream = new MemoryStream())
|
||||||
|
{
|
||||||
|
var binForm = new BinaryFormatter();
|
||||||
|
memStream.Write(arrBytes, 0, arrBytes.Length);
|
||||||
|
memStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
var obj = binForm.Deserialize(memStream);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetCommandLine(int port)
|
||||||
|
{
|
||||||
|
return $"nblood -client {HttpContext.Request.Host.Host} -port {port}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
WebInterface/Program.cs
Normal file
26
WebInterface/Program.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
20
WebInterface/Properties/launchSettings.json
Normal file
20
WebInterface/Properties/launchSettings.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:61021",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"WebInterface": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"applicationUrl": "http://localhost:5000",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
WebInterface/Startup.cs
Normal file
66
WebInterface/Startup.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
StartSupervisor();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StartSupervisor()
|
||||||
|
{
|
||||||
|
string nbloodPath = Configuration.GetValue<string>("NBloodPath");
|
||||||
|
bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||||
|
string supervisor = "Supervisor";
|
||||||
|
if (isWindows)
|
||||||
|
supervisor += ".exe";
|
||||||
|
if (!File.Exists(nbloodPath))
|
||||||
|
throw new Exception($"Couldn't find nblood_server at {nbloodPath}");
|
||||||
|
if (!File.Exists(supervisor))
|
||||||
|
throw new Exception($"Couldn't find {supervisor} in {Directory.GetCurrentDirectory()}");
|
||||||
|
Process.Start(supervisor, nbloodPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.AddControllers();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.UseRouting();
|
||||||
|
|
||||||
|
app.UseAuthorization();
|
||||||
|
|
||||||
|
app.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
WebInterface/ViewModel/ListServersResponse.cs
Normal file
23
WebInterface/ViewModel/ListServersResponse.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class ListServersResponse
|
||||||
|
{
|
||||||
|
public IList<Server> Servers { get; set; }
|
||||||
|
|
||||||
|
string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public ListServersResponse()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ListServersResponse(string errorMessage)
|
||||||
|
{
|
||||||
|
ErrorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
WebInterface/ViewModel/Player.cs
Normal file
13
WebInterface/ViewModel/Player.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class Player
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Score { get; set; }
|
||||||
|
}
|
||||||
|
}
|
19
WebInterface/ViewModel/Server.cs
Normal file
19
WebInterface/ViewModel/Server.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class Server
|
||||||
|
{
|
||||||
|
public DateTime SpawnedAtUtc { get; set; } = DateTime.UtcNow;
|
||||||
|
public int Port { get; set; }
|
||||||
|
public bool IsStarted { get; set; }
|
||||||
|
public string CommandLine { get; set; }
|
||||||
|
public int CurrentPlayers { get; set; }
|
||||||
|
public int MaximumPlayers { get; set; }
|
||||||
|
public string GameType { get; set; }
|
||||||
|
public IList<Player> Players { get; set; } = new List<Player>();
|
||||||
|
}
|
||||||
|
}
|
14
WebInterface/ViewModel/ServerParameters.cs
Normal file
14
WebInterface/ViewModel/ServerParameters.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class ServerParameters
|
||||||
|
{
|
||||||
|
public string ApiKey { get; set; }
|
||||||
|
public int Players { get; set; }
|
||||||
|
public bool IsBroadcast { get; set; }
|
||||||
|
}
|
||||||
|
}
|
27
WebInterface/ViewModel/StartServerResponse.cs
Normal file
27
WebInterface/ViewModel/StartServerResponse.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace WebInterface
|
||||||
|
{
|
||||||
|
public class StartServerResponse
|
||||||
|
{
|
||||||
|
public bool IsSuccess { get; set; }
|
||||||
|
public int Port { get; set; }
|
||||||
|
public string CommandLine { get; set; }
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public StartServerResponse(string errorMessage = "")
|
||||||
|
{
|
||||||
|
IsSuccess = false;
|
||||||
|
ErrorMessage = errorMessage ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public StartServerResponse(int port)
|
||||||
|
{
|
||||||
|
IsSuccess = true;
|
||||||
|
Port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
WebInterface/WebInterface.csproj
Normal file
18
WebInterface/WebInterface.csproj
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.1.0" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Common\Common.csproj" />
|
||||||
|
<ProjectReference Include="..\Model\Model.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
|
</Project>
|
9
WebInterface/appsettings.Development.json
Normal file
9
WebInterface/appsettings.Development.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
WebInterface/appsettings.json
Normal file
13
WebInterface/appsettings.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*",
|
||||||
|
"MaximumServers": 20,
|
||||||
|
"NBloodPath": "\\path\\to\\nblood_server",
|
||||||
|
"ApiKey": "!!! CHANGE ME !!!"
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user