Skip to content

Commit 820cfca

Browse files
committed
10.0.0 Major enhancements to connection stability for autoplug web users. 🎉
Using Netty as networking library under the hood to achieve this. Make sure you update if you are using the web panel since the old client won't be able to connect to the panel anymore. I currently have more time to focus on this project, thus feel free to dm me any ideas and bugs/annoyances that you have when using the web panel. 🙌
1 parent c5fae0d commit 820cfca

17 files changed

+779
-1176
lines changed

pom.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ AUTO-GENERATED FILE, CHANGES SHOULD BE DONE IN ./JPM.java or ./src/main/java/JPM
1111
<modelVersion>4.0.0</modelVersion>
1212
<groupId>com.osiris.autoplug.client</groupId>
1313
<artifactId>AutoPlug-Client</artifactId>
14-
<version>9.0.2</version>
14+
<version>10.0.0</version>
1515
<properties>
1616
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
1717
<java.version>9</java.version>
18-
<version>9.0.2</version>
18+
<version>10.0.0</version>
1919
<main-class>com.osiris.autoplug.client.Main</main-class>
2020
<slf4j.version>2.0.13</slf4j.version>
2121
<name>AutoPlug-Client</name>
@@ -297,6 +297,12 @@ AUTO-GENERATED FILE, CHANGES SHOULD BE DONE IN ./JPM.java or ./src/main/java/JPM
297297
<version>20250107</version>
298298
<scope>compile</scope>
299299
</dependency>
300+
<dependency>
301+
<groupId>io.netty</groupId>
302+
<artifactId>netty-all</artifactId>
303+
<version>4.1.101.Final</version>
304+
<scope>compile</scope>
305+
</dependency>
300306
<dependency>
301307
<groupId>com.google.api-client</groupId>
302308
<artifactId>google-api-client</artifactId>

