2017-01-09 20:52:56 +00:00
using System ;
2017-01-21 18:13:42 +00:00
using System.Threading.Tasks ;
2017-07-20 12:15:11 +00:00
using System.Threading ;
2017-01-19 13:13:35 +00:00
using Nibriboard.RippleSpace ;
2017-01-21 18:13:42 +00:00
using Nibriboard.Client ;
2017-07-20 12:15:11 +00:00
using System.Net.Sockets ;
using System.Net ;
using System.IO ;
2017-01-19 13:13:35 +00:00
2017-01-09 20:52:56 +00:00
namespace Nibriboard
{
2017-01-28 18:49:26 +00:00
/// <summary>
/// The main Nibriboard server.
/// This class manages not only the connected clients, but also holds the master reference to the <see cref="Nibriboard.RippleSpace.RippleSpaceManager"/>.
/// </summary>
2017-01-10 19:52:27 +00:00
public class NibriboardServer
2017-01-09 20:52:56 +00:00
{
2017-07-20 12:15:11 +00:00
private TcpListener commandServer ;
2017-09-09 20:33:01 +00:00
private WebsocketsServer httpServer ;
2017-01-09 20:52:56 +00:00
2017-01-21 18:13:42 +00:00
private ClientSettings clientSettings ;
2017-07-23 21:02:24 +00:00
private RippleSpaceManager planeManager ;
2017-01-19 13:13:35 +00:00
2017-02-19 16:35:12 +00:00
private readonly CancellationTokenSource clientManagerCanceller = new CancellationTokenSource ( ) ;
2017-03-04 19:59:22 +00:00
private NibriClientManager clientManager ;
2017-02-19 16:35:12 +00:00
2017-07-20 12:15:11 +00:00
public readonly int CommandPort = 31587 ;
2017-01-09 20:52:56 +00:00
public readonly int Port = 31586 ;
2017-07-23 21:02:24 +00:00
public NibriboardServer ( string pathToRippleSpace , int inPort = 31586 )
2017-01-09 20:52:56 +00:00
{
2017-01-19 13:13:35 +00:00
Port = inPort ;
2017-01-09 20:52:56 +00:00
2017-07-23 21:02:24 +00:00
// Load the specified packed ripple space file if it exists - otherwise save it to disk
if ( File . Exists ( pathToRippleSpace ) ) {
planeManager = RippleSpaceManager . FromFile ( pathToRippleSpace ) . Result ;
} else {
Log . WriteLine ( "[NibriboardServer] Couldn't find packed ripple space at {0} - creating new ripple space instead." , pathToRippleSpace ) ;
planeManager = new RippleSpaceManager ( ) { SourceFilename = pathToRippleSpace } ;
}
2017-01-21 18:13:42 +00:00
clientSettings = new ClientSettings ( ) {
2017-05-04 18:53:38 +00:00
SecureWebSocket = false ,
WebSocketHost = "192.168.0.56" ,
WebSocketPort = Port ,
WebSocketPath = "/RipplespaceLink"
2017-01-21 18:13:42 +00:00
} ;
// HTTP Server setup
2017-01-09 20:52:56 +00:00
httpServer = new HttpServer ( Port ) ;
httpServer . AddHttpRequestHandler (
2017-01-10 19:51:49 +00:00
"/" ,
2017-01-21 17:06:19 +00:00
new HttpEmbeddedFileHandler ( "Nibriboard.ClientFiles" )
/ * new HttpResourceHandler (
2017-01-10 19:51:49 +00:00
Assembly . GetExecutingAssembly ( ) ,
2017-01-21 17:06:19 +00:00
"ClientFiles" ,
2017-01-10 19:52:27 +00:00
"index.html"
2017-01-21 17:06:19 +00:00
) * /
2017-01-09 20:52:56 +00:00
) ;
2017-01-21 18:13:42 +00:00
httpServer . AddHttpRequestHandler (
"/Settings.json" ,
new HttpClientSettingsHandler ( clientSettings )
) ;
// Websocket setup
2017-03-04 19:59:22 +00:00
clientManager = new NibriClientManager (
clientSettings ,
2017-03-04 21:49:51 +00:00
planeManager ,
2017-03-04 19:59:22 +00:00
clientManagerCanceller . Token
) ;
2017-01-10 19:52:27 +00:00
httpServer . AddWebSocketRequestHandler (
2017-05-04 18:53:38 +00:00
clientSettings . WebSocketPath ,
2017-03-04 19:59:22 +00:00
clientManager
2017-01-10 19:52:27 +00:00
) ;
2017-01-19 13:13:35 +00:00
}
public async Task Start ( )
{
2017-01-10 19:51:49 +00:00
httpServer . Start ( ) ;
2017-01-10 19:52:27 +00:00
Log . WriteLine ( "[NibriboardServer] Started on port {0}" , Port ) ;
2017-01-19 13:13:35 +00:00
await planeManager . StartMaintenanceMonkey ( ) ;
2017-01-10 19:52:27 +00:00
}
2017-07-20 12:15:11 +00:00
/// <summary>
/// Starts the command listener.
/// The command listener is a light tcp-based command console that allows control
/// of the Nibriboard server, since C# doesn't currently have support for signal handling.
/// It listeners on [::1] _only_, to avoid security issues.
/// In the future, a simple secret might be required to use it to aid security further.
/// </summary>
public async Task StartCommandListener ( )
{
commandServer = new TcpListener ( IPAddress . IPv6Loopback , CommandPort ) ;
commandServer . Start ( ) ;
Log . WriteLine ( "[CommandConsole] Listening on {0}." , new IPEndPoint ( IPAddress . IPv6Loopback , CommandPort ) ) ;
while ( true )
{
TcpClient nextClient = await commandServer . AcceptTcpClientAsync ( ) ;
StreamReader source = new StreamReader ( nextClient . GetStream ( ) ) ;
StreamWriter destination = new StreamWriter ( nextClient . GetStream ( ) ) { AutoFlush = true } ;
string rawCommand = await source . ReadLineAsync ( ) ;
string [ ] commandParts = rawCommand . Split ( " \t" . ToCharArray ( ) ) ;
2017-07-23 18:43:33 +00:00
Log . WriteLine ( "[CommandConsole] Client executing {0}" , rawCommand ) ;
2017-07-20 12:15:11 +00:00
try
{
switch ( commandParts [ 0 ] . Trim ( ) )
{
case "help" :
await destination . WriteLineAsync ( "NibriboardServer Command Console" ) ;
await destination . WriteLineAsync ( "================================" ) ;
await destination . WriteLineAsync ( "Available commands:" ) ;
await destination . WriteLineAsync ( " help Show this message" ) ;
await destination . WriteLineAsync ( " save Save the ripplespace to disk" ) ;
break ;
case "save" :
2017-07-29 12:20:12 +00:00
await destination . WriteAsync ( "Saving ripple space - " ) ;
2017-07-20 12:15:11 +00:00
await planeManager . Save ( ) ;
2017-07-29 12:20:12 +00:00
await destination . WriteLineAsync ( "done." ) ;
2017-07-20 12:15:11 +00:00
break ;
}
}
catch ( Exception error )
{
await destination . WriteLineAsync ( error . ToString ( ) ) ;
}
nextClient . Close ( ) ;
}
}
2017-01-09 20:52:56 +00:00
}
}