From a7f3038261f94677ccdc018a470385ddce247494 Mon Sep 17 00:00:00 2001 From: TheClashFruit Date: Sat, 30 Sep 2023 15:36:31 +0200 Subject: [PATCH] feat: switch over to jetty --- build.gradle | 9 +- .../java/me/theclashfruit/crss/Plugin.java | 48 ++++++----- .../theclashfruit/crss/api/ChatServlet.java | 15 ++++ .../me/theclashfruit/crss/api/ChatSocket.java | 85 +++++++++++++------ .../crss/listener/ChatListener.java | 70 +++++---------- .../theclashfruit/crss/models/SocketData.java | 37 ++++++++ .../crss/models/SocketMessage.java | 27 ++++++ .../crss/util/MessageDecoder.java | 33 +++++++ .../crss/util/MessageEncoder.java | 28 ++++++ .../theclashfruit/crss/util/StringUtil.java | 8 ++ 10 files changed, 266 insertions(+), 94 deletions(-) create mode 100644 src/main/java/me/theclashfruit/crss/api/ChatServlet.java create mode 100644 src/main/java/me/theclashfruit/crss/models/SocketData.java create mode 100644 src/main/java/me/theclashfruit/crss/models/SocketMessage.java create mode 100644 src/main/java/me/theclashfruit/crss/util/MessageDecoder.java create mode 100644 src/main/java/me/theclashfruit/crss/util/MessageEncoder.java create mode 100644 src/main/java/me/theclashfruit/crss/util/StringUtil.java diff --git a/build.gradle b/build.gradle index 8e8699f..46fd92c 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,14 @@ dependencies { implementation files('/home/tcf/MinecraftDev/craftbukkit-1.1-R5-SNAPSHOT.jar') - implementation 'org.java-websocket:Java-WebSocket:1.5.4' + implementation 'org.eclipse.jetty:jetty-server:9.4.52.v20230823' + implementation 'org.eclipse.jetty:jetty-servlet:9.4.52.v20230823' + implementation 'org.eclipse.jetty.websocket:websocket-server:9.4.52.v20230823' + implementation 'org.eclipse.jetty.websocket:websocket-servlet:9.4.52.v20230823' + + implementation 'javax.servlet:javax.servlet-api:4.0.1' + implementation 'javax.websocket:javax.websocket-api:1.1' + implementation 'com.google.code.gson:gson:2.10.1' } diff --git a/src/main/java/me/theclashfruit/crss/Plugin.java b/src/main/java/me/theclashfruit/crss/Plugin.java index 1fccb69..3086842 100644 --- a/src/main/java/me/theclashfruit/crss/Plugin.java +++ b/src/main/java/me/theclashfruit/crss/Plugin.java @@ -1,37 +1,48 @@ package me.theclashfruit.crss; -import com.google.gson.Gson; +import me.theclashfruit.crss.api.ChatServlet; import me.theclashfruit.crss.api.ChatSocket; import me.theclashfruit.crss.listener.ChatListener; import me.theclashfruit.crss.listener.FunListener; -import me.theclashfruit.crss.models.SystemData; -import me.theclashfruit.crss.models.SystemMessage; +import me.theclashfruit.crss.models.SocketData; +import me.theclashfruit.crss.models.SocketMessage; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; - -import java.net.UnknownHostException; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletHandler; public class Plugin extends JavaPlugin { ChatSocket chatSocket; + Server server = new Server(25580); + Connector connector = new ServerConnector(server); + ServletHandler handler = new ServletHandler(); + @Override public void onEnable() { getLogger().info("Plugin enabled!"); PluginManager pluginManager = getServer().getPluginManager(); - // create chat socket + // create api server + // ContextHandler context = new ContextHandler(new ApiHandler(), "/v1"); + + handler.addServletWithMapping(ChatServlet.class, "/v1/chat"); + + server.addConnector(connector); + server.setHandler(handler); + try { - chatSocket = new ChatSocket(25580); - - chatSocket.start(); - } catch (UnknownHostException e) { + server.start(); + } catch (Exception e) { throw new RuntimeException(e); } // register events pluginManager.registerEvents(new FunListener(), this); - pluginManager.registerEvents(new ChatListener(chatSocket), this); + pluginManager.registerEvents(new ChatListener(), this); } @Override @@ -40,20 +51,17 @@ public class Plugin extends JavaPlugin { // stop chat socket try { - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new SystemMessage( + ChatSocket.broadcast(new SocketMessage( "serverStop", - new SystemData( + new SocketData( + null, "Server stopped.", - 0xFF5555 + false ) )); - chatSocket.broadcast(chatMessage); - - chatSocket.stop(); - } catch (InterruptedException e) { + server.stop(); + } catch (Exception e) { throw new RuntimeException(e); } } diff --git a/src/main/java/me/theclashfruit/crss/api/ChatServlet.java b/src/main/java/me/theclashfruit/crss/api/ChatServlet.java new file mode 100644 index 0000000..a9726ea --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/api/ChatServlet.java @@ -0,0 +1,15 @@ +package me.theclashfruit.crss.api; + +import org.eclipse.jetty.websocket.servlet.WebSocketServlet; +import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; + +import javax.servlet.annotation.WebServlet; + +@WebServlet(name = "Chat WebSocket API") +public class ChatServlet extends WebSocketServlet { + @Override + public void configure(WebSocketServletFactory factory) { + factory.register(ChatSocket.class); + } +} + diff --git a/src/main/java/me/theclashfruit/crss/api/ChatSocket.java b/src/main/java/me/theclashfruit/crss/api/ChatSocket.java index 3205889..8eb240d 100644 --- a/src/main/java/me/theclashfruit/crss/api/ChatSocket.java +++ b/src/main/java/me/theclashfruit/crss/api/ChatSocket.java @@ -1,42 +1,75 @@ package me.theclashfruit.crss.api; -import org.java_websocket.WebSocket; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; +import com.google.gson.Gson; +import me.theclashfruit.crss.models.SocketData; +import me.theclashfruit.crss.models.SocketMessage; +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.annotations.*; -import java.net.InetSocketAddress; -import java.net.UnknownHostException; +import javax.websocket.EncodeException; +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; import static org.bukkit.Bukkit.getLogger; -import static org.bukkit.Bukkit.getPlayer; -public class ChatSocket extends WebSocketServer { - public ChatSocket(int port) throws UnknownHostException { - super(new InetSocketAddress(port)); - } +@WebSocket +public class ChatSocket { + private static Set chatConnections = new CopyOnWriteArraySet<>(); - @Override - public void onOpen(WebSocket conn, ClientHandshake handshake) { + private Session session; + + @OnWebSocketConnect + public void onConnect(Session session) throws IOException { + this.session = session; - } - - @Override - public void onClose(WebSocket conn, int code, String reason, boolean remote) { + SocketMessage greeting = new SocketMessage( + "connectionGreet", + new SocketData( + null, + "Greetings!", + true + ) + ); - } - - @Override - public void onMessage(WebSocket conn, String message) { + Gson gson = new Gson(); + session.getRemote().sendString( + gson.toJson(greeting) + ); + + chatConnections.add(this); } - @Override - public void onError(WebSocket conn, Exception ex) { - + @OnWebSocketMessage + public void onMessage(String message) throws IOException, EncodeException { + getLogger().info(message); } - @Override - public void onStart() { - getLogger().info("WebSocket server started on port " + getAddress().getPort() + "!"); + @OnWebSocketClose + public void onClose(int statusCode, String reason) throws IOException { + chatConnections.remove(this); + } + + @OnWebSocketError + public void onError(Throwable throwable) { + getLogger().throwing(ChatSocket.class.getName(), "WebSocket", throwable); + } + + public static void broadcast(SocketMessage message) { + chatConnections.forEach(endpoint -> { + try { + Gson gson = new Gson(); + + endpoint + .session + .getRemote() + .sendString( + gson.toJson(message) + ); + } catch (IOException e) { + getLogger().throwing(ChatSocket.class.getName(), "WebSocket", e); + } + }); } } diff --git a/src/main/java/me/theclashfruit/crss/listener/ChatListener.java b/src/main/java/me/theclashfruit/crss/listener/ChatListener.java index 393ea5f..3c5c5df 100644 --- a/src/main/java/me/theclashfruit/crss/listener/ChatListener.java +++ b/src/main/java/me/theclashfruit/crss/listener/ChatListener.java @@ -1,12 +1,9 @@ package me.theclashfruit.crss.listener; import com.google.gson.Gson; -import me.theclashfruit.crss.Plugin; import me.theclashfruit.crss.api.ChatSocket; -import me.theclashfruit.crss.models.ChatData; -import me.theclashfruit.crss.models.ChatMessage; -import me.theclashfruit.crss.models.SystemData; -import me.theclashfruit.crss.models.SystemMessage; +import me.theclashfruit.crss.models.*; +import me.theclashfruit.crss.util.StringUtil; import org.bukkit.ChatColor; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -17,88 +14,67 @@ import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.server.ServerCommandEvent; public class ChatListener implements Listener { - private final ChatSocket chatSocket; - - public ChatListener(ChatSocket chatSocket) { - this.chatSocket = chatSocket; - } @EventHandler public void onChat(PlayerChatEvent event) { - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new ChatMessage( + ChatSocket.broadcast(new SocketMessage( "chatMessage", - new ChatData( - event.getMessage(), + new SocketData( event.getPlayer().getName(), - 0xFFFFFF + event.getMessage(), + false ) )); - - chatSocket.broadcast(chatMessage); } @EventHandler public void onJoin(PlayerJoinEvent event) { - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new SystemMessage( + ChatSocket.broadcast(new SocketMessage( "playerJoin", - new SystemData( + new SocketData( + null, event.getPlayer().getName() + " joined the game.", - 0xFFFF55 + false ) )); - - chatSocket.broadcast(chatMessage); } @EventHandler public void onQuit(PlayerQuitEvent event) { - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new SystemMessage( + ChatSocket.broadcast(new SocketMessage( "playerQuit", - new SystemData( + new SocketData( + null, event.getPlayer().getName() + " left the game.", - 0xFFFF55 + false ) )); - - chatSocket.broadcast(chatMessage); } @EventHandler public void onDeath(PlayerDeathEvent event) { - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new SystemMessage( + ChatSocket.broadcast(new SocketMessage( "playerDeath", - new SystemData( + new SocketData( + null, event.getDeathMessage(), - 0xFFFFFF + false ) )); - - chatSocket.broadcast(chatMessage); } @EventHandler public void onServerCommand(ServerCommandEvent event) { - switch (event.getCommand().substring(0, 3)) { + switch (StringUtil.getFirstPartBeforeSpace(event.getCommand())) { case "say": - Gson gson = new Gson(); - - String chatMessage = gson.toJson(new SystemMessage( + ChatSocket.broadcast(new SocketMessage( "serverSay", - new SystemData( + new SocketData( + null, ChatColor.stripColor(event.getCommand().substring(4)), - 0xAA00AA + false ) )); - - chatSocket.broadcast(chatMessage); break; default: break; diff --git a/src/main/java/me/theclashfruit/crss/models/SocketData.java b/src/main/java/me/theclashfruit/crss/models/SocketData.java new file mode 100644 index 0000000..3756ebc --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/models/SocketData.java @@ -0,0 +1,37 @@ +package me.theclashfruit.crss.models; + +public class SocketData { + private String username; + private String message; + private Boolean isSystemMessage; + + public SocketData(String username, String message, Boolean isSystemMessage) { + this.username = username; + this.message = message; + this.isSystemMessage = isSystemMessage; + } + + public String getUsername() { + return username; + } + + public String getMessage() { + return message; + } + + public Boolean getIsSystemMessage() { + return isSystemMessage; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setMessage(String message) { + this.message = message; + } + + public void setIsSystemMessage(Boolean isSystemMessage) { + this.isSystemMessage = isSystemMessage; + } +} diff --git a/src/main/java/me/theclashfruit/crss/models/SocketMessage.java b/src/main/java/me/theclashfruit/crss/models/SocketMessage.java new file mode 100644 index 0000000..20693c9 --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/models/SocketMessage.java @@ -0,0 +1,27 @@ +package me.theclashfruit.crss.models; + +public class SocketMessage { + private String event; + private SocketData data; + + public SocketMessage(String event, SocketData data) { + this.event = event; + this.data = data; + } + + public String getEvent() { + return event; + } + + public SocketData getData() { + return data; + } + + public void setEvent(String event) { + this.event = event; + } + + public void setData(SocketData data) { + this.data = data; + } +} diff --git a/src/main/java/me/theclashfruit/crss/util/MessageDecoder.java b/src/main/java/me/theclashfruit/crss/util/MessageDecoder.java new file mode 100644 index 0000000..b47fd72 --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/util/MessageDecoder.java @@ -0,0 +1,33 @@ +package me.theclashfruit.crss.util; + +import com.google.gson.Gson; +import me.theclashfruit.crss.models.SocketMessage; + +import javax.websocket.DecodeException; +import javax.websocket.Decoder; +import javax.websocket.EndpointConfig; + +public class MessageDecoder implements Decoder.Text { + + private static final Gson gson = new Gson(); + + @Override + public SocketMessage decode(String s) throws DecodeException { + return gson.fromJson(s, SocketMessage.class); + } + + @Override + public boolean willDecode(String s) { + return (s != null); + } + + @Override + public void init(EndpointConfig endpointConfig) { + // Custom initialization logic + } + + @Override + public void destroy() { + // Close resources + } +} diff --git a/src/main/java/me/theclashfruit/crss/util/MessageEncoder.java b/src/main/java/me/theclashfruit/crss/util/MessageEncoder.java new file mode 100644 index 0000000..bee0a9c --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/util/MessageEncoder.java @@ -0,0 +1,28 @@ +package me.theclashfruit.crss.util; + +import com.google.gson.Gson; +import me.theclashfruit.crss.models.SocketMessage; + +import javax.websocket.EncodeException; +import javax.websocket.Encoder; +import javax.websocket.EndpointConfig; + +public class MessageEncoder implements Encoder.Text { + + private static final Gson gson = new Gson(); + + @Override + public String encode(SocketMessage message) throws EncodeException { + return gson.toJson(message); + } + + @Override + public void init(EndpointConfig endpointConfig) { + // Custom initialization logic + } + + @Override + public void destroy() { + // Close resources + } +} diff --git a/src/main/java/me/theclashfruit/crss/util/StringUtil.java b/src/main/java/me/theclashfruit/crss/util/StringUtil.java new file mode 100644 index 0000000..435bc64 --- /dev/null +++ b/src/main/java/me/theclashfruit/crss/util/StringUtil.java @@ -0,0 +1,8 @@ +package me.theclashfruit.crss.util; + +public interface StringUtil { + static String getFirstPartBeforeSpace(String input) { + int index = input.contains(" ") ? input.indexOf(" ") : 0; + return input.substring(0, index); + } +}