src/main/java/JPM.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public ThisProject(List<String> args) {
2121
// Override default configurations
2222
this.groupId = "com.osiris.autoplug.client";
2323
this.artifactId = "AutoPlug-Client";
24-
this.version = "9.0.2";
24+
this.version = "10.0.0";
2525
this.mainClass = "com.osiris.autoplug.client.Main";
2626
this.jarName = "AutoPlug-Client-original.jar";
2727
this.fatJarName = "AutoPlug-Client.jar";
@@ -74,6 +74,7 @@ public ThisProject(List<String> args) {
7474
implementation("com.formdev:flatlaf:2.2");
7575
implementation("org.apache.sshd:sshd-core:2.13.0");
7676
implementation("org.json:json:20250107");
77+
implementation("io.netty:netty-all:4.1.101.Final");
7778

7879
// Google Drive
7980
implementation("com.google.api-client:google-api-client:2.0.0");

src/main/java/com/osiris/autoplug/client/console/Commands.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ public static boolean execute(@NotNull String command) {
163163
new BeforeServerStartupTasks();
164164
return true;
165165
} else if (command.equals(".con info") || command.equals(".ci")) {
166-
AL.info("Main connection: connected=" + Main.CON.isConnected() + " interrupted=" + Main.CON.isInterrupted() + " user/staff active=" + Main.CON.isUserActive.get());
166+
AL.info("Main connection: connected=" + Main.CON.isConnected() + " user/staff active=" + Main.CON.isUserActive.get());
167167
AL.info(Main.CON.CON_PUBLIC_DETAILS.toString());
168168
AL.info(Main.CON.CON_PRIVATE_DETAILS.toString());
169169
AL.info(Main.CON.CON_CONSOLE_SEND.toString());
Lines changed: 84 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,29 @@
1-
/*
2-
* Copyright (c) 2021-2024 Osiris-Team.
3-
* All rights reserved.
4-
*
5-
* This software is copyrighted work, licensed under the terms
6-
* of the MIT-License. Consult the "LICENSE" file for details.
7-
*/
8-
91
package com.osiris.autoplug.client.network.online;
102

113
import com.osiris.autoplug.client.network.online.connections.*;
124
import com.osiris.jlib.logger.AL;
5+
import io.netty.buffer.ByteBuf;
6+
import io.netty.channel.ChannelHandlerContext;
7+
import io.netty.handler.codec.ReplayingDecoder;
8+
import io.netty.handler.timeout.IdleStateHandler;
139

14-
import java.io.IOException;
1510
import java.security.InvalidKeyException;
11+
import java.util.List;
12+
import java.util.concurrent.TimeUnit;
1613
import java.util.concurrent.atomic.AtomicBoolean;
1714

18-
/**
19-
* This is the main connection to AutoPlugs online server/website.
20-
* It stays active all the time and sends/receives true/false values, regarding the users login status.
21-
* When the user is logged in, it creates the secondary connections and holds them until the user logs out.
22-
* The main connection authenticates using the server_key.
23-
* If it receives a true boolean it means that the user is logged in and opens new connections.
24-
*/
2515
public class ConMain extends DefaultConnection {
2616
public final ConSendPublicDetails CON_PUBLIC_DETAILS = new ConSendPublicDetails();
27-
28-
29-
// Secondary connections:
3017
public final ConAutoPlugConsoleReceive CON_CONSOLE_RECEIVE = new ConAutoPlugConsoleReceive();
3118
public final ConAutoPlugConsoleSend CON_CONSOLE_SEND = new ConAutoPlugConsoleSend();
3219
public final ConSystemConsoleSend CON_SYSTEM_CONSOLE_SEND = new ConSystemConsoleSend();
3320
public final ConSystemConsoleReceive CON_SYSTEM_CONSOLE_RECEIVE = new ConSystemConsoleReceive();
3421
public final ConSendPrivateDetails CON_PRIVATE_DETAILS = new ConSendPrivateDetails();
3522
public final ConFileManager CON_FILE_MANAGER = new ConFileManager();
23+
3624
public boolean isDone = false; // So that the log isn't a mess because of the processes which start right after this.
3725
public AtomicBoolean isUserActive = new AtomicBoolean(false);
38-
public boolean isUserActiveOld = false; // Local variable that holds the auth boolean before the current one
39-
26+
public boolean isUserActiveOld = false;
4027
public int msUntilRetry = 30000;
4128

4229
public ConMain() {
@@ -49,97 +36,83 @@ public boolean open() {
4936
AL.info("Authenticating server...");
5037
super.open();
5138
AL.info("Authentication success!");
52-
socket.setSoTimeout(60000); // 60 seconds for when AP-Web is overloaded
5339
CON_PUBLIC_DETAILS.open();
40+
msUntilRetry = 30000;
5441
isDone = true;
5542
} catch (Exception e) {
56-
AL.warn(e);
5743
isDone = true;
58-
if (e instanceof InvalidKeyException)
59-
return false; // Abort directly since no valid key exists
60-
// else we continue below and retry the connection
44+
AL.warn(e);
45+
if (e instanceof InvalidKeyException) return false;
46+
scheduleReconnect();
47+
return false;
6148
}
62-
super.setAndStartAsync(() -> {
63-
try {
64-
if (!super.isConnected()) {
65-
AL.info("Authenticating server...");
66-
super.open();
67-
AL.info("Authentication success!");
68-
CON_PUBLIC_DETAILS.open();
69-
msUntilRetry = 30000;
70-
}
71-
72-
isDone = true;
73-
while (true) {
74-
isUserActive.set(super.in.readBoolean()); // Ping
75-
super.out.writeBoolean(true); // Pong true/false doesn't matter
76-
77-
if (isUserActive.get()) {
78-
if (!isUserActiveOld) {
79-
AL.debug(this.getClass(), "Owner/Staff is online/active.");
80-
// User is online, so open secondary connections if they weren't already
81-
if (CON_CONSOLE_RECEIVE.isConnected()) CON_CONSOLE_RECEIVE.close();
82-
CON_CONSOLE_RECEIVE.open();
83-
if (CON_CONSOLE_SEND.isConnected()) CON_CONSOLE_SEND.close();
84-
CON_CONSOLE_SEND.open();
85-
if (CON_SYSTEM_CONSOLE_RECEIVE.isConnected()) CON_SYSTEM_CONSOLE_RECEIVE.close();
86-
CON_SYSTEM_CONSOLE_RECEIVE.open();
87-
if (CON_SYSTEM_CONSOLE_SEND.isConnected()) CON_SYSTEM_CONSOLE_SEND.close();
88-
CON_SYSTEM_CONSOLE_SEND.open();
89-
if (CON_FILE_MANAGER.isConnected()) CON_FILE_MANAGER.close();
90-
CON_FILE_MANAGER.open();
91-
if (CON_PRIVATE_DETAILS.isConnected()) CON_PRIVATE_DETAILS.close();
92-
CON_PRIVATE_DETAILS.open();
93-
//if (!CON_PLUGINS_UPDATER.isConnected()) CON_PLUGINS_UPDATER.open(); Only is used at restarts!
94-
}
95-
} else {
96-
if (isUserActiveOld) {
97-
AL.debug(this.getClass(), "Owner/Staff is offline/inactive.");
98-
// Close secondary connections when user is offline/logged out
99-
if (CON_CONSOLE_RECEIVE.isConnected()) CON_CONSOLE_RECEIVE.close();
100-
if (CON_CONSOLE_SEND.isConnected()) CON_CONSOLE_SEND.close();
101-
if (CON_SYSTEM_CONSOLE_RECEIVE.isConnected()) CON_SYSTEM_CONSOLE_RECEIVE.close();
102-
if (CON_SYSTEM_CONSOLE_SEND.isConnected()) CON_SYSTEM_CONSOLE_SEND.close();
103-
if (CON_FILE_MANAGER.isConnected()) CON_FILE_MANAGER.close();
104-
if (CON_PRIVATE_DETAILS.isConnected()) CON_PRIVATE_DETAILS.close();
105-
//if (CON_PLUGINS_UPDATER.isConnected()) CON_PLUGINS_UPDATER.close(); Only is used at restarts!
106-
}
107-
}
108-
isUserActiveOld = isUserActive.get();
109-
Thread.sleep(3000);
110-
}
111-
} catch (Exception e) {
112-
isDone = true;
113-
if (isClosing.get()) {
114-
AL.info("Closed main connection to AutoPlug-Web.");
115-
return;
116-
}
11749

118-
// Since we didn't meant to close, create a new thread that tries to reconnect
119-
new Thread(() -> {
120-
try {
121-
while (true) {
122-
AL.warn("Connection problems! Reconnecting in " + msUntilRetry / 1000 + " seconds...", e);
123-
Thread.sleep(msUntilRetry);
50+
// Add IdleStateHandler FIRST: Wait some seconds for a read. (0 means disable write/all idle checking)
51+
channel.pipeline().addLast(new IdleStateHandler(30, 0, 0, TimeUnit.SECONDS));
52+
channel.pipeline().addLast(new ReplayingDecoder<Void>() {
53+
@Override
54+
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
55+
boolean active = in.readBoolean();
56+
isUserActive.set(active);
57+
58+
ByteBuf pong = ctx.alloc().buffer(1);
59+
pong.writeBoolean(true);
60+
ctx.writeAndFlush(pong);
61+
62+
try {
63+
if (active && !isUserActiveOld) {
64+
AL.debug(ConMain.class, "Owner/Staff is online/active.");
65+
// Offload to a virtual thread so Netty can keep working
66+
Thread.startVirtualThread(() -> {
12467
try {
125-
if (open())
126-
break;
127-
else
128-
AL.warn("Failed to reconnect!");
129-
} catch (Exception ex) {
130-
AL.warn("Failed to reconnect!", ex);
68+
if (!CON_CONSOLE_RECEIVE.isConnected()) CON_CONSOLE_RECEIVE.open();
69+
if (!CON_CONSOLE_SEND.isConnected()) CON_CONSOLE_SEND.open();
70+
if (!CON_SYSTEM_CONSOLE_RECEIVE.isConnected()) CON_SYSTEM_CONSOLE_RECEIVE.open();
71+
if (!CON_SYSTEM_CONSOLE_SEND.isConnected()) CON_SYSTEM_CONSOLE_SEND.open();
72+
if (!CON_FILE_MANAGER.isConnected()) CON_FILE_MANAGER.open();
73+
if (!CON_PRIVATE_DETAILS.isConnected()) CON_PRIVATE_DETAILS.open();
74+
} catch (Exception e) {
75+
AL.warn(e);
13176
}
132-
msUntilRetry += 30000;
133-
}
134-
} catch (Exception exception) {
135-
AL.warn("Connection problems, unexpected error! Reconnect manually by entering '.con reload'.", exception);
77+
});
78+
} else if (!active && isUserActiveOld) {
79+
AL.debug(ConMain.class, "Owner/Staff is offline/inactive.");
80+
closeTempCons();
13681
}
137-
}).start();
82+
isUserActiveOld = active;
83+
} catch (Exception e) {
84+
AL.warn(e);
85+
}
86+
checkpoint();
87+
}
13888

139-
close();
89+
@Override
90+
public void channelInactive(ChannelHandlerContext ctx) {
91+
if (!isClosing.get()) scheduleReconnect();
92+
}
93+
94+
@Override
95+
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
96+
if (!isClosing.get()) scheduleReconnect();
14097
}
14198
});
142-
return true; // Success
99+
100+
return true;
101+
}
102+
103+
private void scheduleReconnect() {
104+
if (isClosing.get()) return;
105+
new Thread(() -> {
106+
try {
107+
AL.warn("Connection problems! Reconnecting in " + msUntilRetry / 1000 + " seconds...");
108+
Thread.sleep(msUntilRetry);
109+
msUntilRetry += 30000;
110+
if (!open()) scheduleReconnect();
111+
} catch (Exception e) {
112+
AL.warn("Reconnect error", e);
113+
}
114+
}).start();
115+
close();
143116
}
144117

145118
@Override
@@ -148,58 +121,19 @@ public void close() {
148121
closePermCons();
149122
}
150123

151-
/**
152-
* Close connections that are alive all the time.
153-
*/
154124
public void closePermCons() {
155-
try {
156-
// Reset booleans
157-
isUserActiveOld = false;
158-
isUserActive.set(false);
159-
super.close();
160-
} catch (Exception e) {
161-
AL.warn(e);
162-
}
163-
164-
try {
165-
if (CON_PUBLIC_DETAILS.isConnected())
166-
CON_PUBLIC_DETAILS.close();
167-
} catch (Exception e1) {
168-
AL.warn(e1);
169-
}
125+
isUserActiveOld = false;
126+
isUserActive.set(false);
127+
super.close();
128+
try { if (CON_PUBLIC_DETAILS.isConnected()) CON_PUBLIC_DETAILS.close(); } catch (Exception ignored) {}
170129
}
171130

172-
/**
173-
* Close connections that are online alive when the user is logged in.
174-
*/
175131
public void closeTempCons() {
176-
try {
177-
if (CON_CONSOLE_RECEIVE.isConnected())
178-
CON_CONSOLE_RECEIVE.close();
179-
} catch (Exception e1) {
180-
AL.warn(e1);
181-
}
182-
183-
try {
184-
if (CON_CONSOLE_SEND.isConnected())
185-
CON_CONSOLE_SEND.close();
186-
} catch (IOException e1) {
187-
AL.warn(e1);
188-
}
189-
190-
try {
191-
if (CON_PRIVATE_DETAILS.isConnected())
192-
CON_PRIVATE_DETAILS.close();
193-
} catch (Exception e1) {
194-
AL.warn(e1);
195-
}
196-
197-
try {
198-
if (CON_FILE_MANAGER.isConnected())
199-
CON_FILE_MANAGER.close();
200-
} catch (Exception e1) {
201-
AL.warn(e1);
202-
}
132+
try { if (CON_CONSOLE_RECEIVE.isConnected()) CON_CONSOLE_RECEIVE.close(); } catch (Exception ignored) {}
133+
try { if (CON_CONSOLE_SEND.isConnected()) CON_CONSOLE_SEND.close(); } catch (Exception ignored) {}
134+
try { if (CON_PRIVATE_DETAILS.isConnected()) CON_PRIVATE_DETAILS.close(); } catch (Exception ignored) {}
135+
try { if (CON_FILE_MANAGER.isConnected()) CON_FILE_MANAGER.close(); } catch (Exception ignored) {}
136+
try { if (CON_SYSTEM_CONSOLE_RECEIVE.isConnected()) CON_SYSTEM_CONSOLE_RECEIVE.close(); } catch (Exception ignored) {}
137+
try { if (CON_SYSTEM_CONSOLE_SEND.isConnected()) CON_SYSTEM_CONSOLE_SEND.close(); } catch (Exception ignored) {}
203138
}
204-
205-
}
139+
}

0 commit comments

Comments
 (0)