Compare commits
4 Commits
f5c330168f
...
c0d6edc99d
Author | SHA1 | Date |
---|---|---|
Lenni0451 | c0d6edc99d | |
RaphiMC | eb1f66763e | |
RaphiMC | 97d15f9c5d | |
RaphiMC | 044e1d65a1 |
|
@ -26,6 +26,7 @@ ViaProxy supports joining to any of the listed server version from any of the li
|
|||
- Support for joining on servers which have chat signing enabled from all listed client versions
|
||||
- Supports transfer and cookies for <=1.20.4 clients on 1.20.5+ servers
|
||||
- Allows joining Minecraft Realms with any supported client version
|
||||
- Supports Simple Voice Chat mod
|
||||
|
||||
## Releases
|
||||
### Executable Jar File
|
||||
|
|
|
@ -67,6 +67,7 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
private final OptionSpec<String> optionCustomMotd;
|
||||
private final OptionSpec<String> optionResourcePackUrl;
|
||||
private final OptionSpec<WildcardDomainHandling> optionWildcardDomainHandling;
|
||||
private final OptionSpec<Boolean> optionSimpleVoiceChatSupport;
|
||||
|
||||
private SocketAddress bindAddress = AddressUtil.parse("0.0.0.0:25568", null);
|
||||
private SocketAddress targetAddress = AddressUtil.parse("127.0.0.1:25565", null);
|
||||
|
@ -86,6 +87,7 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
private String customMotd = "";
|
||||
private String resourcePackUrl = "";
|
||||
private WildcardDomainHandling wildcardDomainHandling = WildcardDomainHandling.NONE;
|
||||
private boolean simpleVoiceChatSupport = false;
|
||||
|
||||
public ViaProxyConfig(final File configFile) {
|
||||
super(configFile);
|
||||
|
@ -110,6 +112,7 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -141,6 +144,7 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
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);
|
||||
}
|
||||
|
||||
public void loadFromArguments(final String[] args) throws IOException {
|
||||
|
@ -176,6 +180,7 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
this.customMotd = options.valueOf(this.optionCustomMotd);
|
||||
this.resourcePackUrl = options.valueOf(this.optionResourcePackUrl);
|
||||
this.wildcardDomainHandling = options.valueOf(this.optionWildcardDomainHandling);
|
||||
this.simpleVoiceChatSupport = options.valueOf(this.optionSimpleVoiceChatSupport);
|
||||
ViaProxy.EVENT_MANAGER.call(new PostOptionsParseEvent(options));
|
||||
return;
|
||||
} catch (OptionException e) {
|
||||
|
@ -374,6 +379,15 @@ public class ViaProxyConfig extends Config implements com.viaversion.viaversion.
|
|||
this.set("wildcard-domain-handling", wildcardDomainHandling.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
public boolean shouldSupportSimpleVoiceChat() {
|
||||
return this.simpleVoiceChatSupport;
|
||||
}
|
||||
|
||||
public void setSimpleVoiceChatSupport(final boolean simpleVoiceChatSupport) {
|
||||
this.simpleVoiceChatSupport = simpleVoiceChatSupport;
|
||||
this.set("simple-voice-chat-support", simpleVoiceChatSupport);
|
||||
}
|
||||
|
||||
private void checkTargetVersion() {
|
||||
if (this.targetVersion == null) {
|
||||
this.targetVersion = ProtocolTranslator.AUTO_DETECT_PROTOCOL;
|
||||
|
|
|
@ -216,6 +216,9 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler<IPacket> {
|
|||
this.proxyConnection.setUserOptions(userOptions);
|
||||
this.proxyConnection.getPacketHandlers().add(new StatusPacketHandler(this.proxyConnection));
|
||||
this.proxyConnection.getPacketHandlers().add(new OpenAuthModPacketHandler(this.proxyConnection));
|
||||
if (ViaProxy.getConfig().shouldSupportSimpleVoiceChat() && serverVersion.newerThan(ProtocolVersion.v1_14) && clientVersion.newerThan(ProtocolVersion.v1_14)) {
|
||||
this.proxyConnection.getPacketHandlers().add(new SimpleVoiceChatPacketHandler(this.proxyConnection));
|
||||
}
|
||||
if (clientVersion.newerThanOrEqualTo(ProtocolVersion.v1_8)) {
|
||||
this.proxyConnection.getPacketHandlers().add(new BrandCustomPayloadPacketHandler(this.proxyConnection));
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ import net.raphimc.viabedrock.protocol.storage.AuthChainData;
|
|||
import net.raphimc.viaproxy.ViaProxy;
|
||||
import net.raphimc.viaproxy.plugins.events.FillPlayerDataEvent;
|
||||
import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig;
|
||||
import net.raphimc.viaproxy.proxy.packethandler.OpenAuthModPacketHandler;
|
||||
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
||||
import net.raphimc.viaproxy.saves.impl.accounts.Account;
|
||||
import net.raphimc.viaproxy.saves.impl.accounts.BedrockAccount;
|
||||
|
@ -109,7 +110,7 @@ public class ExternalInterface {
|
|||
Logger.u_info("auth", proxyConnection.getC2P().remoteAddress(), proxyConnection.getGameProfile(), "Trying to join online mode server");
|
||||
if (ViaProxy.getConfig().getAuthMethod() == ViaProxyConfig.AuthMethod.OPENAUTHMOD) {
|
||||
try {
|
||||
final ByteBuf response = proxyConnection.sendCustomPayload(OpenAuthModConstants.JOIN_CHANNEL, PacketTypes.writeString(Unpooled.buffer(), serverIdHash)).get(6, TimeUnit.SECONDS);
|
||||
final ByteBuf response = proxyConnection.getPacketHandler(OpenAuthModPacketHandler.class).sendCustomPayload(OpenAuthModConstants.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) {
|
||||
|
@ -132,7 +133,7 @@ public class ExternalInterface {
|
|||
|
||||
if (ViaProxy.getConfig().getAuthMethod() == ViaProxyConfig.AuthMethod.OPENAUTHMOD) {
|
||||
try {
|
||||
final ByteBuf response = proxyConnection.sendCustomPayload(OpenAuthModConstants.SIGN_NONCE_CHANNEL, PacketTypes.writeByteArray(Unpooled.buffer(), nonce)).get(5, TimeUnit.SECONDS);
|
||||
final ByteBuf response = proxyConnection.getPacketHandler(OpenAuthModPacketHandler.class).sendCustomPayload(OpenAuthModConstants.SIGN_NONCE_CHANNEL, PacketTypes.writeByteArray(Unpooled.buffer(), nonce)).get(5, TimeUnit.SECONDS);
|
||||
if (response == null) throw new TimeoutException();
|
||||
if (!response.readBoolean()) throw new TimeoutException();
|
||||
packet.salt = response.readLong();
|
||||
|
|
|
@ -20,62 +20,44 @@ package net.raphimc.viaproxy.proxy.packethandler;
|
|||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.raphimc.netminecraft.constants.ConnectionState;
|
||||
import net.raphimc.netminecraft.constants.MCPackets;
|
||||
import net.raphimc.netminecraft.packet.IPacket;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.UnknownPacket;
|
||||
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BrandCustomPayloadPacketHandler extends PacketHandler {
|
||||
public class BrandCustomPayloadPacketHandler extends CustomPayloadPacketHandler {
|
||||
|
||||
private static final String BRAND_CHANNEL = "minecraft:brand";
|
||||
private static final String LEGACY_BRAND_CHANNEL = "MC|Brand";
|
||||
|
||||
private final int customPayloadId;
|
||||
private final int configCustomPayloadId;
|
||||
|
||||
public BrandCustomPayloadPacketHandler(ProxyConnection proxyConnection) {
|
||||
super(proxyConnection);
|
||||
|
||||
this.customPayloadId = MCPackets.S2C_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.configCustomPayloadId = MCPackets.S2C_CONFIG_CUSTOM_PAYLOAD.getId(proxyConnection.getClientVersion().getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleP2S(IPacket packet, List<ChannelFutureListener> listeners) {
|
||||
if (packet instanceof UnknownPacket unknownPacket
|
||||
&& (unknownPacket.packetId == this.customPayloadId && this.proxyConnection.getP2sConnectionState() == ConnectionState.PLAY
|
||||
|| unknownPacket.packetId == this.configCustomPayloadId && this.proxyConnection.getP2sConnectionState() == ConnectionState.CONFIGURATION)) {
|
||||
final ByteBuf data = Unpooled.wrappedBuffer(unknownPacket.data);
|
||||
final String channel = PacketTypes.readString(data, Short.MAX_VALUE); // channel
|
||||
if (Key.namespaced(channel).equals(BRAND_CHANNEL) || channel.equals(LEGACY_BRAND_CHANNEL)) {
|
||||
|
||||
String brand;
|
||||
try {
|
||||
brand = PacketTypes.readString(data, Short.MAX_VALUE);
|
||||
} catch (Exception e) {
|
||||
if (this.proxyConnection.getServerVersion().newerThan(ProtocolVersion.v1_20)) {
|
||||
throw e;
|
||||
} else { // <=1.20 clients ignore errors
|
||||
brand = "Unknown";
|
||||
}
|
||||
public ByteBuf handleP2S(UnknownPacket packet, String channel, ByteBuf data, List<ChannelFutureListener> listeners) throws Exception {
|
||||
if (Key.namespaced(channel).equals(BRAND_CHANNEL) || channel.equals(LEGACY_BRAND_CHANNEL)) {
|
||||
String brand;
|
||||
try {
|
||||
brand = PacketTypes.readString(data, Short.MAX_VALUE);
|
||||
} catch (Exception e) {
|
||||
if (this.proxyConnection.getServerVersion().newerThan(ProtocolVersion.v1_20)) {
|
||||
throw e;
|
||||
} else { // <=1.20 clients ignore errors
|
||||
brand = "Unknown";
|
||||
}
|
||||
final String newBrand = "ViaProxy (" + this.proxyConnection.getClientVersion().getName() + ") -> " + brand + " §r(" + this.proxyConnection.getServerVersion().getName() + ")";
|
||||
|
||||
final ByteBuf newCustomPayloadData = Unpooled.buffer();
|
||||
PacketTypes.writeString(newCustomPayloadData, channel); // channel
|
||||
PacketTypes.writeString(newCustomPayloadData, newBrand); // data
|
||||
unknownPacket.data = ByteBufUtil.getBytes(newCustomPayloadData);
|
||||
}
|
||||
final String newBrand = "ViaProxy (" + this.proxyConnection.getClientVersion().getName() + ") -> " + brand + " §r(" + this.proxyConnection.getServerVersion().getName() + ")";
|
||||
|
||||
final ByteBuf newData = Unpooled.buffer();
|
||||
PacketTypes.writeString(newData, newBrand);
|
||||
return newData;
|
||||
}
|
||||
|
||||
return true;
|
||||
return super.handleP2S(packet, channel, data, listeners);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.raphimc.viaproxy.proxy.packethandler;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.raphimc.netminecraft.constants.ConnectionState;
|
||||
import net.raphimc.netminecraft.constants.MCPackets;
|
||||
import net.raphimc.netminecraft.packet.IPacket;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.UnknownPacket;
|
||||
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class CustomPayloadPacketHandler extends PacketHandler {
|
||||
|
||||
private final int s2cCustomPayloadId;
|
||||
private final int s2cConfigCustomPayloadId;
|
||||
private final int c2sCustomPayloadId;
|
||||
private final int c2sConfigCustomPayloadId;
|
||||
|
||||
public CustomPayloadPacketHandler(ProxyConnection proxyConnection) {
|
||||
super(proxyConnection);
|
||||
|
||||
this.s2cCustomPayloadId = MCPackets.S2C_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.s2cConfigCustomPayloadId = MCPackets.S2C_CONFIG_CUSTOM_PAYLOAD.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.c2sCustomPayloadId = MCPackets.C2S_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.c2sConfigCustomPayloadId = MCPackets.C2S_CONFIG_CUSTOM_PAYLOAD.getId(proxyConnection.getClientVersion().getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleP2S(IPacket packet, List<ChannelFutureListener> listeners) throws Exception {
|
||||
if (packet instanceof UnknownPacket unknownPacket
|
||||
&& (unknownPacket.packetId == this.s2cCustomPayloadId && this.proxyConnection.getP2sConnectionState() == ConnectionState.PLAY
|
||||
|| unknownPacket.packetId == this.s2cConfigCustomPayloadId && this.proxyConnection.getP2sConnectionState() == ConnectionState.CONFIGURATION)) {
|
||||
final ByteBuf data = Unpooled.wrappedBuffer(unknownPacket.data);
|
||||
final String channel = PacketTypes.readString(data, Short.MAX_VALUE); // channel
|
||||
final ByteBuf newData = this.handleP2S(unknownPacket, channel, data, listeners);
|
||||
if (newData == data) {
|
||||
return true;
|
||||
} else if (newData != null) {
|
||||
final ByteBuf newCustomPayloadData = Unpooled.buffer();
|
||||
PacketTypes.writeString(newCustomPayloadData, channel); // channel
|
||||
newCustomPayloadData.writeBytes(newData);
|
||||
unknownPacket.data = ByteBufUtil.getBytes(newCustomPayloadData);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleP2S(packet, listeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleC2P(IPacket packet, List<ChannelFutureListener> listeners) throws Exception {
|
||||
if (packet instanceof UnknownPacket unknownPacket
|
||||
&& (unknownPacket.packetId == this.c2sCustomPayloadId && this.proxyConnection.getC2pConnectionState() == ConnectionState.PLAY
|
||||
|| unknownPacket.packetId == this.c2sConfigCustomPayloadId && this.proxyConnection.getC2pConnectionState() == ConnectionState.CONFIGURATION)) {
|
||||
final ByteBuf data = Unpooled.wrappedBuffer(unknownPacket.data);
|
||||
final String channel = PacketTypes.readString(data, Short.MAX_VALUE); // channel
|
||||
final ByteBuf newData = this.handleC2P(unknownPacket, channel, data, listeners);
|
||||
if (newData == data) {
|
||||
return true;
|
||||
} else if (newData != null) {
|
||||
final ByteBuf newCustomPayloadData = Unpooled.buffer();
|
||||
PacketTypes.writeString(newCustomPayloadData, channel); // channel
|
||||
newCustomPayloadData.writeBytes(newData);
|
||||
unknownPacket.data = ByteBufUtil.getBytes(newCustomPayloadData);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleC2P(packet, listeners);
|
||||
}
|
||||
|
||||
public ByteBuf handleC2P(final UnknownPacket packet, final String channel, final ByteBuf data, final List<ChannelFutureListener> listeners) throws Exception {
|
||||
return data;
|
||||
}
|
||||
|
||||
public ByteBuf handleP2S(final UnknownPacket packet, final String channel, final ByteBuf data, final List<ChannelFutureListener> listeners) throws Exception {
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
|
@ -19,8 +19,10 @@ package net.raphimc.viaproxy.proxy.packethandler;
|
|||
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.lenni0451.mcstructs.text.components.StringComponent;
|
||||
import net.raphimc.netminecraft.constants.ConnectionState;
|
||||
import net.raphimc.netminecraft.constants.MCPackets;
|
||||
import net.raphimc.netminecraft.packet.IPacket;
|
||||
|
@ -28,40 +30,51 @@ import net.raphimc.netminecraft.packet.PacketTypes;
|
|||
import net.raphimc.netminecraft.packet.UnknownPacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.C2SLoginCustomPayloadPacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.C2SLoginKeyPacket1_7;
|
||||
import net.raphimc.netminecraft.packet.impl.login.S2CLoginCustomPayloadPacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.S2CLoginDisconnectPacket1_20_3;
|
||||
import net.raphimc.viaproxy.proxy.external_interface.OpenAuthModConstants;
|
||||
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class OpenAuthModPacketHandler extends PacketHandler {
|
||||
|
||||
private final int customPayloadId;
|
||||
private final int c2sCustomPayloadId;
|
||||
private final int s2cCustomPayloadId;
|
||||
private final AtomicInteger id = new AtomicInteger(0);
|
||||
private final Map<Integer, CompletableFuture<ByteBuf>> customPayloadListener = new ConcurrentHashMap<>();
|
||||
|
||||
public OpenAuthModPacketHandler(ProxyConnection proxyConnection) {
|
||||
super(proxyConnection);
|
||||
|
||||
this.customPayloadId = MCPackets.C2S_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.c2sCustomPayloadId = MCPackets.C2S_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
this.s2cCustomPayloadId = MCPackets.S2C_PLUGIN_MESSAGE.getId(proxyConnection.getClientVersion().getVersion());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleC2P(IPacket packet, List<ChannelFutureListener> listeners) {
|
||||
if (packet instanceof UnknownPacket unknownPacket && this.proxyConnection.getC2pConnectionState() == ConnectionState.PLAY) {
|
||||
if (unknownPacket.packetId == this.customPayloadId) {
|
||||
if (unknownPacket.packetId == this.c2sCustomPayloadId) {
|
||||
final ByteBuf data = Unpooled.wrappedBuffer(unknownPacket.data);
|
||||
final String channel = PacketTypes.readString(data, Short.MAX_VALUE); // channel
|
||||
if (channel.equals(OpenAuthModConstants.DATA_CHANNEL) && this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(data), data)) {
|
||||
if (channel.equals(OpenAuthModConstants.DATA_CHANNEL) && this.handleCustomPayload(PacketTypes.readVarInt(data), data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (packet instanceof C2SLoginCustomPayloadPacket loginCustomPayload) {
|
||||
if (loginCustomPayload.response != null && this.proxyConnection.handleCustomPayload(loginCustomPayload.queryId, Unpooled.wrappedBuffer(loginCustomPayload.response))) {
|
||||
if (loginCustomPayload.response != null && this.handleCustomPayload(loginCustomPayload.queryId, Unpooled.wrappedBuffer(loginCustomPayload.response))) {
|
||||
return false;
|
||||
}
|
||||
} else if (packet instanceof C2SLoginKeyPacket1_7 loginKeyPacket) {
|
||||
if (this.proxyConnection.getClientVersion().olderThanOrEqualTo(ProtocolVersion.v1_12_2) && new String(loginKeyPacket.encryptedNonce, StandardCharsets.UTF_8).equals(OpenAuthModConstants.DATA_CHANNEL)) { // 1.8-1.12.2 OpenAuthMod response handling
|
||||
final ByteBuf byteBuf = Unpooled.wrappedBuffer(loginKeyPacket.encryptedSecretKey);
|
||||
this.proxyConnection.handleCustomPayload(PacketTypes.readVarInt(byteBuf), byteBuf);
|
||||
this.handleCustomPayload(PacketTypes.readVarInt(byteBuf), byteBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -69,4 +82,45 @@ public class OpenAuthModPacketHandler extends PacketHandler {
|
|||
return true;
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> sendCustomPayload(final String channel, final ByteBuf data) {
|
||||
if (channel.length() > 20) throw new IllegalStateException("Channel name can't be longer than 20 characters");
|
||||
final CompletableFuture<ByteBuf> future = new CompletableFuture<>();
|
||||
final int id = this.id.getAndIncrement();
|
||||
|
||||
switch (this.proxyConnection.getC2pConnectionState()) {
|
||||
case LOGIN:
|
||||
if (this.proxyConnection.getClientVersion().newerThanOrEqualTo(ProtocolVersion.v1_13)) {
|
||||
this.proxyConnection.getC2P().writeAndFlush(new S2CLoginCustomPayloadPacket(id, channel, PacketTypes.readReadableBytes(data))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
} else {
|
||||
final ByteBuf disconnectPacketData = Unpooled.buffer();
|
||||
PacketTypes.writeString(disconnectPacketData, channel);
|
||||
PacketTypes.writeVarInt(disconnectPacketData, id);
|
||||
disconnectPacketData.writeBytes(data);
|
||||
this.proxyConnection.getC2P().writeAndFlush(new S2CLoginDisconnectPacket1_20_3(new StringComponent("§cYou need to install OpenAuthMod in order to join this server.§k\n" + Base64.getEncoder().encodeToString(ByteBufUtil.getBytes(disconnectPacketData)) + "\n" + OpenAuthModConstants.LEGACY_MAGIC_STRING))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
break;
|
||||
case PLAY:
|
||||
final ByteBuf customPayloadPacket = Unpooled.buffer();
|
||||
PacketTypes.writeVarInt(customPayloadPacket, this.s2cCustomPayloadId);
|
||||
PacketTypes.writeString(customPayloadPacket, channel); // channel
|
||||
PacketTypes.writeVarInt(customPayloadPacket, id);
|
||||
customPayloadPacket.writeBytes(data);
|
||||
this.proxyConnection.getC2P().writeAndFlush(customPayloadPacket).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can't send a custom payload packet during " + this.proxyConnection.getC2pConnectionState());
|
||||
}
|
||||
|
||||
this.customPayloadListener.put(id, future);
|
||||
return future;
|
||||
}
|
||||
|
||||
private boolean handleCustomPayload(final int id, final ByteBuf data) {
|
||||
if (this.customPayloadListener.containsKey(id)) {
|
||||
this.customPayloadListener.remove(id).complete(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.raphimc.viaproxy.proxy.packethandler;
|
||||
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.UnknownPacket;
|
||||
import net.raphimc.viaproxy.proxy.session.ProxyConnection;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
public class SimpleVoiceChatPacketHandler extends CustomPayloadPacketHandler {
|
||||
|
||||
private static final String SECRET_CHANNEL = "voicechat:secret";
|
||||
|
||||
public SimpleVoiceChatPacketHandler(ProxyConnection proxyConnection) {
|
||||
super(proxyConnection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuf handleP2S(UnknownPacket packet, String channel, ByteBuf data, List<ChannelFutureListener> listeners) throws Exception {
|
||||
if (Key.namespaced(channel).equals(SECRET_CHANNEL)) {
|
||||
try {
|
||||
final ByteBuf newData = Unpooled.buffer();
|
||||
PacketTypes.writeUuid(newData, PacketTypes.readUuid(data)); // secret
|
||||
final int port = data.readInt(); // port
|
||||
newData.writeInt(port);
|
||||
PacketTypes.writeUuid(newData, PacketTypes.readUuid(data)); // player uuid
|
||||
newData.writeByte(data.readByte()); // codec
|
||||
newData.writeInt(data.readInt()); // mtu size
|
||||
newData.writeDouble(data.readDouble()); // voice chat distance
|
||||
newData.writeInt(data.readInt()); // keep alive
|
||||
newData.writeBoolean(data.readBoolean()); // groups enabled
|
||||
final String voiceHost = PacketTypes.readString(data, Short.MAX_VALUE); // voice host
|
||||
if (voiceHost.isEmpty()) {
|
||||
if (this.proxyConnection.getServerAddress() instanceof InetSocketAddress serverAddress) {
|
||||
PacketTypes.writeString(newData, new InetSocketAddress(serverAddress.getAddress(), port).toString());
|
||||
} else {
|
||||
throw new IllegalArgumentException("Server address must be an InetSocketAddress");
|
||||
}
|
||||
} else {
|
||||
PacketTypes.writeString(newData, voiceHost);
|
||||
}
|
||||
newData.writeBytes(data);
|
||||
return newData;
|
||||
} catch (Throwable e) {
|
||||
Logger.LOGGER.error("Failed to handle simple voice chat packet", e);
|
||||
return super.handleP2S(packet, channel, data, listeners);
|
||||
}
|
||||
}
|
||||
|
||||
return super.handleP2S(packet, channel, data, listeners);
|
||||
}
|
||||
|
||||
}
|
|
@ -21,7 +21,6 @@ import com.mojang.authlib.GameProfile;
|
|||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import net.raphimc.netminecraft.constants.ConnectionState;
|
||||
|
@ -30,7 +29,6 @@ import net.raphimc.netminecraft.util.ChannelType;
|
|||
|
||||
import java.net.SocketAddress;
|
||||
import java.security.Key;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class DummyProxyConnection extends ProxyConnection {
|
||||
|
||||
|
@ -118,16 +116,6 @@ public class DummyProxyConnection extends ProxyConnection {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<ByteBuf> sendCustomPayload(String channel, ByteBuf data) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleCustomPayload(int id, ByteBuf data) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isClosed() {
|
||||
return false;
|
||||
|
|
|
@ -24,7 +24,6 @@ import com.viaversion.viaversion.libs.gson.JsonObject;
|
|||
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.util.AttributeKey;
|
||||
|
@ -39,13 +38,11 @@ import net.raphimc.netminecraft.netty.connection.NetClient;
|
|||
import net.raphimc.netminecraft.netty.crypto.AESEncryption;
|
||||
import net.raphimc.netminecraft.packet.PacketTypes;
|
||||
import net.raphimc.netminecraft.packet.impl.login.C2SLoginHelloPacket1_7;
|
||||
import net.raphimc.netminecraft.packet.impl.login.S2CLoginCustomPayloadPacket;
|
||||
import net.raphimc.netminecraft.packet.impl.login.S2CLoginDisconnectPacket1_20_3;
|
||||
import net.raphimc.netminecraft.packet.impl.status.S2CStatusResponsePacket;
|
||||
import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil;
|
||||
import net.raphimc.netminecraft.util.ChannelType;
|
||||
import net.raphimc.viaproxy.cli.ConsoleFormatter;
|
||||
import net.raphimc.viaproxy.proxy.external_interface.OpenAuthModConstants;
|
||||
import net.raphimc.viaproxy.proxy.packethandler.PacketHandler;
|
||||
import net.raphimc.viaproxy.proxy.util.CloseAndReturn;
|
||||
import net.raphimc.viaproxy.util.logging.Logger;
|
||||
|
@ -54,12 +51,7 @@ import java.net.SocketAddress;
|
|||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -70,9 +62,6 @@ public class ProxyConnection extends NetClient {
|
|||
private final Channel c2p;
|
||||
private final List<PacketHandler> packetHandlers = new ArrayList<>();
|
||||
|
||||
private final AtomicInteger customPayloadId = new AtomicInteger(0);
|
||||
private final Map<Integer, CompletableFuture<ByteBuf>> customPayloadListener = new ConcurrentHashMap<>();
|
||||
|
||||
private SocketAddress serverAddress;
|
||||
|
||||
private ProtocolVersion serverVersion;
|
||||
|
@ -128,6 +117,15 @@ public class ProxyConnection extends NetClient {
|
|||
return this.packetHandlers;
|
||||
}
|
||||
|
||||
public <T> T getPacketHandler(final Class<T> packetHandlerType) {
|
||||
for (final PacketHandler packetHandler : this.packetHandlers) {
|
||||
if (packetHandlerType.isInstance(packetHandler)) {
|
||||
return packetHandlerType.cast(packetHandler);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public SocketAddress getServerAddress() {
|
||||
return this.serverAddress;
|
||||
}
|
||||
|
@ -239,47 +237,6 @@ public class ProxyConnection extends NetClient {
|
|||
}
|
||||
}
|
||||
|
||||
public CompletableFuture<ByteBuf> sendCustomPayload(final String channel, final ByteBuf data) {
|
||||
if (channel.length() > 20) throw new IllegalStateException("Channel name can't be longer than 20 characters");
|
||||
final CompletableFuture<ByteBuf> future = new CompletableFuture<>();
|
||||
final int id = this.customPayloadId.getAndIncrement();
|
||||
|
||||
switch (this.c2pConnectionState) {
|
||||
case LOGIN:
|
||||
if (this.clientVersion.newerThanOrEqualTo(ProtocolVersion.v1_13)) {
|
||||
this.c2p.writeAndFlush(new S2CLoginCustomPayloadPacket(id, channel, PacketTypes.readReadableBytes(data))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
} else {
|
||||
final ByteBuf disconnectPacketData = Unpooled.buffer();
|
||||
PacketTypes.writeString(disconnectPacketData, channel);
|
||||
PacketTypes.writeVarInt(disconnectPacketData, id);
|
||||
disconnectPacketData.writeBytes(data);
|
||||
this.c2p.writeAndFlush(new S2CLoginDisconnectPacket1_20_3(new StringComponent("§cYou need to install OpenAuthMod in order to join this server.§k\n" + Base64.getEncoder().encodeToString(ByteBufUtil.getBytes(disconnectPacketData)) + "\n" + OpenAuthModConstants.LEGACY_MAGIC_STRING))).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
}
|
||||
break;
|
||||
case PLAY:
|
||||
final ByteBuf customPayloadPacket = Unpooled.buffer();
|
||||
PacketTypes.writeVarInt(customPayloadPacket, MCPackets.S2C_PLUGIN_MESSAGE.getId(this.clientVersion.getVersion()));
|
||||
PacketTypes.writeString(customPayloadPacket, channel); // channel
|
||||
PacketTypes.writeVarInt(customPayloadPacket, id);
|
||||
customPayloadPacket.writeBytes(data);
|
||||
this.c2p.writeAndFlush(customPayloadPacket).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Can't send a custom payload packet during " + this.c2pConnectionState);
|
||||
}
|
||||
|
||||
this.customPayloadListener.put(id, future);
|
||||
return future;
|
||||
}
|
||||
|
||||
public boolean handleCustomPayload(final int id, final ByteBuf data) {
|
||||
if (this.customPayloadListener.containsKey(id)) {
|
||||
this.customPayloadListener.remove(id).complete(data);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void kickClient(final String message) throws CloseAndReturn {
|
||||
Logger.u_err("proxy kick", this.c2p.remoteAddress(), this.getGameProfile(), ConsoleFormatter.convert(message));
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ public class AdvancedTab extends UITab {
|
|||
JCheckBox chatSigning;
|
||||
JCheckBox ignorePacketTranslationErrors;
|
||||
JCheckBox allowBetaPinging;
|
||||
JCheckBox simpleVoiceChatSupport;
|
||||
JButton viaVersionDumpButton;
|
||||
JButton uploadLogsButton;
|
||||
|
||||
|
@ -68,6 +69,9 @@ public class AdvancedTab extends UITab {
|
|||
JPanel body = new JPanel();
|
||||
body.setLayout(new GridBagLayout());
|
||||
|
||||
JPanel checkboxes = new JPanel();
|
||||
checkboxes.setLayout(new GridLayout(0, 2, BORDER_PADDING, BORDER_PADDING));
|
||||
|
||||
int gridy = 0;
|
||||
{
|
||||
JLabel bindPortLabel = new JLabel(I18n.get("tab.advanced.bind_address.label"));
|
||||
|
@ -94,34 +98,42 @@ public class AdvancedTab extends UITab {
|
|||
this.proxyOnlineMode = new JCheckBox(I18n.get("tab.advanced.proxy_online_mode.label"));
|
||||
this.proxyOnlineMode.setToolTipText(I18n.get("tab.advanced.proxy_online_mode.tooltip"));
|
||||
this.proxyOnlineMode.setSelected(ViaProxy.getConfig().isProxyOnlineMode());
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, 0).anchor(GBC.NORTHWEST).add(this.proxyOnlineMode);
|
||||
checkboxes.add(this.proxyOnlineMode);
|
||||
}
|
||||
{
|
||||
this.legacySkinLoading = new JCheckBox(I18n.get("tab.advanced.legacy_skin_loading.label"));
|
||||
this.legacySkinLoading.setToolTipText(I18n.get("tab.advanced.legacy_skin_loading.tooltip"));
|
||||
ViaProxy.getSaveManager().uiSave.loadCheckBox("legacy_skin_loading", this.legacySkinLoading);
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, 0).anchor(GBC.NORTHWEST).add(this.legacySkinLoading);
|
||||
checkboxes.add(this.legacySkinLoading);
|
||||
}
|
||||
{
|
||||
this.chatSigning = new JCheckBox(I18n.get("tab.advanced.chat_signing.label"));
|
||||
this.chatSigning.setToolTipText(I18n.get("tab.advanced.chat_signing.tooltip"));
|
||||
this.chatSigning.setSelected(ViaProxy.getConfig().shouldSignChat());
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, 0).anchor(GBC.NORTHWEST).add(this.chatSigning);
|
||||
checkboxes.add(this.chatSigning);
|
||||
}
|
||||
{
|
||||
this.ignorePacketTranslationErrors = new JCheckBox(I18n.get("tab.advanced.ignore_packet_translation_errors.label"));
|
||||
this.ignorePacketTranslationErrors.setToolTipText(I18n.get("tab.advanced.ignore_packet_translation_errors.tooltip"));
|
||||
this.ignorePacketTranslationErrors.setSelected(false);
|
||||
this.ignorePacketTranslationErrors.setSelected(ViaProxy.getConfig().shouldIgnoreProtocolTranslationErrors());
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, 0).anchor(GBC.NORTHWEST).add(this.ignorePacketTranslationErrors);
|
||||
checkboxes.add(this.ignorePacketTranslationErrors);
|
||||
}
|
||||
{
|
||||
this.allowBetaPinging = new JCheckBox(I18n.get("tab.advanced.allow_beta_pinging.label"));
|
||||
this.allowBetaPinging.setToolTipText(I18n.get("tab.advanced.allow_beta_pinging.tooltip"));
|
||||
this.allowBetaPinging.setSelected(false);
|
||||
this.allowBetaPinging.setSelected(ViaProxy.getConfig().shouldAllowBetaPinging());
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, 0).anchor(GBC.NORTHWEST).add(this.allowBetaPinging);
|
||||
checkboxes.add(this.allowBetaPinging);
|
||||
}
|
||||
{
|
||||
this.simpleVoiceChatSupport = new JCheckBox(I18n.get("tab.advanced.simple_voice_chat_support.label"));
|
||||
this.simpleVoiceChatSupport.setToolTipText(I18n.get("tab.advanced.simple_voice_chat_support.tooltip"));
|
||||
this.simpleVoiceChatSupport.setSelected(false);
|
||||
this.simpleVoiceChatSupport.setSelected(ViaProxy.getConfig().shouldSupportSimpleVoiceChat());
|
||||
checkboxes.add(this.simpleVoiceChatSupport);
|
||||
}
|
||||
GBC.create(body).grid(0, gridy++).insets(BODY_BLOCK_PADDING, BORDER_PADDING, 0, BODY_BLOCK_PADDING).fill(GBC.BOTH).weight(1, 1).add(checkboxes);
|
||||
|
||||
parent.add(body, BorderLayout.NORTH);
|
||||
}
|
||||
|
@ -197,6 +209,7 @@ public class AdvancedTab extends UITab {
|
|||
ViaProxy.getConfig().setChatSigning(this.chatSigning.isSelected());
|
||||
ViaProxy.getConfig().setIgnoreProtocolTranslationErrors(this.ignorePacketTranslationErrors.isSelected());
|
||||
ViaProxy.getConfig().setAllowBetaPinging(this.allowBetaPinging.isSelected());
|
||||
ViaProxy.getConfig().setSimpleVoiceChatSupport(this.simpleVoiceChatSupport.isSelected());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ tab.advanced.ignore_packet_translation_errors.label=Datenpaket Übersetzungsfehl
|
|||
tab.advanced.ignore_packet_translation_errors.tooltip=Wenn du diese Option aktivierst, wirst du nicht mehr vom Server gekickt, wenn es einen Datenpaket Übersetzungsfehler gibt, sondern der Fehler wird nur in der Konsole angezeigt.\nDiese Option könnte abhängig vom Datenpaket, welches nicht übersetzt werden konnte, Folgeprobleme verursachen.
|
||||
tab.advanced.allow_beta_pinging.label=Aktiviere Pings für <= b1.7.3
|
||||
tab.advanced.allow_beta_pinging.tooltip=Aktiviert das Pingen für <= b1.7.3 Server. Das könnte zu Problemen bei Servern, welche zu schnelle Verbindungen blockieren führen.
|
||||
tab.advanced.simple_voice_chat_support.label=Simple Voice Chat Unterstützung
|
||||
tab.advanced.simple_voice_chat_support.tooltip=Aktiviert lesen und ändern der Simple Voice Chat Mod Pakete.
|
||||
tab.advanced.create_viaversion_dump.label=ViaVersion Dump erstellen
|
||||
tab.advanced.create_viaversion_dump.success=Der ViaVersion Dump Link wurde in die Zwischenablage kopiert.
|
||||
tab.advanced.upload_latest_log.label=Neueste Log-Datei hochladen
|
||||
|
|
|
@ -56,6 +56,8 @@ tab.advanced.ignore_packet_translation_errors.label=Ignore packet translation er
|
|||
tab.advanced.ignore_packet_translation_errors.tooltip=Enabling this will prevent getting disconnected from the server when a packet translation error occurs and instead only print the error in the console.\nThis may cause issues depending on the type of packet which failed to translate.
|
||||
tab.advanced.allow_beta_pinging.label=Allow <= b1.7.3 pinging
|
||||
tab.advanced.allow_beta_pinging.tooltip=Enabling this will allow you to ping <= b1.7.3 servers. This may cause issues with servers that block too frequent connections.
|
||||
tab.advanced.simple_voice_chat_support.label=Simple Voice Chat Support
|
||||
tab.advanced.simple_voice_chat_support.tooltip=Enables handling and rewriting of Simple Voice Chat mod packets.
|
||||
tab.advanced.create_viaversion_dump.label=Create ViaVersion dump
|
||||
tab.advanced.create_viaversion_dump.success=Copied ViaVersion dump link to clipboard.
|
||||
tab.advanced.upload_latest_log.label=Upload latest log file
|
||||
|
|
|
@ -68,5 +68,8 @@ resource-pack-url: ""
|
|||
# internal: Internal wildcard domain handling. Intended for local usage by custom clients. (Example: address:port\7version\7classic-mppass)
|
||||
wildcard-domain-handling: "none"
|
||||
#
|
||||
# Enables handling and rewriting of Simple Voice Chat mod packets.
|
||||
simple-voice-chat-support: false
|
||||
#
|
||||
# Configuration version. Do not change this.
|
||||
config-version: 1
|
||||
|
|
Loading…
Reference in New Issue