diff --git a/PixelServer/DiscoveryBeacon.cs b/PixelServer/DiscoveryBeacon.cs index 46ea2c7..13ed641 100644 --- a/PixelServer/DiscoveryBeacon.cs +++ b/PixelServer/DiscoveryBeacon.cs @@ -19,13 +19,19 @@ namespace PixelServer.Net { TextWriter logger; UdpClient beacon; + int emitFrequency = 2000; + string role = "server"; + /// /// Whether the beacon is currently active. /// If true then the beacon needs disposing of properly by calling the Dispose() method before the program ends. /// public bool Active { get; private set; } = true; + /// + /// Whether the logging output should be verbose. + /// + public bool Verbose { get; private set; } = false; - string role = "server"; /// /// The current IP address of the local machine. @@ -47,7 +53,9 @@ namespace PixelServer.Net /// The port that the beacon should broadcast on. /// public int Port { get; set; } = 5050; - + /// + /// The role that the program hosted at this beacon plays. Default: server. + /// public string Role { get{ return role; @@ -60,9 +68,17 @@ namespace PixelServer.Net } /// - /// The frequency at which the beacon should emit broadcasts. + /// The time, in milliseconds, that the beacon should wait between broadcasts. /// - public int EmitFrequency { get; set; } = 2000; + public int EmitFrequency { + get { + return emitFrequency; + } + set { + emitFrequency = value; + logger.WriteLine("Beacon broadcast frequency changed to {0}ms.", EmitFrequency); + } + } /// /// Initializes a new instance of the class. @@ -103,7 +119,7 @@ namespace PixelServer.Net public async Task Emit() { connect(); - + logger.WriteLine("Beacon activated with broadcast interval of {0}ms.", EmitFrequency); while(true) { await emitOnce(); @@ -129,6 +145,7 @@ namespace PixelServer.Net beacon = new UdpClient(Port); Active = true; beacon.JoinMulticastGroup(DestinationAddress); + logger.WriteLine("One way beacon set up pointing at {0}.", MulticastChannel); } /// @@ -154,7 +171,7 @@ namespace PixelServer.Net /// The message payload. private async Task Send(IPEndPoint destination, string data) { - logger.WriteLine("Sending '{0}' to {1}.", Utilities.EscapeString(data), destination); + if(Verbose) logger.WriteLine("Sending '{0}' to {1}.", Utilities.EscapeString(data), destination); byte[] payload = Encoding.UTF8.GetBytes(data); await Send(destination, payload); } @@ -182,7 +199,12 @@ namespace PixelServer.Net /// was occupying. public void Dispose() { - beacon.Close(); + if(Active) + { + beacon.Close(); + Active = false; + } + } } } diff --git a/PixelServer/PixelServer.cs b/PixelServer/PixelServer.cs new file mode 100644 index 0000000..a595eb5 --- /dev/null +++ b/PixelServer/PixelServer.cs @@ -0,0 +1,94 @@ +using System; +using System.Net.Sockets; +using System.Threading.Tasks; +using System.Net; +using SBRL.Utilities; +using System.IO; + +namespace PixelServer +{ + /// + /// Provides the server component of the Hull Pixelbot server that can be used to control any number of pixel bots at once. + /// + public class PixelServer + { + TextWriter logger; + IPAddress bindAddress = IPAddress.Any; + int port; + + TcpListener server; + /// + /// Whether the Hull Pixelbot serve is currently active and listening. + /// + public bool Active { get; private set; } = false; + public IPAddress BindAddress + { + get { + return bindAddress; + } + set { + if (Active == true) + throw new InvalidOperationException("Error: The bind address can't be changed once the server has started listening!"); + bindAddress = value; + } + } + public int Port + { + get { + return port; + } + set { + if (Active == true) + throw new InvalidOperationException("Error: The port can't be changed once the server has started listening!"); + port = value; + } + } + public IPEndPoint Endpoint + { + get { + return new IPEndPoint(BindAddress, Port); + } + } + + public PixelServer(int inPort, TextWriter inLogger) + { + Port = inPort; + logger = inLogger; + } + public PixelServer(int inPort) : this(inPort, Console.Out) + { + } + + public async Task Listen() + { + server = new TcpListener(Endpoint); + logger.WriteLine("TCP Listener set up on {0}.", Endpoint); + Active = true; + while(true) + { + TcpClient nextClient = await server.AcceptTcpClientAsync(); + AsyncTools.ForgetTask(Handle(nextClient)); + } + // TODO: Add an shutdown CancellationToken thingy here + //Active = false; + } + + private async Task Handle(TcpClient client) + { + logger.WriteLine("Accepted connection from {0}.", client.Client.RemoteEndPoint); + + using (StreamReader incoming = new StreamReader(client.GetStream())) + using (StreamWriter outgoing = new StreamWriter(client.GetStream()) { AutoFlush = true }) + { + string nextLine; + while((nextLine = await incoming.ReadLineAsync()) != null) + { + Console.WriteLine("Got message from client: '{0}'", nextLine.Trim()); + } + } + + Console.WriteLine("Lost connection from {0}.", client.Client.RemoteEndPoint); + } + } +} + diff --git a/PixelServer/PixelServer.csproj b/PixelServer/PixelServer.csproj index 66a53b8..9052bb6 100644 --- a/PixelServer/PixelServer.csproj +++ b/PixelServer/PixelServer.csproj @@ -38,6 +38,8 @@ + + \ No newline at end of file diff --git a/PixelServer/PrefixedWriter.cs b/PixelServer/PrefixedWriter.cs new file mode 100644 index 0000000..9c6b623 --- /dev/null +++ b/PixelServer/PrefixedWriter.cs @@ -0,0 +1,67 @@ +using System; +using System.IO; +using System.Text; + +namespace SBRL.Utilities +{ + public class PrefixedWriter : TextWriter, IDisposable + { + private TextWriter destination; + public string Prefix { get; set; } = ""; + + public override Encoding Encoding { + get { + return Encoding.UTF8; + } + } + + public PrefixedWriter(TextWriter inDestination) + { + destination = inDestination; + } + + public override void Write(string str, params object[] args) + { + destination.Write(Prefix + str, args); + } + public override void Write(string str) + { + Write(str, new object[] {}); + } + public override void Write(string str, object arg1) + { + Write(str, new object[] { arg1 }); + } + public override void Write(string str, object arg1, object arg2) + { + Write(str, new object[] { arg1, arg2 }); + } + public override void Write(string str, object arg1, object arg2, object arg3) + { + Write(str, new object[] { arg1, arg2, arg3 }); + } + + + public override void WriteLine(string str, params object[] args) + { + Write(str + Environment.NewLine, args); + } + public override void WriteLine(string str) + { + WriteLine(str, new object[] {}); + } + public override void WriteLine(string str, object arg1) + { + WriteLine(str, new object[] { arg1 }); + } + public override void WriteLine(string str, object arg1, object arg2) + { + WriteLine(str, new object[] { arg1, arg2 }); + } + public override void WriteLine(string str, object arg1, object arg2, object arg3) + { + WriteLine(str, new object[] { arg1, arg2, arg3 }); + } + } +} + diff --git a/PixelServer/Program.cs b/PixelServer/Program.cs index bf50619..92e711c 100644 --- a/PixelServer/Program.cs +++ b/PixelServer/Program.cs @@ -1,22 +1,34 @@ using System; -using PixelServer.Net; using System.Net; using System.Threading.Tasks; using SBRL.Utilities; -using System.Net.NetworkInformation; -using System.Net.Sockets; +using PixelServer.Net; namespace PixelServer { - class MainClass + static class Program { + private static int port = 5050; + private static PrefixedWriter systemWriter = new PrefixedWriter(Console.Out) { Prefix = "[System] " }; + private static PrefixedWriter beaconWriter = new PrefixedWriter(Console.Out) { Prefix = "[Beacon] " }; + private static PrefixedWriter serverWriter = new PrefixedWriter(Console.Out) { Prefix = "[Server] " }; + public static void Main(string[] args) { - DiscoveryBeacon beacon = new DiscoveryBeacon(IPAddress.Parse("239.62.148.30")); + systemWriter.WriteLine("Booting server."); + DiscoveryBeacon beacon = new DiscoveryBeacon( + IPAddress.Parse("239.62.148.30"), port, + beaconWriter + ); + PixelServer server = new PixelServer(port, serverWriter); + systemWriter.WriteLine("Server booting complete. Beginning async loop."); //AsyncTools.ForgetTask(beacon.Emit()); - beacon.Emit().Wait(); + Task.WaitAll(new Task[] { + beacon.Emit(), + server.Listen() + }); } } }