diff --git a/core/src/cn/harryh/arkpets/Const.java b/core/src/cn/harryh/arkpets/Const.java index 912827cd..60f70483 100644 --- a/core/src/cn/harryh/arkpets/Const.java +++ b/core/src/cn/harryh/arkpets/Const.java @@ -70,6 +70,7 @@ public final class Const { // Socket C/S constants public static final String serverHost = "localhost"; public static final int[] serverPorts = {8686, 8866, 8989, 8899, 8800}; + public static final int reconnectDelayMillis = 5 * 1000; public static final int reconnectPeriodMillis = 5 * 1000; // Misc constants diff --git a/core/src/cn/harryh/arkpets/concurrent/PortUtils.java b/core/src/cn/harryh/arkpets/concurrent/PortUtils.java index 53680560..4ce48aa5 100644 --- a/core/src/cn/harryh/arkpets/concurrent/PortUtils.java +++ b/core/src/cn/harryh/arkpets/concurrent/PortUtils.java @@ -25,11 +25,11 @@ public static int getServerPort(int[] expectedPorts) socket.setSoTimeout(100); PrintWriter out = new PrintWriter(socket.getOutputStream(), true); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); - out.println(JSONObject.toJSONString(new SocketData(UUID.randomUUID(), SocketData.Operation.VERIFY))); + out.println(SocketData.ofOperation(UUID.randomUUID(), SocketData.Operation.HANDSHAKE_REQUEST)); SocketData socketData = JSONObject.parseObject(in.readLine(), SocketData.class); out.close(); in.close(); - if (socketData.operation == SocketData.Operation.SERVER_ONLINE) + if (socketData.operation == SocketData.Operation.HANDSHAKE_RESPONSE) return serverPort; } catch (IOException ignored) { } diff --git a/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java b/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java index c2eaa2af..64ec9f25 100644 --- a/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java +++ b/core/src/cn/harryh/arkpets/concurrent/ProcessPool.java @@ -6,8 +6,8 @@ import java.util.concurrent.*; -public class ProcessPool { - public static final ExecutorService executorService = +public final class ProcessPool implements Executor { + private final ExecutorService executorService = new ThreadPoolExecutor(20, Integer.MAX_VALUE, 60L, @@ -30,12 +30,20 @@ public static synchronized ProcessPool getInstance() { private ProcessPool() { } - public Future submit(Runnable task) { - return executorService.submit(task); + @Override + public void execute(Runnable task) { + try { + executorService.submit(task); + } catch (RejectedExecutionException ignored) { + } + } + + public void shutdown() { + executorService.shutdown(); } - public FutureTask submit(Class clazz, List jvmArgs, List args) { - Callable task = () -> { + public Future submit(Class clazz, List jvmArgs, List args) { + FutureTask task = new FutureTask<> (() -> { // Attributes preparation String javaHome = System.getProperty("java.home"); String javaBin = javaHome + File.separator + "bin" + File.separator + "java"; @@ -56,10 +64,9 @@ public FutureTask submit(Class clazz, List jvmArgs, Li Process process = builder.inheritIO().start(); int exitValue = process.waitFor(); return new ProcessResult(exitValue, process.pid()); - }; - FutureTask futureTask = new FutureTask<>(task); - executorService.submit(futureTask); - return futureTask; + }); + executorService.submit(task); + return task; } diff --git a/core/src/cn/harryh/arkpets/concurrent/SocketClient.java b/core/src/cn/harryh/arkpets/concurrent/SocketClient.java index 3431aeb5..71e25c0a 100644 --- a/core/src/cn/harryh/arkpets/concurrent/SocketClient.java +++ b/core/src/cn/harryh/arkpets/concurrent/SocketClient.java @@ -16,34 +16,42 @@ public class SocketClient { private boolean connected = false; - private Socket socket; private SocketSession session; private Timer timer; public SocketClient() { } - public void connectWithRetry(Runnable onConnected) { + public void connectWithRetry(Runnable onConnected, SocketSession session) { timer = new Timer(); timer.schedule(new TimerTask() { - @Override - public void run() { - connect(onConnected); - if (connected) - timer.cancel(); - } - }, 0, reconnectPeriodMillis); + @Override + public void run() { + connect(onConnected, session); + if (connected) + timer.cancel(); + } + }, + reconnectDelayMillis, + reconnectPeriodMillis + ); } - public void connect(Runnable onConnected) { + public void connect(Runnable onConnected, SocketSession session) { if (connected) return; try { int port = PortUtils.getServerPort(serverPorts); - Logger.info("SocketClient", "Connecting to server on port" + port); + Logger.info("SocketClient", "Connecting to server on port " + port); try { - socket = new Socket(serverHost, port); + Socket socket = new Socket(serverHost, port); connected = true; + if (this.session != null) + this.session.close(); + session.setTarget(socket); + ProcessPool.getInstance().execute(session); + this.session = session; + Logger.info("SocketClient", "(+)" + session + " connected"); if (onConnected != null) onConnected.run(); } catch (IOException e) { @@ -54,28 +62,20 @@ public void connect(Runnable onConnected) { } } - public void setHandler(SocketSession session) { - if (!connected) - throw new IllegalStateException("The socket was not yet connected."); - if (this.session != null) - this.session.close(); - Thread listener = new Thread(() -> ProcessPool.executorService.execute(session)); - ProcessPool.executorService.execute(listener); - this.session = session; + public void disconnect() { + if (connected) + connected = false; + if (session != null) + session.close(); } - public void disconnect() { - if (!connected) - return; - connected = false; - session.close(); + public boolean isConnected() { + return connected; } public void sendRequest(SocketData socketData) { - if (!connected) - return; - String data = JSONObject.toJSONString(socketData); - session.send(data); + if (connected && session != null) + session.send(JSONObject.toJSONString(socketData)); } @@ -85,7 +85,7 @@ public static class ClientSocketSession extends SocketSession { private UUID uuid = null; public ClientSocketSession(SocketClient client, MemberTrayImpl memberTray) { - super(client.socket); + super(); this.client = client; this.memberTray = memberTray; } @@ -93,12 +93,12 @@ public ClientSocketSession(SocketClient client, MemberTrayImpl memberTray) { @Override public void receive(String request) { try { - SocketData socketData = JSONObject.parseObject(request, SocketData.class); - if (socketData.operation == null) + SocketData socketData = SocketData.of(request); + if (socketData == null || socketData.operation == null) return; if (uuid == null) uuid = socketData.uuid; - if (socketData.uuid.compareTo(this.uuid) == 0) { + if (socketData.uuid.compareTo(this.uuid) == 0 && memberTray != null) { // If the connection is normal: switch (socketData.operation) { case LOGOUT -> memberTray.onExit(); @@ -113,11 +113,17 @@ public void receive(String request) { } } + @Override + protected void onClosed() { + Logger.info("SocketClient", "(-)" + this + " closed"); + } + @Override protected void onBroken() { + Logger.info("SocketClient", "(x)" + this + " broken"); memberTray.onDisconnected(); client.disconnect(); - client.connectWithRetry(memberTray::onReconnected); + client.connectWithRetry(memberTray::onConnected, new ClientSocketSession(client, memberTray)); } } } diff --git a/core/src/cn/harryh/arkpets/concurrent/SocketData.java b/core/src/cn/harryh/arkpets/concurrent/SocketData.java index 21924c0d..72bf9a75 100644 --- a/core/src/cn/harryh/arkpets/concurrent/SocketData.java +++ b/core/src/cn/harryh/arkpets/concurrent/SocketData.java @@ -1,5 +1,8 @@ package cn.harryh.arkpets.concurrent; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONField; + import java.io.Serializable; import java.nio.charset.Charset; import java.util.UUID; @@ -13,25 +16,62 @@ public enum Operation { NO_KEEP_ACTION, TRANSPARENT_MODE, NO_TRANSPARENT_MODE, + CAN_CHANGE_STAGE, CHANGE_STAGE, - VERIFY, - SERVER_ONLINE, + HANDSHAKE_REQUEST, + HANDSHAKE_RESPONSE, ACTIVATE_LAUNCHER } public UUID uuid; public Operation operation; - public byte[] name; - public boolean canChangeStage; + public StringDTO msg; + + private SocketData(UUID uuid, Operation operation, StringDTO msg) { + this.uuid = uuid; + this.operation = operation; + this.msg = msg; + } + + @JSONField(serialize = false, deserialize = false) + public String getMsgString() { + return msg.toString(); + } + + @Override + public String toString() { + return JSONObject.toJSONString(this); + } - public SocketData(UUID uuid, Operation operation) { - this(uuid, operation, "", false); + public static SocketData of(String jsonString) { + return JSONObject.parseObject(jsonString, SocketData.class); } - public SocketData(UUID uuid, Operation operation, String name, boolean canChangeStage) { - this.uuid = uuid; - this.operation = operation; - this.name = name.getBytes(Charset.forName("GBK")); - this.canChangeStage = canChangeStage; + public static SocketData ofLogin(UUID uuid, String name) { + return new SocketData(uuid, Operation.LOGIN, StringDTO.of(name)); + } + + public static SocketData ofOperation(UUID uuid, Operation operation) { + return new SocketData(uuid, operation, null); + } + + + private static class StringDTO { + public byte[] bytes; + public String encoding; + + private StringDTO(byte[] bytes, String encoding) { + this.bytes = bytes; + this.encoding = encoding; + } + + @Override + public String toString() { + return new String(bytes, Charset.forName(encoding)); + } + + public static StringDTO of(String string) { + return new StringDTO(string.getBytes(Charset.defaultCharset()), Charset.defaultCharset().name()); + } } } diff --git a/core/src/cn/harryh/arkpets/concurrent/SocketServer.java b/core/src/cn/harryh/arkpets/concurrent/SocketServer.java index 13b419ed..83feaa11 100644 --- a/core/src/cn/harryh/arkpets/concurrent/SocketServer.java +++ b/core/src/cn/harryh/arkpets/concurrent/SocketServer.java @@ -1,11 +1,9 @@ package cn.harryh.arkpets.concurrent; import cn.harryh.arkpets.tray.HostTray; -import cn.harryh.arkpets.tray.MemberTray; import cn.harryh.arkpets.tray.MemberTrayProxy; import cn.harryh.arkpets.utils.Logger; import com.alibaba.fastjson2.JSONException; -import com.alibaba.fastjson2.JSONObject; import java.io.IOException; import java.net.ServerSocket; @@ -18,10 +16,10 @@ import static cn.harryh.arkpets.Const.serverPorts; -public class SocketServer { +public final class SocketServer { private int port; private ServerSocket serverSocket = null; - private final Set sessionList = new CopyOnWriteArraySet<>(); + private final Set sessionList = new CopyOnWriteArraySet<>(); private Thread listener; private static SocketServer instance = null; @@ -43,11 +41,12 @@ public synchronized void startServer(HostTray hostTray) try { serverSocket = new ServerSocket(port); Logger.info("SocketServer", "Server is running on port " + port); - while (!listener.isInterrupted() && !ProcessPool.executorService.isShutdown()) { - Socket clientSocket = serverSocket.accept(); - ServerSocketSession session = new ServerSocketSession(clientSocket, hostTray); + while (!listener.isInterrupted()) { + Socket socket = serverSocket.accept(); + SocketSession session = new ServerSocketSession(hostTray); + session.setTarget(socket); sessionList.add(session); - ProcessPool.executorService.execute(session); + ProcessPool.getInstance().execute(session); Logger.info("SocketServer", "(+)" + session + " connected"); } serverSocket.close(); @@ -57,14 +56,14 @@ public synchronized void startServer(HostTray hostTray) } catch (RejectedExecutionException ignored) { } }); - ProcessPool.executorService.execute(listener); + ProcessPool.getInstance().execute(listener); } public synchronized void stopServer() { Logger.info("SocketServer", "Request to stop server"); if (listener != null) listener.interrupt(); - sessionList.forEach(ServerSocketSession::close); + sessionList.forEach(SocketSession::close); } @Override @@ -89,44 +88,47 @@ public String toString() { public static class ServerSocketSession extends SocketSession { private final HostTray hostTray; - private MemberTray tray; + private MemberTrayProxy tray; private UUID uuid = null; - public ServerSocketSession(Socket target, HostTray hostTray) { - super(target); + public ServerSocketSession(HostTray hostTray) { + super(); this.hostTray = hostTray; } @Override public void receive(String request) { try { - SocketData socketData = JSONObject.parseObject(request, SocketData.class); - if (socketData.operation == null) + SocketData socketData = SocketData.of(request); + if (socketData == null || socketData.operation == null) return; if (uuid == null) uuid = socketData.uuid; switch (socketData.operation) { - case VERIFY -> { - this.send(JSONObject.toJSONString(new SocketData(uuid, SocketData.Operation.SERVER_ONLINE))); + case HANDSHAKE_REQUEST -> { + this.send(SocketData.ofOperation(uuid, SocketData.Operation.HANDSHAKE_RESPONSE)); close(); } case ACTIVATE_LAUNCHER -> hostTray.showStage(); case LOGIN -> { - tray = new MemberTrayProxy(socketData, target, hostTray); - hostTray.addMemberTray(socketData.uuid, tray); + tray = new MemberTrayProxy(socketData, this, hostTray); + hostTray.addMemberTray(uuid, tray); } case LOGOUT -> { - hostTray.removeMemberTray(socketData.uuid);tray.onExit(); + hostTray.removeMemberTray(uuid); + tray.onExit(); close(); } - case KEEP_ACTION -> tray.onKeepAnimEn(); - case NO_KEEP_ACTION -> tray.onKeepAnimDis(); - case TRANSPARENT_MODE -> tray.onTransparentEn(); - case NO_TRANSPARENT_MODE -> tray.onTransparentDis(); - case CHANGE_STAGE -> tray.onChangeStage(); + case KEEP_ACTION -> tray.onKeepAnimEn(); + case NO_KEEP_ACTION -> tray.onKeepAnimDis(); + case TRANSPARENT_MODE -> tray.onTransparentEn(); + case NO_TRANSPARENT_MODE -> tray.onTransparentDis(); + case CAN_CHANGE_STAGE -> tray.onCanChangeStage(); + case CHANGE_STAGE -> tray.onChangeStage(); } } catch (JSONException ignored) { + } } @@ -135,5 +137,11 @@ protected void onClosed() { Logger.info("SocketServer", "(-)" + this + " closed"); SocketServer.getInstance().sessionList.remove(this); } + + @Override + protected void onBroken() { + Logger.info("SocketServer", "(x)" + this + " broken"); + hostTray.removeMemberTray(uuid); + } } } diff --git a/core/src/cn/harryh/arkpets/concurrent/SocketSession.java b/core/src/cn/harryh/arkpets/concurrent/SocketSession.java index 6be13df3..c6d54a09 100644 --- a/core/src/cn/harryh/arkpets/concurrent/SocketSession.java +++ b/core/src/cn/harryh/arkpets/concurrent/SocketSession.java @@ -11,36 +11,43 @@ abstract public class SocketSession implements Runnable { - protected final Socket target; - protected final BufferedReader in; - protected final PrintWriter out; - protected boolean hasRun = false; - protected boolean hasClosed = false; + protected Socket target; + protected BufferedReader in; + protected PrintWriter out; + private boolean hasTarget = false; + private boolean hasRun = false; + private boolean hasClosed = false; - public SocketSession(Socket target) { + public SocketSession() { + } + + public final void setTarget(Socket target) { try { this.target = target; in = new BufferedReader(new InputStreamReader(target.getInputStream())); out = new PrintWriter(target.getOutputStream(), true); + hasTarget = true; } catch (IOException e) { throw new RuntimeException(e); } } public final String getHostAddress() { - return target.getInetAddress().getHostAddress(); + return target != null ? target.getInetAddress().getHostAddress() : "0.0.0.0"; } public final int getPort() { - return target.getPort(); + return target != null ? target.getPort() : 0; } public final void close() { - if (hasClosed) + if (!hasTarget || hasClosed) return; hasClosed = true; try { target.close(); + in.close(); + out.close(); this.onClosed(); } catch (IOException ignored) { } @@ -50,6 +57,9 @@ public final void close() { public final void run() { if (hasRun) throw new IllegalStateException("The session thread has run yet."); + if (!hasTarget) + throw new IllegalStateException("The target socket has not been set yet."); + hasRun = true; try { while (!target.isClosed()) { try { @@ -73,7 +83,9 @@ public final void run() { } } - public final void send(String request) { + public final void send(Object request) { + if (!hasTarget) + throw new IllegalStateException("The target socket has not been set yet."); Logger.debug("SocketSession", this + " <- " + request); out.println(request); } diff --git a/core/src/cn/harryh/arkpets/tray/HostTray.java b/core/src/cn/harryh/arkpets/tray/HostTray.java index 07929a68..73ecadfd 100644 --- a/core/src/cn/harryh/arkpets/tray/HostTray.java +++ b/core/src/cn/harryh/arkpets/tray/HostTray.java @@ -20,8 +20,8 @@ public class HostTray { protected boolean initialized = false; protected Map arkPetTrays = new HashMap<>(); - private JPopupMenu popupMenu; private JDialog popWindow; + private JPopupMenu popMenu; private JMenu playerMenu; private Stage stage; @@ -32,7 +32,6 @@ public class HostTray { public HostTray(Stage boundStage) { if (SystemTray.isSupported()) { Platform.setImplicitExit(false); - SystemTray tray = SystemTray.getSystemTray(); // Ui Components: popWindow = new JDialog(); @@ -48,17 +47,17 @@ public HostTray(Stage boundStage) { Platform.exit(); }); - popupMenu = new JPopupMenu() { + popMenu = new JPopupMenu() { @Override public void firePopupMenuWillBecomeInvisible() { popWindow.setVisible(false); // Hide the container when the menu is invisible. } }; - popupMenu.add(innerLabel); - popupMenu.addSeparator(); - popupMenu.add(playerMenu); - popupMenu.add(exitItem); - popupMenu.setSize(100, 24 * popupMenu.getSubElements().length); + popMenu.add(innerLabel); + popMenu.addSeparator(); + popMenu.add(playerMenu); + popMenu.add(exitItem); + popMenu.setSize(100, 24 * popMenu.getSubElements().length); Image image = Toolkit.getDefaultToolkit().getImage(HostTray.class.getResource(Const.iconFilePng)); trayIcon = new TrayIcon(image, "ArkPets"); @@ -73,16 +72,11 @@ public void mouseReleased(MouseEvent e) { // Bind JavaFX stage: stage = boundStage; - stage.xProperty().addListener((observable, oldValue, newValue) -> { - }); - stage.yProperty().addListener(((observable, oldValue, newValue) -> { - })); - stage.iconifiedProperty().addListener(((observable, oldValue, newValue) -> { if (newValue) - hideStage(); + stage.hide(); })); - stage.setOnCloseRequest(e -> hideStage()); + stage.setOnCloseRequest(e -> stage.setIconified(true)); trayIcon.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -90,44 +84,45 @@ public void mouseClicked(MouseEvent e) { showStage(); } }); - - try { - tray.add(trayIcon); - initialized = true; - Logger.info("HostTray", "HostTray icon applied"); - } catch (AWTException e) { - Logger.error("HostTray", "Unable to apply HostTray icon, details see below.", e); - } } else { Logger.error("HostTray", "Tray is not supported."); } } + public void applyTrayIcon() { + if (initialized) + return; + try { + SystemTray.getSystemTray().add(trayIcon); + Logger.info("HostTray", "HostTray icon applied"); + initialized = true; + } catch (AWTException e) { + Logger.error("HostTray", "Unable to apply HostTray icon, details see below.", e); + } + } + public void showDialog(int x, int y) { + if (!initialized) + return; + /* Use `System.setProperty("sun.java2d.uiScale", "1")` can also avoid system scaling. + Here we will adapt the coordinate for system scaling artificially. See below. */ AffineTransform at = popWindow.getGraphicsConfiguration().getDefaultTransform(); + int scaledX = (int) (x / at.getScaleX()); + int scaledY = (int) (y / at.getScaleY()); // Show the JDialog together with the JPopupMenu. popWindow.setVisible(true); - popWindow.setLocation((int) (x / at.getScaleX()), (int) (y / at.getScaleY()) - popupMenu.getHeight()); - popupMenu.show(popWindow, 0, 0); - } - - public void hideStage() { - if (!initialized) - return; - stage.hide(); + popWindow.setLocation(scaledX, scaledY - popMenu.getHeight()); + popMenu.show(popWindow, 0, 0); } public void showStage() { if (!initialized) return; + Logger.info("HostTray", "Request to show stage"); Platform.runLater(() -> { - if (stage.isIconified()) { - stage.setIconified(false); - } - if (!stage.isShowing()) { - stage.show(); - } + stage.setIconified(false); + stage.show(); stage.toFront(); }); } diff --git a/core/src/cn/harryh/arkpets/tray/MemberTray.java b/core/src/cn/harryh/arkpets/tray/MemberTray.java index 798b7311..9e620d0c 100644 --- a/core/src/cn/harryh/arkpets/tray/MemberTray.java +++ b/core/src/cn/harryh/arkpets/tray/MemberTray.java @@ -32,12 +32,12 @@ public MemberTray(UUID uuid, String name) { optChangeStage .addActionListener(e -> onChangeStage()); optExit .addActionListener(e -> onExit()); - optKeepAnimEn .addActionListener(e -> sendRequest(SocketData.Operation.KEEP_ACTION)); - optKeepAnimDis .addActionListener(e -> sendRequest(SocketData.Operation.NO_KEEP_ACTION)); - optTransparentEn .addActionListener(e -> sendRequest(SocketData.Operation.TRANSPARENT_MODE)); - optTransparentDis .addActionListener(e -> sendRequest(SocketData.Operation.NO_TRANSPARENT_MODE)); - optChangeStage .addActionListener(e -> sendRequest(SocketData.Operation.CHANGE_STAGE)); - optExit .addActionListener(e -> sendRequest(SocketData.Operation.LOGOUT)); + optKeepAnimEn .addActionListener(e -> sendOperation(SocketData.Operation.KEEP_ACTION)); + optKeepAnimDis .addActionListener(e -> sendOperation(SocketData.Operation.NO_KEEP_ACTION)); + optTransparentEn .addActionListener(e -> sendOperation(SocketData.Operation.TRANSPARENT_MODE)); + optTransparentDis .addActionListener(e -> sendOperation(SocketData.Operation.NO_TRANSPARENT_MODE)); + optChangeStage .addActionListener(e -> sendOperation(SocketData.Operation.CHANGE_STAGE)); + optExit .addActionListener(e -> sendOperation(SocketData.Operation.LOGOUT)); } abstract public void onExit(); @@ -54,5 +54,5 @@ public MemberTray(UUID uuid, String name) { abstract public void remove(); - abstract protected void sendRequest(SocketData.Operation operation); + abstract protected void sendOperation(SocketData.Operation operation); } diff --git a/core/src/cn/harryh/arkpets/tray/MemberTrayImpl.java b/core/src/cn/harryh/arkpets/tray/MemberTrayImpl.java index eb079818..e0ceb29f 100644 --- a/core/src/cn/harryh/arkpets/tray/MemberTrayImpl.java +++ b/core/src/cn/harryh/arkpets/tray/MemberTrayImpl.java @@ -7,6 +7,7 @@ import cn.harryh.arkpets.animations.AnimData; import cn.harryh.arkpets.concurrent.SocketClient; import cn.harryh.arkpets.concurrent.SocketData; +import cn.harryh.arkpets.concurrent.SocketSession; import cn.harryh.arkpets.utils.Logger; import com.badlogic.gdx.Gdx; @@ -25,7 +26,7 @@ public class MemberTrayImpl extends MemberTray { private final ArkPets arkPets; - private final SocketClient socketClient; + private final SocketClient client; private final JDialog popWindow; private final JPopupMenu popMenu; private TrayIcon icon; @@ -35,10 +36,10 @@ public class MemberTrayImpl extends MemberTray { * Must be used after Gdx.app was initialized. * @param boundArkPets The ArkPets instance that bound to the tray icon. */ - public MemberTrayImpl(ArkPets boundArkPets, SocketClient socketClient, UUID uuid) { + public MemberTrayImpl(ArkPets boundArkPets, SocketClient client, UUID uuid) { super(uuid, getName(boundArkPets)); arkPets = boundArkPets; - this.socketClient = socketClient; + this.client = client; // Ui Components: popWindow = new JDialog(); @@ -61,10 +62,13 @@ public void firePopupMenuWillBecomeInvisible() { popMenu.add(optExit); popMenu.setSize(100, 24 * popMenu.getSubElements().length); - socketClient.connectWithRetry(() -> { - socketClient.setHandler(new SocketClient.ClientSocketSession(socketClient, this)); - socketClient.sendRequest(new SocketData(this.uuid, SocketData.Operation.LOGIN, name, arkPets.canChangeStage())); - }); + Runnable onConnected = this::onConnected; + SocketSession session = new SocketClient.ClientSocketSession(client, this); + client.connect(onConnected, session); + if (!client.isConnected()) { + onDisconnected(); + client.connectWithRetry(onConnected, session); + } } private static String getName(ArkPets boundArkPets) { @@ -73,25 +77,24 @@ private static String getName(ArkPets boundArkPets) { } private TrayIcon getTrayIcon(Image image) { - if (icon == null) { - icon = new TrayIcon(image, name); - icon.setImageAutoSize(true); - icon.addMouseListener(new MouseAdapter() { - @Override - public void mouseReleased(MouseEvent e) { - if (e.getButton() == 3 && e.isPopupTrigger()) - showDialog(e.getX() + 5, e.getY()); - } - }); - } + icon = new TrayIcon(image, name); + icon.setImageAutoSize(true); + icon.addMouseListener(new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent e) { + if (e.getButton() == 3 && e.isPopupTrigger()) + showDialog(e.getX() + 5, e.getY()); + } + }); return icon; } @Override public void onExit() { Logger.info("MemberTray", "Request to exit"); - arkPets.windowAlpha.reset(0f); remove(); + client.disconnect(); + arkPets.windowAlpha.reset(0f); new Timer().schedule(new TimerTask() { @Override public void run() { @@ -146,15 +149,30 @@ public void onKeepAnimEn() { } @Override - protected void sendRequest(SocketData.Operation operation) { - socketClient.sendRequest(new SocketData(uuid, operation)); + protected void sendOperation(SocketData.Operation operation) { + client.sendRequest(SocketData.ofOperation(uuid, operation)); } @Override public void remove() { popMenu.removeAll(); popWindow.dispose(); - socketClient.disconnect(); + client.disconnect(); + } + + public void onConnected() { + // If integration was succeeded, remove the ISOLATED tray icon. + Logger.info("MemberTray", "Integrated tray service connected"); + SystemTray.getSystemTray().remove(icon); + client.sendRequest(SocketData.ofLogin(uuid, name)); + for (MenuElement element : popMenu.getSubElements()) { + if (arkPets.canChangeStage()) + sendOperation(SocketData.Operation.CAN_CHANGE_STAGE); + if (element.equals(optKeepAnimDis)) + sendOperation(SocketData.Operation.KEEP_ACTION); + if (element.equals(optTransparentDis)) + sendOperation(SocketData.Operation.TRANSPARENT_MODE); + } } public void onDisconnected() { @@ -172,19 +190,6 @@ public void onDisconnected() { } } - public void onReconnected() { - // If integration was succeeded, remove the ISOLATED tray icon. - Logger.info("MemberTray", "Integrated tray service reconnected"); - SystemTray.getSystemTray().remove(icon); - socketClient.sendRequest(new SocketData(this.uuid, SocketData.Operation.LOGIN, name, arkPets.canChangeStage())); - for (MenuElement element : popMenu.getSubElements()) { - if (element.equals(optKeepAnimDis)) - sendRequest(SocketData.Operation.KEEP_ACTION); - if (element.equals(optTransparentDis)) - sendRequest(SocketData.Operation.TRANSPARENT_MODE); - } - } - /** Hides the menu. */ public void hideDialog() { diff --git a/core/src/cn/harryh/arkpets/tray/MemberTrayProxy.java b/core/src/cn/harryh/arkpets/tray/MemberTrayProxy.java index ce675909..c211c96a 100644 --- a/core/src/cn/harryh/arkpets/tray/MemberTrayProxy.java +++ b/core/src/cn/harryh/arkpets/tray/MemberTrayProxy.java @@ -1,29 +1,21 @@ package cn.harryh.arkpets.tray; import cn.harryh.arkpets.concurrent.SocketData; +import cn.harryh.arkpets.concurrent.SocketSession; import cn.harryh.arkpets.utils.Logger; -import com.alibaba.fastjson2.JSONObject; import javax.swing.*; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.Socket; -import java.nio.charset.Charset; public class MemberTrayProxy extends MemberTray { - private final PrintWriter socketOut; + private final SocketSession session; private final HostTray hostTray; private final JMenu popMenu; - public MemberTrayProxy(SocketData socketData, Socket clientSocket, HostTray hostTray) { - super(socketData.uuid, new String(socketData.name, Charset.forName("GBK"))); + public MemberTrayProxy(SocketData socketData, SocketSession session, HostTray hostTray) { + super(socketData.uuid, socketData.getMsgString()); + this.session = session; this.hostTray = hostTray; - try { - socketOut = new PrintWriter(clientSocket.getOutputStream(), true); - } catch (IOException e) { - throw new RuntimeException(e); - } // Ui Components: JLabel innerLabel = new JLabel(" " + name + " "); @@ -33,14 +25,17 @@ public MemberTrayProxy(SocketData socketData, Socket clientSocket, HostTray host popMenu.add(innerLabel); popMenu.add(optKeepAnimEn); popMenu.add(optTransparentEn); - if (socketData.canChangeStage) - popMenu.add(optChangeStage); popMenu.add(optExit); popMenu.setSize(100, 24 * popMenu.getSubElements().length); hostTray.addMemberTray(popMenu); } + public void onCanChangeStage() { + Logger.info("ProxyTray", "Can change stage"); + popMenu.add(optChangeStage, popMenu.getSubElements().length - 2); + } + @Override public void onExit() { Logger.info("ProxyTray", "Request to exit"); @@ -83,14 +78,13 @@ public void onKeepAnimEn() { } @Override - protected void sendRequest(SocketData.Operation operation) { - socketOut.println(JSONObject.toJSONString(new SocketData(uuid, operation))); + protected void sendOperation(SocketData.Operation operation) { + session.send(SocketData.ofOperation(uuid, operation)); } @Override public void remove() { hostTray.removeMemberTray(popMenu); - sendRequest(SocketData.Operation.LOGOUT); - socketOut.close(); + sendOperation(SocketData.Operation.LOGOUT); } } diff --git a/desktop/src/cn/harryh/arkpets/ArkHomeFX.java b/desktop/src/cn/harryh/arkpets/ArkHomeFX.java index 91ff2ab6..3cf16b62 100644 --- a/desktop/src/cn/harryh/arkpets/ArkHomeFX.java +++ b/desktop/src/cn/harryh/arkpets/ArkHomeFX.java @@ -36,7 +36,6 @@ public class ArkHomeFX extends Application { public Stage stage; public ArkConfig config; public ModelsDataset modelsDataset; - public HostTray hostTray; public StackPane root; public RootModule rootModule; @@ -54,22 +53,26 @@ public void start(Stage stage) throws Exception { this.stage = stage; // Initialize socket server and HostTray - hostTray = new HostTray(stage); + HostTray hostTray = new HostTray(stage); try { SocketServer.getInstance().startServer(hostTray); + hostTray.applyTrayIcon(); } catch (PortUtils.NoPortAvailableException ignored) { Logger.error("SocketServer", "No available port"); // TODO What if there are no port available Platform.exit(); + return; } catch (PortUtils.ServerCollisionException ignored) { Logger.error("SocketServer", "Server is already running"); SocketClient socketClient = new SocketClient(); socketClient.connect(() -> { - socketClient.sendRequest(new SocketData(UUID.randomUUID(), SocketData.Operation.ACTIVATE_LAUNCHER)); - socketClient.disconnect(); - Logger.info("Launcher", "ArkPets Launcher has started."); - }); + Logger.info("Launcher", "Request to start an existed Launcher"); + socketClient.sendRequest(SocketData.ofOperation(UUID.randomUUID(), SocketData.Operation.ACTIVATE_LAUNCHER)); + socketClient.disconnect(); + }, + new SocketClient.ClientSocketSession(socketClient, null)); Platform.exit(); + return; } // Load FXML for root node. @@ -118,7 +121,7 @@ public void start(Stage stage) throws Exception { public void stop() throws Exception { super.stop(); SocketServer.getInstance().stopServer(); - ProcessPool.executorService.shutdown(); + ProcessPool.getInstance().shutdown(); } public boolean initModelsDataset(boolean popNotice) { diff --git a/desktop/src/cn/harryh/arkpets/DesktopLauncher.java b/desktop/src/cn/harryh/arkpets/DesktopLauncher.java index 48d07e6d..37a21ba0 100644 --- a/desktop/src/cn/harryh/arkpets/DesktopLauncher.java +++ b/desktop/src/cn/harryh/arkpets/DesktopLauncher.java @@ -3,17 +3,15 @@ */ package cn.harryh.arkpets; -import cn.harryh.arkpets.concurrent.ProcessPool; import cn.harryh.arkpets.utils.ArgPending; import cn.harryh.arkpets.utils.Logger; import javafx.application.Application; import java.nio.charset.Charset; import java.util.Objects; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import static cn.harryh.arkpets.Const.*; +import static cn.harryh.arkpets.Const.LogConfig; +import static cn.harryh.arkpets.Const.appVersion; /** The entrance of the whole program, also the bootstrap for ArkHomeFX. @@ -61,12 +59,7 @@ protected void process(String command, String addition) { }; // Java FX bootstrap - Future future = ProcessPool.getInstance().submit(() -> Application.launch(ArkHomeFX.class, args)); - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); - } + Application.launch(ArkHomeFX.class, args); Logger.info("System", "Exited from DesktopLauncher successfully"); System.exit(0); } diff --git a/desktop/src/cn/harryh/arkpets/controllers/RootModule.java b/desktop/src/cn/harryh/arkpets/controllers/RootModule.java index 095c18e8..acc764c3 100644 --- a/desktop/src/cn/harryh/arkpets/controllers/RootModule.java +++ b/desktop/src/cn/harryh/arkpets/controllers/RootModule.java @@ -6,9 +6,9 @@ import cn.harryh.arkpets.ArkConfig; import cn.harryh.arkpets.ArkHomeFX; import cn.harryh.arkpets.EmbeddedLauncher; +import cn.harryh.arkpets.concurrent.ProcessPool; import cn.harryh.arkpets.guitasks.CheckAppUpdateTask; import cn.harryh.arkpets.guitasks.GuiTask; -import cn.harryh.arkpets.concurrent.ProcessPool; import cn.harryh.arkpets.utils.ArgPending; import cn.harryh.arkpets.utils.GuiPrefabs; import cn.harryh.arkpets.utils.JavaProcess; @@ -30,7 +30,7 @@ import java.util.*; import java.util.concurrent.ExecutionException; -import java.util.concurrent.FutureTask; +import java.util.concurrent.Future; import static cn.harryh.arkpets.Const.*; import static cn.harryh.arkpets.utils.GuiPrefabs.DialogUtil; @@ -145,7 +145,7 @@ protected Boolean call() throws InterruptedException, ExecutionException { // Start ArkPets core. Logger.info("Launcher", "Launching " + app.config.character_asset); Logger.debug("Launcher", "With args " + args); - FutureTask future = ProcessPool.getInstance().submit(EmbeddedLauncher.class, List.of(), args); + Future future = ProcessPool.getInstance().submit(EmbeddedLauncher.class, List.of(), args); // ArkPets core finalized. if (!future.get().isSuccess()) { Logger.warn("Launcher", "Detected an abnormal finalization of an ArkPets thread (exit code -1). Please check the log file for details.");