diff --git a/src/main/java/net/raphimc/viaproxy/ViaProxy.java b/src/main/java/net/raphimc/viaproxy/ViaProxy.java index 6602e5c..71c4d30 100644 --- a/src/main/java/net/raphimc/viaproxy/ViaProxy.java +++ b/src/main/java/net/raphimc/viaproxy/ViaProxy.java @@ -21,6 +21,7 @@ import net.raphimc.viaproxy.protocolhack.ProtocolHack; import net.raphimc.viaproxy.proxy.ProxyConnection; import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyChannelInitializer; import net.raphimc.viaproxy.proxy.client2proxy.Client2ProxyHandler; +import net.raphimc.viaproxy.saves.SaveManager; import net.raphimc.viaproxy.ui.ViaProxyUI; import net.raphimc.viaproxy.util.logging.Logger; @@ -31,8 +32,10 @@ public class ViaProxy { public static final String VERSION = "${version}"; + public static SaveManager saveManager; public static NetServer currentProxyServer; public static Thread loaderThread; + public static Thread accountRefreshThread; public static ChannelGroup c2pChannels; public static void main(String[] args) throws Throwable { @@ -49,6 +52,7 @@ public class ViaProxy { Logger.setup(); ConsoleHandler.hookConsole(); Logger.LOGGER.info("Initializing ViaProxy v" + VERSION + "..."); + saveManager = new SaveManager(); VersionEnum.init(); setNettyParameters(); MCPipeline.useOptimizedPipeline(); @@ -57,12 +61,17 @@ public class ViaProxy { ProtocolHack.init(); PluginManager.loadPlugins(); }, "ViaProtocolHack-Loader"); + accountRefreshThread = new Thread(() -> { + saveManager.accountsSave.refreshAccounts(); + }, "AccountRefresh"); if (args.length == 0 && !GraphicsEnvironment.isHeadless()) { loaderThread.start(); + accountRefreshThread.start(); final ViaProxyUI[] ui = new ViaProxyUI[1]; SwingUtilities.invokeLater(() -> ui[0] = new ViaProxyUI()); loaderThread.join(); + accountRefreshThread.join(); ui[0].setReady(); Logger.LOGGER.info("ViaProxy started successfully!"); return; diff --git a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java index 0726669..e98f5ed 100644 --- a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java +++ b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java @@ -1,6 +1,7 @@ package net.raphimc.viaproxy.cli.options; import joptsimple.*; +import net.raphimc.mcauth.step.java.StepMCProfile; import net.raphimc.vialegacy.util.VersionEnum; import net.raphimc.viaproxy.util.logging.Logger; @@ -27,6 +28,9 @@ public class Options { public static boolean LOCAL_SOCKET_AUTH; public static boolean BETACRAFT_AUTH; + // GUI only config options + public static StepMCProfile.MCProfile MC_ACCOUNT; + public static void parse(final String[] args) throws IOException { final OptionParser parser = new OptionParser(); final OptionSpec help = parser.acceptsAll(asList("help", "h", "?"), "Get a list of all arguments").forHelp(); diff --git a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java index db759c6..396ae9e 100644 --- a/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java +++ b/src/main/java/net/raphimc/viaproxy/protocolhack/providers/ViaProxyOldAuthProvider.java @@ -2,14 +2,14 @@ package net.raphimc.viaproxy.protocolhack.providers; import com.viaversion.viaversion.api.connection.UserConnection; import net.raphimc.vialegacy.protocols.release.protocol1_3_1_2to1_2_4_5.providers.OldAuthProvider; -import net.raphimc.viaproxy.proxy.CustomPayloadInterface; +import net.raphimc.viaproxy.proxy.ExternalInterface; import net.raphimc.viaproxy.proxy.ProxyConnection; public class ViaProxyOldAuthProvider extends OldAuthProvider { @Override public void sendAuthRequest(final UserConnection user, final String serverId) throws Throwable { - CustomPayloadInterface.joinServer(serverId, ProxyConnection.fromUserConnection(user)); + ExternalInterface.joinServer(serverId, ProxyConnection.fromUserConnection(user)); } } diff --git a/src/main/java/net/raphimc/viaproxy/proxy/CustomPayloadInterface.java b/src/main/java/net/raphimc/viaproxy/proxy/CustomPayloadInterface.java deleted file mode 100644 index 022ea3e..0000000 --- a/src/main/java/net/raphimc/viaproxy/proxy/CustomPayloadInterface.java +++ /dev/null @@ -1,66 +0,0 @@ -package net.raphimc.viaproxy.proxy; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import net.raphimc.netminecraft.packet.PacketTypes; -import net.raphimc.netminecraft.packet.impl.login.C2SLoginKeyPacket1_19; -import net.raphimc.viaproxy.cli.options.Options; -import net.raphimc.viaproxy.util.LocalSocketClient; -import net.raphimc.viaproxy.util.logging.Logger; - -import java.math.BigInteger; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.concurrent.*; - -public class CustomPayloadInterface { - - public static final String OPENAUTHMOD_BASE_CHANNEL = "oam:"; - public static final byte[] OPENAUTHMOD_LEGACY_MAGIC_BYTES = new byte[]{2, 20, 12, 3}; // 1.8 - 1.12.2 - public static final String OPENAUTHMOD_LEGACY_MAGIC_STRING = new String(OPENAUTHMOD_LEGACY_MAGIC_BYTES, StandardCharsets.UTF_8); // 1.8 - 1.12.2 - public static final int OPENAUTHMOD_LEGACY_MAGIC_INT = new BigInteger(OPENAUTHMOD_LEGACY_MAGIC_BYTES).intValueExact(); // 1.8 - 1.12.2 - - // Request - public static final String OPENAUTHMOD_JOIN_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "join"; // 1.8 - latest - public static final String OPENAUTHMOD_SIGN_NONCE_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "sign_nonce"; // 1.19 - latest - - // Response - public static final String OPENAUTHMOD_DATA_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "data"; // 1.8 - latest - - public static void joinServer(final String serverIdHash, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException { - Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Trying to join online mode server"); - if (Options.OPENAUTHMOD_AUTH) { - try { - final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_JOIN_CHANNEL, PacketTypes.writeString(Unpooled.buffer(), serverIdHash)).get(6, TimeUnit.SECONDS); - if (response == null) throw new TimeoutException(); - if (response.isReadable() && !response.readBoolean()) throw new TimeoutException(); - } catch (TimeoutException e) { - proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server."); - } - } else if (Options.LOCAL_SOCKET_AUTH) { - new LocalSocketClient(48941).request("authenticate", serverIdHash); - } - } - - public static void signNonce(final byte[] nonce, final C2SLoginKeyPacket1_19 packet, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException { - Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Requesting nonce signature"); - if (Options.OPENAUTHMOD_AUTH) { - try { - final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_SIGN_NONCE_CHANNEL, PacketTypes.writeByteArray(Unpooled.buffer(), nonce)).get(4, TimeUnit.SECONDS); - if (response == null) throw new TimeoutException(); - if (!response.readBoolean()) throw new TimeoutException(); - packet.salt = response.readLong(); - packet.signature = PacketTypes.readByteArray(response); - } catch (TimeoutException e) { - proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server."); - } - } else if (Options.LOCAL_SOCKET_AUTH) { - final String[] response = new LocalSocketClient(48941).request("sign_nonce", Base64.getEncoder().encodeToString(nonce)); - if (response != null && response[0].equals("success")) { - packet.salt = Long.valueOf(response[1]); - packet.signature = Base64.getDecoder().decode(response[2]); - } - } - } - -} diff --git a/src/main/java/net/raphimc/viaproxy/proxy/ExternalInterface.java b/src/main/java/net/raphimc/viaproxy/proxy/ExternalInterface.java new file mode 100644 index 0000000..824a6bc --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/proxy/ExternalInterface.java @@ -0,0 +1,123 @@ +package net.raphimc.viaproxy.proxy; + +import com.github.steveice10.mc.auth.data.GameProfile; +import com.github.steveice10.mc.auth.util.UUIDSerializer; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.raphimc.netminecraft.packet.PacketTypes; +import net.raphimc.netminecraft.packet.impl.login.*; +import net.raphimc.viaproxy.cli.options.Options; +import net.raphimc.viaproxy.protocolhack.providers.ViaProxyGameProfileFetcher; +import net.raphimc.viaproxy.util.LocalSocketClient; +import net.raphimc.viaproxy.util.logging.Logger; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.time.Instant; +import java.util.Base64; +import java.util.UUID; +import java.util.concurrent.*; + +public class ExternalInterface { + + public static final String OPENAUTHMOD_BASE_CHANNEL = "oam:"; + public static final byte[] OPENAUTHMOD_LEGACY_MAGIC_BYTES = new byte[]{2, 20, 12, 3}; // 1.8 - 1.12.2 + public static final String OPENAUTHMOD_LEGACY_MAGIC_STRING = new String(OPENAUTHMOD_LEGACY_MAGIC_BYTES, StandardCharsets.UTF_8); // 1.8 - 1.12.2 + public static final int OPENAUTHMOD_LEGACY_MAGIC_INT = new BigInteger(OPENAUTHMOD_LEGACY_MAGIC_BYTES).intValueExact(); // 1.8 - 1.12.2 + + // Request + public static final String OPENAUTHMOD_JOIN_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "join"; // 1.8 - latest + public static final String OPENAUTHMOD_SIGN_NONCE_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "sign_nonce"; // 1.19 - latest + + // Response + public static final String OPENAUTHMOD_DATA_CHANNEL = OPENAUTHMOD_BASE_CHANNEL + "data"; // 1.8 - latest + + + public static void fillPlayerData(final C2SLoginHelloPacket1_7 loginHello, final ProxyConnection proxyConnection) throws NoSuchAlgorithmException, InvalidKeySpecException { + proxyConnection.setLoginHelloPacket(loginHello); + if (proxyConnection.getLoginHelloPacket() instanceof C2SLoginHelloPacket1_19_3) { + proxyConnection.setGameProfile(new GameProfile(((C2SLoginHelloPacket1_19_3) proxyConnection.getLoginHelloPacket()).uuid, proxyConnection.getLoginHelloPacket().name)); + } else { + proxyConnection.setGameProfile(new GameProfile((UUID) null, loginHello.name)); + } + + if (Options.LOCAL_SOCKET_AUTH) { + String[] response = new LocalSocketClient(48941).request("getusername"); + if (response != null && response[0].equals("success")) { + proxyConnection.setGameProfile(new GameProfile((UUID) null, response[1])); + } + + response = new LocalSocketClient(48941).request("get_public_key_data"); + if (response != null && response[0].equals("success")) { + final String name = proxyConnection.getGameProfile().getName(); + final UUID uuid = UUIDSerializer.fromString(response[1].replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5")); + final PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(response[2]))); + final byte[] keySignature = Base64.getDecoder().decode(response[3]); + final Instant expiresAt = Instant.ofEpochMilli(Long.parseLong(response[4])); + + proxyConnection.setGameProfile(new GameProfile(uuid, name)); + proxyConnection.setLoginHelloPacket(new C2SLoginHelloPacket1_19_3(name, expiresAt, publicKey, keySignature, uuid)); + } + } else if (Options.MC_ACCOUNT != null) { + proxyConnection.setGameProfile(new GameProfile(Options.MC_ACCOUNT.id(), Options.MC_ACCOUNT.name())); + // TODO: Key + } + + proxyConnection.getLoginHelloPacket().name = proxyConnection.getGameProfile().getName(); + if (proxyConnection.getLoginHelloPacket() instanceof C2SLoginHelloPacket1_19_3) { + ((C2SLoginHelloPacket1_19_3) proxyConnection.getLoginHelloPacket()).uuid = proxyConnection.getGameProfile().getId(); + } + } + + public static void joinServer(final String serverIdHash, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException { + Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Trying to join online mode server"); + if (Options.OPENAUTHMOD_AUTH) { + try { + final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_JOIN_CHANNEL, PacketTypes.writeString(Unpooled.buffer(), serverIdHash)).get(6, TimeUnit.SECONDS); + if (response == null) throw new TimeoutException(); + if (response.isReadable() && !response.readBoolean()) throw new TimeoutException(); + } catch (TimeoutException e) { + proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server."); + } + } else if (Options.LOCAL_SOCKET_AUTH) { + new LocalSocketClient(48941).request("authenticate", serverIdHash); + } else if (Options.MC_ACCOUNT != null) { + try { + ViaProxyGameProfileFetcher.sessionService.joinServer(new GameProfile(Options.MC_ACCOUNT.id(), Options.MC_ACCOUNT.name()), Options.MC_ACCOUNT.prevResult().prevResult().access_token(), serverIdHash); + } catch (Throwable e) { + proxyConnection.kickClient("§cFailed to authenticate with Mojang servers! Please try again later."); + } + } else { + proxyConnection.kickClient("§cThis server is in online mode and requires a valid authentication mode."); + } + } + + public static void signNonce(final byte[] nonce, final C2SLoginKeyPacket1_19 packet, final ProxyConnection proxyConnection) throws InterruptedException, ExecutionException { + Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Requesting nonce signature"); + if (Options.OPENAUTHMOD_AUTH) { + try { + final ByteBuf response = proxyConnection.sendCustomPayload(OPENAUTHMOD_SIGN_NONCE_CHANNEL, PacketTypes.writeByteArray(Unpooled.buffer(), nonce)).get(4, TimeUnit.SECONDS); + if (response == null) throw new TimeoutException(); + if (!response.readBoolean()) throw new TimeoutException(); + packet.salt = response.readLong(); + packet.signature = PacketTypes.readByteArray(response); + } catch (TimeoutException e) { + proxyConnection.kickClient("§cAuthentication cancelled! You need to install OpenAuthMod in order to join this server."); + } + } else if (Options.LOCAL_SOCKET_AUTH) { + final String[] response = new LocalSocketClient(48941).request("sign_nonce", Base64.getEncoder().encodeToString(nonce)); + if (response != null && response[0].equals("success")) { + packet.salt = Long.valueOf(response[1]); + packet.signature = Base64.getDecoder().decode(response[2]); + } + } else if (Options.MC_ACCOUNT != null) { // TODO: Key + proxyConnection.kickClient("§cJoining servers which requires a signed nonce is not yet supported."); + } else { + proxyConnection.kickClient("§cThis server is in online mode and requires a valid authentication mode."); + } + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java b/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java index 573ffe3..07fceba 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/ProxyConnection.java @@ -179,7 +179,7 @@ public class ProxyConnection extends NetClient { PacketTypes.writeString(disconnectPacketData, channel); PacketTypes.writeVarInt(disconnectPacketData, id); disconnectPacketData.writeBytes(data); - this.c2p.writeAndFlush(new S2CLoginDisconnectPacket(messageToJson("§cYou need to install OpenAuthMod in order to join this server.§k\n" + Base64.getEncoder().encodeToString(ByteBufUtil.getBytes(disconnectPacketData)) + "\n" + CustomPayloadInterface.OPENAUTHMOD_LEGACY_MAGIC_STRING))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + this.c2p.writeAndFlush(new S2CLoginDisconnectPacket(messageToJson("§cYou need to install OpenAuthMod in order to join this server.§k\n" + Base64.getEncoder().encodeToString(ByteBufUtil.getBytes(disconnectPacketData)) + "\n" + ExternalInterface.OPENAUTHMOD_LEGACY_MAGIC_STRING))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } break; case PLAY: diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java index 2d4513e..d5725fb 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java @@ -1,7 +1,5 @@ package net.raphimc.viaproxy.proxy.client2proxy; -import com.github.steveice10.mc.auth.data.GameProfile; -import com.github.steveice10.mc.auth.util.UUIDSerializer; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; @@ -26,7 +24,6 @@ import net.raphimc.viaproxy.proxy.proxy2server.Proxy2ServerHandler; import net.raphimc.viaproxy.proxy.util.CloseAndReturn; import net.raphimc.viaproxy.proxy.util.ExceptionUtil; import net.raphimc.viaproxy.util.ArrayHelper; -import net.raphimc.viaproxy.util.LocalSocketClient; import net.raphimc.viaproxy.util.logging.Logger; import javax.crypto.SecretKey; @@ -34,9 +31,9 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidKeySpecException; -import java.security.spec.X509EncodedKeySpec; import java.time.Instant; -import java.util.*; +import java.util.Arrays; +import java.util.Random; import java.util.regex.Pattern; public class Client2ProxyHandler extends SimpleChannelInboundHandler { @@ -208,31 +205,17 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler { } } - this.proxyConnection.setLoginHelloPacket(packet); - this.proxyConnection.setGameProfile(new GameProfile((UUID) null, packet.name)); + ExternalInterface.fillPlayerData(packet, this.proxyConnection); if (Options.ONLINE_MODE) { this.proxyConnection.getC2P().writeAndFlush(new S2CLoginKeyPacket1_8("", KEY_PAIR.getPublic().getEncoded(), this.verifyToken)).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } else { - if (Options.LOCAL_SOCKET_AUTH) { - String[] response = new LocalSocketClient(48941).request("getusername"); - if (response != null && response[0].equals("success")) { - this.proxyConnection.setGameProfile(new GameProfile((UUID) null, response[1])); - } - response = new LocalSocketClient(48941).request("get_public_key_data"); - if (response != null && response[0].equals("success")) { - final UUID uuid = UUIDSerializer.fromString(response[1].replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5")); - final PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(response[2]))); - packet = new C2SLoginHelloPacket1_19_3(packet.name, Instant.ofEpochMilli(Long.parseLong(response[4])), publicKey, Base64.getDecoder().decode(response[3]), uuid); - } - } - - this.proxyConnection.getChannel().writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + this.proxyConnection.getChannel().writeAndFlush(this.proxyConnection.getLoginHelloPacket()).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); } } private void handleLoginKey(final C2SLoginKeyPacket1_7 packet) throws GeneralSecurityException, InterruptedException { - if (this.proxyConnection.getClientVersion().isOlderThanOrEqualTo(VersionEnum.r1_12_2) && new String(packet.encryptedNonce, StandardCharsets.UTF_8).equals(CustomPayloadInterface.OPENAUTHMOD_DATA_CHANNEL)) { // 1.8-1.12.2 OpenAuthMod response handling + if (this.proxyConnection.getClientVersion().isOlderThanOrEqualTo(VersionEnum.r1_12_2) && new String(packet.encryptedNonce, StandardCharsets.UTF_8).equals(ExternalInterface.OPENAUTHMOD_DATA_CHANNEL)) { // 1.8-1.12.2 OpenAuthMod response handling final ByteBuf byteBuf = Unpooled.wrappedBuffer(packet.encryptedSecretKey); this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(byteBuf), byteBuf); return; @@ -283,7 +266,7 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler { private boolean handlePlayCustomPayload(final ByteBuf packet) { final String channel = PacketTypes.readString(packet, Short.MAX_VALUE); // channel - if (channel.equals(CustomPayloadInterface.OPENAUTHMOD_DATA_CHANNEL)) { + if (channel.equals(ExternalInterface.OPENAUTHMOD_DATA_CHANNEL)) { return this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(packet), packet); } return false; diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java index 6ad35b1..ae4c021 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerHandler.java @@ -11,7 +11,7 @@ import net.raphimc.netminecraft.packet.impl.login.*; import net.raphimc.vialegacy.protocols.release.protocol1_7_2_5to1_6_4.storage.ProtocolMetadataStorage; import net.raphimc.vialegacy.util.VersionEnum; import net.raphimc.viaproxy.cli.options.Options; -import net.raphimc.viaproxy.proxy.CustomPayloadInterface; +import net.raphimc.viaproxy.proxy.ExternalInterface; import net.raphimc.viaproxy.proxy.ProxyConnection; import net.raphimc.viaproxy.proxy.util.ExceptionUtil; import net.raphimc.viaproxy.util.logging.Logger; @@ -76,7 +76,7 @@ public class Proxy2ServerHandler extends SimpleChannelInboundHandler { auth = this.proxyConnection.getUserConnection().get(ProtocolMetadataStorage.class).authenticate; } if (auth) { - CustomPayloadInterface.joinServer(serverHash, this.proxyConnection); + ExternalInterface.joinServer(serverHash, this.proxyConnection); } final byte[] encryptedSecretKey = CryptUtil.encryptData(publicKey, secretKey.getEncoded()); @@ -84,7 +84,7 @@ public class Proxy2ServerHandler extends SimpleChannelInboundHandler { final C2SLoginKeyPacket1_19_3 loginKey = new C2SLoginKeyPacket1_19_3(encryptedSecretKey, encryptedNonce); if (this.proxyConnection.getLoginHelloPacket() instanceof C2SLoginHelloPacket1_19 && ((C2SLoginHelloPacket1_19) this.proxyConnection.getLoginHelloPacket()).key != null) { - CustomPayloadInterface.signNonce(packet.nonce, loginKey, this.proxyConnection); + ExternalInterface.signNonce(packet.nonce, loginKey, this.proxyConnection); } this.proxyConnection.getChannel().writeAndFlush(loginKey).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); diff --git a/src/main/java/net/raphimc/viaproxy/saves/AbstractSave.java b/src/main/java/net/raphimc/viaproxy/saves/AbstractSave.java new file mode 100644 index 0000000..b08c93c --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/saves/AbstractSave.java @@ -0,0 +1,21 @@ +package net.raphimc.viaproxy.saves; + +import com.google.gson.JsonElement; + +public abstract class AbstractSave { + + private final String name; + + public AbstractSave(final String name) { + this.name = name; + } + + public abstract void load(final JsonElement jsonElement) throws Throwable; + + public abstract JsonElement save() throws Throwable; + + public String getName() { + return this.name; + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/saves/SaveManager.java b/src/main/java/net/raphimc/viaproxy/saves/SaveManager.java new file mode 100644 index 0000000..ca1978d --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/saves/SaveManager.java @@ -0,0 +1,76 @@ +package net.raphimc.viaproxy.saves; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import net.lenni0451.reflect.stream.RStream; +import net.raphimc.viaproxy.saves.impl.AccountsSave; +import net.raphimc.viaproxy.util.logging.Logger; + +import java.io.*; + +public class SaveManager { + + private static final File SAVE_FILE = new File("saves.json"); + private static final Gson GSON = new Gson(); + + public final AccountsSave accountsSave = new AccountsSave(); + + public SaveManager() { + this.load(); + } + + public void load() { + try { + if (!SAVE_FILE.exists()) { + SAVE_FILE.createNewFile(); + this.save(); + } + + final FileReader reader = new FileReader(SAVE_FILE); + final JsonObject saveObject = GSON.fromJson(reader, JsonObject.class); + reader.close(); + + RStream + .of(this) + .fields() + .filter(field -> AbstractSave.class.isAssignableFrom(field.type())) + .forEach(field -> { + final AbstractSave save = field.get(); + try { + if (saveObject.has(save.getName())) { + save.load(saveObject.get(save.getName())); + } + } catch (Throwable e) { + Logger.LOGGER.error("Failed to load save " + save.getName(), e); + } + }); + } catch (Throwable e) { + Logger.LOGGER.error("Failed to load saves from file", e); + } + } + + public void save() { + try { + final JsonObject saveObject = new JsonObject(); + RStream + .of(this) + .fields() + .filter(field -> AbstractSave.class.isAssignableFrom(field.type())) + .forEach(field -> { + final AbstractSave save = field.get(); + try { + saveObject.add(save.getName(), save.save()); + } catch (Throwable e) { + Logger.LOGGER.error("Failed to save save " + save.getName(), e); + } + }); + + final FileWriter writer = new FileWriter(SAVE_FILE); + GSON.toJson(saveObject, writer); + writer.close(); + } catch (Throwable e) { + Logger.LOGGER.error("Failed to save saves to file", e); + } + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/saves/impl/AccountsSave.java b/src/main/java/net/raphimc/viaproxy/saves/impl/AccountsSave.java new file mode 100644 index 0000000..9598dce --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/saves/impl/AccountsSave.java @@ -0,0 +1,63 @@ +package net.raphimc.viaproxy.saves.impl; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import net.raphimc.mcauth.MinecraftAuth; +import net.raphimc.mcauth.step.java.StepMCProfile; +import net.raphimc.mcauth.util.MicrosoftConstants; +import net.raphimc.viaproxy.saves.AbstractSave; +import net.raphimc.viaproxy.util.logging.Logger; +import org.apache.http.impl.client.CloseableHttpClient; + +import java.util.*; + +public class AccountsSave extends AbstractSave { + + private List accounts = new ArrayList<>(); + + public AccountsSave() { + super("accounts"); + } + + @Override + public void load(JsonElement jsonElement) throws Exception { + this.accounts = new ArrayList<>(); + for (JsonElement element : jsonElement.getAsJsonArray()) { + this.accounts.add(MinecraftAuth.Java.Title.MC_PROFILE.fromJson(element.getAsJsonObject())); + } + } + + @Override + public JsonElement save() { + final JsonArray array = new JsonArray(); + for (StepMCProfile.MCProfile account : this.accounts) { + array.add(account.toJson()); + } + return array; + } + + public void addAccount(final StepMCProfile.MCProfile profile) { + this.accounts.add(profile); + } + + public void removeAccount(final StepMCProfile.MCProfile profile) { + this.accounts.remove(profile); + } + + public void refreshAccounts() { + final List accounts = new ArrayList<>(); + for (StepMCProfile.MCProfile account : this.accounts) { + try (final CloseableHttpClient httpClient = MicrosoftConstants.createHttpClient()) { + accounts.add(MinecraftAuth.Java.Title.MC_PROFILE.refresh(httpClient, account)); + } catch (Throwable e) { + Logger.LOGGER.error("Failed to refresh account " + account.name() + ", removing it from the list.", e); + } + } + this.accounts = accounts; + } + + public List getAccounts() { + return Collections.unmodifiableList(this.accounts); + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/ui/impl/GeneralTab.java b/src/main/java/net/raphimc/viaproxy/ui/impl/GeneralTab.java index 3da415c..992a9d5 100644 --- a/src/main/java/net/raphimc/viaproxy/ui/impl/GeneralTab.java +++ b/src/main/java/net/raphimc/viaproxy/ui/impl/GeneralTab.java @@ -94,7 +94,7 @@ public class GeneralTab extends AUITab { authMethodLabel.setBounds(10, 200, 400, 20); contentPane.add(authMethodLabel); - this.authMethod = new JComboBox<>(new String[]{"Use OpenAuthMod"}); + this.authMethod = new JComboBox<>(new String[]{"Use stored account", "Use OpenAuthMod"}); this.authMethod.setBounds(10, 220, 465, 20); contentPane.add(this.authMethod); } @@ -149,7 +149,7 @@ public class GeneralTab extends AUITab { final String serverAddress = this.serverAddress.getText(); final VersionEnum serverVersion = (VersionEnum) this.serverVersion.getSelectedItem(); final int bindPort = (int) this.bindPort.getValue(); - final String authMethod = (String) this.authMethod.getSelectedItem(); + final int authMethod = this.authMethod.getSelectedIndex(); final boolean betaCraftAuth = this.betaCraftAuth.isSelected(); final boolean proxyOnlineMode = this.proxyOnlineMode.isSelected(); @@ -162,10 +162,14 @@ public class GeneralTab extends AUITab { Options.CONNECT_ADDRESS = hostAndPort.getHost(); Options.CONNECT_PORT = hostAndPort.getPortOrDefault(25565); Options.PROTOCOL_VERSION = serverVersion; - - Options.OPENAUTHMOD_AUTH = true; Options.BETACRAFT_AUTH = betaCraftAuth; + if (authMethod == 0 && Options.MC_ACCOUNT == null && !ViaProxy.saveManager.accountsSave.getAccounts().isEmpty()) { + Options.MC_ACCOUNT = ViaProxy.saveManager.accountsSave.getAccounts().get(0); + } else if (authMethod == 1) { + Options.OPENAUTHMOD_AUTH = true; + } + ViaProxy.startProxy(); SwingUtilities.invokeLater(() -> { diff --git a/src/main/java/net/raphimc/viaproxy/ui/impl/OnlineModeTab.java b/src/main/java/net/raphimc/viaproxy/ui/impl/OnlineModeTab.java index 07deb9b..525c4b1 100644 --- a/src/main/java/net/raphimc/viaproxy/ui/impl/OnlineModeTab.java +++ b/src/main/java/net/raphimc/viaproxy/ui/impl/OnlineModeTab.java @@ -2,6 +2,8 @@ package net.raphimc.viaproxy.ui.impl; import net.raphimc.mcauth.MinecraftAuth; import net.raphimc.mcauth.step.java.StepMCProfile; +import net.raphimc.viaproxy.ViaProxy; +import net.raphimc.viaproxy.cli.options.Options; import net.raphimc.viaproxy.ui.AUITab; import net.raphimc.viaproxy.ui.ViaProxyUI; import net.raphimc.viaproxy.ui.popups.AddAccountPopup; @@ -30,17 +32,22 @@ public class OnlineModeTab extends AUITab { infoLabel.setBounds(10, 10, 500, 20); contentPane.add(infoLabel); } + { + JLabel info2Label = new JLabel("You can select the account to use by right clicking it. By default the first one will be used."); + info2Label.setBounds(10, 30, 500, 20); + contentPane.add(info2Label); + } { JLabel infoLabel = new JLabel("If you change your account frequently, you might want to install OpenAuthMod on your"); - infoLabel.setBounds(10, 40, 500, 20); + infoLabel.setBounds(10, 60, 500, 20); contentPane.add(infoLabel); JLabel infoLabel2 = new JLabel("client. This allows ViaProxy to use the account you are logged in with on the client."); - infoLabel2.setBounds(10, 60, 500, 20); + infoLabel2.setBounds(10, 80, 500, 20); contentPane.add(infoLabel2); JLabel clickRect = new JLabel(); - clickRect.setBounds(353, 40, 80, 20); + clickRect.setBounds(353, 60, 80, 20); clickRect.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { @@ -51,7 +58,7 @@ public class OnlineModeTab extends AUITab { } { JScrollPane scrollPane = new JScrollPane(); - scrollPane.setBounds(10, 85, 465, 205); + scrollPane.setBounds(10, 105, 465, 185); contentPane.add(scrollPane); DefaultListModel model = new DefaultListModel<>(); @@ -59,12 +66,31 @@ public class OnlineModeTab extends AUITab { scrollPane.setViewportView(this.accountsList); JPopupMenu contextMenu = new JPopupMenu(); + JMenuItem selectItem = new JMenuItem("Use to join online mode servers"); + selectItem.addActionListener(e -> { + int index = this.accountsList.getSelectedIndex(); + if (index != -1) { + final StepMCProfile.MCProfile account = ViaProxy.saveManager.accountsSave.getAccounts().get(index); + if (account != null) { + Options.MC_ACCOUNT = account; + } else { + throw new IllegalStateException("Account is null"); + } + } + }); + contextMenu.add(selectItem); JMenuItem removeItem = new JMenuItem("Remove"); removeItem.addActionListener(e -> { int index = this.accountsList.getSelectedIndex(); if (index != -1) { model.remove(index); - //TODO: Remove from save + final StepMCProfile.MCProfile account = ViaProxy.saveManager.accountsSave.getAccounts().get(index); + if (account != null) { + ViaProxy.saveManager.accountsSave.removeAccount(account); + ViaProxy.saveManager.save(); + } else { + throw new IllegalStateException("Account is null"); + } } if (index < model.getSize()) this.accountsList.setSelectedIndex(index); else if (index > 0) this.accountsList.setSelectedIndex(index - 1); @@ -89,10 +115,11 @@ public class OnlineModeTab extends AUITab { }); SwingUtilities.invokeLater(() -> { this.closePopup(); + ViaProxy.saveManager.accountsSave.addAccount(profile); + ViaProxy.saveManager.save(); DefaultListModel model = (DefaultListModel) this.accountsList.getModel(); model.addElement(profile.name()); this.frame.showInfo("The account " + profile.name() + " was added successfully."); - //TODO: Add to save }); } catch (InterruptedException ignored) { } catch (TimeoutException e) { @@ -114,6 +141,12 @@ public class OnlineModeTab extends AUITab { } } + @Override + public void setReady() { + final DefaultListModel model = (DefaultListModel) this.accountsList.getModel(); + ViaProxy.saveManager.accountsSave.getAccounts().forEach(account -> model.addElement(account.name())); + } + private void closePopup() { this.addAccountPopup.markExternalClose(); this.addAccountPopup.setVisible(false);