using System.Collections.Concurrent; using System.Diagnostics; using System.Net.Sockets; using BSO.Net; using BSO.Server; // BSO.Server [--port 7777] [--name "Mein Server"] [--description "..."] [--max 8] [--bot] // [--private] [--public-address host] [--master https://bso.mbm.wtf] [--no-auth] // BSO.Server --probe host[:port] (Test: verbindet sich wie die Mod und zeigt, was ankommt) int port = Protocol.DefaultPort, max = 8; string name = "BSO Server", description = "", probe = null, publicAddress = null, master = MasterClient.DefaultUrl; bool bot = false, listed = true, auth = true; for (int i = 0; i < args.Length; i++) { switch (args[i]) { case "--port": port = int.Parse(args[++i]); break; case "--name": name = args[++i]; break; case "--description": description = args[++i]; break; case "--max": max = int.Parse(args[++i]); break; case "--bot": bot = true; break; case "--private": listed = false; break; // nicht in der Serverliste case "--public-address": publicAddress = args[++i]; break; // Adresse fuer die Liste (hinter NAT/Proxy) case "--master": master = args[++i]; break; // andere Website (Tests: http://localhost:5080) case "--no-auth": auth = false; break; // ohne BSO-Konto (LAN, Tests) case "--probe": probe = args[++i]; break; default: Console.WriteLine("BSO.Server [--port 7777] [--name \"Name\"] [--description \"...\"] [--max 8] [--bot] [--private]\n" + " [--public-address host] [--master url] [--no-auth] | --probe host[:port]"); return 1; } } if (probe != null) return Probe(probe); var server = new GameServer(port, name, max) { RequireAccount = auth }; var masterClient = new MasterClient(master, server, port, listed, publicAddress, description); server.Master = masterClient; if (auth) masterClient.LoadTicketKeyAsync().Wait(); else GameServer.Log("--no-auth: Spieler brauchen kein BSO-Konto, Namen sind frei waehlbar"); masterClient.Start(); if (bot) server.AddBot(); GameServer.Log("Befehle: help, status, bot, kick , stop"); var commands = new ConcurrentQueue(); new Thread(() => { string line; while ((line = Console.ReadLine()) != null) commands.Enqueue(line); }) { IsBackground = true, Name = "Konsole" }.Start(); bool running = true; Console.CancelKeyPress += (_, e) => { e.Cancel = true; running = false; }; while (running) { server.Tick(); while (commands.TryDequeue(out string cmd)) { if (cmd.Trim().Equals("stop", StringComparison.OrdinalIgnoreCase)) running = false; else server.Command(cmd); } Thread.Sleep(5); } masterClient.Stop(); server.Stop(); GameServer.Log("Server beendet"); return 0; /// Verbindet sich wie die Mod (Info, dann Hello als "Probe") und zeigt 5 s lang alles, was ankommt. static int Probe(string target) { string[] hp = target.Split(':'); int p = hp.Length > 1 ? int.Parse(hp[1]) : Protocol.DefaultPort; var tcp = new TcpClient(); if (!tcp.ConnectAsync(hp[0], p).Wait(5000)) { Console.WriteLine("Keine Verbindung zu " + target); return 2; } var conn = new Connection(tcp); conn.Send(MsgType.Info, null); conn.Send(MsgType.Hello, Wire.Build(w => { w.Write(Protocol.Version); w.Write("probe"); w.Write("Probe"); w.Write(Environment.GetEnvironmentVariable("BSO_PROBE_TICKET") ?? ""); })); conn.Send(MsgType.Ping, Wire.Build(w => w.Write(Stopwatch.GetTimestamp()))); // Eigene Position, damit der Testbot losfaehrt var me = new PlayerState { Mode = PlayerMode.InBus, X = 70000, Y = 30000, Z = 760 }; var watch = Stopwatch.StartNew(); int states = 0; while (watch.Elapsed.TotalSeconds < 5 && !conn.IsClosed) { conn.Send(MsgType.PlayerState, Wire.Build(w => me.Write(w))); foreach (Message m in conn.Poll()) { BinaryReader r = m.Reader(); switch (m.Type) { case MsgType.InfoReply: Console.WriteLine($"Info: \"{r.ReadString()}\" Version {r.ReadString()}, {r.ReadInt32()}/{r.ReadInt32()} Spieler"); break; case MsgType.Welcome: int id = r.ReadInt32(); string server = r.ReadString(); int n = r.ReadInt32(); var list = Enumerable.Range(0, n).Select(_ => $"#{r.ReadInt32()} {r.ReadString()}").ToList(); Console.WriteLine($"Welcome: ich bin #{id} auf \"{server}\", andere: {(n == 0 ? "-" : string.Join(", ", list))}"); break; case MsgType.Reject: Console.WriteLine("Abgelehnt: " + r.ReadString()); break; case MsgType.Pong: Console.WriteLine($"Ping: {Stopwatch.GetElapsedTime(r.ReadInt64()).TotalMilliseconds:0.0} ms"); break; case MsgType.PlayerJoined: Console.WriteLine($"Beigetreten: #{r.ReadInt32()} {r.ReadString()}"); break; case MsgType.PlayerLeft: Console.WriteLine($"Gegangen: #{r.ReadInt32()}"); break; case MsgType.PlayerState: int who = r.ReadInt32(); PlayerState s = PlayerState.Read(r); if (states++ % 10 == 0) Console.WriteLine($"Zustand #{who}: {s.Mode} {s.X / 100:0.0} / {s.Y / 100:0.0} / {s.Z / 100:0.0} m, {s.Speed * 0.036f:0} km/h, {s.DistanceTo(me) / 100:0.0} m entfernt"); break; } } Thread.Sleep(100); } Console.WriteLine($"{states} Zustaende in 5 s" + (conn.IsClosed ? " - getrennt: " + conn.CloseReason : "")); conn.Close("Probe fertig"); return 0; }