1
0
Fork 0
Nibriboard/Nibriboard/CommandConsole.cs

210 lines
8.0 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Nibriboard.Client;
using Nibriboard.RippleSpace;
namespace Nibriboard
{
public class CommandConsole
{
private NibriboardServer server;
private TcpListener commandServer;
private int commandPort;
public CommandConsole(NibriboardServer inServer, int inCommandPort)
{
server = inServer;
commandPort = inCommandPort;
}
public async Task Start()
{
commandServer = new TcpListener(IPAddress.IPv6Loopback, server.CommandPort);
commandServer.Start();
Log.WriteLine("[CommandConsole] Listening on {0}.", new IPEndPoint(IPAddress.IPv6Loopback, server.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());
Log.WriteLine("[CommandConsole] Client executing {0}", rawCommand);
try
{
await executeCommand(destination, commandParts);
}
catch(Exception error)
{
try
{
await destination.WriteLineAsync(error.ToString());
}
catch { nextClient.Close(); } // Make absolutely sure that the command server won't die
}
nextClient.Close();
}
}
private async Task executeCommand(StreamWriter destination, string[] commandParts)
{
string commandName = commandParts[0].Trim();
switch(commandName)
{
case "help":
await destination.WriteLineAsync("Nibriboard Server Command Console");
await destination.WriteLineAsync("=================================");
await destination.WriteLineAsync("Available commands:");
await destination.WriteLineAsync(" help Show this message");
await destination.WriteLineAsync(" version Show the version of nibriboard that is currently running");
await destination.WriteLineAsync(" save Save the ripplespace to disk");
await destination.WriteLineAsync(" plane {subcommand} Interact with planes");
await destination.WriteLineAsync(" clients List the currently connected clients");
break;
case "version":
await destination.WriteLineAsync($"Nibriboard Server {NibriboardServer.Version}, built on {NibriboardServer.BuildDate.ToString("R")}");
await destination.WriteLineAsync("By Starbeamrainbowlabs, licensed under MPL-2.0");
break;
case "save":
await destination.WriteAsync("Saving ripple space - ");
await server.PlaneManager.Save();
await destination.WriteLineAsync("done.");
await destination.WriteLineAsync($"Save is now {BytesToString(server.PlaneManager.LastSaveFileSize)} in size.");
break;
case "plane":
if(commandParts.Length < 2) {
await destination.WriteLineAsync("Nibriboard Server Command Console: plane");
await destination.WriteLineAsync("----------------------------------------");
await destination.WriteLineAsync("Interact with planes.");
await destination.WriteLineAsync("Usage:");
await destination.WriteLineAsync(" plane {subcommand}");
await destination.WriteLineAsync();
await destination.WriteLineAsync("Subcommands:");
await destination.WriteLineAsync(" list");
await destination.WriteLineAsync(" List all the currently loaded planes");
await destination.WriteLineAsync(" create {new-plane-name} [{chunkSize}]");
await destination.WriteLineAsync(" Create a new named plane, optionally with the specified chunk size");
await destination.WriteLineAsync(" status {plane-name}");
await destination.WriteLineAsync(" Show the statistics of the specified plane");
break;
}
string subAction = commandParts[1].Trim();
switch(subAction)
{
case "list":
await destination.WriteLineAsync("Planes:");
foreach(Plane plane in server.PlaneManager.Planes)
await destination.WriteLineAsync($" {plane.Name} @ {plane.ChunkSize} ({plane.LoadedChunks} / ~{plane.SoftLoadedChunkLimit} chunks loaded, {plane.UnloadableChunks} inactive, {plane.TotalChunks} total at last save)");
await destination.WriteLineAsync();
await destination.WriteLineAsync($"Total {server.PlaneManager.Planes.Count}");
break;
case "create":
if(commandParts.Length < 3) {
await destination.WriteLineAsync("Error: No name specified for the new plane!");
return;
}
string newPlaneName = commandParts[2].Trim();
int chunkSize = server.PlaneManager.DefaultChunkSize;
if(commandParts.Length >= 4)
chunkSize = int.Parse(commandParts[3]);
server.PlaneManager.CreatePlane(new PlaneInfo(
newPlaneName,
chunkSize
));
await destination.WriteLineAsync($"Created plane with name {newPlaneName} and chunk size {chunkSize}.");
break;
case "status":
if(commandParts.Length < 3) {
await destination.WriteLineAsync("Error: No plane name specified!");
return;
}
string targetPlaneName = commandParts[2].Trim();
Plane targetPlane = server.PlaneManager.GetByName(targetPlaneName);
if(targetPlane == null) {
await destination.WriteLineAsync($"Error: A plane with the name {targetPlaneName} doesn't exist.");
return;
}
await destination.WriteLineAsync($"Name: {targetPlane.Name}");
await destination.WriteLineAsync($"Chunk size: {targetPlane.ChunkSize}");
await destination.WriteLineAsync($"Loaded chunks: {targetPlane.LoadedChunks}");
await destination.WriteLineAsync($"Unloaded chunks: {targetPlane.TotalChunks - targetPlane.LoadedChunks}");
await destination.WriteLineAsync($"Total chunks: {targetPlane.TotalChunks}");
await destination.WriteLineAsync($"Primary chunk area size: {targetPlane.PrimaryChunkAreaSize}");
await destination.WriteLineAsync($"Min unloadeable chunks: {targetPlane.MinUnloadeableChunks}");
await destination.WriteLineAsync($"Soft loaded chunk limit: {targetPlane.SoftLoadedChunkLimit}");
break;
default:
await destination.WriteLineAsync($"Error: Unknown sub-action {subAction}.");
break;
}
break;
case "clients":
foreach(NibriClient client in server.AppServer.NibriClients) {
await destination.WriteLineAsync($"{client.Id}: {client.Name} from {client.RemoteEndpoint}, on {client.CurrentPlane.Name} looking at {client.CurrentViewPort}");
}
await destination.WriteLineAsync();
await destination.WriteLineAsync($"Total {server.AppServer.ClientCount} clients");
break;
/*case "chunk":
if(commandParts.Length < 2) {
await destination.WriteLineAsync("Error: No sub-action specified.");
break;
}
string chunkSubAction = commandParts[1].Trim();
switch(chunkSubAction)
{
case "list":
if(commandParts.Length < 3) {
await destination.WriteLineAsync("Error: No plane specified to list the chunks of!");
return;
}
Plane plane = server.PlaneManager.GetByName(commandParts[2].Trim());
foreach(Chunk chunk in plane.
break;
default:
await destination.WriteLineAsync($"Error: Unknown sub-action {chunkSubAction}.");
break;
}
break;*/
default:
await destination.WriteLineAsync($"Error: Unrecognised command {commandName}");
break;
}
}
public static string BytesToString(long byteCount)
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; // Longs run out around EB
if(byteCount == 0)
return "0" + suf[0];
long bytes = Math.Abs(byteCount);
int place = (int)Math.Floor(Math.Log(bytes, 1024));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}
}
}