diff --git a/build.gradle b/build.gradle index 1f2a482..c7b26c0 100644 --- a/build.gradle +++ b/build.gradle @@ -98,6 +98,7 @@ dependencies { exclude group: "io.netty" } include "gs.mclo:api:3.0.1" + include "net.lenni0451:optconfig:1.0.0-SNAPSHOT" includeJ8(compileOnly("xyz.wagyourtail.jvmdowngrader:jvmdowngrader:0.9.1")) includeJ8 "xyz.wagyourtail.jvmdowngrader:jvmdowngrader-java-api:0.9.1:downgraded-8" diff --git a/src/main/java/net/raphimc/viaproxy/ViaProxy.java b/src/main/java/net/raphimc/viaproxy/ViaProxy.java index f45b031..a3297d3 100644 --- a/src/main/java/net/raphimc/viaproxy/ViaProxy.java +++ b/src/main/java/net/raphimc/viaproxy/ViaProxy.java @@ -30,6 +30,8 @@ import net.lenni0451.classtransform.utils.tree.IClassProvider; import net.lenni0451.lambdaevents.LambdaManager; import net.lenni0451.lambdaevents.generator.LambdaMetaFactoryGenerator; +import net.lenni0451.optconfig.ConfigLoader; +import net.lenni0451.optconfig.provider.ConfigProvider; import net.lenni0451.reflect.Agents; import net.lenni0451.reflect.ClassLoaders; import net.lenni0451.reflect.JavaBypass; @@ -229,8 +231,13 @@ public static void injectedMain(final String injectionMethod, final String[] arg progressConsumer.accept("Loading Saves"); SAVE_MANAGER = new SaveManager(); progressConsumer.accept("Loading Config"); - CONFIG = new ViaProxyConfig(viaProxyConfigFile); - CONFIG.reload(); + final ConfigLoader configLoader = new ConfigLoader<>(ViaProxyConfig.class); + configLoader.getConfigOptions().setResetInvalidOptions(true).setRewriteConfig(true).setCommentSpacing(1); + try { + CONFIG = configLoader.load(ConfigProvider.file(viaProxyConfigFile)).getConfigInstance(); + } catch (Throwable e) { + throw new RuntimeException("Failed to load config", e); + } if (useUI) { progressConsumer.accept("Loading GUI"); @@ -253,7 +260,11 @@ public static void injectedMain(final String injectionMethod, final String[] arg if (useCLI) { final String[] cliArgs = new String[args.length - 1]; System.arraycopy(args, 1, cliArgs, 0, cliArgs.length); - CONFIG.loadFromArguments(cliArgs); + try { + CONFIG.loadFromArguments(cliArgs); + } catch (Throwable e) { + throw new RuntimeException("Failed to load CLI arguments", e); + } } else if (firstStart) { Logger.LOGGER.info("This is the first start of ViaProxy. Please configure the settings in the " + viaProxyConfigFile.getName() + " file and restart ViaProxy."); System.exit(0); diff --git a/src/main/java/net/raphimc/viaproxy/protocoltranslator/viaproxy/ViaProxyConfig.java b/src/main/java/net/raphimc/viaproxy/protocoltranslator/viaproxy/ViaProxyConfig.java index ceedac0..b7568c4 100644 --- a/src/main/java/net/raphimc/viaproxy/protocoltranslator/viaproxy/ViaProxyConfig.java +++ b/src/main/java/net/raphimc/viaproxy/protocoltranslator/viaproxy/ViaProxyConfig.java @@ -18,223 +18,236 @@ package net.raphimc.viaproxy.protocoltranslator.viaproxy; import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; -import com.viaversion.viaversion.util.Config; import joptsimple.OptionException; import joptsimple.OptionParser; import joptsimple.OptionSet; import joptsimple.OptionSpec; -import net.raphimc.vialoader.util.JLoggerToSLF4J; +import net.lenni0451.optconfig.ConfigContext; +import net.lenni0451.optconfig.annotations.*; +import net.lenni0451.optconfig.index.ClassIndexer; +import net.lenni0451.optconfig.index.ConfigType; +import net.lenni0451.optconfig.index.types.ConfigOption; +import net.lenni0451.optconfig.index.types.SectionIndex; import net.raphimc.viaproxy.ViaProxy; import net.raphimc.viaproxy.cli.BetterHelpFormatter; import net.raphimc.viaproxy.cli.HelpRequestedException; -import net.raphimc.viaproxy.cli.ProtocolVersionConverter; import net.raphimc.viaproxy.plugins.events.PostOptionsParseEvent; import net.raphimc.viaproxy.plugins.events.PreOptionsParseEvent; import net.raphimc.viaproxy.protocoltranslator.ProtocolTranslator; import net.raphimc.viaproxy.saves.impl.accounts.Account; import net.raphimc.viaproxy.util.AddressUtil; +import net.raphimc.viaproxy.util.config.*; import net.raphimc.viaproxy.util.logging.Logger; -import org.slf4j.LoggerFactory; -import java.io.File; -import java.io.IOException; import java.net.SocketAddress; import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Collections; -import java.util.List; -import java.util.Locale; +import java.util.HashMap; import java.util.Map; +import java.util.Stack; -public class ViaProxyConfig extends Config implements com.viaversion.viaversion.api.configuration.Config { - - private static final java.util.logging.Logger LOGGER = new JLoggerToSLF4J(LoggerFactory.getLogger("ViaProxy")); - - private final OptionParser optionParser; - private final OptionSpec optionHelp; - private final OptionSpec optionBindAddress; - private final OptionSpec optionTargetAddress; - private final OptionSpec optionTargetVersion; - private final OptionSpec optionProxyOnlineMode; - private final OptionSpec optionConnectTimeout; - private final OptionSpec optionAuthMethod; - private final OptionSpec optionMinecraftAccountIndex; - private final OptionSpec optionBetacraftAuth; - private final OptionSpec optionBackendProxyUrl; - private final OptionSpec optionBackendHaProxy; - private final OptionSpec optionFrontendHaProxy; - private final OptionSpec optionChatSigning; - private final OptionSpec optionCompressionThreshold; - private final OptionSpec optionAllowBetaPinging; - private final OptionSpec optionIgnoreProtocolTranslationErrors; - private final OptionSpec optionSuppressClientProtocolErrors; - private final OptionSpec optionAllowLegacyClientPassthrough; - private final OptionSpec optionBungeecordPlayerInfoPassthrough; - private final OptionSpec optionCustomMotd; - private final OptionSpec optionResourcePackUrl; - private final OptionSpec optionWildcardDomainHandling; - private final OptionSpec optionSimpleVoiceChatSupport; - private final OptionSpec optionFakeAcceptResourcePacks; +@OptConfig(header = "ViaProxy configuration file", version = 1) +public class ViaProxyConfig { + private ConfigContext configContext; + + @NotReloadable + @Option("bind-address") + @Description("The address ViaProxy should listen for connections.") + @TypeSerializer(SocketAddressTypeSerializer.class) private SocketAddress bindAddress = AddressUtil.parse("0.0.0.0:25568", null); + + @Option(value = "target-address", dependencies = "target-version") + @Description("The address of the server ViaProxy should connect to.") + @TypeSerializer(TargetAddressTypeSerializer.class) private SocketAddress targetAddress = AddressUtil.parse("127.0.0.1:25565", null); + + @Option("target-version") + @Description("The version ViaProxy should translate to. (See ViaProxy GUI for a list of versions)") + @TypeSerializer(ProtocolVersionTypeSerializer.class) private ProtocolVersion targetVersion = ProtocolTranslator.AUTO_DETECT_PROTOCOL; + + @Option("connect-timeout") + @Description("The connect timeout for backend server connections in milliseconds.") private int connectTimeout = 8000; + + @Option("proxy-online-mode") + @Description({ + "Proxy Online Mode allows you to see skins on online mode servers and use the signed chat features.", + "Enabling Proxy Online Mode requires your client to have a valid minecraft account." + }) private boolean proxyOnlineMode = false; + + @Option("auth-method") + @Description({ + "The authentication method to use for joining the target server.", + "none: No authentication (Offline mode)", + "openauthmod: Requires the OpenAuthMod client mod (https://modrinth.com/mod/openauthmod)", + "account: Use an account for joining the target server. (Has to be configured in ViaProxy GUI)" + }) private AuthMethod authMethod = AuthMethod.NONE; + + @Option(value = "minecraft-account-index", dependencies = "auth-method") + @Description("The GUI account list index (0 indexed) of the account if the auth method is set to account.") + @TypeSerializer(AccountTypeSerializer.class) private Account account = null; + + @Option("betacraft-auth") + @Description({ + "Use BetaCraft authentication for classic servers.", + "Enabling BetaCraft Auth allows you to join classic servers which have online mode enabled." + }) private boolean betacraftAuth = false; + + @Option("backend-proxy-url") + @Description({ + "URL of a SOCKS(4/5)/HTTP(S) proxy which will be used for backend server connections. Leave empty to connect directly.", + "Supported formats:", + "- type://address:port", + "- type://username:password@address:port" + }) + @TypeSerializer(ProxyUriTypeSerializer.class) private URI backendProxyUrl = null; + + @Option("backend-haproxy") + @Description("Send HAProxy protocol messages to the target server.") private boolean backendHaProxy = false; + + @Option("frontend-haproxy") + @Description("Read HAProxy protocol messages from client connections.") private boolean frontendHaProxy = false; + + @Option("chat-signing") + @Description("Enables sending signed chat messages on >= 1.19 servers.") private boolean chatSigning = true; + + @Option("compression-threshold") + @Description("The threshold for packet compression. Packets larger than this size will be compressed. (-1 to disable)") private int compressionThreshold = 256; + + @Option("allow-beta-pinging") + @Description("Enabling this will allow you to ping <= b1.7.3 servers. This may cause issues with servers that block too frequent connections.") private boolean allowBetaPinging = false; + + @Option("ignore-protocol-translation-errors") + @Description({ + "Enabling this will prevent getting disconnected from the server when a packet translation error occurs and instead only print the error in the console.", + "This may cause issues depending on the type of packet which failed to translate." + }) private boolean ignoreProtocolTranslationErrors = false; + + @Option("suppress-client-protocol-errors") + @Description({ + "Enabling this will suppress client protocol errors to prevent lag when ViaProxy is getting spammed with invalid packets.", + "This may cause issues with debugging client connection issues because no error messages will be printed." + }) private boolean suppressClientProtocolErrors = false; + + @Option("allow-legacy-client-passthrough") + @Description("Allow <= 1.6.4 clients to connect through ViaProxy to the target server. (No protocol translation or packet handling)") private boolean allowLegacyClientPassthrough = false; + + @Option("bungeecord-player-info-passthrough") + @Description({ + "Allow additional information like player ip, player uuid to be passed through to the backend server.", + "This is typically used by proxies like BungeeCord and requires support from the backend server as well." + }) private boolean bungeecordPlayerInfoPassthrough = false; + + @Option("custom-motd") + @Description("Custom MOTD to send when clients ping the proxy. Leave empty to use the target server's MOTD.") private String customMotd = ""; + + @Option("resource-pack-url") + @Description({"URL of a resource pack which clients can optionally download when connecting to the server. Leave empty to disable.", "Example: http://example.com/resourcepack.zip"}) private String resourcePackUrl = ""; + + @Option("wildcard-domain-handling") + @Description({ + "Allows clients to specify a target server and version using wildcard domains.", + "none: No wildcard domain handling.", + "public: Public wildcard domain handling. Intended for usage by external clients. (Example: address_port_version.viaproxy.127.0.0.1.nip.io)", + "internal: Internal wildcard domain handling. Intended for local usage by custom clients. (Example: original-handshake-address\\7address:port\\7version\\7classic-mppass)" + }) private WildcardDomainHandling wildcardDomainHandling = WildcardDomainHandling.NONE; + + @Option("simple-voice-chat-support") + @Description("Enables handling and rewriting of Simple Voice Chat mod packets.") private boolean simpleVoiceChatSupport = false; + + @Option("fake-accept-resource-packs") + @Description({ + "Accepts resource packs from the server without showing a prompt to the client.", + "This is required for servers that require a resource pack, but the client can't load it due to version differences." + }) private boolean fakeAcceptResourcePacks = false; - public ViaProxyConfig(final File configFile) { - super(configFile, LOGGER); - - this.optionParser = new OptionParser(); - this.optionHelp = this.optionParser.accepts("help").forHelp(); - this.optionBindAddress = this.optionParser.accepts("bind-address").withRequiredArg().ofType(String.class).defaultsTo(AddressUtil.toString(this.bindAddress)); - this.optionTargetAddress = this.optionParser.accepts("target-address").withRequiredArg().ofType(String.class).defaultsTo(AddressUtil.toString(this.targetAddress)); - this.optionTargetVersion = this.optionParser.accepts("target-version").withRequiredArg().withValuesConvertedBy(new ProtocolVersionConverter()).defaultsTo(this.targetVersion); - this.optionProxyOnlineMode = this.optionParser.accepts("proxy-online-mode").withRequiredArg().ofType(Boolean.class).defaultsTo(this.proxyOnlineMode); - this.optionConnectTimeout = this.optionParser.accepts("connect-timeout").withRequiredArg().ofType(Integer.class).defaultsTo(this.connectTimeout); - this.optionAuthMethod = this.optionParser.accepts("auth-method").withRequiredArg().ofType(AuthMethod.class).defaultsTo(this.authMethod); - this.optionMinecraftAccountIndex = this.optionParser.accepts("minecraft-account-index").withRequiredArg().ofType(Integer.class).defaultsTo(0); - this.optionBetacraftAuth = this.optionParser.accepts("betacraft-auth").withRequiredArg().ofType(Boolean.class).defaultsTo(this.betacraftAuth); - this.optionBackendProxyUrl = this.optionParser.accepts("backend-proxy-url").withRequiredArg().ofType(String.class).defaultsTo(""); - this.optionBackendHaProxy = this.optionParser.accepts("backend-haproxy").withRequiredArg().ofType(Boolean.class).defaultsTo(this.backendHaProxy); - this.optionFrontendHaProxy = this.optionParser.accepts("frontend-haproxy").withRequiredArg().ofType(Boolean.class).defaultsTo(this.frontendHaProxy); - this.optionChatSigning = this.optionParser.accepts("chat-signing").withRequiredArg().ofType(Boolean.class).defaultsTo(this.chatSigning); - this.optionCompressionThreshold = this.optionParser.accepts("compression-threshold").withRequiredArg().ofType(Integer.class).defaultsTo(this.compressionThreshold); - this.optionAllowBetaPinging = this.optionParser.accepts("allow-beta-pinging").withRequiredArg().ofType(Boolean.class).defaultsTo(this.allowBetaPinging); - this.optionIgnoreProtocolTranslationErrors = this.optionParser.accepts("ignore-protocol-translation-errors").withRequiredArg().ofType(Boolean.class).defaultsTo(this.ignoreProtocolTranslationErrors); - this.optionSuppressClientProtocolErrors = this.optionParser.accepts("suppress-client-protocol-errors").withRequiredArg().ofType(Boolean.class).defaultsTo(this.suppressClientProtocolErrors); - this.optionAllowLegacyClientPassthrough = this.optionParser.accepts("allow-legacy-client-passthrough").withRequiredArg().ofType(Boolean.class).defaultsTo(this.allowLegacyClientPassthrough); - this.optionBungeecordPlayerInfoPassthrough = this.optionParser.accepts("bungeecord-player-info-passthrough").withRequiredArg().ofType(Boolean.class).defaultsTo(this.bungeecordPlayerInfoPassthrough); - this.optionCustomMotd = this.optionParser.accepts("custom-motd").withRequiredArg().ofType(String.class).defaultsTo(this.customMotd); - this.optionResourcePackUrl = this.optionParser.accepts("resource-pack-url").withRequiredArg().ofType(String.class).defaultsTo(this.resourcePackUrl); - this.optionWildcardDomainHandling = this.optionParser.accepts("wildcard-domain-handling").withRequiredArg().ofType(WildcardDomainHandling.class).defaultsTo(this.wildcardDomainHandling); - this.optionSimpleVoiceChatSupport = this.optionParser.accepts("simple-voice-chat-support").withRequiredArg().ofType(Boolean.class).defaultsTo(this.simpleVoiceChatSupport); - this.optionFakeAcceptResourcePacks = this.optionParser.accepts("fake-accept-resource-packs").withRequiredArg().ofType(Boolean.class).defaultsTo(this.fakeAcceptResourcePacks); - } - - @Override - public void reload() { - super.reload(); - - this.bindAddress = AddressUtil.parse(this.getString("bind-address", AddressUtil.toString(this.bindAddress)), null); - this.targetVersion = ProtocolVersion.getClosest(this.getString("target-version", this.targetVersion.getName())); - this.checkTargetVersion(); - this.targetAddress = AddressUtil.parse(this.getString("target-address", AddressUtil.toString(this.targetAddress)), this.targetVersion); - this.connectTimeout = this.getInt("connect-timeout", this.connectTimeout); - this.proxyOnlineMode = this.getBoolean("proxy-online-mode", this.proxyOnlineMode); - this.authMethod = AuthMethod.byName(this.getString("auth-method", this.authMethod.name())); - final List accounts = ViaProxy.getSaveManager().accountsSave.getAccounts(); - final int accountIndex = this.getInt("minecraft-account-index", 0); - if (this.authMethod == AuthMethod.ACCOUNT && accountIndex >= 0 && accountIndex < accounts.size()) { - this.account = accounts.get(accountIndex); - } else { - this.account = null; + @SuppressWarnings("UnstableApiUsage") + public void loadFromArguments(final String[] args) throws Exception { + final OptionParser optionParser = new OptionParser(); + final OptionSpec optionHelp = optionParser.accepts("help").forHelp(); + + final Map, ConfigOption> optionMap = new HashMap<>(); + final Stack stack = new Stack<>(); + stack.push(ClassIndexer.indexClass(ConfigType.INSTANCED, ViaProxyConfig.class)); + while (!stack.isEmpty()) { + final SectionIndex index = stack.pop(); + stack.addAll(index.getSubSections().values()); + + for (ConfigOption option : index.getOptions()) { + if (index.getSubSections().containsKey(option)) continue; + + Object defaultValue = option.getField().get(this); + if (option.getTypeSerializer() != null) { + defaultValue = option.createTypeSerializer(null, ViaProxyConfig.class, this).serialize(defaultValue); + } + final OptionSpec cliOption = optionParser.accepts(option.getName()).withRequiredArg().ofType((Class) defaultValue.getClass()).defaultsTo(defaultValue); + optionMap.put(cliOption, option); + } } - this.betacraftAuth = this.getBoolean("betacraft-auth", this.betacraftAuth); - this.backendProxyUrl = this.parseProxyUrl(this.getString("backend-proxy-url", "")); - this.backendHaProxy = this.getBoolean("backend-haproxy", this.backendHaProxy); - this.frontendHaProxy = this.getBoolean("frontend-haproxy", this.frontendHaProxy); - this.chatSigning = this.getBoolean("chat-signing", this.chatSigning); - this.compressionThreshold = this.getInt("compression-threshold", this.compressionThreshold); - this.allowBetaPinging = this.getBoolean("allow-beta-pinging", this.allowBetaPinging); - this.ignoreProtocolTranslationErrors = this.getBoolean("ignore-protocol-translation-errors", this.ignoreProtocolTranslationErrors); - this.suppressClientProtocolErrors = this.getBoolean("suppress-client-protocol-errors", this.suppressClientProtocolErrors); - this.allowLegacyClientPassthrough = this.getBoolean("allow-legacy-client-passthrough", this.allowLegacyClientPassthrough); - this.bungeecordPlayerInfoPassthrough = this.getBoolean("bungeecord-player-info-passthrough", this.bungeecordPlayerInfoPassthrough); - this.customMotd = this.getString("custom-motd", this.customMotd); - this.resourcePackUrl = this.getString("resource-pack-url", this.resourcePackUrl); - this.wildcardDomainHandling = WildcardDomainHandling.byName(this.getString("wildcard-domain-handling", this.wildcardDomainHandling.name())); - this.simpleVoiceChatSupport = this.getBoolean("simple-voice-chat-support", this.simpleVoiceChatSupport); - this.fakeAcceptResourcePacks = this.getBoolean("fake-accept-resource-packs", this.fakeAcceptResourcePacks); - } - - public void loadFromArguments(final String[] args) throws IOException { + try { - ViaProxy.EVENT_MANAGER.call(new PreOptionsParseEvent(this.optionParser)); - final OptionSet options = this.optionParser.parse(args); - if (options.has(this.optionHelp)) { + ViaProxy.EVENT_MANAGER.call(new PreOptionsParseEvent(optionParser)); + final OptionSet options = optionParser.parse(args); + if (options.has(optionHelp)) { throw new HelpRequestedException(); } - this.bindAddress = AddressUtil.parse(options.valueOf(this.optionBindAddress), null); - this.targetVersion = options.valueOf(this.optionTargetVersion); - this.checkTargetVersion(); - this.targetAddress = AddressUtil.parse(options.valueOf(this.optionTargetAddress), this.targetVersion); - this.connectTimeout = options.valueOf(this.optionConnectTimeout); - this.proxyOnlineMode = options.valueOf(this.optionProxyOnlineMode); - this.authMethod = options.valueOf(this.optionAuthMethod); - final List accounts = ViaProxy.getSaveManager().accountsSave.getAccounts(); - final int accountIndex = options.valueOf(this.optionMinecraftAccountIndex); - if (options.has(this.optionMinecraftAccountIndex) && accountIndex >= 0 && accountIndex < accounts.size()) { + + if (options.has("minecraft-account-index")) { this.authMethod = AuthMethod.ACCOUNT; - this.account = accounts.get(accountIndex); - } else { - this.account = null; } - this.betacraftAuth = options.valueOf(this.optionBetacraftAuth); - this.backendProxyUrl = this.parseProxyUrl(options.valueOf(this.optionBackendProxyUrl)); - this.backendHaProxy = options.valueOf(this.optionBackendHaProxy); - this.frontendHaProxy = options.valueOf(this.optionFrontendHaProxy); - this.chatSigning = options.valueOf(this.optionChatSigning); - this.compressionThreshold = options.valueOf(this.optionCompressionThreshold); - this.allowBetaPinging = options.valueOf(this.optionAllowBetaPinging); - this.ignoreProtocolTranslationErrors = options.valueOf(this.optionIgnoreProtocolTranslationErrors); - this.suppressClientProtocolErrors = options.valueOf(this.optionSuppressClientProtocolErrors); - this.allowLegacyClientPassthrough = options.valueOf(this.optionAllowLegacyClientPassthrough); - this.bungeecordPlayerInfoPassthrough = options.valueOf(this.optionBungeecordPlayerInfoPassthrough); - this.customMotd = options.valueOf(this.optionCustomMotd); - this.resourcePackUrl = options.valueOf(this.optionResourcePackUrl); - this.wildcardDomainHandling = options.valueOf(this.optionWildcardDomainHandling); - this.simpleVoiceChatSupport = options.valueOf(this.optionSimpleVoiceChatSupport); - this.fakeAcceptResourcePacks = options.valueOf(this.optionFakeAcceptResourcePacks); + for (Map.Entry, ConfigOption> entry : optionMap.entrySet()) { + final ConfigOption option = entry.getValue(); + if (options.has(entry.getKey())) { + Object value = options.valueOf(entry.getKey()); + if (option.getTypeSerializer() != null) { + value = option.createTypeSerializer(null, ViaProxyConfig.class, this).deserialize((Class) option.getField().getType(), value); + } + if (option.getValidator() != null) { + value = option.getValidator().invoke(this, value); + } + option.getField().set(this, value); + } + } + ViaProxy.EVENT_MANAGER.call(new PostOptionsParseEvent(options)); return; } catch (OptionException e) { - this.logger.severe("Error parsing CLI options: " + e.getMessage()); + Logger.LOGGER.fatal("Error parsing CLI options: " + e.getMessage()); } catch (HelpRequestedException ignored) { } - this.optionParser.formatHelpWith(new BetterHelpFormatter()); - this.optionParser.printHelpOn(Logger.SYSOUT); - this.logger.info("For a more detailed description of the options, please refer to the viaproxy.yml file."); + optionParser.formatHelpWith(new BetterHelpFormatter()); + optionParser.printHelpOn(Logger.SYSOUT); + Logger.LOGGER.info("For a more detailed description of the options, please refer to the viaproxy.yml file."); System.exit(1); } - @Override - public URL getDefaultConfigURL() { - return this.getClass().getClassLoader().getResource("assets/viaproxy/viaproxy.yml"); - } - - @Override - protected void handleConfig(Map map) { - } - - @Override - public List getUnsupportedOptions() { - return Collections.emptyList(); - } - - @Override - public void set(String path, Object value) { - super.set(path, value); + public void save() { + try { + this.configContext.save(); + } catch (Throwable e) { + throw new RuntimeException("Failed to save config", e); + } } public SocketAddress getBindAddress() { @@ -243,7 +256,7 @@ public SocketAddress getBindAddress() { public void setBindAddress(final SocketAddress bindAddress) { this.bindAddress = bindAddress; - this.set("bind-address", AddressUtil.toString(bindAddress)); + this.save(); } public SocketAddress getTargetAddress() { @@ -252,7 +265,7 @@ public SocketAddress getTargetAddress() { public void setTargetAddress(final SocketAddress targetAddress) { this.targetAddress = targetAddress; - this.set("target-address", AddressUtil.toString(targetAddress)); + this.save(); } public ProtocolVersion getTargetVersion() { @@ -261,7 +274,7 @@ public ProtocolVersion getTargetVersion() { public void setTargetVersion(final ProtocolVersion targetVersion) { this.targetVersion = targetVersion; - this.set("target-version", targetVersion.getName()); + this.save(); } public int getConnectTimeout() { @@ -270,7 +283,7 @@ public int getConnectTimeout() { public void setConnectTimeout(final int connectTimeout) { this.connectTimeout = connectTimeout; - this.set("connect-timeout", connectTimeout); + this.save(); } public boolean isProxyOnlineMode() { @@ -279,7 +292,7 @@ public boolean isProxyOnlineMode() { public void setProxyOnlineMode(final boolean proxyOnlineMode) { this.proxyOnlineMode = proxyOnlineMode; - this.set("proxy-online-mode", proxyOnlineMode); + this.save(); } public AuthMethod getAuthMethod() { @@ -288,7 +301,7 @@ public AuthMethod getAuthMethod() { public void setAuthMethod(final AuthMethod authMethod) { this.authMethod = authMethod; - this.set("auth-method", authMethod.name().toLowerCase(Locale.ROOT)); + this.save(); } public Account getAccount() { @@ -297,7 +310,7 @@ public Account getAccount() { public void setAccount(final Account account) { this.account = account; - this.set("minecraft-account-index", ViaProxy.getSaveManager().accountsSave.getAccounts().indexOf(account)); + this.save(); } public boolean useBetacraftAuth() { @@ -306,7 +319,7 @@ public boolean useBetacraftAuth() { public void setBetacraftAuth(final boolean betacraftAuth) { this.betacraftAuth = betacraftAuth; - this.set("betacraft-auth", betacraftAuth); + this.save(); } public URI getBackendProxyUrl() { @@ -315,11 +328,7 @@ public URI getBackendProxyUrl() { public void setBackendProxyUrl(final URI backendProxyUrl) { this.backendProxyUrl = backendProxyUrl; - if (backendProxyUrl != null) { - this.set("backend-proxy-url", backendProxyUrl.toString()); - } else { - this.set("backend-proxy-url", ""); - } + this.save(); } public boolean useBackendHaProxy() { @@ -328,7 +337,7 @@ public boolean useBackendHaProxy() { public void setBackendHaProxy(final boolean backendHaProxy) { this.backendHaProxy = backendHaProxy; - this.set("backend-haproxy", backendHaProxy); + this.save(); } public boolean useFrontendHaProxy() { @@ -337,7 +346,7 @@ public boolean useFrontendHaProxy() { public void setFrontendHaProxy(final boolean frontendHaProxy) { this.frontendHaProxy = frontendHaProxy; - this.set("frontend-haproxy", frontendHaProxy); + this.save(); } public boolean shouldSignChat() { @@ -346,7 +355,7 @@ public boolean shouldSignChat() { public void setChatSigning(final boolean chatSigning) { this.chatSigning = chatSigning; - this.set("chat-signing", chatSigning); + this.save(); } public int getCompressionThreshold() { @@ -355,7 +364,7 @@ public int getCompressionThreshold() { public void setCompressionThreshold(final int compressionThreshold) { this.compressionThreshold = compressionThreshold; - this.set("compression-threshold", compressionThreshold); + this.save(); } public boolean shouldAllowBetaPinging() { @@ -364,7 +373,7 @@ public boolean shouldAllowBetaPinging() { public void setAllowBetaPinging(final boolean allowBetaPinging) { this.allowBetaPinging = allowBetaPinging; - this.set("allow-beta-pinging", allowBetaPinging); + this.save(); } public boolean shouldIgnoreProtocolTranslationErrors() { @@ -373,7 +382,7 @@ public boolean shouldIgnoreProtocolTranslationErrors() { public void setIgnoreProtocolTranslationErrors(final boolean ignoreProtocolTranslationErrors) { this.ignoreProtocolTranslationErrors = ignoreProtocolTranslationErrors; - this.set("ignore-protocol-translation-errors", ignoreProtocolTranslationErrors); + this.save(); } public boolean shouldSuppressClientProtocolErrors() { @@ -382,7 +391,7 @@ public boolean shouldSuppressClientProtocolErrors() { public void setSuppressClientProtocolErrors(final boolean suppressClientProtocolErrors) { this.suppressClientProtocolErrors = suppressClientProtocolErrors; - this.set("suppress-client-protocol-errors", suppressClientProtocolErrors); + this.save(); } public boolean shouldAllowLegacyClientPassthrough() { @@ -391,7 +400,7 @@ public boolean shouldAllowLegacyClientPassthrough() { public void setAllowLegacyClientPassthrough(final boolean allowLegacyClientPassthrough) { this.allowLegacyClientPassthrough = allowLegacyClientPassthrough; - this.set("allow-legacy-client-passthrough", allowLegacyClientPassthrough); + this.save(); } public boolean shouldPassthroughBungeecordPlayerInfo() { @@ -400,7 +409,7 @@ public boolean shouldPassthroughBungeecordPlayerInfo() { public void setPassthroughBungeecordPlayerInfo(final boolean bungeecordPlayerInfoPassthrough) { this.bungeecordPlayerInfoPassthrough = bungeecordPlayerInfoPassthrough; - this.set("bungeecord-player-info-passthrough", bungeecordPlayerInfoPassthrough); + this.save(); } public String getCustomMotd() { @@ -409,7 +418,7 @@ public String getCustomMotd() { public void setCustomMotd(final String customMotd) { this.customMotd = customMotd; - this.set("custom-motd", customMotd); + this.save(); } public String getResourcePackUrl() { @@ -418,7 +427,7 @@ public String getResourcePackUrl() { public void setResourcePackUrl(final String resourcePackUrl) { this.resourcePackUrl = resourcePackUrl; - this.set("resource-pack-url", resourcePackUrl); + this.save(); } public WildcardDomainHandling getWildcardDomainHandling() { @@ -427,7 +436,7 @@ public WildcardDomainHandling getWildcardDomainHandling() { public void setWildcardDomainHandling(final WildcardDomainHandling wildcardDomainHandling) { this.wildcardDomainHandling = wildcardDomainHandling; - this.set("wildcard-domain-handling", wildcardDomainHandling.name().toLowerCase(Locale.ROOT)); + this.save(); } public boolean shouldSupportSimpleVoiceChat() { @@ -436,7 +445,7 @@ public boolean shouldSupportSimpleVoiceChat() { public void setSimpleVoiceChatSupport(final boolean simpleVoiceChatSupport) { this.simpleVoiceChatSupport = simpleVoiceChatSupport; - this.set("simple-voice-chat-support", simpleVoiceChatSupport); + this.save(); } public boolean shouldFakeAcceptResourcePacks() { @@ -445,31 +454,21 @@ public boolean shouldFakeAcceptResourcePacks() { public void setFakeAcceptResourcePacks(final boolean fakeAcceptResourcePacks) { this.fakeAcceptResourcePacks = fakeAcceptResourcePacks; - this.set("fake-accept-resource-packs", fakeAcceptResourcePacks); + this.save(); } - private void checkTargetVersion() { - if (this.targetVersion == null) { - this.targetVersion = ProtocolTranslator.AUTO_DETECT_PROTOCOL; - this.logger.info("Invalid target version: " + this.getString("target-version", "") + ". Defaulting to auto detect."); - this.logger.info("=== Supported Protocol Versions ==="); + @Validator("target-version") + private ProtocolVersion validateTargetVersion(final ProtocolVersion targetVersion) { + if (targetVersion == null) { + Logger.LOGGER.warn("Invalid target server version. Defaulting to auto detect."); + Logger.LOGGER.warn("=== Supported Protocol Versions ==="); for (ProtocolVersion version : ProtocolVersion.getProtocols()) { - this.logger.info(version.getName()); - } - this.logger.info("==================================="); - } - } - - private URI parseProxyUrl(final String proxyUrl) { - if (!proxyUrl.isBlank()) { - try { - return new URI(proxyUrl); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid proxy url: " + proxyUrl + ". Proxy url format: type://address:port or type://username:password@address:port"); + Logger.LOGGER.warn(version.getName()); } + Logger.LOGGER.warn("==================================="); + return ProtocolTranslator.AUTO_DETECT_PROTOCOL; } - - return null; + return targetVersion; } public enum AuthMethod { @@ -493,16 +492,6 @@ public enum AuthMethod { this.guiTranslationKey = guiTranslationKey; } - public static AuthMethod byName(String name) { - for (AuthMethod mode : values()) { - if (mode.name().equalsIgnoreCase(name)) { - return mode; - } - } - - return NONE; - } - public String getGuiTranslationKey() { return this.guiTranslationKey; } @@ -522,17 +511,7 @@ public enum WildcardDomainHandling { /** * Iternal wildcard domain handling */ - INTERNAL; - - public static WildcardDomainHandling byName(String name) { - for (WildcardDomainHandling mode : values()) { - if (mode.name().equalsIgnoreCase(name)) { - return mode; - } - } - - return NONE; - } + INTERNAL, } diff --git a/src/main/java/net/raphimc/viaproxy/util/config/AccountTypeSerializer.java b/src/main/java/net/raphimc/viaproxy/util/config/AccountTypeSerializer.java new file mode 100644 index 0000000..7697f3a --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/config/AccountTypeSerializer.java @@ -0,0 +1,53 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2021-2024 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util.config; + +import net.lenni0451.optconfig.serializer.ConfigTypeSerializer; +import net.raphimc.viaproxy.ViaProxy; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; +import net.raphimc.viaproxy.saves.impl.accounts.Account; + +import java.util.List; + +public class AccountTypeSerializer extends ConfigTypeSerializer { + + public AccountTypeSerializer(final ViaProxyConfig config) { + super(config); + } + + @Override + public Account deserialize(final Class typeClass, final Object serializedObject) { + final List accounts = ViaProxy.getSaveManager().accountsSave.getAccounts(); + final int accountIndex = (int) serializedObject; + if (this.config.getAuthMethod() == ViaProxyConfig.AuthMethod.ACCOUNT && accountIndex >= 0 && accountIndex < accounts.size()) { + return accounts.get(accountIndex); + } else { + return null; + } + } + + @Override + public Object serialize(final Account object) { + if (object != null) { + return ViaProxy.getSaveManager().accountsSave.getAccounts().indexOf(object); + } else { + return 0; + } + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/util/config/ProtocolVersionTypeSerializer.java b/src/main/java/net/raphimc/viaproxy/util/config/ProtocolVersionTypeSerializer.java new file mode 100644 index 0000000..b15d68f --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/config/ProtocolVersionTypeSerializer.java @@ -0,0 +1,40 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2021-2024 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util.config; + +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import net.lenni0451.optconfig.serializer.ConfigTypeSerializer; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; + +public class ProtocolVersionTypeSerializer extends ConfigTypeSerializer { + + public ProtocolVersionTypeSerializer(final ViaProxyConfig config) { + super(config); + } + + @Override + public ProtocolVersion deserialize(final Class typeClass, final Object serializedObject) { + return ProtocolVersion.getClosest((String) serializedObject); + } + + @Override + public Object serialize(final ProtocolVersion object) { + return object.getName(); + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/util/config/ProxyUriTypeSerializer.java b/src/main/java/net/raphimc/viaproxy/util/config/ProxyUriTypeSerializer.java new file mode 100644 index 0000000..c8c944f --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/config/ProxyUriTypeSerializer.java @@ -0,0 +1,54 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2021-2024 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util.config; + +import net.lenni0451.optconfig.serializer.ConfigTypeSerializer; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; + +import java.net.URI; +import java.net.URISyntaxException; + +public class ProxyUriTypeSerializer extends ConfigTypeSerializer { + + public ProxyUriTypeSerializer(final ViaProxyConfig config) { + super(config); + } + + @Override + public URI deserialize(final Class typeClass, final Object serializedObject) { + final String proxyUrl = (String) serializedObject; + if (!proxyUrl.isBlank()) { + try { + return new URI(proxyUrl); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid proxy url: " + proxyUrl + ". Proxy url format: type://address:port or type://username:password@address:port"); + } + } + return null; + } + + @Override + public Object serialize(final URI object) { + if (object != null) { + return object.toString(); + } else { + return ""; + } + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/util/config/SocketAddressTypeSerializer.java b/src/main/java/net/raphimc/viaproxy/util/config/SocketAddressTypeSerializer.java new file mode 100644 index 0000000..2ad35e8 --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/config/SocketAddressTypeSerializer.java @@ -0,0 +1,42 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2021-2024 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util.config; + +import net.lenni0451.optconfig.serializer.ConfigTypeSerializer; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; +import net.raphimc.viaproxy.util.AddressUtil; + +import java.net.SocketAddress; + +public class SocketAddressTypeSerializer extends ConfigTypeSerializer { + + public SocketAddressTypeSerializer(final ViaProxyConfig config) { + super(config); + } + + @Override + public SocketAddress deserialize(final Class typeClass, final Object serializedObject) { + return AddressUtil.parse((String) serializedObject, null); + } + + @Override + public Object serialize(final SocketAddress object) { + return AddressUtil.toString(object); + } + +} diff --git a/src/main/java/net/raphimc/viaproxy/util/config/TargetAddressTypeSerializer.java b/src/main/java/net/raphimc/viaproxy/util/config/TargetAddressTypeSerializer.java new file mode 100644 index 0000000..f77ff12 --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/config/TargetAddressTypeSerializer.java @@ -0,0 +1,42 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2021-2024 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util.config; + +import net.lenni0451.optconfig.serializer.ConfigTypeSerializer; +import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig; +import net.raphimc.viaproxy.util.AddressUtil; + +import java.net.SocketAddress; + +public class TargetAddressTypeSerializer extends ConfigTypeSerializer { + + public TargetAddressTypeSerializer(final ViaProxyConfig config) { + super(config); + } + + @Override + public SocketAddress deserialize(final Class typeClass, final Object serializedObject) { + return AddressUtil.parse((String) serializedObject, this.config.getTargetVersion()); + } + + @Override + public Object serialize(final SocketAddress object) { + return AddressUtil.toString(object); + } + +} diff --git a/src/main/resources/assets/viaproxy/viaproxy.yml b/src/main/resources/assets/viaproxy/viaproxy.yml deleted file mode 100644 index 3f9e3ce..0000000 --- a/src/main/resources/assets/viaproxy/viaproxy.yml +++ /dev/null @@ -1,90 +0,0 @@ -# ViaProxy configuration file - -# -# The address ViaProxy should listen for connections. -bind-address: "0.0.0.0:25568" -# -# The address of the server ViaProxy should connect to. -target-address: "127.0.0.1:25565" -# -# The version ViaProxy should translate to. (See ViaProxy GUI for a list of versions) -target-version: "Auto Detect (1.7+ servers)" -# -# The connect timeout for backend server connections in milliseconds. -connect-timeout: 8000 -# -# Proxy Online Mode allows you to see skins on online mode servers and use the signed chat features. -# Enabling Proxy Online Mode requires your client to have a valid minecraft account. -proxy-online-mode: false -# -# The authentication method to use for joining the target server. -# none: No authentication (Offline mode) -# openauthmod: Requires the OpenAuthMod client mod (https://modrinth.com/mod/openauthmod) -# account: Use an account for joining the target server. (Has to be configured in ViaProxy GUI) -auth-method: "none" -# -# The GUI account list index (0 indexed) of the account if the auth method is set to account. -minecraft-account-index: 0 -# -# Use BetaCraft authentication for classic servers. -# Enabling BetaCraft Auth allows you to join classic servers which have online mode enabled. -betacraft-auth: false -# -# URL of a SOCKS(4/5)/HTTP(S) proxy which will be used for backend server connections. Leave empty to connect directly. -# Supported formats: -# - type://address:port -# - type://username:password@address:port -backend-proxy-url: "" -# -# Send HAProxy protocol messages to the target server. -backend-haproxy: false -# -# Read HAProxy protocol messages from client connections. -frontend-haproxy: false -# -# Enables sending signed chat messages on >= 1.19 servers. -chat-signing: true -# -# The threshold for packet compression. Packets larger than this size will be compressed. (-1 to disable) -compression-threshold: 256 -# -# Enabling this will allow you to ping <= b1.7.3 servers. This may cause issues with servers that block too frequent connections. -allow-beta-pinging: false -# -# Enabling this will prevent getting disconnected from the server when a packet translation error occurs and instead only print the error in the console. -# This may cause issues depending on the type of packet which failed to translate. -ignore-protocol-translation-errors: false -# -# Enabling this will suppress client protocol errors to prevent lag when ViaProxy is getting spammed with invalid packets. -# This may cause issues with debugging client connection issues because no error messages will be printed. -suppress-client-protocol-errors: false -# -# Allow <= 1.6.4 clients to connect through ViaProxy to the target server. (No protocol translation or packet handling) -allow-legacy-client-passthrough: false -# -# Allow additional information like player ip, player uuid to be passed through to the backend server. -# This is typically used by proxies like BungeeCord and requires support from the backend server as well. -bungeecord-player-info-passthrough: false -# -# Custom MOTD to send when clients ping the proxy. Leave empty to use the target server's MOTD. -custom-motd: "" -# -# URL of a resource pack which clients can optionally download when connecting to the server. Leave empty to disable. -# Example: http://example.com/resourcepack.zip -resource-pack-url: "" -# -# Allows clients to specify a target server and version using wildcard domains. -# none: No wildcard domain handling. -# public: Public wildcard domain handling. Intended for usage by external clients. (Example: address_port_version.viaproxy.127.0.0.1.nip.io) -# internal: Internal wildcard domain handling. Intended for local usage by custom clients. (Example: original-handshake-address\7address:port\7version\7classic-mppass) -wildcard-domain-handling: "none" -# -# Enables handling and rewriting of Simple Voice Chat mod packets. -simple-voice-chat-support: false -# -# Accepts resource packs from the server without showing a prompt to the client. -# This is required for servers that require a resource pack, but the client can't load it due to version differences. -fake-accept-resource-packs: false -# -# Configuration version. Do not change this. -config-version: 1