diff --git a/.github/workflows/pr-pipeline.yml b/.github/workflows/pr-pipeline.yml
index cda68c6532..a44c2607ac 100644
--- a/.github/workflows/pr-pipeline.yml
+++ b/.github/workflows/pr-pipeline.yml
@@ -14,10 +14,10 @@ jobs:
steps:
- uses: actions/checkout@v2
- - name: Set up JDK 16
+ - name: Set up JDK 17
uses: actions/setup-java@v2
with:
- java-version: '16'
+ java-version: '17'
distribution: 'temurin'
- name: Build with Maven (compile -> test -> package)
run: mvn -B package --file pom.xml
diff --git a/Dockerfile b/Dockerfile
index c286375f60..d65c5576e8 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@
#
# Cosmic JAR creation stage
#
-FROM maven:3.8.1-openjdk-16 AS jar
+FROM maven:3.8.4-openjdk-17 AS jar
# Build in a separated location which won't have permissions issues.
WORKDIR /opt/cosmic
@@ -21,7 +21,7 @@ RUN mvn -f ./pom.xml clean package -Dmaven.test.skip -T 1C
#
# Server creation stage
#
-FROM openjdk:16
+FROM openjdk:17.0.2
# Host the server in a location that won't have permissions issues.
WORKDIR /opt/server
diff --git a/pom.xml b/pom.xml
index 0857beffb6..2d53675e85 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,45 +11,58 @@
jar
+
UTF-8
- 16
+ 17
${java.version}
${java.version}
net.server.Server
- 2.17.1
- 21.1.0
- 4.1.67.Final
- 5.7.2
+
+ 3.0.0-M5
+ 3.2.0
+ 3.3.0
+
+
+ 1.7.36
+ 2.17.1
+ 21.1.0
+ 4.1.74.Final
+ 5.8.2
+ 1.15
+ 1.0
+ 2.11.0
+ 5.0.1
+ 8.0.28
com.esotericsoftware.yamlbeans
yamlbeans
- 1.15
+ ${yamlbeans.version}
net.jcip
jcip-annotations
- 1.0
+ ${jcip-annotations.version}
commons-io
commons-io
- 2.11.0
+ ${commons-io.version}
com.zaxxer
HikariCP
- 5.0.0
+ ${HikariCP.version}
mysql
mysql-connector-java
- 8.0.26
+ ${mysql-connector-java.version}
@@ -78,7 +91,7 @@
org.slf4j
slf4j-api
- 1.7.32
+ ${slf4j-api.version}
org.apache.logging.log4j
@@ -120,7 +133,6 @@
${junit.version}
-
@@ -129,7 +141,7 @@
org.apache.maven.plugins
maven-jar-plugin
- 3.2.0
+ ${maven-jar-plugin.version}
default-jar
@@ -141,14 +153,14 @@
org.apache.maven.plugins
maven-surefire-plugin
- 3.0.0-M5
+ ${maven-surefire-plugin.version}
org.apache.maven.plugins
maven-assembly-plugin
- 3.3.0
+ ${maven-assembly-plugin.version}
jar-with-dependencies
diff --git a/src/main/java/client/Client.java b/src/main/java/client/Client.java
index 12ca7278f9..5ed36fbcb7 100644
--- a/src/main/java/client/Client.java
+++ b/src/main/java/client/Client.java
@@ -1335,7 +1335,7 @@ public class Client extends ChannelInboundHandlerAdapter {
try {
MessageDigest digester = MessageDigest.getInstance(type);
digester.update(password.getBytes(StandardCharsets.UTF_8), 0, password.length());
- return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
+ return HexTool.toHexString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Encoding the string failed", e);
}
diff --git a/src/main/java/client/command/commands/gm3/PeCommand.java b/src/main/java/client/command/commands/gm3/PeCommand.java
index 41978e7da8..da2af746c3 100644
--- a/src/main/java/client/command/commands/gm3/PeCommand.java
+++ b/src/main/java/client/command/commands/gm3/PeCommand.java
@@ -64,7 +64,7 @@ public class PeCommand extends Command {
}
- byte[] packetContent = HexTool.getByteArrayFromHexString(packet);
+ byte[] packetContent = HexTool.toBytes(packet);
InPacket inPacket = new ByteBufInPacket(Unpooled.wrappedBuffer(packetContent));
short packetId = inPacket.readShort();
final PacketHandler packetHandler = PacketProcessor.getProcessor(0, c.getChannel()).getHandler(packetId);
diff --git a/src/main/java/net/encryption/MapleAESOFB.java b/src/main/java/net/encryption/MapleAESOFB.java
index f55b2a277f..4de36a078b 100644
--- a/src/main/java/net/encryption/MapleAESOFB.java
+++ b/src/main/java/net/encryption/MapleAESOFB.java
@@ -180,7 +180,7 @@ public class MapleAESOFB {
@Override
public String toString() {
- return "IV: " + HexTool.toString(this.iv);
+ return "IV: " + HexTool.toHexString(this.iv);
}
private static byte[] funnyShit(byte inputByte, byte[] in) {
diff --git a/src/main/java/net/packet/logging/InPacketLogger.java b/src/main/java/net/packet/logging/InPacketLogger.java
index da9e8aa16d..adf48616a6 100644
--- a/src/main/java/net/packet/logging/InPacketLogger.java
+++ b/src/main/java/net/packet/logging/InPacketLogger.java
@@ -35,9 +35,9 @@ public class InPacketLogger extends ChannelInboundHandlerAdapter implements Pack
final String opcodeName = getRecvOpcodeName(opcode);
final String prefix = opcodeName == null ? " " : "";
log.debug("{}ClientSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength,
- HexTool.toString(content), HexTool.toStringFromAscii(content));
+ HexTool.toHexString(content), HexTool.toStringFromAscii(content));
} else {
- log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + "...");
+ log.debug(HexTool.toHexString(new byte[]{content[0], content[1]}) + "...");
}
}
diff --git a/src/main/java/net/packet/logging/MonitoredChrLogger.java b/src/main/java/net/packet/logging/MonitoredChrLogger.java
index dc3785e550..782e78c794 100644
--- a/src/main/java/net/packet/logging/MonitoredChrLogger.java
+++ b/src/main/java/net/packet/logging/MonitoredChrLogger.java
@@ -75,7 +75,7 @@ public class MonitoredChrLogger {
return;
}
- String packet = packetContent.length > 0 ? HexTool.toString(packetContent) : "";
+ String packet = packetContent.length > 0 ? HexTool.toHexString(packetContent) : "";
log.info("{}-{} {}-{}", c.getAccountName(), chr.getName(), packetId, packet);
}
diff --git a/src/main/java/net/packet/logging/OutPacketLogger.java b/src/main/java/net/packet/logging/OutPacketLogger.java
index ea8a96356e..5de9307afd 100644
--- a/src/main/java/net/packet/logging/OutPacketLogger.java
+++ b/src/main/java/net/packet/logging/OutPacketLogger.java
@@ -36,9 +36,9 @@ public class OutPacketLogger extends ChannelOutboundHandlerAdapter implements Pa
String opcodeName = getSendOpcodeName(opcode);
String prefix = opcodeName == null ? " " : "";
log.debug("{}ServerSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength,
- HexTool.toString(content), HexTool.toStringFromAscii(content));
+ HexTool.toHexString(content), HexTool.toStringFromAscii(content));
} else {
- log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + " ...");
+ log.debug(HexTool.toHexString(new byte[]{content[0], content[1]}) + " ...");
}
}
diff --git a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java
index 7d07dcc46d..4dbbae373b 100644
--- a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java
+++ b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java
@@ -50,7 +50,7 @@ public final class LoginPasswordHandler implements PacketHandler {
private static String hashpwSHA512(String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest digester = MessageDigest.getInstance("SHA-512");
digester.update(pwd.getBytes(StandardCharsets.UTF_8), 0, pwd.length());
- return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase();
+ return HexTool.toHexString(digester.digest()).replace(" ", "").toLowerCase();
}
@Override
@@ -67,7 +67,7 @@ public final class LoginPasswordHandler implements PacketHandler {
p.skip(6); // localhost masked the initial part with zeroes...
byte[] hwidNibbles = p.readBytes(4);
- Hwid hwid = new Hwid(HexTool.bytesToHex(hwidNibbles));
+ Hwid hwid = new Hwid(HexTool.toCompactHexString(hwidNibbles));
int loginok = c.login(login, pwd, hwid);
diff --git a/src/main/java/tools/HexTool.java b/src/main/java/tools/HexTool.java
index 03ae30e755..64b7959a0a 100644
--- a/src/main/java/tools/HexTool.java
+++ b/src/main/java/tools/HexTool.java
@@ -22,94 +22,68 @@
package tools;
import constants.string.CharsetConstants;
-import io.netty.buffer.ByteBufUtil;
-import java.io.ByteArrayOutputStream;
+import java.util.HexFormat;
+/**
+ * Handles converting back and forth from byte arrays to hex strings.
+ */
public class HexTool {
- private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
- private static String toString(byte byteValue) {
- int tmp = byteValue << 8;
- char[] retstr = new char[]{HEX[(tmp >> 12) & 0x0F], HEX[(tmp >> 8) & 0x0F]};
- return String.valueOf(retstr);
- }
-
- public static String toString(byte[] bytes) {
- StringBuilder hexed = new StringBuilder();
- for (byte aByte : bytes) {
- hexed.append(toString(aByte));
- hexed.append(' ');
- }
- return hexed.substring(0, hexed.length() - 1);
- }
-
- public static String toCompressedString(byte[] bytes) {
- StringBuilder hexed = new StringBuilder();
- for (byte aByte : bytes) {
- hexed.append(toString(aByte));
- }
- return hexed.substring(0, hexed.length());
- }
-
- public static byte[] getByteArrayFromHexString(String hex) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- int nexti = 0;
- int nextb = 0;
- boolean highoc = true;
- outer:
- for (; ; ) {
- int number = -1;
- while (number == -1) {
- if (nexti == hex.length()) {
- break outer;
- }
- char chr = hex.charAt(nexti);
- if (chr >= '0' && chr <= '9') {
- number = chr - '0';
- } else if (chr >= 'a' && chr <= 'f') {
- number = chr - 'a' + 10;
- } else if (chr >= 'A' && chr <= 'F') {
- number = chr - 'A' + 10;
- } else {
- number = -1;
- }
- nexti++;
- }
- if (highoc) {
- nextb = number << 4;
- highoc = false;
- } else {
- nextb |= number;
- highoc = true;
- baos.write(nextb);
- }
- }
- return baos.toByteArray();
- }
-
- public static String toStringFromAscii(final byte[] bytes) {
- byte[] ret = new byte[bytes.length];
- for (int x = 0; x < bytes.length; x++) {
- if (bytes[x] < 32 && bytes[x] >= 0) {
- ret[x] = '.';
- } else {
- int chr = ((short) bytes[x]) & 0xFF;
- ret[x] = (byte) chr;
- }
- }
-
- return new String(ret, CharsetConstants.CHARSET);
+ /**
+ * Convert a byte array to its hex string representation (upper case).
+ * Each byte value is converted to two hex characters delimited by a space.
+ *
+ * @param bytes Byte array to convert to a hex string.
+ * Example: {1, 16, 127, -1} is converted to "01 F0 7F FF"
+ * @return The hex string
+ */
+ public static String toHexString(byte[] bytes) {
+ return HexFormat.ofDelimiter(" ").withUpperCase().formatHex(bytes);
}
/**
- * Get upper case hex dump
+ * Convert a byte array to its hex string representation (upper case).
+ * Like {@link #toHexString(byte[]) HexTool.toString}, but with no space delimiter.
+ *
+ * @return The compact hex string
*/
- public static String bytesToHex(byte[] bytes) {
- return ByteBufUtil.hexDump(bytes).toUpperCase();
+ public static String toCompactHexString(byte[] bytes) {
+ return HexFormat.of().withUpperCase().formatHex(bytes);
}
- public static byte[] hexToBytes(String hex) {
- return ByteBufUtil.decodeHexDump(hex);
+ /**
+ * Convert a hex string to its byte array representation. Two consecutive hex characters are converted to one byte.
+ *
+ * @param hexString Hex string to convert to bytes. May be lower or upper case, and hex character pairs may be
+ * delimited by a space or not.
+ * Example: "01 10 7F FF" is converted to {1, 16, 127, -1}.
+ * The following hex strings are considered identical and are converted to the same byte array:
+ * "01 10 7F FF", "01107FFF", "01 10 7f ff", "01107fff"
+ * @return The byte array
+ */
+ public static byte[] toBytes(String hexString) {
+ return HexFormat.of().parseHex(removeAllSpaces(hexString));
+ }
+
+ private static String removeAllSpaces(String input) {
+ return input.replaceAll("\\s", "");
+ }
+
+ public static String toStringFromAscii(final byte[] bytes) {
+ byte[] filteredBytes = new byte[bytes.length];
+ for (int i = 0; i < bytes.length; i++) {
+ if (isSpecialCharacter(bytes[i])) {
+ filteredBytes[i] = '.';
+ } else {
+ filteredBytes[i] = (byte) (bytes[i] & 0xFF);
+ }
+ }
+
+ return new String(filteredBytes, CharsetConstants.CHARSET);
+ }
+
+ private static boolean isSpecialCharacter(byte asciiCode) {
+ return asciiCode >= 0 && asciiCode <= 31;
}
}
diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java
index ba2d646fa2..24af096e62 100644
--- a/src/main/java/tools/PacketCreator.java
+++ b/src/main/java/tools/PacketCreator.java
@@ -3307,7 +3307,7 @@ public class PacketCreator {
p.writeByte(msgType);
p.writeByte(speaker);
p.writeString(talk);
- p.writeBytes(HexTool.getByteArrayFromHexString(endBytes));
+ p.writeBytes(HexTool.toBytes(endBytes));
return p;
}
@@ -6887,7 +6887,7 @@ public class PacketCreator {
public static Packet customPacket(String packet) {
OutPacket p = new ByteBufOutPacket();
- p.writeBytes(HexTool.getByteArrayFromHexString(packet));
+ p.writeBytes(HexTool.toBytes(packet));
return p;
}
diff --git a/src/test/java/tools/HexToolTest.java b/src/test/java/tools/HexToolTest.java
index 8ea59c403a..0354ca9a97 100644
--- a/src/test/java/tools/HexToolTest.java
+++ b/src/test/java/tools/HexToolTest.java
@@ -2,21 +2,57 @@ package tools;
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
class HexToolTest {
@Test
- void upperCaseHexToBytesAndBack() {
- String hex = "A1B2C3";
- byte[] bytes = HexTool.hexToBytes(hex);
- assertEquals(hex, HexTool.bytesToHex(bytes));
+ void bytesToHexString() {
+ byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ String expectedHexString = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 7F FF";
+ assertEquals(expectedHexString, HexTool.toHexString(bytes));
}
@Test
- void mixedCaseHexToBytesAndBack() {
- String hex = "aB5DaA";
- byte[] bytes = HexTool.hexToBytes(hex);
- assertEquals(hex.toUpperCase(), HexTool.bytesToHex(bytes));
+ void bytesToCompactHexString() {
+ byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ String expectedHexString = "0102030405060708090A0B0C0D0E0F10117FFF";
+ assertEquals(expectedHexString, HexTool.toCompactHexString(bytes));
+ }
+
+ @Test
+ void hexStringWithSpacesToBytes() {
+ String hexString = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 7F FF";
+ byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ assertArrayEquals(expectedBytes, HexTool.toBytes(hexString));
+ }
+
+ @Test
+ void compactHexStringToBytes() {
+ String hexString = "0102030405060708090A0B0C0D0E0F10117FFF";
+ byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ assertArrayEquals(expectedBytes, HexTool.toBytes(hexString));
+ }
+
+ @Test
+ void lowerCaseHexStringToBytes() {
+ String lowerCaseHexString = "01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 7f ff";
+ byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ assertArrayEquals(expectedBytes, HexTool.toBytes(lowerCaseHexString));
+ }
+
+ @Test
+ void lowerCaseCompactHexStringToBytes() {
+ String hexString = "0102030405060708090a0b0c0d0e0f10117fff";
+ byte[] expectedBytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 127, -1};
+ assertArrayEquals(expectedBytes, HexTool.toBytes(hexString));
+ }
+
+ @Test
+ void toStringFromAscii() {
+ byte[] asciiBytes = new byte[]{1, 10, 20, 30, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 31};
+ String expectedString = "....0123456789..";
+ assertEquals(expectedString, HexTool.toStringFromAscii(asciiBytes));
}
}
\ No newline at end of file