diff --git a/README.md b/README.md index d605250..0412ea3 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,51 @@ + # WebConsole WebConsole is a Spigot plugin for Minecraft 1.14 that enables you to view your server console and manage your server from anywhere. It creates a WebSocket server in the background used by the web interface to send commands, receive your console log and manage your server. + Dont worry about privacy: all data is stored in your browser offline and your PC will connect directly to your minecraft server. No intermediary web servers, just you and your server. + ## Plugin installation + 1. Plugin download 2. Filling config.yml. Port and password configuration + ## How it works + 1. How to install web interface / connect to github pages 2. How to add servers + ## Technical information ### WebSocket commands + The following tables represent how server communicates with the client(s), something like a language between them. + #### Websocket Server -> Client -| Code |Meaning | -|---------------------|--------------------------------------| -|200 *(message)* |Query was processed with no errors | -|403 *(message)* |You are not allowed to do that action | -|LOG *(message)* |Message is a console output | + +Server communicate with all connected clients using JSON. The following table shows all possible JSON variables. +| Variable |Meaning | +|---------------------|-----------------------------------------------------------------------------| +| status |Status code (as integer), representing response type. See table below | +| statusDescription |Status description (as String) describing status code | +| respondsTo |`(Optional)` Original command sent by client which triggered this response| +| message |Response content | + +*Status codes are listed below: + - **010**: Console output. + - **200**: Ok/Processed. + - **400**: Unknown command. + - **401**: Login required/Not logged in. + - **403**: Forbidden/Unauthorised (Probably not logged in). #### Client -> Websocket Server + +Clients can communicate with server using commands. The following table shows existing commands. | Code |Meaning |Extra info | |---------------------|-----------------------------------------|--------------| |LOGIN *(password)* |Login to start communication with server | | diff --git a/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java b/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java index 6f3fa99..7bc0ee6 100644 --- a/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java +++ b/src/com/mesacarlos/webconsole/command/ExecuteCmdCommand.java @@ -8,20 +8,22 @@ import org.java_websocket.WebSocket; import com.mesacarlos.webconsole.websockets.WSServer; -public class ExecuteCmdCommand implements WSCommand{ +public class ExecuteCmdCommand implements WSCommand { @Override public void execute(WSServer wsServer, WebSocket conn, String command) { + + Bukkit.getLogger().info("[WebConsole] " + conn.getRemoteSocketAddress() + " executed '" + command + "'"); ConsoleCommandSender sender = Bukkit.getServer().getConsoleSender(); - + try { @SuppressWarnings("unused") - boolean success = Bukkit.getScheduler().callSyncMethod( wsServer.getMainClass(), () -> Bukkit.dispatchCommand( sender, command ) ).get(); + boolean success = Bukkit.getScheduler() + .callSyncMethod(wsServer.getMainClass(), () -> Bukkit.dispatchCommand(sender, command)).get(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } - } } \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/command/LogInCommand.java b/src/com/mesacarlos/webconsole/command/LogInCommand.java index fb98c52..8162770 100644 --- a/src/com/mesacarlos/webconsole/command/LogInCommand.java +++ b/src/com/mesacarlos/webconsole/command/LogInCommand.java @@ -3,29 +3,32 @@ package com.mesacarlos.webconsole.command; import org.bukkit.Bukkit; import org.java_websocket.WebSocket; +import com.mesacarlos.webconsole.json.Forbidden; +import com.mesacarlos.webconsole.json.Processed; import com.mesacarlos.webconsole.util.LoginManager; import com.mesacarlos.webconsole.websockets.WSServer; -public class LogInCommand implements WSCommand{ +public class LogInCommand implements WSCommand { @Override public void execute(WSServer wsServer, WebSocket conn, String password) { - //If user is logged in, then return. - if(LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString())) + // If user is logged in, then return. + if (LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString())) return; - - //Get password from config files + + // Get password from config files String receivedPassword = wsServer.getMainClass().getConfig().getString("password"); - - if(receivedPassword.equals(password)) { - //Password is correct, logging in + + if (receivedPassword.equals(password)) { + // Password is correct, logging in LoginManager.getInstance().logIn(conn.getRemoteSocketAddress().getAddress().toString()); - conn.send("200 Logged In"); + wsServer.sendToClient(conn, new Processed("Logged In", "LOGIN ********")); Bukkit.getLogger().info("[WebConsole] Successfully logged in from " + conn.getRemoteSocketAddress()); - }else { - conn.send("403 Forbidden"); - Bukkit.getLogger().info("[WebConsole] Password incorrect while login from " + conn.getRemoteSocketAddress()); + } else { + wsServer.sendToClient(conn, new Forbidden("Forbidden","LOGIN ********")); + Bukkit.getLogger() + .info("[WebConsole] Password incorrect while login from " + conn.getRemoteSocketAddress()); } } - + } \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/ConsoleOutput.java b/src/com/mesacarlos/webconsole/json/ConsoleOutput.java new file mode 100644 index 0000000..625ba64 --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/ConsoleOutput.java @@ -0,0 +1,36 @@ +package com.mesacarlos.webconsole.json; + +import com.google.gson.JsonObject; + +public class ConsoleOutput implements JSONOutput{ + private String message; + + public ConsoleOutput(String message) { + this.message = message; + } + + @Override + public int getStatusCode() { + return 10; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getRespondsTo() { + return null; + } + + @Override + public String toJSON() { + JsonObject object = new JsonObject(); + object.addProperty("status", getStatusCode()); + object.addProperty("statusDescription", "Console Output"); + object.addProperty("message", getMessage()); + return object.toString(); + } + +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/Forbidden.java b/src/com/mesacarlos/webconsole/json/Forbidden.java new file mode 100644 index 0000000..bb33270 --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/Forbidden.java @@ -0,0 +1,39 @@ +package com.mesacarlos.webconsole.json; + +import com.google.gson.JsonObject; + +public class Forbidden implements JSONOutput{ + private String message; + private String respondsTo; + + public Forbidden(String message, String respondsTo) { + this.message = message; + this.respondsTo = respondsTo; + } + + @Override + public int getStatusCode() { + return 403; + } + + @Override + public String getRespondsTo() { + return respondsTo; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String toJSON() { + JsonObject object = new JsonObject(); + object.addProperty("status", getStatusCode()); + object.addProperty("statusDescription", "Forbidden"); + object.addProperty("respondsTo", getRespondsTo()); + object.addProperty("message", getMessage()); + return object.toString(); + } + +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/JSONOutput.java b/src/com/mesacarlos/webconsole/json/JSONOutput.java new file mode 100644 index 0000000..b73722b --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/JSONOutput.java @@ -0,0 +1,28 @@ +package com.mesacarlos.webconsole.json; + +public interface JSONOutput { + /** + * Gets status code representing this message. See docs for code meanings. + * @return Status code representing this message + */ + int getStatusCode(); + + /** + * Returns the command sended by client who created this response. + * In case of a server-generated response (like ConsoleOutput), this will be null + * @return + */ + String getRespondsTo(); + + /** + * Explanatory message of this response + * @return Explanatory message of this response + */ + String getMessage(); + + /** + * Coverts this object into JSON, ready to send it over WS + * @return JSON Object Stringified + */ + String toJSON(); +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/LoginRequired.java b/src/com/mesacarlos/webconsole/json/LoginRequired.java new file mode 100644 index 0000000..b128a21 --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/LoginRequired.java @@ -0,0 +1,36 @@ +package com.mesacarlos.webconsole.json; + +import com.google.gson.JsonObject; + +public class LoginRequired implements JSONOutput{ + private String message; + + public LoginRequired(String message) { + this.message = message; + } + + @Override + public int getStatusCode() { + return 401; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getRespondsTo() { + return null; + } + + @Override + public String toJSON() { + JsonObject object = new JsonObject(); + object.addProperty("status", getStatusCode()); + object.addProperty("statusDescription", "Login Required"); + object.addProperty("message", getMessage()); + return object.toString(); + } + +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/Processed.java b/src/com/mesacarlos/webconsole/json/Processed.java new file mode 100644 index 0000000..e2231dc --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/Processed.java @@ -0,0 +1,39 @@ +package com.mesacarlos.webconsole.json; + +import com.google.gson.JsonObject; + +public class Processed implements JSONOutput{ + private String message; + private String respondsTo; + + public Processed(String message, String respondsTo) { + this.message = message; + this.respondsTo = respondsTo; + } + + @Override + public int getStatusCode() { + return 200; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getRespondsTo() { + return respondsTo; + } + + @Override + public String toJSON() { + JsonObject object = new JsonObject(); + object.addProperty("status", getStatusCode()); + object.addProperty("statusDescription", "Processed"); + object.addProperty("respondsTo", getRespondsTo()); + object.addProperty("message", getMessage()); + return object.toString(); + } + +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/json/UnknownWSCmd.java b/src/com/mesacarlos/webconsole/json/UnknownWSCmd.java new file mode 100644 index 0000000..bbc2201 --- /dev/null +++ b/src/com/mesacarlos/webconsole/json/UnknownWSCmd.java @@ -0,0 +1,38 @@ +package com.mesacarlos.webconsole.json; + +import com.google.gson.JsonObject; + +public class UnknownWSCmd implements JSONOutput{ + private String message; + private String respondsTo; + + public UnknownWSCmd(String message, String respondsTo) { + this.message = message; + this.respondsTo = respondsTo; + } + + @Override + public int getStatusCode() { + return 400; + } + + @Override + public String getMessage() { + return message; + } + + @Override + public String getRespondsTo() { + return respondsTo; + } + + @Override + public String toJSON() { + JsonObject object = new JsonObject(); + object.addProperty("status", getStatusCode()); + object.addProperty("statusDescription", "Unknown Command"); + object.addProperty("respondsTo", getRespondsTo()); + object.addProperty("message", getMessage()); + return object.toString(); + } +} \ No newline at end of file diff --git a/src/com/mesacarlos/webconsole/websockets/WSServer.java b/src/com/mesacarlos/webconsole/websockets/WSServer.java index 56ad7d2..0c3183e 100644 --- a/src/com/mesacarlos/webconsole/websockets/WSServer.java +++ b/src/com/mesacarlos/webconsole/websockets/WSServer.java @@ -12,11 +12,16 @@ import org.java_websocket.server.WebSocketServer; import com.mesacarlos.webconsole.WebConsole; import com.mesacarlos.webconsole.command.CommandFactory; import com.mesacarlos.webconsole.command.WSCommand; +import com.mesacarlos.webconsole.json.ConsoleOutput; +import com.mesacarlos.webconsole.json.Forbidden; +import com.mesacarlos.webconsole.json.JSONOutput; +import com.mesacarlos.webconsole.json.LoginRequired; +import com.mesacarlos.webconsole.json.UnknownWSCmd; import com.mesacarlos.webconsole.util.LoginManager; public class WSServer extends WebSocketServer { - private WebConsole plugin; private HashMap commands = CommandFactory.getCommandsHashMap(); + private WebConsole plugin; public WSServer(WebConsole plugin, InetSocketAddress address) { super(address); @@ -25,15 +30,12 @@ public class WSServer extends WebSocketServer { @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { - conn.send("Connection started, waiting login"); + sendToClient(conn, new LoginRequired("Connection started, waiting login")); Bukkit.getLogger().info("[WebConsole] Connected and waiting login from " + conn.getRemoteSocketAddress()); } @Override public void onMessage(WebSocket conn, String message) { - // Log this action to console - Bukkit.getLogger().info("[WebConsole] Received signal from " + conn.getRemoteSocketAddress() + ": " + message); - // Get command and params String wsCommand = message.split(" ")[0]; String wsCommandParams = ""; @@ -44,14 +46,16 @@ public class WSServer extends WebSocketServer { WSCommand cmd = commands.get(wsCommand); if (cmd == null) { + //Command does not exist + sendToClient(conn, new UnknownWSCmd("Unknown command", message)); Bukkit.getLogger().info( - "[WebConsole] Signal was not processed since is not valid. Is your plugin/web interface up to date?"); + "[WebConsole] Signal '" + message + "' was not processed since is not valid. Is your plugin/web interface up to date?"); } else if (!LoginManager.getInstance().isLoggedIn(conn.getRemoteSocketAddress().getAddress().toString()) && !wsCommand.equals("LOGIN")) { - // DO NOTHING. User is not authorised - conn.send("403 Forbidden"); + //User is not authorised. DO NOTHING, IMPORTANT! + sendToClient(conn, new Forbidden("Forbidden", message)); Bukkit.getLogger().warning("[WebConsole] " + conn.getRemoteSocketAddress() - + " tried to run a command while not authenticated!"); + + " tried to run '" + message + "' while not logged in!"); } else { cmd.execute(this, conn, wsCommandParams); } @@ -72,9 +76,17 @@ public class WSServer extends WebSocketServer { @Override public void onStart() { - Bukkit.getLogger().info("[WebConsole] WebSockets Server started successfully"); + Bukkit.getLogger().info("[WebConsole] WebSocket Server started successfully"); } - + + /** + * Returns main class + * @return Main plugin class + */ + public WebConsole getMainClass() { + return plugin; + } + /** * Sends the message to all connected AND logged-in users */ @@ -82,17 +94,17 @@ public class WSServer extends WebSocketServer { Collection connections = getConnections(); for (WebSocket connection : connections) { if (LoginManager.getInstance().isLoggedIn(connection.getRemoteSocketAddress().getAddress().toString())) - connection.send("LOG " + line); + sendToClient(connection, new ConsoleOutput(line)); } } - + /** - * Returns main class - * - * @return + * Sends this JSONOutput to client + * @param conn Connection to client + * @param content JSONOutput object */ - public WebConsole getMainClass() { - return plugin; + public void sendToClient(WebSocket conn, JSONOutput content) { + conn.send(content.toJSON()); } } \ No newline at end of file