Implemented joining with local minecraft accounts

This commit is contained in:
RaphiMC 2023-01-07 12:40:18 +01:00
parent d036d37bd0
commit 8dd9d0032c
13 changed files with 355 additions and 105 deletions

View file

@ -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;

View file

@ -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<Void> help = parser.acceptsAll(asList("help", "h", "?"), "Get a list of all arguments").forHelp();

View file

@ -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));
}
}

View file

@ -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]);
}
}
}
}

View file

@ -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.");
}
}
}

View file

@ -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:

View file

@ -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<IPacket> {
@ -208,31 +205,17 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
}
}
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<IPacket> {
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;

View file

@ -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<IPacket> {
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<IPacket> {
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);

View file

@ -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;
}
}

View file

@ -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);
}
}
}

View file

@ -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<StepMCProfile.MCProfile> 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<StepMCProfile.MCProfile> 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<StepMCProfile.MCProfile> getAccounts() {
return Collections.unmodifiableList(this.accounts);
}
}

View file

@ -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(() -> {

View file

@ -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("<html>If you change your account frequently, you might want to install <a href=\"\">OpenAuthMod</a> on your</html>");
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<String> 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<String> model = (DefaultListModel<String>) 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<String> model = (DefaultListModel<String>) this.accountsList.getModel();
ViaProxy.saveManager.accountsSave.getAccounts().forEach(account -> model.addElement(account.name()));
}
private void closePopup() {
this.addAccountPopup.markExternalClose();
this.addAccountPopup.setVisible(false);