summaryrefslogtreecommitdiff
path: root/enigma-server
diff options
context:
space:
mode:
authorGravatar modmuss502022-09-14 13:12:55 +0100
committerGravatar GitHub2022-09-14 13:12:55 +0100
commit9c736848fb7aa82d295b3aa2946e6cd132ee998f (patch)
treeb982613cfa7201b2db25cb64a5950f9a2c34a5b3 /enigma-server
parentNested packages in Swing UI (#458) (diff)
downloadenigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.tar.gz
enigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.tar.xz
enigma-fork-9c736848fb7aa82d295b3aa2946e6cd132ee998f.zip
Add checkstyle (#460)
Diffstat (limited to 'enigma-server')
-rw-r--r--enigma-server/build.gradle6
-rw-r--r--enigma-server/docs/protocol.md50
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java18
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java74
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java25
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java47
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/Message.java155
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java44
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java1
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java4
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java13
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java5
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java109
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java2
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java9
-rw-r--r--enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java8
-rw-r--r--enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java6
23 files changed, 358 insertions, 234 deletions
diff --git a/enigma-server/build.gradle b/enigma-server/build.gradle
index 2764558..873adb9 100644
--- a/enigma-server/build.gradle
+++ b/enigma-server/build.gradle
@@ -1,10 +1,10 @@
1plugins { 1plugins {
2 id 'application' 2 id 'application'
3} 3}
4 4
5dependencies { 5dependencies {
6 implementation project(':enigma') 6 implementation project(':enigma')
7 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3' 7 implementation 'net.sf.jopt-simple:jopt-simple:6.0-alpha-3'
8} 8}
9 9
10mainClassName = 'cuchaz.enigma.network.DedicatedEnigmaServer' 10mainClassName = 'cuchaz.enigma.network.DedicatedEnigmaServer'
diff --git a/enigma-server/docs/protocol.md b/enigma-server/docs/protocol.md
index 83ef4c0..f642e13 100644
--- a/enigma-server/docs/protocol.md
+++ b/enigma-server/docs/protocol.md
@@ -1,4 +1,5 @@
1# Enigma protocol 1# Enigma protocol
2
2Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being 3Enigma uses TCP sockets for communication. Data is sent in each direction as a continuous stream, with packets being
3concatenated one after the other. 4concatenated one after the other.
4 5
@@ -10,6 +11,7 @@ use the same modified UTF format as in `DataOutputStream`, I repeat, the normal
10Strings, see below. 11Strings, see below.
11 12
12## Login protocol 13## Login protocol
14
13``` 15```
14Client Server 16Client Server
15| | 17| |
@@ -22,6 +24,7 @@ Client Server
22| ConfirmChange | 24| ConfirmChange |
23| >>>>>>>>>>>>> | 25| >>>>>>>>>>>>> |
24``` 26```
27
251. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client, 281. On connect, the client sends a login packet to the server. This allows the server to test the validity of the client,
26 as well as allowing the client to declare metadata about itself, such as the username. 29 as well as allowing the client to declare metadata about itself, such as the username.
271. After validating the login packet, the server sends all its mappings to the client, and the client will apply them. 301. After validating the login packet, the server sends all its mappings to the client, and the client will apply them.
@@ -29,15 +32,18 @@ Client Server
29 has received the mappings and is in sync with the server. Once the server receives this packet, the client will be 32 has received the mappings and is in sync with the server. Once the server receives this packet, the client will be
30 allowed to modify mappings. 33 allowed to modify mappings.
31 34
32The server will not accept any other packets from the client until this entire exchange has been completed. 35The server will not accept any other packets from the client until this entire exchange has been completed.
33 36
34## Kicking clients 37## Kicking clients
38
35When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which 39When the server kicks a client, it may optionally send a `Kick` packet immediately before closing the connection, which
36contains the reason why the client was kicked (so the client can display it to the user). This is not required though - 40contains the reason why the client was kicked (so the client can display it to the user). This is not required though -
37the server may simply terminate the connection. 41the server may simply terminate the connection.
38 42
39## Changing mappings 43## Changing mappings
44
40This section uses the example of renaming, but the same pattern applies to all mapping changes. 45This section uses the example of renaming, but the same pattern applies to all mapping changes.
46
41``` 47```
42Client A Server Client B 48Client A Server Client B
43| | | 49| | |
@@ -66,19 +72,23 @@ Client A Server Client B
66 server will unlock that mapping for that client and allow them to make changes again. 72 server will unlock that mapping for that client and allow them to make changes again.
67 73
68## Packets 74## Packets
75
69```c 76```c
70struct Packet { 77struct Packet {
71 unsigned short packet_id; 78 unsigned short packet_id;
72 data[]; // depends on packet_id 79 data[]; // depends on packet_id
73} 80}
74``` 81```
82
75The IDs for client-to-server packets are as follows: 83The IDs for client-to-server packets are as follows:
84
76- 0: `Login` 85- 0: `Login`
77- 1: `ConfirmChange` 86- 1: `ConfirmChange`
78- 6: `Message` 87- 6: `Message`
79- 7: `EntryChange` 88- 7: `EntryChange`
80 89
81The IDs for server-to-client packets are as follows: 90The IDs for server-to-client packets are as follows:
91
82- 0: `Kick` 92- 0: `Kick`
83- 1: `SyncMappings` 93- 1: `SyncMappings`
84- 6: `Message` 94- 6: `Message`
@@ -86,17 +96,20 @@ The IDs for server-to-client packets are as follows:
86- 8: `EntryChange` 96- 8: `EntryChange`
87 97
88### The utf struct 98### The utf struct
99
89```c 100```c
90struct utf { 101struct utf {
91 unsigned short length; 102 unsigned short length;
92 byte data[length]; 103 byte data[length];
93} 104}
94``` 105```
106
95- `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of 107- `length`: The number of bytes in the UTF-8 encoding of the string. Note, this may not be the same as the number of
96 Unicode characters in the string. 108 Unicode characters in the string.
97- `data`: A standard UTF-8 encoded byte array representing the string. 109- `data`: A standard UTF-8 encoded byte array representing the string.
98 110
99### The Entry struct 111### The Entry struct
112
100```c 113```c
101enum EntryType { 114enum EntryType {
102 ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; 115 ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3;
@@ -121,9 +134,10 @@ struct Entry {
121 } 134 }
122} 135}
123``` 136```
137
124- `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`. 138- `type`: The type of entry this is. One of `ENTRY_CLASS`, `ENTRY_FIELD`, `ENTRY_METHOD` or `ENTRY_LOCAL_VAR`.
125- `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their 139- `parent`: The parent entry. Only class entries may have no parent. fields, methods and inner classes must have their
126 containing class as their parent. Local variables have a method as a parent. 140 containing class as their parent. Local variables have a method as a parent.
127- `name`: The class/field/method/variable name. 141- `name`: The class/field/method/variable name.
128- `javadoc`: The javadoc of an entry, if present. 142- `javadoc`: The javadoc of an entry, if present.
129- `descriptor`: The field/method descriptor. 143- `descriptor`: The field/method descriptor.
@@ -131,6 +145,7 @@ struct Entry {
131- `parameter`: Whether the local variable is a parameter. 145- `parameter`: Whether the local variable is a parameter.
132 146
133### The Message struct 147### The Message struct
148
134```c 149```c
135enum MessageType { 150enum MessageType {
136 MESSAGE_CHAT = 0, 151 MESSAGE_CHAT = 0,
@@ -176,8 +191,9 @@ struct Message {
176 } data; 191 } data;
177}; 192};
178``` 193```
194
179- `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`, 195- `type`: The type of message this is. One of `MESSAGE_CHAT`, `MESSAGE_CONNECT`, `MESSAGE_DISCONNECT`,
180 `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`. 196 `MESSAGE_EDIT_DOCS`, `MESSAGE_MARK_DEOBF`, `MESSAGE_REMOVE_MAPPING`, `MESSAGE_RENAME`.
181- `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT` 197- `chat`: Chat message. Use in case `type` is `MESSAGE_CHAT`
182- `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT` 198- `connect`: Sent when a user connects. Use in case `type` is `MESSAGE_CONNECT`
183- `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT` 199- `disconnect`: Sent when a user disconnects. Use in case `type` is `MESSAGE_DISCONNECT`
@@ -191,6 +207,7 @@ struct Message {
191- `new_name`: The new name for the entry. 207- `new_name`: The new name for the entry.
192 208
193### The entry_change struct 209### The entry_change struct
210
194```c 211```c
195typedef enum tristate_change { 212typedef enum tristate_change {
196 TRISTATE_CHANGE_UNCHANGED = 0, 213 TRISTATE_CHANGE_UNCHANGED = 0,
@@ -224,6 +241,7 @@ struct entry_change {
224 } 241 }
225} 242}
226``` 243```
244
227- `entry`: The entry this change gets applied to. 245- `entry`: The entry this change gets applied to.
228- `flags`: See definition of `entry_change_flags`. 246- `flags`: See definition of `entry_change_flags`.
229- `deobf_name`: The new deobfuscated name, if deobf_name_change == TRISTATE_CHANGE_SET 247- `deobf_name`: The new deobfuscated name, if deobf_name_change == TRISTATE_CHANGE_SET
@@ -231,6 +249,7 @@ struct entry_change {
231- `access_modifiers`: The new access modifier, if access_change == TRISTATE_CHANGE_SET (otherwise 0) 249- `access_modifiers`: The new access modifier, if access_change == TRISTATE_CHANGE_SET (otherwise 0)
232 250
233### Login (client-to-server) 251### Login (client-to-server)
252
234```c 253```c
235struct LoginC2SPacket { 254struct LoginC2SPacket {
236 unsigned short protocol_version; 255 unsigned short protocol_version;
@@ -240,47 +259,57 @@ struct LoginC2SPacket {
240 utf username; 259 utf username;
241} 260}
242``` 261```
262
243- `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be 263- `protocol_version`: the version of the protocol. If the version does not match on the server, then the client will be
244 kicked immediately. Currently always equal to 0. 264 kicked immediately. Currently always equal to 0.
245- `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file 265- `checksum`: the SHA-1 hash of the JAR file the client has open. If this does not match the SHA-1 hash of the JAR file
246 the server has open, the client will be kicked. 266 the server has open, the client will be kicked.
247- `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type. 267- `password`: the password needed to log into the server. Note that each `char` is 2 bytes, as per the Java data type.
248 If this password is incorrect, the client will be kicked. 268 If this password is incorrect, the client will be kicked.
249- `username`: the username of the user logging in. If the username is not unique, the client will be kicked. 269- `username`: the username of the user logging in. If the username is not unique, the client will be kicked.
250 270
251### ConfirmChange (client-to-server) 271### ConfirmChange (client-to-server)
272
252```c 273```c
253struct ConfirmChangeC2SPacket { 274struct ConfirmChangeC2SPacket {
254 unsigned short sync_id; 275 unsigned short sync_id;
255} 276}
256``` 277```
278
257- `sync_id`: the sync ID to confirm. 279- `sync_id`: the sync ID to confirm.
258 280
259### Message (client-to-server) 281### Message (client-to-server)
282
260```c 283```c
261struct MessageC2SPacket { 284struct MessageC2SPacket {
262 utf message; 285 utf message;
263} 286}
264``` 287```
288
265- `message`: The text message the user sent. 289- `message`: The text message the user sent.
266 290
267### EntryChange (client-to-server) 291### EntryChange (client-to-server)
292
268```c 293```c
269struct EntryChangeC2SPacket { 294struct EntryChangeC2SPacket {
270 entry_change change; 295 entry_change change;
271} 296}
272``` 297```
298
273- `change`: The change to apply. 299- `change`: The change to apply.
274 300
275### Kick (server-to-client) 301### Kick (server-to-client)
302
276```c 303```c
277struct KickS2CPacket { 304struct KickS2CPacket {
278 utf reason; 305 utf reason;
279} 306}
280``` 307```
308
281- `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user. 309- `reason`: the reason for the kick, may or may not be a translation key for the client to display to the user.
282 310
283### SyncMappings (server-to-client) 311### SyncMappings (server-to-client)
312
284```c 313```c
285struct SyncMappingsS2CPacket { 314struct SyncMappingsS2CPacket {
286 int num_roots; 315 int num_roots;
@@ -296,6 +325,7 @@ struct MappingNode {
296} 325}
297typedef { Entry but without the has_parent or parent fields } NoParentEntry; 326typedef { Entry but without the has_parent or parent fields } NoParentEntry;
298``` 327```
328
299- `roots`: The root mapping nodes, containing all the entries without parents. 329- `roots`: The root mapping nodes, containing all the entries without parents.
300- `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry. 330- `obf_entry`: The value of a node, containing the obfuscated name and descriptor of the entry.
301- `name`: The deobfuscated name of the entry, if it exists, otherwise the empty string. 331- `name`: The deobfuscated name of the entry, if it exists, otherwise the empty string.
@@ -303,6 +333,7 @@ typedef { Entry but without the has_parent or parent fields } NoParentEntry;
303- `children`: The children of this node 333- `children`: The children of this node
304 334
305### Message (server-to-client) 335### Message (server-to-client)
336
306```c 337```c
307struct MessageS2CPacket { 338struct MessageS2CPacket {
308 Message message; 339 Message message;
@@ -310,6 +341,7 @@ struct MessageS2CPacket {
310``` 341```
311 342
312### UserList (server-to-client) 343### UserList (server-to-client)
344
313```c 345```c
314struct UserListS2CPacket { 346struct UserListS2CPacket {
315 unsigned short len; 347 unsigned short len;
@@ -318,11 +350,13 @@ struct UserListS2CPacket {
318``` 350```
319 351
320### EntryChange (server-to-client) 352### EntryChange (server-to-client)
353
321```c 354```c
322struct EntryChangeS2CPacket { 355struct EntryChangeS2CPacket {
323 uint16_t sync_id; 356 uint16_t sync_id;
324 entry_change change; 357 entry_change change;
325} 358}
326``` 359```
360
327- `sync_id`: The sync ID of the change for locking purposes. 361- `sync_id`: The sync ID of the change for locking purposes.
328- `change`: The change to apply. \ No newline at end of file 362- `change`: The change to apply. \ No newline at end of file
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
index a651fe8..8fcd437 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ClientPacketHandler.java
@@ -1,22 +1,22 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import java.util.List;
4
5import cuchaz.enigma.network.packet.Packet;
3import cuchaz.enigma.translation.mapping.EntryChange; 6import cuchaz.enigma.translation.mapping.EntryChange;
4import cuchaz.enigma.translation.mapping.EntryMapping; 7import cuchaz.enigma.translation.mapping.EntryMapping;
5import cuchaz.enigma.translation.mapping.tree.EntryTree; 8import cuchaz.enigma.translation.mapping.tree.EntryTree;
6import cuchaz.enigma.network.packet.Packet;
7
8import java.util.List;
9 9
10public interface ClientPacketHandler { 10public interface ClientPacketHandler {
11 void openMappings(EntryTree<EntryMapping> mappings); 11 void openMappings(EntryTree<EntryMapping> mappings);
12 12
13 boolean applyChangeFromServer(EntryChange<?> change); 13 boolean applyChangeFromServer(EntryChange<?> change);
14 14
15 void disconnectIfConnected(String reason); 15 void disconnectIfConnected(String reason);
16 16
17 void sendPacket(Packet<ServerPacketHandler> packet); 17 void sendPacket(Packet<ServerPacketHandler> packet);
18 18
19 void addMessage(Message message); 19 void addMessage(Message message);
20 20
21 void updateUserList(List<String> users); 21 void updateUserList(List<String> users);
22} 22}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
index 41f0834..eb22a50 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/DedicatedEnigmaServer.java
@@ -1,17 +1,5 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import com.google.common.io.MoreFiles;
4import cuchaz.enigma.*;
5import cuchaz.enigma.classprovider.ClasspathClassProvider;
6import cuchaz.enigma.translation.mapping.serde.MappingParseException;
7import cuchaz.enigma.translation.mapping.EntryRemapper;
8import cuchaz.enigma.translation.mapping.serde.MappingFormat;
9import cuchaz.enigma.utils.Utils;
10import joptsimple.OptionParser;
11import joptsimple.OptionSet;
12import joptsimple.OptionSpec;
13import joptsimple.ValueConverter;
14
15import java.io.IOException; 3import java.io.IOException;
16import java.io.PrintWriter; 4import java.io.PrintWriter;
17import java.nio.file.Files; 5import java.nio.file.Files;
@@ -22,24 +10,30 @@ import java.util.concurrent.Executors;
22import java.util.concurrent.LinkedBlockingDeque; 10import java.util.concurrent.LinkedBlockingDeque;
23import java.util.concurrent.TimeUnit; 11import java.util.concurrent.TimeUnit;
24 12
25public class DedicatedEnigmaServer extends EnigmaServer { 13import com.google.common.io.MoreFiles;
14import joptsimple.OptionParser;
15import joptsimple.OptionSet;
16import joptsimple.OptionSpec;
17import joptsimple.ValueConverter;
18
19import cuchaz.enigma.Enigma;
20import cuchaz.enigma.EnigmaProfile;
21import cuchaz.enigma.EnigmaProject;
22import cuchaz.enigma.ProgressListener;
23import cuchaz.enigma.classprovider.ClasspathClassProvider;
24import cuchaz.enigma.translation.mapping.EntryRemapper;
25import cuchaz.enigma.translation.mapping.serde.MappingFormat;
26import cuchaz.enigma.translation.mapping.serde.MappingParseException;
27import cuchaz.enigma.utils.Utils;
26 28
29public class DedicatedEnigmaServer extends EnigmaServer {
27 private final EnigmaProfile profile; 30 private final EnigmaProfile profile;
28 private final MappingFormat mappingFormat; 31 private final MappingFormat mappingFormat;
29 private final Path mappingsFile; 32 private final Path mappingsFile;
30 private final PrintWriter log; 33 private final PrintWriter log;
31 private BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>(); 34 private BlockingQueue<Runnable> tasks = new LinkedBlockingDeque<>();
32 35
33 public DedicatedEnigmaServer( 36 public DedicatedEnigmaServer(byte[] jarChecksum, char[] password, EnigmaProfile profile, MappingFormat mappingFormat, Path mappingsFile, PrintWriter log, EntryRemapper mappings, int port) {
34 byte[] jarChecksum,
35 char[] password,
36 EnigmaProfile profile,
37 MappingFormat mappingFormat,
38 Path mappingsFile,
39 PrintWriter log,
40 EntryRemapper mappings,
41 int port
42 ) {
43 super(jarChecksum, password, mappings, port); 37 super(jarChecksum, password, mappings, port);
44 this.profile = profile; 38 this.profile = profile;
45 this.mappingFormat = mappingFormat; 39 this.mappingFormat = mappingFormat;
@@ -61,33 +55,17 @@ public class DedicatedEnigmaServer extends EnigmaServer {
61 public static void main(String[] args) { 55 public static void main(String[] args) {
62 OptionParser parser = new OptionParser(); 56 OptionParser parser = new OptionParser();
63 57
64 OptionSpec<Path> jarOpt = parser.accepts("jar", "Jar file to open at startup") 58 OptionSpec<Path> jarOpt = parser.accepts("jar", "Jar file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE);
65 .withRequiredArg()
66 .required()
67 .withValuesConvertedBy(PathConverter.INSTANCE);
68 59
69 OptionSpec<Path> mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup") 60 OptionSpec<Path> mappingsOpt = parser.accepts("mappings", "Mappings file to open at startup").withRequiredArg().required().withValuesConvertedBy(PathConverter.INSTANCE);
70 .withRequiredArg()
71 .required()
72 .withValuesConvertedBy(PathConverter.INSTANCE);
73 61
74 OptionSpec<Path> profileOpt = parser.accepts("profile", "Profile json to apply at startup") 62 OptionSpec<Path> profileOpt = parser.accepts("profile", "Profile json to apply at startup").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE);
75 .withRequiredArg()
76 .withValuesConvertedBy(PathConverter.INSTANCE);
77 63
78 OptionSpec<Integer> portOpt = parser.accepts("port", "Port to run the server on") 64 OptionSpec<Integer> portOpt = parser.accepts("port", "Port to run the server on").withOptionalArg().ofType(Integer.class).defaultsTo(EnigmaServer.DEFAULT_PORT);
79 .withOptionalArg()
80 .ofType(Integer.class)
81 .defaultsTo(EnigmaServer.DEFAULT_PORT);
82 65
83 OptionSpec<String> passwordOpt = parser.accepts("password", "The password to join the server") 66 OptionSpec<String> passwordOpt = parser.accepts("password", "The password to join the server").withRequiredArg().defaultsTo("");
84 .withRequiredArg()
85 .defaultsTo("");
86 67
87 OptionSpec<Path> logFileOpt = parser.accepts("log", "The log file to write to") 68 OptionSpec<Path> logFileOpt = parser.accepts("log", "The log file to write to").withRequiredArg().withValuesConvertedBy(PathConverter.INSTANCE).defaultsTo(Paths.get("log.txt"));
88 .withRequiredArg()
89 .withValuesConvertedBy(PathConverter.INSTANCE)
90 .defaultsTo(Paths.get("log.txt"));
91 69
92 OptionSet parsedArgs = parser.parse(args); 70 OptionSet parsedArgs = parser.parse(args);
93 Path jar = parsedArgs.valueOf(jarOpt); 71 Path jar = parsedArgs.valueOf(jarOpt);
@@ -95,14 +73,17 @@ public class DedicatedEnigmaServer extends EnigmaServer {
95 Path profileFile = parsedArgs.valueOf(profileOpt); 73 Path profileFile = parsedArgs.valueOf(profileOpt);
96 int port = parsedArgs.valueOf(portOpt); 74 int port = parsedArgs.valueOf(portOpt);
97 char[] password = parsedArgs.valueOf(passwordOpt).toCharArray(); 75 char[] password = parsedArgs.valueOf(passwordOpt).toCharArray();
76
98 if (password.length > EnigmaServer.MAX_PASSWORD_LENGTH) { 77 if (password.length > EnigmaServer.MAX_PASSWORD_LENGTH) {
99 System.err.println("Password too long, must be at most " + EnigmaServer.MAX_PASSWORD_LENGTH + " characters"); 78 System.err.println("Password too long, must be at most " + EnigmaServer.MAX_PASSWORD_LENGTH + " characters");
100 System.exit(1); 79 System.exit(1);
101 } 80 }
81
102 Path logFile = parsedArgs.valueOf(logFileOpt); 82 Path logFile = parsedArgs.valueOf(logFileOpt);
103 83
104 System.out.println("Starting Enigma server"); 84 System.out.println("Starting Enigma server");
105 DedicatedEnigmaServer server; 85 DedicatedEnigmaServer server;
86
106 try { 87 try {
107 byte[] checksum = Utils.zipSha1(parsedArgs.valueOf(jarOpt)); 88 byte[] checksum = Utils.zipSha1(parsedArgs.valueOf(jarOpt));
108 89
@@ -113,10 +94,12 @@ public class DedicatedEnigmaServer extends EnigmaServer {
113 94
114 MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY; 95 MappingFormat mappingFormat = MappingFormat.ENIGMA_DIRECTORY;
115 EntryRemapper mappings; 96 EntryRemapper mappings;
97
116 if (!Files.exists(mappingsFile)) { 98 if (!Files.exists(mappingsFile)) {
117 mappings = EntryRemapper.empty(project.getJarIndex()); 99 mappings = EntryRemapper.empty(project.getJarIndex());
118 } else { 100 } else {
119 System.out.println("Reading mappings..."); 101 System.out.println("Reading mappings...");
102
120 if (Files.isDirectory(mappingsFile)) { 103 if (Files.isDirectory(mappingsFile)) {
121 mappingFormat = MappingFormat.ENIGMA_DIRECTORY; 104 mappingFormat = MappingFormat.ENIGMA_DIRECTORY;
122 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsFile))) { 105 } else if ("zip".equalsIgnoreCase(MoreFiles.getFileExtension(mappingsFile))) {
@@ -124,6 +107,7 @@ public class DedicatedEnigmaServer extends EnigmaServer {
124 } else { 107 } else {
125 mappingFormat = MappingFormat.ENIGMA_FILE; 108 mappingFormat = MappingFormat.ENIGMA_FILE;
126 } 109 }
110
127 mappings = EntryRemapper.mapped(project.getJarIndex(), mappingFormat.read(mappingsFile, ProgressListener.none(), profile.getMappingSaveParameters())); 111 mappings = EntryRemapper.mapped(project.getJarIndex(), mappingFormat.read(mappingsFile, ProgressListener.none(), profile.getMappingSaveParameters()));
128 } 112 }
129 113
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
index 71bd011..69e4f5e 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaClient.java
@@ -1,15 +1,20 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import cuchaz.enigma.network.packet.Packet; 3import java.io.DataInput;
4import cuchaz.enigma.network.packet.PacketRegistry; 4import java.io.DataInputStream;
5 5import java.io.DataOutput;
6import javax.swing.*; 6import java.io.DataOutputStream;
7import java.io.*; 7import java.io.EOFException;
8import java.io.IOException;
8import java.net.Socket; 9import java.net.Socket;
9import java.net.SocketException; 10import java.net.SocketException;
10 11
11public class EnigmaClient { 12import javax.swing.SwingUtilities;
12 13
14import cuchaz.enigma.network.packet.Packet;
15import cuchaz.enigma.network.packet.PacketRegistry;
16
17public class EnigmaClient {
13 private final ClientPacketHandler controller; 18 private final ClientPacketHandler controller;
14 19
15 private final String ip; 20 private final String ip;
@@ -29,17 +34,22 @@ public class EnigmaClient {
29 Thread thread = new Thread(() -> { 34 Thread thread = new Thread(() -> {
30 try { 35 try {
31 DataInput input = new DataInputStream(socket.getInputStream()); 36 DataInput input = new DataInputStream(socket.getInputStream());
37
32 while (true) { 38 while (true) {
33 int packetId; 39 int packetId;
40
34 try { 41 try {
35 packetId = input.readUnsignedByte(); 42 packetId = input.readUnsignedByte();
36 } catch (EOFException | SocketException e) { 43 } catch (EOFException | SocketException e) {
37 break; 44 break;
38 } 45 }
46
39 Packet<ClientPacketHandler> packet = PacketRegistry.createS2CPacket(packetId); 47 Packet<ClientPacketHandler> packet = PacketRegistry.createS2CPacket(packetId);
48
40 if (packet == null) { 49 if (packet == null) {
41 throw new IOException("Received invalid packet id " + packetId); 50 throw new IOException("Received invalid packet id " + packetId);
42 } 51 }
52
43 packet.read(input); 53 packet.read(input);
44 SwingUtilities.invokeLater(() -> packet.handle(controller)); 54 SwingUtilities.invokeLater(() -> packet.handle(controller));
45 } 55 }
@@ -47,6 +57,7 @@ public class EnigmaClient {
47 controller.disconnectIfConnected(e.toString()); 57 controller.disconnectIfConnected(e.toString());
48 return; 58 return;
49 } 59 }
60
50 controller.disconnectIfConnected("Disconnected"); 61 controller.disconnectIfConnected("Disconnected");
51 }); 62 });
52 thread.setName("Client I/O thread"); 63 thread.setName("Client I/O thread");
@@ -65,7 +76,6 @@ public class EnigmaClient {
65 } 76 }
66 } 77 }
67 78
68
69 public void sendPacket(Packet<ServerPacketHandler> packet) { 79 public void sendPacket(Packet<ServerPacketHandler> packet) {
70 try { 80 try {
71 output.writeByte(PacketRegistry.getC2SId(packet)); 81 output.writeByte(PacketRegistry.getC2SId(packet));
@@ -74,5 +84,4 @@ public class EnigmaClient {
74 controller.disconnectIfConnected(e.toString()); 84 controller.disconnectIfConnected(e.toString());
75 } 85 }
76 } 86 }
77
78} 87}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
index 1ce359b..8872735 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/EnigmaServer.java
@@ -1,20 +1,35 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import java.io.*; 3import java.io.DataInput;
4import java.io.DataInputStream;
5import java.io.DataOutput;
6import java.io.DataOutputStream;
7import java.io.EOFException;
8import java.io.IOException;
4import java.net.ServerSocket; 9import java.net.ServerSocket;
5import java.net.Socket; 10import java.net.Socket;
6import java.net.SocketException; 11import java.net.SocketException;
7import java.util.*; 12import java.util.ArrayList;
13import java.util.Collections;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Set;
8import java.util.concurrent.CopyOnWriteArrayList; 19import java.util.concurrent.CopyOnWriteArrayList;
9 20
10import cuchaz.enigma.network.packet.*; 21import cuchaz.enigma.network.packet.EntryChangeS2CPacket;
22import cuchaz.enigma.network.packet.KickS2CPacket;
23import cuchaz.enigma.network.packet.MessageS2CPacket;
24import cuchaz.enigma.network.packet.Packet;
25import cuchaz.enigma.network.packet.PacketRegistry;
26import cuchaz.enigma.network.packet.UserListS2CPacket;
11import cuchaz.enigma.translation.mapping.EntryChange; 27import cuchaz.enigma.translation.mapping.EntryChange;
12import cuchaz.enigma.translation.mapping.EntryMapping; 28import cuchaz.enigma.translation.mapping.EntryMapping;
13import cuchaz.enigma.translation.mapping.EntryRemapper; 29import cuchaz.enigma.translation.mapping.EntryRemapper;
14import cuchaz.enigma.translation.representation.entry.Entry; 30import cuchaz.enigma.translation.representation.entry.Entry;
15 31
16public abstract class EnigmaServer { 32public abstract class EnigmaServer {
17
18 // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347 33 // https://discordapp.com/channels/507304429255393322/566418023372816394/700292322918793347
19 public static final int DEFAULT_PORT = 34712; 34 public static final int DEFAULT_PORT = 34712;
20 public static final int PROTOCOL_VERSION = 1; 35 public static final int PROTOCOL_VERSION = 1;
@@ -71,17 +86,22 @@ public abstract class EnigmaServer {
71 Thread thread = new Thread(() -> { 86 Thread thread = new Thread(() -> {
72 try { 87 try {
73 DataInput input = new DataInputStream(client.getInputStream()); 88 DataInput input = new DataInputStream(client.getInputStream());
89
74 while (true) { 90 while (true) {
75 int packetId; 91 int packetId;
92
76 try { 93 try {
77 packetId = input.readUnsignedByte(); 94 packetId = input.readUnsignedByte();
78 } catch (EOFException | SocketException e) { 95 } catch (EOFException | SocketException e) {
79 break; 96 break;
80 } 97 }
98
81 Packet<ServerPacketHandler> packet = PacketRegistry.createC2SPacket(packetId); 99 Packet<ServerPacketHandler> packet = PacketRegistry.createC2SPacket(packetId);
100
82 if (packet == null) { 101 if (packet == null) {
83 throw new IOException("Received invalid packet id " + packetId); 102 throw new IOException("Received invalid packet id " + packetId);
84 } 103 }
104
85 packet.read(input); 105 packet.read(input);
86 runOnThread(() -> packet.handle(new ServerPacketHandler(client, this))); 106 runOnThread(() -> packet.handle(new ServerPacketHandler(client, this)));
87 } 107 }
@@ -90,6 +110,7 @@ public abstract class EnigmaServer {
90 e.printStackTrace(); 110 e.printStackTrace();
91 return; 111 return;
92 } 112 }
113
93 kick(client, "disconnect.disconnected"); 114 kick(client, "disconnect.disconnected");
94 }); 115 });
95 thread.setName("Server I/O thread #" + (nextIoId++)); 116 thread.setName("Server I/O thread #" + (nextIoId++));
@@ -103,6 +124,7 @@ public abstract class EnigmaServer {
103 for (Socket client : clients) { 124 for (Socket client : clients) {
104 kick(client, "disconnect.server_closed"); 125 kick(client, "disconnect.server_closed");
105 } 126 }
127
106 try { 128 try {
107 socket.close(); 129 socket.close();
108 } catch (IOException e) { 130 } catch (IOException e) {
@@ -114,7 +136,9 @@ public abstract class EnigmaServer {
114 } 136 }
115 137
116 public void kick(Socket client, String reason) { 138 public void kick(Socket client, String reason) {
117 if (!clients.remove(client)) return; 139 if (!clients.remove(client)) {
140 return;
141 }
118 142
119 sendPacket(client, new KickS2CPacket(reason)); 143 sendPacket(client, new KickS2CPacket(reason));
120 144
@@ -123,6 +147,7 @@ public abstract class EnigmaServer {
123 return list.isEmpty(); 147 return list.isEmpty();
124 }); 148 });
125 String username = usernames.remove(client); 149 String username = usernames.remove(client);
150
126 try { 151 try {
127 client.close(); 152 client.close();
128 } catch (IOException e) { 153 } catch (IOException e) {
@@ -134,6 +159,7 @@ public abstract class EnigmaServer {
134 System.out.println("Kicked " + username + " because " + reason); 159 System.out.println("Kicked " + username + " because " + reason);
135 sendMessage(Message.disconnect(username)); 160 sendMessage(Message.disconnect(username));
136 } 161 }
162
137 sendUsernamePacket(); 163 sendUsernamePacket();
138 } 164 }
139 165
@@ -159,6 +185,7 @@ public abstract class EnigmaServer {
159 public void sendPacket(Socket client, Packet<ClientPacketHandler> packet) { 185 public void sendPacket(Socket client, Packet<ClientPacketHandler> packet) {
160 if (!client.isClosed()) { 186 if (!client.isClosed()) {
161 int packetId = PacketRegistry.getS2CId(packet); 187 int packetId = PacketRegistry.getS2CId(packet);
188
162 try { 189 try {
163 DataOutput output = new DataOutputStream(client.getOutputStream()); 190 DataOutput output = new DataOutputStream(client.getOutputStream());
164 output.writeByte(packetId); 191 output.writeByte(packetId);
@@ -192,9 +219,11 @@ public abstract class EnigmaServer {
192 } 219 }
193 220
194 Integer syncId = syncIds.get(entry); 221 Integer syncId = syncIds.get(entry);
222
195 if (syncId == null) { 223 if (syncId == null) {
196 return true; 224 return true;
197 } 225 }
226
198 Set<Socket> clients = clientsNeedingConfirmation.get(syncId); 227 Set<Socket> clients = clientsNeedingConfirmation.get(syncId);
199 return clients == null || !clients.contains(client); 228 return clients == null || !clients.contains(client);
200 } 229 }
@@ -202,14 +231,18 @@ public abstract class EnigmaServer {
202 public int lockEntry(Socket exception, Entry<?> entry) { 231 public int lockEntry(Socket exception, Entry<?> entry) {
203 int syncId = nextSyncId; 232 int syncId = nextSyncId;
204 nextSyncId++; 233 nextSyncId++;
234
205 // sync id is sent as an unsigned short, can't have more than 65536 235 // sync id is sent as an unsigned short, can't have more than 65536
206 if (nextSyncId == 65536) { 236 if (nextSyncId == 65536) {
207 nextSyncId = DUMMY_SYNC_ID + 1; 237 nextSyncId = DUMMY_SYNC_ID + 1;
208 } 238 }
239
209 Integer oldSyncId = syncIds.get(entry); 240 Integer oldSyncId = syncIds.get(entry);
241
210 if (oldSyncId != null) { 242 if (oldSyncId != null) {
211 clientsNeedingConfirmation.remove(oldSyncId); 243 clientsNeedingConfirmation.remove(oldSyncId);
212 } 244 }
245
213 syncIds.put(entry, syncId); 246 syncIds.put(entry, syncId);
214 inverseSyncIds.put(syncId, entry); 247 inverseSyncIds.put(syncId, entry);
215 Set<Socket> clients = new HashSet<>(this.clients); 248 Set<Socket> clients = new HashSet<>(this.clients);
@@ -224,8 +257,10 @@ public abstract class EnigmaServer {
224 } 257 }
225 258
226 Set<Socket> clients = clientsNeedingConfirmation.get(syncId); 259 Set<Socket> clients = clientsNeedingConfirmation.get(syncId);
260
227 if (clients != null) { 261 if (clients != null) {
228 clients.remove(client); 262 clients.remove(client);
263
229 if (clients.isEmpty()) { 264 if (clients.isEmpty()) {
230 clientsNeedingConfirmation.remove(syncId); 265 clientsNeedingConfirmation.remove(syncId);
231 syncIds.remove(inverseSyncIds.remove(syncId)); 266 syncIds.remove(inverseSyncIds.remove(syncId));
@@ -236,6 +271,7 @@ public abstract class EnigmaServer {
236 public void sendCorrectMapping(Socket client, Entry<?> entry, boolean refreshClassTree) { 271 public void sendCorrectMapping(Socket client, Entry<?> entry, boolean refreshClassTree) {
237 EntryMapping oldMapping = mappings.getDeobfMapping(entry); 272 EntryMapping oldMapping = mappings.getDeobfMapping(entry);
238 String oldName = oldMapping.targetName(); 273 String oldName = oldMapping.targetName();
274
239 if (oldName == null) { 275 if (oldName == null) {
240 sendPacket(client, new EntryChangeS2CPacket(DUMMY_SYNC_ID, EntryChange.modify(entry).clearDeobfName())); 276 sendPacket(client, new EntryChangeS2CPacket(DUMMY_SYNC_ID, EntryChange.modify(entry).clearDeobfName()));
241 } else { 277 } else {
@@ -269,5 +305,4 @@ public abstract class EnigmaServer {
269 log(String.format("[MSG] %s", message.translate())); 305 log(String.format("[MSG] %s", message.translate()));
270 sendToAll(new MessageS2CPacket(message)); 306 sendToAll(new MessageS2CPacket(message));
271 } 307 }
272
273} 308}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
index 21c6825..99e4e99 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/IntegratedEnigmaServer.java
@@ -1,8 +1,8 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import cuchaz.enigma.translation.mapping.EntryRemapper; 3import javax.swing.SwingUtilities;
4 4
5import javax.swing.*; 5import cuchaz.enigma.translation.mapping.EntryRemapper;
6 6
7public class IntegratedEnigmaServer extends EnigmaServer { 7public class IntegratedEnigmaServer extends EnigmaServer {
8 public IntegratedEnigmaServer(byte[] jarChecksum, char[] password, EntryRemapper mappings, int port) { 8 public IntegratedEnigmaServer(byte[] jarChecksum, char[] password, EntryRemapper mappings, int port) {
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
index c157838..d49e60c 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/Message.java
@@ -10,9 +10,8 @@ import cuchaz.enigma.translation.representation.entry.Entry;
10import cuchaz.enigma.utils.I18n; 10import cuchaz.enigma.utils.I18n;
11 11
12public abstract class Message { 12public abstract class Message {
13
14 public final String user; 13 public final String user;
15 14
16 public static Chat chat(String user, String message) { 15 public static Chat chat(String user, String message) {
17 return new Chat(user, message); 16 return new Chat(user, message);
18 } 17 }
@@ -47,34 +46,37 @@ public abstract class Message {
47 46
48 public static Message read(DataInput input) throws IOException { 47 public static Message read(DataInput input) throws IOException {
49 byte typeId = input.readByte(); 48 byte typeId = input.readByte();
49
50 if (typeId < 0 || typeId >= Type.values().length) { 50 if (typeId < 0 || typeId >= Type.values().length) {
51 throw new IOException(String.format("Invalid message type ID %d", typeId)); 51 throw new IOException(String.format("Invalid message type ID %d", typeId));
52 } 52 }
53
53 Type type = Type.values()[typeId]; 54 Type type = Type.values()[typeId];
54 String user = input.readUTF(); 55 String user = input.readUTF();
56
55 switch (type) { 57 switch (type) {
56 case CHAT: 58 case CHAT:
57 String message = input.readUTF(); 59 String message = input.readUTF();
58 return chat(user, message); 60 return chat(user, message);
59 case CONNECT: 61 case CONNECT:
60 return connect(user); 62 return connect(user);
61 case DISCONNECT: 63 case DISCONNECT:
62 return disconnect(user); 64 return disconnect(user);
63 case EDIT_DOCS: 65 case EDIT_DOCS:
64 Entry<?> entry = PacketHelper.readEntry(input); 66 Entry<?> entry = PacketHelper.readEntry(input);
65 return editDocs(user, entry); 67 return editDocs(user, entry);
66 case MARK_DEOBF: 68 case MARK_DEOBF:
67 entry = PacketHelper.readEntry(input); 69 entry = PacketHelper.readEntry(input);
68 return markDeobf(user, entry); 70 return markDeobf(user, entry);
69 case REMOVE_MAPPING: 71 case REMOVE_MAPPING:
70 entry = PacketHelper.readEntry(input); 72 entry = PacketHelper.readEntry(input);
71 return removeMapping(user, entry); 73 return removeMapping(user, entry);
72 case RENAME: 74 case RENAME:
73 entry = PacketHelper.readEntry(input); 75 entry = PacketHelper.readEntry(input);
74 String newName = input.readUTF(); 76 String newName = input.readUTF();
75 return rename(user, entry, newName); 77 return rename(user, entry, newName);
76 default: 78 default:
77 throw new IllegalStateException("unreachable"); 79 throw new IllegalStateException("unreachable");
78 } 80 }
79 } 81 }
80 82
@@ -89,8 +91,14 @@ public abstract class Message {
89 91
90 @Override 92 @Override
91 public boolean equals(Object o) { 93 public boolean equals(Object o) {
92 if (this == o) return true; 94 if (this == o) {
93 if (o == null || getClass() != o.getClass()) return false; 95 return true;
96 }
97
98 if (o == null || getClass() != o.getClass()) {
99 return false;
100 }
101
94 Message message = (Message) o; 102 Message message = (Message) o;
95 return Objects.equals(user, message.user); 103 return Objects.equals(user, message.user);
96 } 104 }
@@ -111,7 +119,6 @@ public abstract class Message {
111 } 119 }
112 120
113 public static final class Chat extends Message { 121 public static final class Chat extends Message {
114
115 public final String message; 122 public final String message;
116 123
117 private Chat(String user, String message) { 124 private Chat(String user, String message) {
@@ -137,9 +144,18 @@ public abstract class Message {
137 144
138 @Override 145 @Override
139 public boolean equals(Object o) { 146 public boolean equals(Object o) {
140 if (this == o) return true; 147 if (this == o) {
141 if (o == null || getClass() != o.getClass()) return false; 148 return true;
142 if (!super.equals(o)) return false; 149 }
150
151 if (o == null || getClass() != o.getClass()) {
152 return false;
153 }
154
155 if (!super.equals(o)) {
156 return false;
157 }
158
143 Chat chat = (Chat) o; 159 Chat chat = (Chat) o;
144 return Objects.equals(message, chat.message); 160 return Objects.equals(message, chat.message);
145 } 161 }
@@ -153,11 +169,9 @@ public abstract class Message {
153 public String toString() { 169 public String toString() {
154 return String.format("Message.Chat { user: '%s', message: '%s' }", user, message); 170 return String.format("Message.Chat { user: '%s', message: '%s' }", user, message);
155 } 171 }
156
157 } 172 }
158 173
159 public static final class Connect extends Message { 174 public static final class Connect extends Message {
160
161 private Connect(String user) { 175 private Connect(String user) {
162 super(user); 176 super(user);
163 } 177 }
@@ -176,11 +190,9 @@ public abstract class Message {
176 public String toString() { 190 public String toString() {
177 return String.format("Message.Connect { user: '%s' }", user); 191 return String.format("Message.Connect { user: '%s' }", user);
178 } 192 }
179
180 } 193 }
181 194
182 public static final class Disconnect extends Message { 195 public static final class Disconnect extends Message {
183
184 private Disconnect(String user) { 196 private Disconnect(String user) {
185 super(user); 197 super(user);
186 } 198 }
@@ -199,11 +211,9 @@ public abstract class Message {
199 public String toString() { 211 public String toString() {
200 return String.format("Message.Disconnect { user: '%s' }", user); 212 return String.format("Message.Disconnect { user: '%s' }", user);
201 } 213 }
202
203 } 214 }
204 215
205 public static final class EditDocs extends Message { 216 public static final class EditDocs extends Message {
206
207 public final Entry<?> entry; 217 public final Entry<?> entry;
208 218
209 private EditDocs(String user, Entry<?> entry) { 219 private EditDocs(String user, Entry<?> entry) {
@@ -229,9 +239,18 @@ public abstract class Message {
229 239
230 @Override 240 @Override
231 public boolean equals(Object o) { 241 public boolean equals(Object o) {
232 if (this == o) return true; 242 if (this == o) {
233 if (o == null || getClass() != o.getClass()) return false; 243 return true;
234 if (!super.equals(o)) return false; 244 }
245
246 if (o == null || getClass() != o.getClass()) {
247 return false;
248 }
249
250 if (!super.equals(o)) {
251 return false;
252 }
253
235 EditDocs editDocs = (EditDocs) o; 254 EditDocs editDocs = (EditDocs) o;
236 return Objects.equals(entry, editDocs.entry); 255 return Objects.equals(entry, editDocs.entry);
237 } 256 }
@@ -245,11 +264,9 @@ public abstract class Message {
245 public String toString() { 264 public String toString() {
246 return String.format("Message.EditDocs { user: '%s', entry: %s }", user, entry); 265 return String.format("Message.EditDocs { user: '%s', entry: %s }", user, entry);
247 } 266 }
248
249 } 267 }
250 268
251 public static final class MarkDeobf extends Message { 269 public static final class MarkDeobf extends Message {
252
253 public final Entry<?> entry; 270 public final Entry<?> entry;
254 271
255 private MarkDeobf(String user, Entry<?> entry) { 272 private MarkDeobf(String user, Entry<?> entry) {
@@ -275,9 +292,18 @@ public abstract class Message {
275 292
276 @Override 293 @Override
277 public boolean equals(Object o) { 294 public boolean equals(Object o) {
278 if (this == o) return true; 295 if (this == o) {
279 if (o == null || getClass() != o.getClass()) return false; 296 return true;
280 if (!super.equals(o)) return false; 297 }
298
299 if (o == null || getClass() != o.getClass()) {
300 return false;
301 }
302
303 if (!super.equals(o)) {
304 return false;
305 }
306
281 MarkDeobf markDeobf = (MarkDeobf) o; 307 MarkDeobf markDeobf = (MarkDeobf) o;
282 return Objects.equals(entry, markDeobf.entry); 308 return Objects.equals(entry, markDeobf.entry);
283 } 309 }
@@ -291,11 +317,9 @@ public abstract class Message {
291 public String toString() { 317 public String toString() {
292 return String.format("Message.MarkDeobf { user: '%s', entry: %s }", user, entry); 318 return String.format("Message.MarkDeobf { user: '%s', entry: %s }", user, entry);
293 } 319 }
294
295 } 320 }
296 321
297 public static final class RemoveMapping extends Message { 322 public static final class RemoveMapping extends Message {
298
299 public final Entry<?> entry; 323 public final Entry<?> entry;
300 324
301 private RemoveMapping(String user, Entry<?> entry) { 325 private RemoveMapping(String user, Entry<?> entry) {
@@ -321,9 +345,18 @@ public abstract class Message {
321 345
322 @Override 346 @Override
323 public boolean equals(Object o) { 347 public boolean equals(Object o) {
324 if (this == o) return true; 348 if (this == o) {
325 if (o == null || getClass() != o.getClass()) return false; 349 return true;
326 if (!super.equals(o)) return false; 350 }
351
352 if (o == null || getClass() != o.getClass()) {
353 return false;
354 }
355
356 if (!super.equals(o)) {
357 return false;
358 }
359
327 RemoveMapping that = (RemoveMapping) o; 360 RemoveMapping that = (RemoveMapping) o;
328 return Objects.equals(entry, that.entry); 361 return Objects.equals(entry, that.entry);
329 } 362 }
@@ -337,11 +370,9 @@ public abstract class Message {
337 public String toString() { 370 public String toString() {
338 return String.format("Message.RemoveMapping { user: '%s', entry: %s }", user, entry); 371 return String.format("Message.RemoveMapping { user: '%s', entry: %s }", user, entry);
339 } 372 }
340
341 } 373 }
342 374
343 public static final class Rename extends Message { 375 public static final class Rename extends Message {
344
345 public final Entry<?> entry; 376 public final Entry<?> entry;
346 public final String newName; 377 public final String newName;
347 378
@@ -370,12 +401,20 @@ public abstract class Message {
370 401
371 @Override 402 @Override
372 public boolean equals(Object o) { 403 public boolean equals(Object o) {
373 if (this == o) return true; 404 if (this == o) {
374 if (o == null || getClass() != o.getClass()) return false; 405 return true;
375 if (!super.equals(o)) return false; 406 }
407
408 if (o == null || getClass() != o.getClass()) {
409 return false;
410 }
411
412 if (!super.equals(o)) {
413 return false;
414 }
415
376 Rename rename = (Rename) o; 416 Rename rename = (Rename) o;
377 return Objects.equals(entry, rename.entry) && 417 return Objects.equals(entry, rename.entry) && Objects.equals(newName, rename.newName);
378 Objects.equals(newName, rename.newName);
379 } 418 }
380 419
381 @Override 420 @Override
@@ -387,7 +426,5 @@ public abstract class Message {
387 public String toString() { 426 public String toString() {
388 return String.format("Message.Rename { user: '%s', entry: %s, newName: '%s' }", user, entry, newName); 427 return String.format("Message.Rename { user: '%s', entry: %s, newName: '%s' }", user, entry, newName);
389 } 428 }
390
391 } 429 }
392
393} 430}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
index 45e0750..a8a1029 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerAddress.java
@@ -5,7 +5,6 @@ import java.util.Objects;
5import javax.annotation.Nullable; 5import javax.annotation.Nullable;
6 6
7public class ServerAddress { 7public class ServerAddress {
8
9 public final String address; 8 public final String address;
10 public final int port; 9 public final int port;
11 10
@@ -16,11 +15,26 @@ public class ServerAddress {
16 15
17 @Nullable 16 @Nullable
18 public static ServerAddress of(String address, int port) { 17 public static ServerAddress of(String address, int port) {
19 if (port < 0 || port > 65535) return null; 18 if (port < 0 || port > 65535) {
20 if (address == null) return null; 19 return null;
21 if (address.equals("")) return null; 20 }
22 if (!address.matches("[a-zA-Z0-9.:-]+")) return null; 21
23 if (address.startsWith("-") || address.endsWith("-")) return null; 22 if (address == null) {
23 return null;
24 }
25
26 if (address.equals("")) {
27 return null;
28 }
29
30 if (!address.matches("[a-zA-Z0-9.:-]+")) {
31 return null;
32 }
33
34 if (address.startsWith("-") || address.endsWith("-")) {
35 return null;
36 }
37
24 return new ServerAddress(address, port); 38 return new ServerAddress(address, port);
25 } 39 }
26 40
@@ -28,6 +42,7 @@ public class ServerAddress {
28 public static ServerAddress from(String s, int defaultPort) { 42 public static ServerAddress from(String s, int defaultPort) {
29 String address; 43 String address;
30 int idx = s.indexOf(']'); 44 int idx = s.indexOf(']');
45
31 if (s.startsWith("[") && idx != -1) { 46 if (s.startsWith("[") && idx != -1) {
32 address = s.substring(1, idx); 47 address = s.substring(1, idx);
33 s = s.substring(idx + 1); 48 s = s.substring(idx + 1);
@@ -41,10 +56,12 @@ public class ServerAddress {
41 } 56 }
42 57
43 int port; 58 int port;
59
44 if (s.isEmpty()) { 60 if (s.isEmpty()) {
45 port = defaultPort; 61 port = defaultPort;
46 } else if (s.startsWith(":")) { 62 } else if (s.startsWith(":")) {
47 s = s.substring(1); 63 s = s.substring(1);
64
48 try { 65 try {
49 port = Integer.parseInt(s); 66 port = Integer.parseInt(s);
50 } catch (NumberFormatException e) { 67 } catch (NumberFormatException e) {
@@ -53,16 +70,22 @@ public class ServerAddress {
53 } else { 70 } else {
54 return null; 71 return null;
55 } 72 }
73
56 return ServerAddress.of(address, port); 74 return ServerAddress.of(address, port);
57 } 75 }
58 76
59 @Override 77 @Override
60 public boolean equals(Object o) { 78 public boolean equals(Object o) {
61 if (this == o) return true; 79 if (this == o) {
62 if (o == null || getClass() != o.getClass()) return false; 80 return true;
81 }
82
83 if (o == null || getClass() != o.getClass()) {
84 return false;
85 }
86
63 ServerAddress that = (ServerAddress) o; 87 ServerAddress that = (ServerAddress) o;
64 return port == that.port && 88 return port == that.port && Objects.equals(address, that.address);
65 Objects.equals(address, that.address);
66 } 89 }
67 90
68 @Override 91 @Override
@@ -74,5 +97,4 @@ public class ServerAddress {
74 public String toString() { 97 public String toString() {
75 return String.format("ServerAddress { address: '%s', port: %d }", address, port); 98 return String.format("ServerAddress { address: '%s', port: %d }", address, port);
76 } 99 }
77
78} 100}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
index 8618553..5b5b0e6 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/ServerPacketHandler.java
@@ -3,7 +3,6 @@ package cuchaz.enigma.network;
3import java.net.Socket; 3import java.net.Socket;
4 4
5public class ServerPacketHandler { 5public class ServerPacketHandler {
6
7 private final Socket client; 6 private final Socket client;
8 private final EnigmaServer server; 7 private final EnigmaServer server;
9 8
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
index 78ef964..ab4f5a1 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/ConfirmChangeC2SPacket.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ServerPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8 6
7import cuchaz.enigma.network.ServerPacketHandler;
8
9public class ConfirmChangeC2SPacket implements Packet<ServerPacketHandler> { 9public class ConfirmChangeC2SPacket implements Packet<ServerPacketHandler> {
10 private int syncId; 10 private int syncId;
11 11
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
index b97877c..6a7ffe9 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeC2SPacket.java
@@ -12,7 +12,6 @@ import cuchaz.enigma.utils.validation.PrintValidatable;
12import cuchaz.enigma.utils.validation.ValidationContext; 12import cuchaz.enigma.utils.validation.ValidationContext;
13 13
14public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> { 14public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> {
15
16 private EntryChange<?> change; 15 private EntryChange<?> change;
17 16
18 EntryChangeC2SPacket() { 17 EntryChangeC2SPacket() {
@@ -62,5 +61,4 @@ public class EntryChangeC2SPacket implements Packet<ServerPacketHandler> {
62 handler.getServer().sendMessage(Message.editDocs(handler.getServer().getUsername(handler.getClient()), this.change.getTarget())); 61 handler.getServer().sendMessage(Message.editDocs(handler.getServer().getUsername(handler.getClient()), this.change.getTarget()));
63 } 62 }
64 } 63 }
65
66} 64}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
index a237b91..8e4688e 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/EntryChangeS2CPacket.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.translation.mapping.EntryChange; 8import cuchaz.enigma.translation.mapping.EntryChange;
9 9
10public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> { 10public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> {
11
12 private int syncId; 11 private int syncId;
13 private EntryChange<?> change; 12 private EntryChange<?> change;
14 13
@@ -38,5 +37,4 @@ public class EntryChangeS2CPacket implements Packet<ClientPacketHandler> {
38 handler.sendPacket(new ConfirmChangeC2SPacket(this.syncId)); 37 handler.sendPacket(new ConfirmChangeC2SPacket(this.syncId));
39 } 38 }
40 } 39 }
41
42} 40}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
index 9a112a8..bd238dc 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/KickS2CPacket.java
@@ -1,11 +1,11 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ClientPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8 6
7import cuchaz.enigma.network.ClientPacketHandler;
8
9public class KickS2CPacket implements Packet<ClientPacketHandler> { 9public class KickS2CPacket implements Packet<ClientPacketHandler> {
10 private String reason; 10 private String reason;
11 11
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
index da0f44a..e93c1d4 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/LoginC2SPacket.java
@@ -1,14 +1,14 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.EnigmaServer;
4import cuchaz.enigma.network.ServerPacketHandler;
5import cuchaz.enigma.network.Message;
6
7import java.io.DataInput; 3import java.io.DataInput;
8import java.io.DataOutput; 4import java.io.DataOutput;
9import java.io.IOException; 5import java.io.IOException;
10import java.util.Arrays; 6import java.util.Arrays;
11 7
8import cuchaz.enigma.network.EnigmaServer;
9import cuchaz.enigma.network.Message;
10import cuchaz.enigma.network.ServerPacketHandler;
11
12public class LoginC2SPacket implements Packet<ServerPacketHandler> { 12public class LoginC2SPacket implements Packet<ServerPacketHandler> {
13 private byte[] jarChecksum; 13 private byte[] jarChecksum;
14 private char[] password; 14 private char[] password;
@@ -28,12 +28,15 @@ public class LoginC2SPacket implements Packet<ServerPacketHandler> {
28 if (input.readUnsignedShort() != EnigmaServer.PROTOCOL_VERSION) { 28 if (input.readUnsignedShort() != EnigmaServer.PROTOCOL_VERSION) {
29 throw new IOException("Mismatching protocol"); 29 throw new IOException("Mismatching protocol");
30 } 30 }
31
31 this.jarChecksum = new byte[EnigmaServer.CHECKSUM_SIZE]; 32 this.jarChecksum = new byte[EnigmaServer.CHECKSUM_SIZE];
32 input.readFully(jarChecksum); 33 input.readFully(jarChecksum);
33 this.password = new char[input.readUnsignedByte()]; 34 this.password = new char[input.readUnsignedByte()];
35
34 for (int i = 0; i < password.length; i++) { 36 for (int i = 0; i < password.length; i++) {
35 password[i] = input.readChar(); 37 password[i] = input.readChar();
36 } 38 }
39
37 this.username = PacketHelper.readString(input); 40 this.username = PacketHelper.readString(input);
38 } 41 }
39 42
@@ -42,9 +45,11 @@ public class LoginC2SPacket implements Packet<ServerPacketHandler> {
42 output.writeShort(EnigmaServer.PROTOCOL_VERSION); 45 output.writeShort(EnigmaServer.PROTOCOL_VERSION);
43 output.write(jarChecksum); 46 output.write(jarChecksum);
44 output.writeByte(password.length); 47 output.writeByte(password.length);
48
45 for (char c : password) { 49 for (char c : password) {
46 output.writeChar(c); 50 output.writeChar(c);
47 } 51 }
52
48 PacketHelper.writeString(output, username); 53 PacketHelper.writeString(output, username);
49 } 54 }
50 55
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
index 3bc09e7..b0610b0 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageC2SPacket.java
@@ -4,11 +4,10 @@ import java.io.DataInput;
4import java.io.DataOutput; 4import java.io.DataOutput;
5import java.io.IOException; 5import java.io.IOException;
6 6
7import cuchaz.enigma.network.ServerPacketHandler;
8import cuchaz.enigma.network.Message; 7import cuchaz.enigma.network.Message;
8import cuchaz.enigma.network.ServerPacketHandler;
9 9
10public class MessageC2SPacket implements Packet<ServerPacketHandler> { 10public class MessageC2SPacket implements Packet<ServerPacketHandler> {
11
12 private String message; 11 private String message;
13 12
14 MessageC2SPacket() { 13 MessageC2SPacket() {
@@ -31,9 +30,9 @@ public class MessageC2SPacket implements Packet<ServerPacketHandler> {
31 @Override 30 @Override
32 public void handle(ServerPacketHandler handler) { 31 public void handle(ServerPacketHandler handler) {
33 String message = this.message.trim(); 32 String message = this.message.trim();
33
34 if (!message.isEmpty()) { 34 if (!message.isEmpty()) {
35 handler.getServer().sendMessage(Message.chat(handler.getServer().getUsername(handler.getClient()), message)); 35 handler.getServer().sendMessage(Message.chat(handler.getServer().getUsername(handler.getClient()), message));
36 } 36 }
37 } 37 }
38
39} 38}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
index 2b07968..9833eeb 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/MessageS2CPacket.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.network.Message; 8import cuchaz.enigma.network.Message;
9 9
10public class MessageS2CPacket implements Packet<ClientPacketHandler> { 10public class MessageS2CPacket implements Packet<ClientPacketHandler> {
11
12 private Message message; 11 private Message message;
13 12
14 MessageS2CPacket() { 13 MessageS2CPacket() {
@@ -32,5 +31,4 @@ public class MessageS2CPacket implements Packet<ClientPacketHandler> {
32 public void handle(ClientPacketHandler handler) { 31 public void handle(ClientPacketHandler handler) {
33 handler.addMessage(message); 32 handler.addMessage(message);
34 } 33 }
35
36} 34}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
index 2f16dfb..15054e7 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/Packet.java
@@ -5,11 +5,9 @@ import java.io.DataOutput;
5import java.io.IOException; 5import java.io.IOException;
6 6
7public interface Packet<H> { 7public interface Packet<H> {
8
9 void read(DataInput input) throws IOException; 8 void read(DataInput input) throws IOException;
10 9
11 void write(DataOutput output) throws IOException; 10 void write(DataOutput output) throws IOException;
12 11
13 void handle(H handler); 12 void handle(H handler);
14
15} 13}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
index 2649cdc..ce767c3 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketHelper.java
@@ -9,11 +9,14 @@ import cuchaz.enigma.translation.mapping.AccessModifier;
9import cuchaz.enigma.translation.mapping.EntryChange; 9import cuchaz.enigma.translation.mapping.EntryChange;
10import cuchaz.enigma.translation.representation.MethodDescriptor; 10import cuchaz.enigma.translation.representation.MethodDescriptor;
11import cuchaz.enigma.translation.representation.TypeDescriptor; 11import cuchaz.enigma.translation.representation.TypeDescriptor;
12import cuchaz.enigma.translation.representation.entry.*; 12import cuchaz.enigma.translation.representation.entry.ClassEntry;
13import cuchaz.enigma.translation.representation.entry.Entry;
14import cuchaz.enigma.translation.representation.entry.FieldEntry;
15import cuchaz.enigma.translation.representation.entry.LocalVariableEntry;
16import cuchaz.enigma.translation.representation.entry.MethodEntry;
13import cuchaz.enigma.utils.TristateChange; 17import cuchaz.enigma.utils.TristateChange;
14 18
15public class PacketHelper { 19public class PacketHelper {
16
17 private static final int ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3; 20 private static final int ENTRY_CLASS = 0, ENTRY_FIELD = 1, ENTRY_METHOD = 2, ENTRY_LOCAL_VAR = 3;
18 private static final int MAX_STRING_LENGTH = 65535; 21 private static final int MAX_STRING_LENGTH = 65535;
19 22
@@ -31,45 +34,46 @@ public class PacketHelper {
31 String name = readString(input); 34 String name = readString(input);
32 35
33 String javadocs = null; 36 String javadocs = null;
37
34 if (input.readBoolean()) { 38 if (input.readBoolean()) {
35 javadocs = readString(input); 39 javadocs = readString(input);
36 } 40 }
37 41
38 switch (type) { 42 switch (type) {
39 case ENTRY_CLASS: { 43 case ENTRY_CLASS: {
40 if (parent != null && !(parent instanceof ClassEntry)) { 44 if (parent != null && !(parent instanceof ClassEntry)) {
41 throw new IOException("Class requires class parent"); 45 throw new IOException("Class requires class parent");
42 }
43
44 return new ClassEntry((ClassEntry) parent, name, javadocs);
45 } 46 }
46 case ENTRY_FIELD: {
47 if (!(parent instanceof ClassEntry parentClass)) {
48 throw new IOException("Field requires class parent");
49 }
50 47
51 TypeDescriptor desc = new TypeDescriptor(readString(input)); 48 return new ClassEntry((ClassEntry) parent, name, javadocs);
52 return new FieldEntry(parentClass, name, desc, javadocs); 49 }
50 case ENTRY_FIELD: {
51 if (!(parent instanceof ClassEntry parentClass)) {
52 throw new IOException("Field requires class parent");
53 } 53 }
54 case ENTRY_METHOD: {
55 if (!(parent instanceof ClassEntry parentClass)) {
56 throw new IOException("Method requires class parent");
57 }
58 54
59 MethodDescriptor desc = new MethodDescriptor(readString(input)); 55 TypeDescriptor desc = new TypeDescriptor(readString(input));
60 return new MethodEntry(parentClass, name, desc, javadocs); 56 return new FieldEntry(parentClass, name, desc, javadocs);
57 }
58 case ENTRY_METHOD: {
59 if (!(parent instanceof ClassEntry parentClass)) {
60 throw new IOException("Method requires class parent");
61 } 61 }
62 case ENTRY_LOCAL_VAR: { 62
63 if (!(parent instanceof MethodEntry parentMethod)) { 63 MethodDescriptor desc = new MethodDescriptor(readString(input));
64 throw new IOException("Local variable requires method parent"); 64 return new MethodEntry(parentClass, name, desc, javadocs);
65 } 65 }
66 66 case ENTRY_LOCAL_VAR: {
67 int index = input.readUnsignedShort(); 67 if (!(parent instanceof MethodEntry parentMethod)) {
68 boolean parameter = input.readBoolean(); 68 throw new IOException("Local variable requires method parent");
69 return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs);
70 } 69 }
71 default: 70
72 throw new IOException("Received unknown entry type " + type); 71 int index = input.readUnsignedShort();
72 boolean parameter = input.readBoolean();
73 return new LocalVariableEntry(parentMethod, index, name, parameter, javadocs);
74 }
75 default:
76 throw new IOException("Received unknown entry type " + type);
73 } 77 }
74 } 78 }
75 79
@@ -94,6 +98,7 @@ public class PacketHelper {
94 // parent 98 // parent
95 if (includeParent) { 99 if (includeParent) {
96 output.writeBoolean(entry.getParent() != null); 100 output.writeBoolean(entry.getParent() != null);
101
97 if (entry.getParent() != null) { 102 if (entry.getParent() != null) {
98 writeEntry(output, entry.getParent(), true); 103 writeEntry(output, entry.getParent(), true);
99 } 104 }
@@ -104,6 +109,7 @@ public class PacketHelper {
104 109
105 // javadocs 110 // javadocs
106 output.writeBoolean(entry.getJavadocs() != null); 111 output.writeBoolean(entry.getJavadocs() != null);
112
107 if (entry.getJavadocs() != null) { 113 if (entry.getJavadocs() != null) {
108 writeString(output, entry.getJavadocs()); 114 writeString(output, entry.getJavadocs());
109 } 115 }
@@ -129,9 +135,11 @@ public class PacketHelper {
129 135
130 public static void writeString(DataOutput output, String str) throws IOException { 136 public static void writeString(DataOutput output, String str) throws IOException {
131 byte[] bytes = str.getBytes(StandardCharsets.UTF_8); 137 byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
138
132 if (bytes.length > MAX_STRING_LENGTH) { 139 if (bytes.length > MAX_STRING_LENGTH) {
133 throw new IOException("String too long, was " + bytes.length + " bytes, max " + MAX_STRING_LENGTH + " allowed"); 140 throw new IOException("String too long, was " + bytes.length + " bytes, max " + MAX_STRING_LENGTH + " allowed");
134 } 141 }
142
135 output.writeShort(bytes.length); 143 output.writeShort(bytes.length);
136 output.write(bytes); 144 output.write(bytes);
137 } 145 }
@@ -146,30 +154,30 @@ public class PacketHelper {
146 TristateChange.Type javadocT = TristateChange.Type.values()[flags >> 4 & 0x3]; 154 TristateChange.Type javadocT = TristateChange.Type.values()[flags >> 4 & 0x3];
147 155
148 switch (deobfNameT) { 156 switch (deobfNameT) {
149 case RESET: 157 case RESET:
150 change = change.clearDeobfName(); 158 change = change.clearDeobfName();
151 break; 159 break;
152 case SET: 160 case SET:
153 change = change.withDeobfName(readString(input)); 161 change = change.withDeobfName(readString(input));
154 break; 162 break;
155 } 163 }
156 164
157 switch (accessT) { 165 switch (accessT) {
158 case RESET: 166 case RESET:
159 change = change.clearAccess(); 167 change = change.clearAccess();
160 break; 168 break;
161 case SET: 169 case SET:
162 change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]); 170 change = change.withAccess(AccessModifier.values()[flags >> 6 & 0x3]);
163 break; 171 break;
164 } 172 }
165 173
166 switch (javadocT) { 174 switch (javadocT) {
167 case RESET: 175 case RESET:
168 change = change.clearJavadoc(); 176 change = change.clearJavadoc();
169 break; 177 break;
170 case SET: 178 case SET:
171 change = change.withJavadoc(readString(input)); 179 change = change.withJavadoc(readString(input));
172 break; 180 break;
173 } 181 }
174 182
175 return change; 183 return change;
@@ -177,9 +185,7 @@ public class PacketHelper {
177 185
178 public static void writeEntryChange(DataOutput output, EntryChange<?> change) throws IOException { 186 public static void writeEntryChange(DataOutput output, EntryChange<?> change) throws IOException {
179 writeEntry(output, change.getTarget()); 187 writeEntry(output, change.getTarget());
180 int flags = change.getDeobfName().getType().ordinal() | 188 int flags = change.getDeobfName().getType().ordinal() | change.getAccess().getType().ordinal() << 2 | change.getJavadoc().getType().ordinal() << 4;
181 change.getAccess().getType().ordinal() << 2 |
182 change.getJavadoc().getType().ordinal() << 4;
183 189
184 if (change.getAccess().isSet()) { 190 if (change.getAccess().isSet()) {
185 flags |= change.getAccess().getNewValue().ordinal() << 6; 191 flags |= change.getAccess().getNewValue().ordinal() << 6;
@@ -195,5 +201,4 @@ public class PacketHelper {
195 writeString(output, change.getJavadoc().getNewValue()); 201 writeString(output, change.getJavadoc().getNewValue());
196 } 202 }
197 } 203 }
198
199} 204}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
index 59999cc..49d56fd 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/PacketRegistry.java
@@ -8,7 +8,6 @@ import cuchaz.enigma.network.ClientPacketHandler;
8import cuchaz.enigma.network.ServerPacketHandler; 8import cuchaz.enigma.network.ServerPacketHandler;
9 9
10public class PacketRegistry { 10public class PacketRegistry {
11
12 private static final Map<Class<? extends Packet<ServerPacketHandler>>, Integer> c2sPacketIds = new HashMap<>(); 11 private static final Map<Class<? extends Packet<ServerPacketHandler>>, Integer> c2sPacketIds = new HashMap<>();
13 private static final Map<Integer, Supplier<? extends Packet<ServerPacketHandler>>> c2sPacketCreators = new HashMap<>(); 12 private static final Map<Integer, Supplier<? extends Packet<ServerPacketHandler>>> c2sPacketCreators = new HashMap<>();
14 private static final Map<Class<? extends Packet<ClientPacketHandler>>, Integer> s2cPacketIds = new HashMap<>(); 13 private static final Map<Class<? extends Packet<ClientPacketHandler>>, Integer> s2cPacketIds = new HashMap<>();
@@ -54,5 +53,4 @@ public class PacketRegistry {
54 Supplier<? extends Packet<ClientPacketHandler>> creator = s2cPacketCreators.get(id); 53 Supplier<? extends Packet<ClientPacketHandler>> creator = s2cPacketCreators.get(id);
55 return creator == null ? null : creator.get(); 54 return creator == null ? null : creator.get();
56 } 55 }
57
58} 56}
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
index 6d9c0bc..7e50938 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/SyncMappingsS2CPacket.java
@@ -28,6 +28,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
28 public void read(DataInput input) throws IOException { 28 public void read(DataInput input) throws IOException {
29 mappings = new HashEntryTree<>(); 29 mappings = new HashEntryTree<>();
30 int size = input.readInt(); 30 int size = input.readInt();
31
31 for (int i = 0; i < size; i++) { 32 for (int i = 0; i < size; i++) {
32 readEntryTreeNode(input, null); 33 readEntryTreeNode(input, null);
33 } 34 }
@@ -40,6 +41,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
40 EntryMapping mapping = new EntryMapping(!name.isEmpty() ? name : null, !javadoc.isEmpty() ? javadoc : null); 41 EntryMapping mapping = new EntryMapping(!name.isEmpty() ? name : null, !javadoc.isEmpty() ? javadoc : null);
41 mappings.insert(entry, mapping); 42 mappings.insert(entry, mapping);
42 int size = input.readUnsignedShort(); 43 int size = input.readUnsignedShort();
44
43 for (int i = 0; i < size; i++) { 45 for (int i = 0; i < size; i++) {
44 readEntryTreeNode(input, entry); 46 readEntryTreeNode(input, entry);
45 } 47 }
@@ -49,6 +51,7 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
49 public void write(DataOutput output) throws IOException { 51 public void write(DataOutput output) throws IOException {
50 List<EntryTreeNode<EntryMapping>> roots = mappings.getRootNodes().toList(); 52 List<EntryTreeNode<EntryMapping>> roots = mappings.getRootNodes().toList();
51 output.writeInt(roots.size()); 53 output.writeInt(roots.size());
54
52 for (EntryTreeNode<EntryMapping> node : roots) { 55 for (EntryTreeNode<EntryMapping> node : roots) {
53 writeEntryTreeNode(output, node); 56 writeEntryTreeNode(output, node);
54 } 57 }
@@ -57,12 +60,16 @@ public class SyncMappingsS2CPacket implements Packet<ClientPacketHandler> {
57 private static void writeEntryTreeNode(DataOutput output, EntryTreeNode<EntryMapping> node) throws IOException { 60 private static void writeEntryTreeNode(DataOutput output, EntryTreeNode<EntryMapping> node) throws IOException {
58 PacketHelper.writeEntry(output, node.getEntry(), false); 61 PacketHelper.writeEntry(output, node.getEntry(), false);
59 EntryMapping value = node.getValue(); 62 EntryMapping value = node.getValue();
60 if (value == null) value = EntryMapping.DEFAULT; 63
64 if (value == null) {
65 value = EntryMapping.DEFAULT;
66 }
61 67
62 PacketHelper.writeString(output, value.targetName() != null ? value.targetName() : ""); 68 PacketHelper.writeString(output, value.targetName() != null ? value.targetName() : "");
63 PacketHelper.writeString(output, value.javadoc() != null ? value.javadoc() : ""); 69 PacketHelper.writeString(output, value.javadoc() != null ? value.javadoc() : "");
64 Collection<? extends EntryTreeNode<EntryMapping>> children = node.getChildNodes(); 70 Collection<? extends EntryTreeNode<EntryMapping>> children = node.getChildNodes();
65 output.writeShort(children.size()); 71 output.writeShort(children.size());
72
66 for (EntryTreeNode<EntryMapping> child : children) { 73 for (EntryTreeNode<EntryMapping> child : children) {
67 writeEntryTreeNode(output, child); 74 writeEntryTreeNode(output, child);
68 } 75 }
diff --git a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
index b4a277a..baac2b3 100644
--- a/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
+++ b/enigma-server/src/main/java/cuchaz/enigma/network/packet/UserListS2CPacket.java
@@ -1,15 +1,14 @@
1package cuchaz.enigma.network.packet; 1package cuchaz.enigma.network.packet;
2 2
3import cuchaz.enigma.network.ClientPacketHandler;
4
5import java.io.DataInput; 3import java.io.DataInput;
6import java.io.DataOutput; 4import java.io.DataOutput;
7import java.io.IOException; 5import java.io.IOException;
8import java.util.ArrayList; 6import java.util.ArrayList;
9import java.util.List; 7import java.util.List;
10 8
11public class UserListS2CPacket implements Packet<ClientPacketHandler> { 9import cuchaz.enigma.network.ClientPacketHandler;
12 10
11public class UserListS2CPacket implements Packet<ClientPacketHandler> {
13 private List<String> users; 12 private List<String> users;
14 13
15 UserListS2CPacket() { 14 UserListS2CPacket() {
@@ -23,6 +22,7 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
23 public void read(DataInput input) throws IOException { 22 public void read(DataInput input) throws IOException {
24 int len = input.readUnsignedShort(); 23 int len = input.readUnsignedShort();
25 users = new ArrayList<>(len); 24 users = new ArrayList<>(len);
25
26 for (int i = 0; i < len; i++) { 26 for (int i = 0; i < len; i++) {
27 users.add(input.readUTF()); 27 users.add(input.readUTF());
28 } 28 }
@@ -31,6 +31,7 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
31 @Override 31 @Override
32 public void write(DataOutput output) throws IOException { 32 public void write(DataOutput output) throws IOException {
33 output.writeShort(users.size()); 33 output.writeShort(users.size());
34
34 for (String user : users) { 35 for (String user : users) {
35 PacketHelper.writeString(output, user); 36 PacketHelper.writeString(output, user);
36 } 37 }
@@ -40,5 +41,4 @@ public class UserListS2CPacket implements Packet<ClientPacketHandler> {
40 public void handle(ClientPacketHandler handler) { 41 public void handle(ClientPacketHandler handler) {
41 handler.updateUserList(users); 42 handler.updateUserList(users);
42 } 43 }
43
44} 44}
diff --git a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
index 3765f7a..c2920e0 100644
--- a/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
+++ b/enigma-server/src/test/java/cuchaz/enigma/network/ServerAddressTest.java
@@ -1,12 +1,11 @@
1package cuchaz.enigma.network; 1package cuchaz.enigma.network;
2 2
3import org.junit.Test;
4
5import static org.junit.Assert.assertEquals; 3import static org.junit.Assert.assertEquals;
6import static org.junit.Assert.assertNull; 4import static org.junit.Assert.assertNull;
7 5
8public class ServerAddressTest { 6import org.junit.Test;
9 7
8public class ServerAddressTest {
10 @Test 9 @Test
11 public void validAddresses() { 10 public void validAddresses() {
12 assertEquals(ServerAddress.of("127.0.0.1", 22), ServerAddress.from("127.0.0.1", 22)); 11 assertEquals(ServerAddress.of("127.0.0.1", 22), ServerAddress.from("127.0.0.1", 22));
@@ -24,5 +23,4 @@ public class ServerAddressTest {
24 assertNull(ServerAddress.from("127.0.0.1:100000000", 22)); 23 assertNull(ServerAddress.from("127.0.0.1:100000000", 22));
25 assertNull(ServerAddress.from("127.0.0.1:lmao", 22)); 24 assertNull(ServerAddress.from("127.0.0.1:lmao", 22));
26 } 25 }
27
28} 26}