Minecraft 1.19 support
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
package net.md_5.bungee;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.google.common.primitives.Longs;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.Key;
|
||||
import java.security.KeyFactory;
|
||||
@@ -7,8 +11,11 @@ import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PublicKey;
|
||||
import java.security.Signature;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.Random;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.SecretKey;
|
||||
@@ -18,6 +25,7 @@ import net.md_5.bungee.jni.NativeCode;
|
||||
import net.md_5.bungee.jni.cipher.BungeeCipher;
|
||||
import net.md_5.bungee.jni.cipher.JavaCipher;
|
||||
import net.md_5.bungee.jni.cipher.NativeCipher;
|
||||
import net.md_5.bungee.protocol.PlayerPublicKey;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionRequest;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionResponse;
|
||||
|
||||
@@ -28,10 +36,12 @@ public class EncryptionUtil
|
||||
{
|
||||
|
||||
private static final Random random = new Random();
|
||||
private static final Base64.Encoder MIME_ENCODER = Base64.getMimeEncoder( 76, "\n".getBytes( StandardCharsets.UTF_8 ) );
|
||||
public static final KeyPair keys;
|
||||
@Getter
|
||||
private static final SecretKey secret = new SecretKeySpec( new byte[ 16 ], "AES" );
|
||||
public static final NativeCode<BungeeCipher> nativeFactory = new NativeCode<>( "native-cipher", JavaCipher::new, NativeCipher::new );
|
||||
private static final PublicKey MOJANG_KEY;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -44,6 +54,14 @@ public class EncryptionUtil
|
||||
{
|
||||
throw new ExceptionInInitializerError( ex );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
MOJANG_KEY = KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( ByteStreams.toByteArray( EncryptionUtil.class.getResourceAsStream( "/yggdrasil_session_pubkey.der" ) ) ) );
|
||||
} catch ( IOException | NoSuchAlgorithmException | InvalidKeySpecException ex )
|
||||
{
|
||||
throw new ExceptionInInitializerError( ex );
|
||||
}
|
||||
}
|
||||
|
||||
public static EncryptionRequest encryptRequest()
|
||||
@@ -55,17 +73,40 @@ public class EncryptionUtil
|
||||
return new EncryptionRequest( hash, pubKey, verify );
|
||||
}
|
||||
|
||||
public static boolean check(PlayerPublicKey publicKey) throws GeneralSecurityException
|
||||
{
|
||||
Signature signature = Signature.getInstance( "SHA1withRSA" );
|
||||
signature.initVerify( MOJANG_KEY );
|
||||
|
||||
signature.update( ( publicKey.getExpiry() + "-----BEGIN RSA PUBLIC KEY-----\n" + MIME_ENCODER.encodeToString( getPubkey( publicKey.getKey() ).getEncoded() ) + "\n-----END RSA PUBLIC KEY-----\n" ).getBytes( StandardCharsets.US_ASCII ) );
|
||||
|
||||
return signature.verify( publicKey.getSignature() );
|
||||
}
|
||||
|
||||
public static boolean check(PlayerPublicKey publicKey, EncryptionResponse resp, EncryptionRequest request) throws GeneralSecurityException
|
||||
{
|
||||
if ( publicKey != null )
|
||||
{
|
||||
Signature signature = Signature.getInstance( "SHA256withRSA" );
|
||||
signature.initVerify( getPubkey( publicKey.getKey() ) );
|
||||
|
||||
signature.update( request.getVerifyToken() );
|
||||
signature.update( Longs.toByteArray( resp.getEncryptionData().getSalt() ) );
|
||||
|
||||
return signature.verify( resp.getEncryptionData().getSignature() );
|
||||
} else
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance( "RSA" );
|
||||
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
|
||||
byte[] decrypted = cipher.doFinal( resp.getVerifyToken() );
|
||||
|
||||
return Arrays.equals( request.getVerifyToken(), decrypted );
|
||||
}
|
||||
}
|
||||
|
||||
public static SecretKey getSecret(EncryptionResponse resp, EncryptionRequest request) throws GeneralSecurityException
|
||||
{
|
||||
Cipher cipher = Cipher.getInstance( "RSA" );
|
||||
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
|
||||
byte[] decrypted = cipher.doFinal( resp.getVerifyToken() );
|
||||
|
||||
if ( !Arrays.equals( request.getVerifyToken(), decrypted ) )
|
||||
{
|
||||
throw new IllegalStateException( "Key pairs do not match!" );
|
||||
}
|
||||
|
||||
cipher.init( Cipher.DECRYPT_MODE, keys.getPrivate() );
|
||||
return new SecretKeySpec( cipher.doFinal( resp.getSharedSecret() ), "AES" );
|
||||
}
|
||||
@@ -80,7 +121,12 @@ public class EncryptionUtil
|
||||
|
||||
public static PublicKey getPubkey(EncryptionRequest request) throws GeneralSecurityException
|
||||
{
|
||||
return KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( request.getPublicKey() ) );
|
||||
return getPubkey( request.getPublicKey() );
|
||||
}
|
||||
|
||||
private static PublicKey getPubkey(byte[] b) throws GeneralSecurityException
|
||||
{
|
||||
return KeyFactory.getInstance( "RSA" ).generatePublic( new X509EncodedKeySpec( b ) );
|
||||
}
|
||||
|
||||
public static byte[] encrypt(Key key, byte[] b) throws GeneralSecurityException
|
||||
|
@@ -123,7 +123,7 @@ public class ServerConnector extends PacketHandler
|
||||
channel.write( copiedHandshake );
|
||||
|
||||
channel.setProtocol( Protocol.LOGIN );
|
||||
channel.write( new LoginRequest( user.getName() ) );
|
||||
channel.write( new LoginRequest( user.getName(), null ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,7 +225,7 @@ public class ServerConnector extends PacketHandler
|
||||
|
||||
// Set tab list size, TODO: what shall we do about packet mutability
|
||||
Login modLogin = new Login( login.getEntityId(), login.isHardcore(), login.getGameMode(), login.getPreviousGameMode(), login.getWorldNames(), login.getDimensions(), login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(),
|
||||
(byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isDebug(), login.isFlat() );
|
||||
(byte) user.getPendingConnection().getListener().getTabListSize(), login.getLevelType(), login.getViewDistance(), login.getSimulationDistance(), login.isReducedDebugInfo(), login.isNormalRespawn(), login.isDebug(), login.isFlat(), login.getDeathLocation() );
|
||||
|
||||
user.unsafe().sendPacket( modLogin );
|
||||
|
||||
@@ -243,7 +243,7 @@ public class ServerConnector extends PacketHandler
|
||||
}
|
||||
user.getSentBossBars().clear();
|
||||
|
||||
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false ) );
|
||||
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) );
|
||||
user.getServer().disconnect( "Quitting" );
|
||||
} else
|
||||
{
|
||||
@@ -292,11 +292,11 @@ public class ServerConnector extends PacketHandler
|
||||
user.setDimensionChange( true );
|
||||
if ( login.getDimension() == user.getDimension() )
|
||||
{
|
||||
user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false ) );
|
||||
user.unsafe().sendPacket( new Respawn( (Integer) login.getDimension() >= 0 ? -1 : 0, login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) );
|
||||
}
|
||||
|
||||
user.setServerEntityId( login.getEntityId() );
|
||||
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false ) );
|
||||
user.unsafe().sendPacket( new Respawn( login.getDimension(), login.getWorldName(), login.getSeed(), login.getDifficulty(), login.getGameMode(), login.getPreviousGameMode(), login.getLevelType(), login.isDebug(), login.isFlat(), false, login.getDeathLocation() ) );
|
||||
if ( user.getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_14 )
|
||||
{
|
||||
user.unsafe().sendPacket( new ViewDistance( login.getViewDistance() ) );
|
||||
|
@@ -59,6 +59,7 @@ import net.md_5.bungee.protocol.packet.Kick;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
|
||||
import net.md_5.bungee.protocol.packet.PluginMessage;
|
||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||
import net.md_5.bungee.protocol.packet.SystemChat;
|
||||
import net.md_5.bungee.tab.ServerUnique;
|
||||
import net.md_5.bungee.tab.TabList;
|
||||
import net.md_5.bungee.util.CaseInsensitiveSet;
|
||||
@@ -416,6 +417,10 @@ public final class UserConnection implements ProxiedPlayer
|
||||
public void chat(String message)
|
||||
{
|
||||
Preconditions.checkState( server != null, "Not connected to server" );
|
||||
if ( getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_19 )
|
||||
{
|
||||
throw new UnsupportedOperationException( "Cannot spoof chat on this client version!" );
|
||||
}
|
||||
server.getCh().write( new Chat( message ) );
|
||||
}
|
||||
|
||||
@@ -472,7 +477,13 @@ public final class UserConnection implements ProxiedPlayer
|
||||
|
||||
private void sendMessage(ChatMessageType position, UUID sender, String message)
|
||||
{
|
||||
unsafe().sendPacket( new Chat( message, (byte) position.ordinal(), sender ) );
|
||||
if ( getPendingConnection().getVersion() >= ProtocolConstants.MINECRAFT_1_19 )
|
||||
{
|
||||
unsafe().sendPacket( new SystemChat( message, position.ordinal() ) );
|
||||
} else
|
||||
{
|
||||
unsafe().sendPacket( new Chat( message, (byte) position.ordinal(), sender ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(ChatMessageType position, UUID sender, BaseComponent... message)
|
||||
|
@@ -47,6 +47,10 @@ public class Configuration implements ProxyConfig
|
||||
* Should we check minecraft.net auth.
|
||||
*/
|
||||
private boolean onlineMode = true;
|
||||
/**
|
||||
* Whether to check the authentication server public key.
|
||||
*/
|
||||
private boolean enforceSecureProfile;
|
||||
/**
|
||||
* Whether we log proxy commands to the proxy log
|
||||
*/
|
||||
@@ -86,6 +90,7 @@ public class Configuration implements ProxyConfig
|
||||
timeout = adapter.getInt( "timeout", timeout );
|
||||
uuid = adapter.getString( "stats", uuid );
|
||||
onlineMode = adapter.getBoolean( "online_mode", onlineMode );
|
||||
enforceSecureProfile = adapter.getBoolean( "enforce_secure_profile", enforceSecureProfile );
|
||||
logCommands = adapter.getBoolean( "log_commands", logCommands );
|
||||
logPings = adapter.getBoolean( "log_pings", logPings );
|
||||
remotePingCache = adapter.getInt( "remote_ping_cache", remotePingCache );
|
||||
|
@@ -9,6 +9,7 @@ import java.net.SocketAddress;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Instant;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
@@ -49,6 +50,7 @@ import net.md_5.bungee.netty.cipher.CipherDecoder;
|
||||
import net.md_5.bungee.netty.cipher.CipherEncoder;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.PlayerPublicKey;
|
||||
import net.md_5.bungee.protocol.Protocol;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import net.md_5.bungee.protocol.packet.EncryptionRequest;
|
||||
@@ -358,6 +360,29 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
disconnect( bungee.getTranslation( "name_invalid" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( BungeeCord.getInstance().config.isEnforceSecureProfile() )
|
||||
{
|
||||
PlayerPublicKey publicKey = loginRequest.getPublicKey();
|
||||
if ( publicKey == null )
|
||||
{
|
||||
disconnect( bungee.getTranslation( "secure_profile_required" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Instant.ofEpochMilli( publicKey.getExpiry() ).isBefore( Instant.now() ) )
|
||||
{
|
||||
disconnect( bungee.getTranslation( "secure_profile_expired" ) );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !EncryptionUtil.check( publicKey ) )
|
||||
{
|
||||
disconnect( bungee.getTranslation( "secure_profile_invalid" ) );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.loginRequest = loginRequest;
|
||||
|
||||
int limit = BungeeCord.getInstance().config.getPlayerLimit();
|
||||
@@ -410,6 +435,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
public void handle(final EncryptionResponse encryptResponse) throws Exception
|
||||
{
|
||||
Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" );
|
||||
Preconditions.checkState( EncryptionUtil.check( loginRequest.getPublicKey(), encryptResponse, request ), "Invalid verification" );
|
||||
|
||||
SecretKey sharedKey = EncryptionUtil.getSecret( encryptResponse, request );
|
||||
BungeeCipher decrypt = EncryptionUtil.getCipher( false, sharedKey );
|
||||
@@ -524,7 +550,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
||||
userCon.setCompressionThreshold( BungeeCord.getInstance().config.getCompressionThreshold() );
|
||||
userCon.init();
|
||||
|
||||
unsafe.sendPacket( new LoginSuccess( getUniqueId(), getName() ) );
|
||||
unsafe.sendPacket( new LoginSuccess( getUniqueId(), getName(), loginProfile.getProperties() ) );
|
||||
ch.setProtocol( Protocol.GAME );
|
||||
|
||||
ch.getHandle().pipeline().get( HandlerBoss.class ).setHandler( new UpstreamBridge( bungee, userCon ) );
|
||||
|
@@ -2,6 +2,7 @@ package net.md_5.bungee.connection;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@@ -11,14 +12,4 @@ public class LoginResult
|
||||
private String id;
|
||||
private String name;
|
||||
private Property[] properties;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
public static class Property
|
||||
{
|
||||
|
||||
private String name;
|
||||
private String value;
|
||||
private String signature;
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,8 @@ import net.md_5.bungee.netty.PacketHandler;
|
||||
import net.md_5.bungee.protocol.PacketWrapper;
|
||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||
import net.md_5.bungee.protocol.packet.Chat;
|
||||
import net.md_5.bungee.protocol.packet.ClientChat;
|
||||
import net.md_5.bungee.protocol.packet.ClientCommand;
|
||||
import net.md_5.bungee.protocol.packet.ClientSettings;
|
||||
import net.md_5.bungee.protocol.packet.KeepAlive;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
@@ -145,9 +147,33 @@ public class UpstreamBridge extends PacketHandler
|
||||
@Override
|
||||
public void handle(Chat chat) throws Exception
|
||||
{
|
||||
for ( int index = 0, length = chat.getMessage().length(); index < length; index++ )
|
||||
String message = handleChat( chat.getMessage() );
|
||||
if ( message != null )
|
||||
{
|
||||
char c = chat.getMessage().charAt( index );
|
||||
chat.setMessage( message );
|
||||
con.getServer().unsafe().sendPacket( chat );
|
||||
}
|
||||
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(ClientChat chat) throws Exception
|
||||
{
|
||||
handleChat( chat.getMessage() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(ClientCommand command) throws Exception
|
||||
{
|
||||
handleChat( "/" + command.getCommand() );
|
||||
}
|
||||
|
||||
private String handleChat(String message)
|
||||
{
|
||||
for ( int index = 0, length = message.length(); index < length; index++ )
|
||||
{
|
||||
char c = message.charAt( index );
|
||||
if ( !AllowedCharacters.isChatAllowedCharacter( c ) )
|
||||
{
|
||||
con.disconnect( bungee.getTranslation( "illegal_chat_characters", Util.unicode( c ) ) );
|
||||
@@ -155,16 +181,16 @@ public class UpstreamBridge extends PacketHandler
|
||||
}
|
||||
}
|
||||
|
||||
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.getMessage() );
|
||||
ChatEvent chatEvent = new ChatEvent( con, con.getServer(), message );
|
||||
if ( !bungee.getPluginManager().callEvent( chatEvent ).isCancelled() )
|
||||
{
|
||||
chat.setMessage( chatEvent.getMessage() );
|
||||
if ( !chatEvent.isCommand() || !bungee.getPluginManager().dispatchCommand( con, chat.getMessage().substring( 1 ) ) )
|
||||
message = chatEvent.getMessage();
|
||||
if ( !chatEvent.isCommand() || !bungee.getPluginManager().dispatchCommand( con, message.substring( 1 ) ) )
|
||||
{
|
||||
con.getServer().unsafe().sendPacket( chat );
|
||||
return message;
|
||||
}
|
||||
}
|
||||
throw CancelSendSignal.INSTANCE;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -74,6 +74,8 @@ public abstract class EntityMap
|
||||
case ProtocolConstants.MINECRAFT_1_18:
|
||||
case ProtocolConstants.MINECRAFT_1_18_2:
|
||||
return EntityMap_1_16_2.INSTANCE_1_18;
|
||||
case ProtocolConstants.MINECRAFT_1_19:
|
||||
return EntityMap_1_16_2.INSTANCE_1_19;
|
||||
}
|
||||
throw new RuntimeException( "Version " + version + " has no entity map" );
|
||||
}
|
||||
|
@@ -17,6 +17,8 @@ class EntityMap_1_16_2 extends EntityMap
|
||||
static final EntityMap_1_16_2 INSTANCE_1_16_2 = new EntityMap_1_16_2( 0x04, 0x2D );
|
||||
static final EntityMap_1_16_2 INSTANCE_1_17 = new EntityMap_1_16_2( 0x04, 0x2D );
|
||||
static final EntityMap_1_16_2 INSTANCE_1_18 = new EntityMap_1_16_2( 0x04, 0x2D );
|
||||
static final EntityMap_1_16_2 INSTANCE_1_19 = new EntityMap_1_16_2( 0x02, 0x2F );
|
||||
|
||||
//
|
||||
private final int spawnPlayerId;
|
||||
private final int spectateId;
|
||||
|
@@ -5,6 +5,7 @@ import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.protocol.Property;
|
||||
import net.md_5.bungee.protocol.packet.PlayerListItem;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@@ -38,20 +39,15 @@ public abstract class TabList
|
||||
LoginResult loginResult = player.getPendingConnection().getLoginProfile();
|
||||
if ( loginResult != null && loginResult.getProperties() != null )
|
||||
{
|
||||
String[][] props = new String[ loginResult.getProperties().length ][];
|
||||
Property[] props = new Property[ loginResult.getProperties().length ];
|
||||
for ( int i = 0; i < props.length; i++ )
|
||||
{
|
||||
props[i] = new String[]
|
||||
{
|
||||
loginResult.getProperties()[i].getName(),
|
||||
loginResult.getProperties()[i].getValue(),
|
||||
loginResult.getProperties()[i].getSignature()
|
||||
};
|
||||
props[i] = new Property( loginResult.getProperties()[i].getName(), loginResult.getProperties()[i].getValue(), loginResult.getProperties()[i].getSignature() );
|
||||
}
|
||||
item.setProperties( props );
|
||||
} else
|
||||
{
|
||||
item.setProperties( new String[ 0 ][ 0 ] );
|
||||
item.setProperties( new Property[ 0 ] );
|
||||
}
|
||||
if ( playerListItem.getAction() == PlayerListItem.Action.ADD_PLAYER || playerListItem.getAction() == PlayerListItem.Action.UPDATE_GAMEMODE )
|
||||
{
|
||||
|
@@ -22,6 +22,9 @@ total_players=Total players online: {0}
|
||||
name_invalid=Username contains invalid characters.
|
||||
ping_cannot_connect=\u00a7c[Bungee] Can''t connect to server.
|
||||
offline_mode_player=Not authenticated with Minecraft.net
|
||||
secure_profile_required=A secure profile is required to join this server.
|
||||
secure_profile_expired=Secure profile expired.
|
||||
secure_profile_invalid=Secure profile invalid.
|
||||
message_needed=\u00a7cYou must supply a message.
|
||||
error_occurred_player=\u00a7cAn error occurred while parsing your message. (Hover for details)
|
||||
error_occurred_console=\u00a7cAn error occurred while parsing your message: {0}
|
||||
|
BIN
proxy/src/main/resources/yggdrasil_session_pubkey.der
Normal file
BIN
proxy/src/main/resources/yggdrasil_session_pubkey.der
Normal file
Binary file not shown.
Reference in New Issue
Block a user