123 lines
6.4 KiB
C#
123 lines
6.4 KiB
C#
using System.Net.Sockets;
|
|
using System.Net;
|
|
|
|
namespace NotPogo
|
|
{
|
|
public class TCPGameServer(ILogger<TCPGameServer> logger, IConfiguration configuration) : BackgroundService
|
|
{
|
|
private readonly ILogger<TCPGameServer> _logger = logger;
|
|
private readonly IConfiguration _configuration = configuration;
|
|
|
|
private int _tcpPort;
|
|
|
|
public TcpListener? Listener { get; private set; }
|
|
|
|
public static List<TcpClient> Clients { get; } = [];
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_tcpPort = _configuration.GetValue<int>("GameServerPort");
|
|
Listener = new(IPAddress.Any, _tcpPort);
|
|
Listener.Start();
|
|
_logger.LogInformation("Game Server Listening On Port {Port}", _tcpPort);
|
|
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
var client = await Listener.AcceptTcpClientAsync(stoppingToken);
|
|
_logger.LogInformation("Client Connected: {RemoteEndPoint}", client.Client.RemoteEndPoint);
|
|
_ = HandleClientAsync(client);
|
|
}
|
|
}
|
|
|
|
private async Task HandleClientAsync(TcpClient client)
|
|
{
|
|
using (client)
|
|
await using (var stream = client.GetStream())
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
Dictionary<string, string> received;
|
|
try
|
|
{
|
|
received = await TCPProtocol.ReadMessageAsync(stream);
|
|
}
|
|
catch (EndOfStreamException) { break; }
|
|
|
|
_logger.LogInformation("Received:");
|
|
foreach (var kv in received)
|
|
_logger.LogInformation(" {Key} = {Value}", kv.Key, kv.Value);
|
|
|
|
if (!received.TryGetValue("msg.op", out var op)) continue;
|
|
|
|
if (op == "logn") // Login Request
|
|
{
|
|
// we know this is a compatible client now so let's add it to the client list
|
|
Clients.Add(client);
|
|
|
|
// send logn response chain
|
|
await SendAsync(stream, ("msg.op", "init"), ("plid", "1"), ("cnid", "1")); // PLID and CNID are placeholder values here
|
|
await SendAsync(stream, ("msg.op", "load"), ("rmst", "start")); // indicate the backend is putting you in a room
|
|
await SendAsync(stream, ("msg.op", "rm.join"), ("scrn", "standalone"), ("plid", "1")); // SCRN and PLID are placeholder values here (PLID = Room ID?)
|
|
await SendAsync(stream, ("msg.op", "gm.crea")); // I don't know what this does but apparently it's required
|
|
await SendAsync(stream, ("msg.op", "load"), ("rmst", "end")); // room placement complete
|
|
await SendAsync(stream, ("msg.op", "say"), ("chnl", "serv"), ("text", "Welcome to Not Pogo! The best place to go for games and friends!")); // MOTD
|
|
await SendAsync(stream, ("msg.op", "say"), ("chnl", "serv"), ("text", "WARNING: This Emulator Is A WIP. Some Things May Not Work As Expected Even With Proper Client Props")); // Alpha Disclaimer
|
|
await SendAsync(stream, ("msg.op", "pl.rm.stte"), ("plid", "1"), ("tokb", "9999")); // PLID and TOKB are placeholder values here
|
|
_logger.LogInformation("Sent 'logn' Response Sequence");
|
|
}
|
|
else if (op == "gm.join") // Room Join Request
|
|
{
|
|
// send gm.join response chain
|
|
await SendAsync(stream, ("msg.op", "gm.join"), ("plid", "1"), ("scrn", "standalone")); // yay more placeholders!
|
|
await SendAsync(stream, ("msg.op", "jcfg"), ("jpot", "1234.56"), ("jmlt", "1.0"), ("cap", "4999.0"), ("intv", "10000"), ("incr", "0.01")); // more than likely jackpot config
|
|
await SendAsync(stream, ("msg.op", "cnfg"), ("impd", "15")); // placeholder, idk what this is
|
|
await SendAsync(stream, ("msg.op", "tokb"), ("tokb", "9999")); // token balance again for whatever reason
|
|
await SendAsync(stream, ("msg.op", "gm.lnch")); // more than likely this tells the shell to launch the game
|
|
await SendAsync(stream, ("msg.op", "ext.pref"), ("pref", "")); // game preferences
|
|
_logger.LogInformation("Sent 'gm.join' Response Sequence (Launches Game)");
|
|
}
|
|
else if (op == "ext.over")
|
|
{
|
|
bool winMet = (received["gm.won"] == "true");
|
|
|
|
if (winMet)
|
|
_logger.LogInformation("Jackpot Won!");
|
|
|
|
// send stubbed results
|
|
await SendAsync(stream, ("msg.op", "ext.rslt"), ("tokb", "999"), ("tokn.won", "0"), // Token Earnings
|
|
("spin.rslt", winMet ? "1" : "0"), ("spin.tokn", "50"), ("inst.desc", ""), // Jackpot?
|
|
("jpot", "1234.0"), ("pzpm", $"conf={Guid.NewGuid()}"), ("bmlt", "1"), ("bspo", "")); // Jackpot Confirmation Stuff - placeholder
|
|
|
|
_logger.LogInformation("Results Sent");
|
|
}
|
|
}
|
|
} catch (Exception e) { _logger.LogError(e, "Error Handling Client"); }
|
|
}
|
|
|
|
Clients.Remove(client);
|
|
_logger.LogInformation("Client Disconnected");
|
|
}
|
|
|
|
static Task SendAsync(NetworkStream stream, params (string key, string value)[] fields)
|
|
{
|
|
var msg = new Dictionary<string, string>();
|
|
foreach (var (key, value) in fields) msg[key] = value;
|
|
return TCPProtocol.WriteMessageAsync(stream, msg);
|
|
}
|
|
|
|
public override Task StopAsync(CancellationToken cancellationToken)
|
|
{
|
|
if(!cancellationToken.IsCancellationRequested)
|
|
{
|
|
Clients.Clear();
|
|
Listener?.Stop();
|
|
Listener?.Dispose();
|
|
}
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
}
|
|
}
|