Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-16 21:10:30 +01:00
Update to 1.19.1 (#772)
* 1.19.1-rc1 * More signature changes * Further 1.19.1 changes I also started on the checkstyle update, see the developers notes for the rest I haven't gotten around to fixing yet. * Fix checkstyle * Checkstyle imports * Fix logic error * Changes 1.19.1-pre2 * 1.19-pre3 * Progress, some parts still WIP * Overlooked changes * Fix ServerData * Fix ServerLogin send check * Workaround the broken behavior of "No Chat Reports" Note that if we ever choose to enforce chat signatures, then the mod will just break again... not our fault if we do that, you get what you pay for. * more Co-authored-by: Shane Freeder <theboyetronic@gmail.com> Co-authored-by: Andrew Steinborn <git@steinborn.me>
Dieser Commit ist enthalten in:
Ursprung
6be344d919
Commit
1a3fba4250
@ -11,7 +11,6 @@ import com.google.common.base.Preconditions;
|
||||
import java.util.Optional;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -74,8 +74,8 @@ public final class CommandExecuteEvent implements ResultedEvent<CommandResult> {
|
||||
*/
|
||||
public static final class CommandResult implements ResultedEvent.Result {
|
||||
|
||||
private static final CommandResult ALLOWED = new CommandResult(true, false,null);
|
||||
private static final CommandResult DENIED = new CommandResult(false, false,null);
|
||||
private static final CommandResult ALLOWED = new CommandResult(true, false, null);
|
||||
private static final CommandResult DENIED = new CommandResult(false, false, null);
|
||||
private static final CommandResult FORWARD_TO_SERVER = new CommandResult(false, true, null);
|
||||
|
||||
private @Nullable String command;
|
||||
|
@ -10,7 +10,6 @@ package com.velocitypowered.api.event.player;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -58,7 +58,8 @@ public enum ProtocolVersion {
|
||||
MINECRAFT_1_17_1(756, "1.17.1"),
|
||||
MINECRAFT_1_18(757, "1.18", "1.18.1"),
|
||||
MINECRAFT_1_18_2(758, "1.18.2"),
|
||||
MINECRAFT_1_19(759, "1.19");
|
||||
MINECRAFT_1_19(759, "1.19"),
|
||||
MINECRAFT_1_19_1(760, "1.19.1");
|
||||
|
||||
private static final int SNAPSHOT_BIT = 30;
|
||||
|
||||
|
@ -43,7 +43,7 @@ public enum Tristate {
|
||||
*
|
||||
* @param val the boolean value
|
||||
* @return {@link #TRUE} or {@link #FALSE}, if the value is <code>true</code> or
|
||||
* <code>false</code>, respectively.
|
||||
* <code>false</code>, respectively.
|
||||
*/
|
||||
public static Tristate fromBoolean(boolean val) {
|
||||
return val ? TRUE : FALSE;
|
||||
@ -57,7 +57,7 @@ public enum Tristate {
|
||||
*
|
||||
* @param val the boolean value
|
||||
* @return {@link #UNDEFINED}, {@link #TRUE} or {@link #FALSE}, if the value is <code>null</code>,
|
||||
* <code>true</code> or <code>false</code>, respectively.
|
||||
* <code>true</code> or <code>false</code>, respectively.
|
||||
*/
|
||||
public static Tristate fromNullableBoolean(@Nullable Boolean val) {
|
||||
if (val == null) {
|
||||
|
@ -8,7 +8,6 @@
|
||||
package com.velocitypowered.api.proxy;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -7,7 +7,12 @@
|
||||
|
||||
package com.velocitypowered.api.proxy.crypto;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import java.security.PublicKey;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Represents session-server cross-signed dated RSA public key.
|
||||
@ -32,4 +37,41 @@ public interface IdentifiedKey extends KeySigned {
|
||||
*/
|
||||
boolean verifyDataSignature(byte[] signature, byte[]... toVerify);
|
||||
|
||||
/**
|
||||
* Retrieves the signature holders UUID.
|
||||
* Returns null before the {@link com.velocitypowered.api.event.connection.LoginEvent}.
|
||||
*
|
||||
* @return the holder UUID or null if not present
|
||||
*/
|
||||
@Nullable
|
||||
UUID getSignatureHolder();
|
||||
|
||||
/**
|
||||
* Retrieves the key revision.
|
||||
*
|
||||
* @return the key revision
|
||||
*/
|
||||
Revision getKeyRevision();
|
||||
|
||||
enum Revision {
|
||||
GENERIC_V1(ImmutableSet.of(), ImmutableSet.of(ProtocolVersion.MINECRAFT_1_19)),
|
||||
LINKED_V2(ImmutableSet.of(), ImmutableSet.of(ProtocolVersion.MINECRAFT_1_19_1));
|
||||
|
||||
final Set<Revision> backwardsCompatibleTo;
|
||||
final Set<ProtocolVersion> applicableTo;
|
||||
|
||||
Revision(Set<Revision> backwardsCompatibleTo, Set<ProtocolVersion> applicableTo) {
|
||||
this.backwardsCompatibleTo = backwardsCompatibleTo;
|
||||
this.applicableTo = applicableTo;
|
||||
}
|
||||
|
||||
public Set<Revision> getBackwardsCompatibleTo() {
|
||||
return backwardsCompatibleTo;
|
||||
}
|
||||
|
||||
public Set<ProtocolVersion> getApplicableTo() {
|
||||
return applicableTo;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
package com.velocitypowered.api.proxy.crypto;
|
||||
|
||||
import com.google.common.annotations.Beta;
|
||||
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public interface KeySigned {
|
||||
@ -56,6 +54,7 @@ public interface KeySigned {
|
||||
* signer public key. Note: This will **not** check for
|
||||
* expiry. You can check for expiry with {@link KeySigned#hasExpired()}.
|
||||
* <p>DOES NOT WORK YET FOR MESSAGES AND COMMANDS!</p>
|
||||
* Addendum: Does not work for 1.19.1 until the user has authenticated.
|
||||
*
|
||||
* @return validity of the signature
|
||||
*/
|
||||
|
@ -49,7 +49,7 @@ public interface TabList {
|
||||
*
|
||||
* @param uuid of the entry
|
||||
* @return {@link Optional} containing the removed {@link TabListEntry} if present, otherwise
|
||||
* {@link Optional#empty()}
|
||||
* {@link Optional#empty()}
|
||||
*/
|
||||
Optional<TabListEntry> removeEntry(UUID uuid);
|
||||
|
||||
@ -71,12 +71,12 @@ public interface TabList {
|
||||
/**
|
||||
* Builds a tab list entry.
|
||||
*
|
||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||
* @param profile profile
|
||||
* @param displayName display name
|
||||
* @param latency latency
|
||||
* @param gameMode game mode
|
||||
* @return entry
|
||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||
@ -85,13 +85,13 @@ public interface TabList {
|
||||
/**
|
||||
* Builds a tab list entry.
|
||||
*
|
||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||
* @param profile profile
|
||||
* @param displayName display name
|
||||
* @param latency latency
|
||||
* @param gameMode game mode
|
||||
* @param key the player key
|
||||
* @return entry
|
||||
* @deprecated Internal usage. Use {@link TabListEntry.Builder} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
TabListEntry buildEntry(GameProfile profile, @Nullable Component displayName, int latency,
|
||||
|
@ -160,6 +160,7 @@ public interface TabListEntry extends KeyIdentifiable {
|
||||
* Sets the {@link IdentifiedKey} of the {@link TabListEntry}.
|
||||
* <p>This is only intended and only works for players currently <b>not</b> connected to this proxy.</p>
|
||||
* <p>For any player currently connected to this proxy this will be filled automatically.</p>
|
||||
* <p>Will ignore mismatching key revisions data.</p>
|
||||
*
|
||||
* @param playerKey key to set
|
||||
* @return {@code this}, for chaining
|
||||
|
@ -18,7 +18,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,6 @@ import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.checkerframework.common.value.qual.IntRange;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -80,7 +80,7 @@ public final class Favicon {
|
||||
public static Favicon create(BufferedImage image) {
|
||||
Preconditions.checkNotNull(image, "image");
|
||||
Preconditions.checkArgument(image.getWidth() == 64 && image.getHeight() == 64,
|
||||
"Image is not 64x64 (found %sx%s)", image.getWidth(),image.getHeight());
|
||||
"Image is not 64x64 (found %sx%s)", image.getWidth(), image.getHeight());
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
ImageIO.write(image, "PNG", os);
|
||||
|
@ -20,7 +20,7 @@ class QueryResponseTest {
|
||||
QueryResponse response = new QueryResponse("test", "test", "test",
|
||||
1, 2, "test", 1234, ImmutableList.of("tuxed"),
|
||||
"0.0.1", ImmutableList.of(new PluginInformation("test", "1.0.0"),
|
||||
new PluginInformation("test2", null)));
|
||||
new PluginInformation("test2", null)));
|
||||
assertEquals(response, response.toBuilder().build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,83 +1,137 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration that checks the Google coding conventions from Google Java Style
|
||||
that can be found at https://google.github.io/styleguide/javaguide.html.
|
||||
that can be found at https://google.github.io/styleguide/javaguide.html
|
||||
|
||||
Checkstyle is very configurable. Be sure to read the documentation at
|
||||
http://checkstyle.sf.net (or in your downloaded distribution).
|
||||
http://checkstyle.org (or in your downloaded distribution).
|
||||
|
||||
To completely disable a check, just comment it out or delete it from the file.
|
||||
To suppress certain violations please review suppression filters.
|
||||
|
||||
Authors: Max Vetrenko, Ruslan Diachenko, Roman Ivanov.
|
||||
-->
|
||||
|
||||
<module name="Checker">
|
||||
<!--
|
||||
Developer notes: Fix MissingJavadocMethod/Type RequireEmptyLineBeforeBlockTagGroup and CustomImportOrder
|
||||
-->
|
||||
<module name = "Checker">
|
||||
<property name="charset" value="UTF-8"/>
|
||||
|
||||
<property name="severity" value="warning"/>
|
||||
|
||||
<property name="fileExtensions" value="java, properties, xml"/>
|
||||
<!-- Excludes all 'module-info.java' files -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value="module\-info\.java$"/>
|
||||
</module>
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionFilter -->
|
||||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionfilter.config}"
|
||||
default="checkstyle-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Checks for whitespace -->
|
||||
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
|
||||
<!-- See http://checkstyle.org/config_whitespace.html -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<module name="LineLength">
|
||||
<property name="fileExtensions" value="java"/>
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
<module name="OuterTypeFilename"/>
|
||||
<!-- <module name="IllegalTokenText">
|
||||
<module name="IllegalTokenText">
|
||||
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
|
||||
<property name="format"
|
||||
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
|
||||
<property name="message"
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module> -->
|
||||
value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
|
||||
</module>
|
||||
<module name="AvoidEscapedUnicodeCharacters">
|
||||
<property name="allowEscapesForControlCharacters" value="true"/>
|
||||
<property name="allowByTailComment" value="true"/>
|
||||
<property name="allowNonPrintableEscapes" value="true"/>
|
||||
</module>
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern"
|
||||
value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
|
||||
</module>
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="OneTopLevelClass"/>
|
||||
<module name="NoLineWrap"/>
|
||||
<module name="NoLineWrap">
|
||||
<property name="tokens" value="PACKAGE_DEF, IMPORT, STATIC_IMPORT"/>
|
||||
</module>
|
||||
<module name="EmptyBlock">
|
||||
<property name="option" value="TEXT"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="NeedBraces">
|
||||
<property name="tokens"
|
||||
value="LITERAL_DO, LITERAL_ELSE, LITERAL_FOR, LITERAL_IF, LITERAL_WHILE"/>
|
||||
</module>
|
||||
<module name="LeftCurly">
|
||||
<property name="tokens"
|
||||
value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF,
|
||||
INTERFACE_DEF, LAMBDA, LITERAL_CASE, LITERAL_CATCH, LITERAL_DEFAULT,
|
||||
LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF,
|
||||
OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="NeedBraces"/>
|
||||
<module name="LeftCurly"/>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlySame"/>
|
||||
<property name="tokens"
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||
value="LITERAL_TRY, LITERAL_CATCH, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE,
|
||||
LITERAL_DO"/>
|
||||
</module>
|
||||
<module name="RightCurly">
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="option" value="alone"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||
INSTANCE_INIT"/>
|
||||
value="CLASS_DEF, METHOD_DEF, CTOR_DEF, LITERAL_FOR, LITERAL_WHILE, STATIC_INIT,
|
||||
INSTANCE_INIT, ANNOTATION_DEF, ENUM_DEF, INTERFACE_DEF, RECORD_DEF,
|
||||
COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="SuppressionXpathSingleFilter">
|
||||
<!-- suppresion is required till https://github.com/checkstyle/checkstyle/issues/7541 -->
|
||||
<property name="id" value="RightCurlyAlone"/>
|
||||
<property name="query" value="//RCURLY[parent::SLIST[count(./*)=1]
|
||||
or preceding-sibling::*[last()][self::LCURLY]]"/>
|
||||
</module>
|
||||
<module name="WhitespaceAfter">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_RETURN,
|
||||
LITERAL_WHILE, LITERAL_DO, LITERAL_FOR, LITERAL_FINALLY, DO_WHILE, ELLIPSIS,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_CATCH, LAMBDA,
|
||||
LITERAL_YIELD, LITERAL_CASE"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyLambdas" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<property name="ignoreEnhancedForColon" value="false"/>
|
||||
<property name="tokens"
|
||||
value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR,
|
||||
BXOR_ASSIGN, COLON, DIV, DIV_ASSIGN, DO_WHILE, EQUAL, GE, GT, LAMBDA, LAND,
|
||||
LCURLY, LE, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY,
|
||||
LITERAL_FOR, LITERAL_IF, LITERAL_RETURN, LITERAL_SWITCH, LITERAL_SYNCHRONIZED,
|
||||
LITERAL_TRY, LITERAL_WHILE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN,
|
||||
NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, RCURLY, SL, SLIST, SL_ASSIGN, SR,
|
||||
SR_ASSIGN, STAR, STAR_ASSIGN, LITERAL_ASSERT, TYPE_EXTENSION_AND"/>
|
||||
<message key="ws.notFollowed"
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks
|
||||
may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
|
||||
<message key="ws.notPreceded"
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="OneStatementPerLine"/>
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
@ -87,6 +141,10 @@
|
||||
<module name="UpperEll"/>
|
||||
<module name="ModifierOrder"/>
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="tokens"
|
||||
value="PACKAGE_DEF, IMPORT, STATIC_IMPORT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
STATIC_INIT, INSTANCE_INIT, METHOD_DEF, CTOR_DEF, VARIABLE_DEF, RECORD_DEF,
|
||||
COMPACT_CTOR_DEF"/>
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
@ -100,13 +158,13 @@
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||
<property name="id" value="SeparatorWrapEllipsis"/>
|
||||
<property name="tokens" value="ELLIPSIS"/>
|
||||
<property name="option" value="EOL"/>
|
||||
</module>
|
||||
<module name="SeparatorWrap">
|
||||
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
|
||||
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/258 -->
|
||||
<property name="id" value="SeparatorWrapArrayDeclarator"/>
|
||||
<property name="tokens" value="ARRAY_DECLARATOR"/>
|
||||
<property name="option" value="EOL"/>
|
||||
@ -119,67 +177,83 @@
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Package name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="TypeName">
|
||||
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
ANNOTATION_DEF, RECORD_DEF"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MemberName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Member name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LambdaParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Lambda parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="CatchParameterName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="LocalVariableName">
|
||||
<property name="tokens" value="VARIABLE_DEF"/>
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Local variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="PatternVariableName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Pattern variable name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="ClassTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Class type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="RecordComponentName">
|
||||
<property name="format" value="^[a-z]([a-z0-9][a-zA-Z0-9]*)?$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Record component name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="RecordTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Record type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="MethodTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Method type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="InterfaceTypeParameterName">
|
||||
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][a-zA-Z0-9]*[T]$)"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
value="Interface type name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="NoFinalizer"/>
|
||||
<module name="GenericWhitespace">
|
||||
<message key="ws.followed"
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
value="GenericWhitespace ''{0}'' is followed by whitespace."/>
|
||||
<message key="ws.preceded"
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
|
||||
<message key="ws.illegalFollow"
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
value="GenericWhitespace ''{0}'' should followed by whitespace."/>
|
||||
<message key="ws.notPreceded"
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
|
||||
</module>
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="2"/>
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="braceAdjustment" value="2"/>
|
||||
<property name="caseIndent" value="2"/>
|
||||
<property name="throwsIndent" value="4"/>
|
||||
<property name="lineWrappingIndentation" value="4"/>
|
||||
@ -188,31 +262,51 @@
|
||||
<module name="AbbreviationAsWordInName">
|
||||
<property name="ignoreFinal" value="false"/>
|
||||
<property name="allowedAbbreviationLength" value="1"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, ANNOTATION_DEF, ANNOTATION_FIELD_DEF,
|
||||
PARAMETER_DEF, VARIABLE_DEF, METHOD_DEF, PATTERN_VARIABLE_DEF, RECORD_DEF,
|
||||
RECORD_COMPONENT_DEF"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBeforeCaseDefaultColon"/>
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="separateLineBetweenGroups" value="true"/>
|
||||
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
|
||||
<property name="tokens" value="IMPORT, STATIC_IMPORT, PACKAGE_DEF"/>
|
||||
</module>
|
||||
<module name="MethodParamPad">
|
||||
<property name="tokens"
|
||||
value="CTOR_DEF, LITERAL_NEW, METHOD_CALL, METHOD_DEF,
|
||||
SUPER_CTOR_CALL, ENUM_CONSTANT_DEF, RECORD_DEF"/>
|
||||
</module>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens"
|
||||
value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
|
||||
value="COMMA, SEMI, POST_INC, POST_DEC, DOT,
|
||||
LABELED_STAT, METHOD_REF"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="ParenPad"/>
|
||||
<module name="ParenPad">
|
||||
<property name="tokens"
|
||||
value="ANNOTATION, ANNOTATION_FIELD_DEF, CTOR_CALL, CTOR_DEF, DOT, ENUM_CONSTANT_DEF,
|
||||
EXPR, LITERAL_CATCH, LITERAL_DO, LITERAL_FOR, LITERAL_IF, LITERAL_NEW,
|
||||
LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_WHILE, METHOD_CALL,
|
||||
METHOD_DEF, QUESTION, RESOURCE_SPECIFICATION, SUPER_CTOR_CALL, LAMBDA,
|
||||
RECORD_DEF"/>
|
||||
</module>
|
||||
<module name="OperatorWrap">
|
||||
<property name="option" value="NL"/>
|
||||
<property name="tokens"
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
|
||||
value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR,
|
||||
LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF,
|
||||
TYPE_EXTENSION_AND "/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationMostCases"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF,
|
||||
RECORD_DEF, COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="AnnotationLocation">
|
||||
<property name="id" value="AnnotationLocationVariables"/>
|
||||
@ -220,37 +314,59 @@
|
||||
<property name="allowSamelineMultipleAnnotations" value="true"/>
|
||||
</module>
|
||||
<module name="NonEmptyAtclauseDescription"/>
|
||||
<module name="InvalidJavadocPosition"/>
|
||||
<module name="JavadocTagContinuationIndentation"/>
|
||||
<module name="SummaryJavadoc">
|
||||
<property name="forbiddenSummaryFragments"
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
|
||||
</module>
|
||||
<module name="JavadocParagraph"/>
|
||||
<!-- <module name="RequireEmptyLineBeforeBlockTagGroup"/>
|
||||
-->
|
||||
<module name="AtclauseOrder">
|
||||
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
|
||||
<property name="target"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
|
||||
</module>
|
||||
<module name="JavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="accessModifiers" value="public"/>
|
||||
<property name="allowMissingParamTags" value="true"/>
|
||||
<property name="allowMissingThrowsTags" value="true"/>
|
||||
<property name="allowMissingReturnTag" value="true"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF, COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<!-- <module name="MissingJavadocMethod">
|
||||
<property name="scope" value="public"/>
|
||||
<property name="minLineCount" value="2"/>
|
||||
<property name="allowedAnnotations" value="Override, Test"/>
|
||||
<property name="allowThrowsTagsForSubclasses" value="true"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, ANNOTATION_FIELD_DEF,
|
||||
COMPACT_CTOR_DEF"/>
|
||||
</module>
|
||||
<module name="MissingJavadocType">
|
||||
<property name="scope" value="protected"/>
|
||||
<property name="tokens"
|
||||
value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF,
|
||||
RECORD_DEF, ANNOTATION_DEF"/>
|
||||
<property name="excludeScope" value="nothing"/>
|
||||
</module>
|
||||
-->
|
||||
<module name="MethodName">
|
||||
<property name="format" value="^[a-z][a-z0-9][a-zA-Z0-9_]*$"/>
|
||||
<message key="name.invalidPattern"
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc">
|
||||
<property name="ignoreInlineTags" value="false"/>
|
||||
value="Method name ''{0}'' must match pattern ''{1}''."/>
|
||||
</module>
|
||||
<module name="SingleLineJavadoc"/>
|
||||
<module name="EmptyCatchBlock">
|
||||
<property name="exceptionVariableName" value="expected"/>
|
||||
</module>
|
||||
<module name="CommentsIndentation"/>
|
||||
<module name="CommentsIndentation">
|
||||
<property name="tokens" value="SINGLE_LINE_COMMENT, BLOCK_COMMENT_BEGIN"/>
|
||||
</module>
|
||||
<!-- https://checkstyle.org/config_filters.html#SuppressionXpathFilter -->
|
||||
<module name="SuppressionXpathFilter">
|
||||
<property name="file" value="${org.checkstyle.google.suppressionxpathfilter.config}"
|
||||
default="checkstyle-xpath-suppressions.xml" />
|
||||
<property name="optional" value="true"/>
|
||||
</module>
|
||||
</module>
|
||||
</module>
|
||||
|
@ -1,5 +1,5 @@
|
||||
checkstyle {
|
||||
toolVersion '8.14'
|
||||
toolVersion '10.3.1'
|
||||
configFile new File(project.rootDir, ['config', 'checkstyle', 'checkstyle.xml'].join(File.separator))
|
||||
|
||||
// The build should immediately fail if we have errors.
|
||||
|
@ -18,7 +18,6 @@
|
||||
package com.velocitypowered.proxy;
|
||||
|
||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
@ -26,7 +25,6 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bstats.MetricsBase;
|
||||
|
@ -318,9 +318,6 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
|
||||
}
|
||||
|
||||
commandManager.setAnnounceProxyCommands(configuration.isAnnounceProxyCommands());
|
||||
if (System.getProperty("auth.forceSecureProfiles") == null) {
|
||||
System.setProperty("auth.forceSecureProfiles", String.valueOf(configuration.isForceKeyAuthentication()));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Unable to read/load/save your velocity.toml. The server will shut down.", e);
|
||||
LogManager.shutdown();
|
||||
|
@ -24,12 +24,11 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder;
|
||||
import com.velocitypowered.api.command.BrigadierCommand;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
|
||||
public final class ShutdownCommand {
|
||||
private ShutdownCommand(){}
|
||||
private ShutdownCommand() {}
|
||||
|
||||
/**
|
||||
* Creates a Velocity Shutdown Command.
|
||||
|
@ -19,7 +19,6 @@ package com.velocitypowered.proxy.command.builtin;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.gson.JsonArray;
|
||||
@ -36,7 +35,6 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.util.InformationUtils;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
@ -47,7 +45,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
|
@ -28,7 +28,6 @@ import com.google.gson.annotations.Expose;
|
||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||
import com.velocitypowered.api.util.Favicon;
|
||||
import com.velocitypowered.proxy.util.AddressUtil;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -90,7 +89,7 @@ public class VelocityConfiguration implements ProxyConfig {
|
||||
boolean preventClientProxyConnections, boolean announceForge,
|
||||
PlayerInfoForwarding playerInfoForwardingMode, byte[] forwardingSecret,
|
||||
boolean onlineModeKickExistingPlayers, PingPassthroughMode pingPassthrough,
|
||||
boolean enablePlayerAddressLogging, Servers servers,ForcedHosts forcedHosts,
|
||||
boolean enablePlayerAddressLogging, Servers servers, ForcedHosts forcedHosts,
|
||||
Advanced advanced, Query query, Metrics metrics, boolean forceKeyAuthentication) {
|
||||
this.bind = bind;
|
||||
this.motd = motd;
|
||||
@ -503,6 +502,14 @@ public class VelocityConfiguration implements ProxyConfig {
|
||||
}
|
||||
forwardingSecret = forwardingSecretString.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
if (configVersion == 1.0 || configVersion == 2.0) {
|
||||
config.set("force-key-authentication", config.getOrElse("force-key-authentication", true));
|
||||
config.setComment("force-key-authentication",
|
||||
"Should the proxy enforce the new public key security standard? By default, this is on.");
|
||||
config.set("config-version", configVersion == 2.0 ? "2.5" : "1.5");
|
||||
mustResave = true;
|
||||
}
|
||||
|
||||
// Handle any cases where the config needs to be saved again
|
||||
if (mustResave) {
|
||||
config.save();
|
||||
|
@ -48,6 +48,7 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.LegacyChat;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChat;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletion;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatPreview;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerCommand;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.ServerChatPreview;
|
||||
@ -263,6 +264,10 @@ public interface MinecraftSessionHandler {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean handle(PlayerChatCompletion packet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean handle(ServerData serverData) {
|
||||
return false;
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ public class VelocityConstants {
|
||||
public static final String VELOCITY_IP_FORWARDING_CHANNEL = "velocity:player_info";
|
||||
public static final int MODERN_FORWARDING_DEFAULT = 1;
|
||||
public static final int MODERN_FORWARDING_WITH_KEY = 2;
|
||||
public static final int MODERN_FORWARDING_WITH_KEY_V2 = 3;
|
||||
public static final int MODERN_FORWARDING_MAX_VERSION = MODERN_FORWARDING_WITH_KEY_V2;
|
||||
|
||||
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
|
||||
}
|
||||
|
@ -55,7 +55,6 @@ import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.handler.timeout.ReadTimeoutException;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@ -281,7 +280,7 @@ public class BackendPlaySessionHandler implements MinecraftSessionHandler {
|
||||
this.playerConnection.write(
|
||||
new ServerData(pingEvent.getPing().getDescriptionComponent(),
|
||||
pingEvent.getPing().getFavicon().orElse(null),
|
||||
packet.isPreviewsChat())
|
||||
packet.isPreviewsChat(), packet.isSecureChatEnforced())
|
||||
), playerConnection.eventLoop());
|
||||
return true;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.connection.VelocityConstants;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
|
||||
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults.Impl;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
@ -81,17 +82,13 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN
|
||||
&& packet.getChannel().equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
|
||||
|
||||
int proposedForwardingVersion = VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
int requestedForwardingVersion = VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
// Check version
|
||||
if (packet.content().readableBytes() == 1) {
|
||||
int requested = packet.content().readByte();
|
||||
Preconditions.checkArgument(requested >= VelocityConstants.MODERN_FORWARDING_DEFAULT,
|
||||
"Invalid modern forwarding version");
|
||||
proposedForwardingVersion = Math.min(requested, VelocityConstants.MODERN_FORWARDING_WITH_KEY);
|
||||
requestedForwardingVersion = packet.content().readByte();
|
||||
}
|
||||
ByteBuf forwardingData = createForwardingData(configuration.getForwardingSecret(),
|
||||
serverConn.getPlayerRemoteAddressAsString(), serverConn.getPlayer().getGameProfile(),
|
||||
serverConn.getPlayer().getIdentifiedKey(), proposedForwardingVersion);
|
||||
serverConn.getPlayerRemoteAddressAsString(), serverConn.getPlayer(), requestedForwardingVersion);
|
||||
|
||||
LoginPluginResponse response = new LoginPluginResponse(packet.getId(), true, forwardingData);
|
||||
mc.write(response);
|
||||
@ -176,24 +173,61 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private static int findForwardingVersion(int requested, ConnectedPlayer player) {
|
||||
// Ensure we are in range
|
||||
requested = Math.min(requested, VelocityConstants.MODERN_FORWARDING_MAX_VERSION);
|
||||
if (requested > VelocityConstants.MODERN_FORWARDING_DEFAULT) {
|
||||
if (player.getIdentifiedKey() != null) {
|
||||
// No enhanced switch on java 11
|
||||
switch (player.getIdentifiedKey().getKeyRevision()) {
|
||||
case GENERIC_V1:
|
||||
return VelocityConstants.MODERN_FORWARDING_WITH_KEY;
|
||||
// Since V2 is not backwards compatible we have to throw the key if v2 and requested is v1
|
||||
case LINKED_V2:
|
||||
return requested >= VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2
|
||||
? VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2 : VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
default:
|
||||
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
}
|
||||
} else {
|
||||
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
}
|
||||
}
|
||||
return VelocityConstants.MODERN_FORWARDING_DEFAULT;
|
||||
}
|
||||
|
||||
private static ByteBuf createForwardingData(byte[] hmacSecret, String address,
|
||||
GameProfile profile, @Nullable IdentifiedKey playerKey,
|
||||
int requestedVersion) {
|
||||
ConnectedPlayer player, int requestedVersion) {
|
||||
ByteBuf forwarded = Unpooled.buffer(2048);
|
||||
try {
|
||||
int actualVersion = requestedVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY
|
||||
? (playerKey != null ? requestedVersion : VelocityConstants.MODERN_FORWARDING_DEFAULT) : requestedVersion;
|
||||
int actualVersion = findForwardingVersion(requestedVersion, player);
|
||||
|
||||
ProtocolUtils.writeVarInt(forwarded, actualVersion);
|
||||
ProtocolUtils.writeString(forwarded, address);
|
||||
ProtocolUtils.writeUuid(forwarded, profile.getId());
|
||||
ProtocolUtils.writeString(forwarded, profile.getName());
|
||||
ProtocolUtils.writeProperties(forwarded, profile.getProperties());
|
||||
ProtocolUtils.writeUuid(forwarded, player.getGameProfile().getId());
|
||||
ProtocolUtils.writeString(forwarded, player.getGameProfile().getName());
|
||||
ProtocolUtils.writeProperties(forwarded, player.getGameProfile().getProperties());
|
||||
|
||||
// This serves as additional redundancy. The key normally is stored in the
|
||||
// login start to the server, but some setups require this.
|
||||
if (actualVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY) {
|
||||
ProtocolUtils.writePlayerKey(forwarded, playerKey);
|
||||
IdentifiedKey key = player.getIdentifiedKey();
|
||||
assert key != null;
|
||||
ProtocolUtils.writePlayerKey(forwarded, key);
|
||||
|
||||
// Provide the signer UUID since the UUID may differ from the
|
||||
// assigned UUID. Doing that breaks the signatures anyway but the server
|
||||
// should be able to verify the key independently.
|
||||
if (actualVersion >= VelocityConstants.MODERN_FORWARDING_WITH_KEY_V2) {
|
||||
if (key.getSignatureHolder() != null) {
|
||||
forwarded.writeBoolean(true);
|
||||
ProtocolUtils.writeUuid(forwarded, key.getSignatureHolder());
|
||||
} else {
|
||||
// Should only not be provided if the player was connected
|
||||
// as offline-mode and the signer UUID was not backfilled
|
||||
forwarded.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SecretKey key = new SecretKeySpec(hmacSecret, "HmacSHA256");
|
||||
|
@ -27,6 +27,7 @@ import com.velocitypowered.api.event.permission.PermissionsSetupEvent;
|
||||
import com.velocitypowered.api.event.player.GameProfileRequestEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerChooseInitialServerEvent;
|
||||
import com.velocitypowered.api.permission.PermissionFunction;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.api.util.UuidUtils;
|
||||
@ -35,10 +36,12 @@ import com.velocitypowered.proxy.config.PlayerInfoForwarding;
|
||||
import com.velocitypowered.proxy.config.VelocityConfiguration;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
|
||||
import com.velocitypowered.proxy.protocol.StateRegistry;
|
||||
import com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess;
|
||||
import com.velocitypowered.proxy.protocol.packet.SetCompression;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -131,6 +134,32 @@ public class AuthSessionHandler implements MinecraftSessionHandler {
|
||||
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.NONE) {
|
||||
playerUniqueId = UuidUtils.generateOfflinePlayerUuid(player.getUsername());
|
||||
}
|
||||
|
||||
if (player.getIdentifiedKey() != null) {
|
||||
IdentifiedKey playerKey = player.getIdentifiedKey();
|
||||
if (playerKey.getSignatureHolder() == null) {
|
||||
if (playerKey instanceof IdentifiedKeyImpl) {
|
||||
IdentifiedKeyImpl unlinkedKey = (IdentifiedKeyImpl) playerKey;
|
||||
// Failsafe
|
||||
if (!unlinkedKey.internalAddHolder(player.getUniqueId())) {
|
||||
if (onlineMode) {
|
||||
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
|
||||
return;
|
||||
} else {
|
||||
logger.warn("Key for player " + player.getUsername() + " could not be verified!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logger.warn("A custom key type has been set for player " + player.getUsername());
|
||||
}
|
||||
} else {
|
||||
if (!Objects.equals(playerKey.getSignatureHolder(), playerUniqueId)) {
|
||||
logger.warn("UUID for Player " + player.getUsername() + " mismatches! "
|
||||
+ "Chat/Commands signatures will not work correctly for this player!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerLoginSuccess success = new ServerLoginSuccess();
|
||||
success.setUsername(player.getUsername());
|
||||
success.setProperties(player.getGameProfileProperties());
|
||||
|
@ -32,6 +32,7 @@ import com.velocitypowered.api.event.player.PlayerChatEvent;
|
||||
import com.velocitypowered.api.event.player.PlayerClientBrandEvent;
|
||||
import com.velocitypowered.api.event.player.TabCompleteEvent;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import com.velocitypowered.api.proxy.messages.ChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.LegacyChannelIdentifier;
|
||||
import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier;
|
||||
@ -67,7 +68,6 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@ -170,15 +170,39 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (chatResult.isAllowed()) {
|
||||
Optional<String> eventMsg = pme.getResult().getMessage();
|
||||
if (eventMsg.isPresent()) {
|
||||
if (player.getProtocolVersion().compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0
|
||||
&& player.getIdentifiedKey() != null) {
|
||||
logger.warn("A plugin changed a signed chat message. The server may not accept it.");
|
||||
String messageNew = eventMsg.get();
|
||||
if (player.getIdentifiedKey() != null) {
|
||||
if (!messageNew.equals(signedMessage.getMessage())) {
|
||||
if (player.getIdentifiedKey().getKeyRevision().compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
|
||||
// Bad, very bad.
|
||||
logger.fatal("A plugin tried to change a signed chat message. "
|
||||
+ "This is no longer possible in 1.19.1 and newer. "
|
||||
+ "Disconnecting player " + player.getUsername());
|
||||
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
|
||||
+ "Contact your network administrator."));
|
||||
} else {
|
||||
logger.warn("A plugin changed a signed chat message. The server may not accept it.");
|
||||
smc.write(ChatBuilder.builder(player.getProtocolVersion())
|
||||
.message(messageNew).toServer());
|
||||
}
|
||||
} else {
|
||||
smc.write(original);
|
||||
}
|
||||
} else {
|
||||
smc.write(ChatBuilder.builder(player.getProtocolVersion())
|
||||
.message(messageNew).toServer());
|
||||
}
|
||||
smc.write(ChatBuilder.builder(player.getProtocolVersion())
|
||||
.message(event.getMessage()).toServer());
|
||||
} else {
|
||||
smc.write(original);
|
||||
}
|
||||
} else {
|
||||
if (player.getIdentifiedKey().getKeyRevision().compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
|
||||
logger.fatal("A plugin tried to cancel a signed chat message."
|
||||
+ " This is no longer possible in 1.19.1 and newer. "
|
||||
+ "Disconnecting player " + player.getUsername());
|
||||
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
|
||||
+ "Contact your network administrator."));
|
||||
}
|
||||
}
|
||||
}, smc.eventLoop())
|
||||
.exceptionally((ex) -> {
|
||||
@ -702,7 +726,16 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
private CompletableFuture<Void> processCommandExecuteResult(String originalCommand,
|
||||
CommandResult result,
|
||||
@Nullable SignedChatCommand signedCommand) {
|
||||
if (result == CommandResult.denied()) {
|
||||
IdentifiedKey playerKey = player.getIdentifiedKey();
|
||||
if (result == CommandResult.denied() && playerKey != null) {
|
||||
if (signedCommand != null && playerKey.getKeyRevision()
|
||||
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
|
||||
logger.fatal("A plugin tried to deny a command with signable component(s). "
|
||||
+ "This is not supported. "
|
||||
+ "Disconnecting player " + player.getUsername());
|
||||
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
|
||||
+ "Contact your network administrator."));
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@ -724,6 +757,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (signedCommand != null && commandToRun.equals(signedCommand.getBaseCommand())) {
|
||||
write.message(signedCommand);
|
||||
} else {
|
||||
if (signedCommand != null && playerKey != null && playerKey.getKeyRevision()
|
||||
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
|
||||
logger.fatal("A plugin tried to change a command with signed component(s). "
|
||||
+ "This is not supported. "
|
||||
+ "Disconnecting player " + player.getUsername());
|
||||
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
|
||||
+ "Contact your network administrator."));
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
write.message("/" + commandToRun);
|
||||
}
|
||||
return CompletableFuture.runAsync(() -> smc.write(write.toServer()), smc.eventLoop());
|
||||
@ -738,6 +780,15 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
if (signedCommand != null && commandToRun.equals(signedCommand.getBaseCommand())) {
|
||||
write.message(signedCommand);
|
||||
} else {
|
||||
if (signedCommand != null && playerKey != null && playerKey.getKeyRevision()
|
||||
.compareTo(IdentifiedKey.Revision.LINKED_V2) >= 0) {
|
||||
logger.fatal("A plugin tried to change a command with signed component(s). "
|
||||
+ "This is not supported. "
|
||||
+ "Disconnecting player " + player.getUsername());
|
||||
player.disconnect(Component.text("A proxy plugin caused an illegal protocol state. "
|
||||
+ "Contact your network administrator."));
|
||||
return;
|
||||
}
|
||||
write.message("/" + commandToRun);
|
||||
}
|
||||
smc.write(write.toServer());
|
||||
@ -746,6 +797,10 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCommandForward() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately send any queued messages to the server.
|
||||
*/
|
||||
|
@ -1068,7 +1068,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player,
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifiedKey getIdentifiedKey() {
|
||||
public @Nullable IdentifiedKey getIdentifiedKey() {
|
||||
return playerKey;
|
||||
}
|
||||
|
||||
|
@ -25,14 +25,19 @@ import static com.velocitypowered.proxy.crypto.EncryptionUtils.generateServerId;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent;
|
||||
import com.velocitypowered.api.event.connection.PreLoginEvent.PreLoginComponentResult;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import com.velocitypowered.api.util.GameProfile;
|
||||
import com.velocitypowered.api.util.UuidUtils;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.crypto.IdentifiedKeyImpl;
|
||||
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
|
||||
import com.velocitypowered.proxy.protocol.packet.EncryptionRequest;
|
||||
import com.velocitypowered.proxy.protocol.packet.EncryptionResponse;
|
||||
@ -45,6 +50,7 @@ import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import net.kyori.adventure.text.Component;
|
||||
@ -54,6 +60,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.asynchttpclient.ListenableFuture;
|
||||
import org.asynchttpclient.Response;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class InitialLoginSessionHandler implements MinecraftSessionHandler {
|
||||
|
||||
@ -75,7 +82,8 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
|
||||
this.server = Preconditions.checkNotNull(server, "server");
|
||||
this.mcConnection = Preconditions.checkNotNull(mcConnection, "mcConnection");
|
||||
this.inbound = Preconditions.checkNotNull(inbound, "inbound");
|
||||
this.forceKeyAuthentication = Boolean.getBoolean("auth.forceSecureProfiles");
|
||||
this.forceKeyAuthentication = System.getProperties().containsKey("auth.forceSecureProfiles")
|
||||
? Boolean.getBoolean("auth.forceSecureProfiles") : server.getConfiguration().isForceKeyAuthentication();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -89,7 +97,16 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!playerKey.isSignatureValid()) {
|
||||
boolean isKeyValid;
|
||||
if (playerKey.getKeyRevision() == IdentifiedKey.Revision.LINKED_V2
|
||||
&& playerKey instanceof IdentifiedKeyImpl) {
|
||||
IdentifiedKeyImpl keyImpl = (IdentifiedKeyImpl) playerKey;
|
||||
isKeyValid = keyImpl.internalAddHolder(packet.getHolderUuid());
|
||||
} else {
|
||||
isKeyValid = playerKey.isSignatureValid();
|
||||
}
|
||||
|
||||
if (!isKeyValid) {
|
||||
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
|
||||
return true;
|
||||
}
|
||||
@ -133,7 +150,7 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
|
||||
this.currentState = LoginState.ENCRYPTION_REQUEST_SENT;
|
||||
} else {
|
||||
mcConnection.setSessionHandler(new AuthSessionHandler(
|
||||
server, inbound, GameProfile.forOfflinePlayer(login.getUsername()), false
|
||||
server, inbound, GameProfile.forOfflinePlayer(login.getUsername()), false
|
||||
));
|
||||
}
|
||||
});
|
||||
@ -214,9 +231,19 @@ public class InitialLoginSessionHandler implements MinecraftSessionHandler {
|
||||
try {
|
||||
Response profileResponse = hasJoinedResponse.get();
|
||||
if (profileResponse.getStatusCode() == 200) {
|
||||
final GameProfile profile = GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class);
|
||||
// Not so fast, now we verify the public key for 1.19.1+
|
||||
if (inbound.getIdentifiedKey() != null
|
||||
&& inbound.getIdentifiedKey().getKeyRevision() == IdentifiedKey.Revision.LINKED_V2
|
||||
&& inbound.getIdentifiedKey() instanceof IdentifiedKeyImpl) {
|
||||
IdentifiedKeyImpl key = (IdentifiedKeyImpl) inbound.getIdentifiedKey();
|
||||
if (!key.internalAddHolder(profile.getId())) {
|
||||
inbound.disconnect(Component.translatable("multiplayer.disconnect.invalid_public_key"));
|
||||
}
|
||||
}
|
||||
// All went well, initialize the session.
|
||||
mcConnection.setSessionHandler(new AuthSessionHandler(
|
||||
server, inbound, GENERAL_GSON.fromJson(profileResponse.getResponseBody(), GameProfile.class), true
|
||||
server, inbound, profile, true
|
||||
));
|
||||
} else if (profileResponse.getStatusCode() == 204) {
|
||||
// Apparently an offline-mode user logged onto this online-mode proxy.
|
||||
|
@ -20,11 +20,9 @@ package com.velocitypowered.proxy.connection.registry;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.kyori.adventure.nbt.BinaryTag;
|
||||
import net.kyori.adventure.nbt.BinaryTagTypes;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
|
@ -27,7 +27,6 @@ import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.util.ClosestLocaleMatcher;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.kyori.adventure.audience.MessageType;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.permission.PermissionChecker;
|
||||
|
@ -21,7 +21,6 @@ import com.google.common.base.Preconditions;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
|
@ -18,29 +18,37 @@
|
||||
package com.velocitypowered.proxy.crypto;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.PublicKey;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class IdentifiedKeyImpl implements IdentifiedKey {
|
||||
|
||||
private final Revision revision;
|
||||
private final PublicKey publicKey;
|
||||
private final byte[] signature;
|
||||
private final Instant expiryTemporal;
|
||||
private @MonotonicNonNull Boolean isSignatureValid;
|
||||
private @MonotonicNonNull UUID holder;
|
||||
|
||||
public IdentifiedKeyImpl(byte[] keyBits, long expiry,
|
||||
public IdentifiedKeyImpl(Revision revision, byte[] keyBits, long expiry,
|
||||
byte[] signature) {
|
||||
this(EncryptionUtils.parseRsaPublicKey(keyBits), Instant.ofEpochMilli(expiry), signature);
|
||||
this(revision, EncryptionUtils.parseRsaPublicKey(keyBits), Instant.ofEpochMilli(expiry), signature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an Identified key from data.
|
||||
*/
|
||||
public IdentifiedKeyImpl(PublicKey publicKey, Instant expiryTemporal, byte[] signature) {
|
||||
public IdentifiedKeyImpl(Revision revision, PublicKey publicKey, Instant expiryTemporal, byte[] signature) {
|
||||
this.revision = revision;
|
||||
this.publicKey = publicKey;
|
||||
this.expiryTemporal = expiryTemporal;
|
||||
this.signature = signature;
|
||||
@ -63,19 +71,68 @@ public class IdentifiedKeyImpl implements IdentifiedKey {
|
||||
|
||||
@Override
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
return signature.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable UUID getSignatureHolder() {
|
||||
return holder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Revision getKeyRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the uuid for this key.
|
||||
* Returns false if incorrect.
|
||||
*/
|
||||
public boolean internalAddHolder(UUID holder) {
|
||||
if (holder == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.holder == null) {
|
||||
Boolean result = validateData(holder);
|
||||
if (result == null || !result) {
|
||||
return false;
|
||||
}
|
||||
isSignatureValid = true;
|
||||
this.holder = holder;
|
||||
return true;
|
||||
}
|
||||
return this.holder.equals(holder) && isSignatureValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSignatureValid() {
|
||||
if (isSignatureValid == null) {
|
||||
isSignatureValid = validateData(holder);
|
||||
}
|
||||
return isSignatureValid != null && isSignatureValid;
|
||||
}
|
||||
|
||||
private Boolean validateData(@Nullable UUID verify) {
|
||||
if (revision == Revision.GENERIC_V1) {
|
||||
String pemKey = EncryptionUtils.pemEncodeRsaKey(publicKey);
|
||||
long expires = expiryTemporal.toEpochMilli();
|
||||
byte[] toVerify = ("" + expires + pemKey).getBytes(StandardCharsets.US_ASCII);
|
||||
isSignatureValid = EncryptionUtils.verifySignature(
|
||||
return EncryptionUtils.verifySignature(
|
||||
EncryptionUtils.SHA1_WITH_RSA, EncryptionUtils.getYggdrasilSessionKey(), signature, toVerify);
|
||||
} else {
|
||||
if (verify == null) {
|
||||
return null;
|
||||
}
|
||||
byte[] keyBytes = publicKey.getEncoded();
|
||||
byte[] toVerify = new byte[keyBytes.length + 24]; // length long * 3
|
||||
ByteBuffer fixedDataSet = ByteBuffer.wrap(toVerify).order(ByteOrder.BIG_ENDIAN);
|
||||
fixedDataSet.putLong(verify.getMostSignificantBits());
|
||||
fixedDataSet.putLong(verify.getLeastSignificantBits());
|
||||
fixedDataSet.putLong(expiryTemporal.toEpochMilli());
|
||||
fixedDataSet.put(keyBytes);
|
||||
return EncryptionUtils.verifySignature(EncryptionUtils.SHA1_WITH_RSA,
|
||||
EncryptionUtils.getYggdrasilSessionKey(), signature, toVerify);
|
||||
}
|
||||
return isSignatureValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,10 +147,12 @@ public class IdentifiedKeyImpl implements IdentifiedKey {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IdentifiedKeyImpl{"
|
||||
+ "publicKey=" + publicKey
|
||||
+ "revision=" + revision
|
||||
+ ", publicKey=" + publicKey
|
||||
+ ", signature=" + Arrays.toString(signature)
|
||||
+ ", expiryTemporal=" + expiryTemporal
|
||||
+ ", isSignatureValid=" + isSignatureValid
|
||||
+ ", holder=" + holder
|
||||
+ '}';
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package com.velocitypowered.proxy.crypto;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SignaturePair {
|
||||
|
||||
private final UUID signer;
|
||||
private final byte[] signature;
|
||||
|
||||
public SignaturePair(UUID signer, byte[] signature) {
|
||||
this.signer = signer;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public UUID getSigner() {
|
||||
return signer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SignaturePair{"
|
||||
+ "signer=" + signer
|
||||
+ ", signature=" + Arrays.toString(signature)
|
||||
+ '}';
|
||||
}
|
||||
}
|
@ -35,12 +35,17 @@ public class SignedChatCommand implements KeySigned {
|
||||
private final boolean isPreviewSigned;
|
||||
|
||||
private final Map<String, byte[]> signatures;
|
||||
private final SignaturePair[] previousSignatures;
|
||||
private final @Nullable SignaturePair lastSignature;
|
||||
|
||||
|
||||
/**
|
||||
* Create a signed command from data.
|
||||
*/
|
||||
public SignedChatCommand(String command, PublicKey signer, UUID sender,
|
||||
Instant expiry, Map<String, byte[]> signature, byte[] salt, boolean isPreviewSigned) {
|
||||
Instant expiry, Map<String, byte[]> signature, byte[] salt,
|
||||
boolean isPreviewSigned, SignaturePair[] previousSignatures,
|
||||
@Nullable SignaturePair lastSignature) {
|
||||
this.command = Preconditions.checkNotNull(command);
|
||||
this.signer = Preconditions.checkNotNull(signer);
|
||||
this.sender = Preconditions.checkNotNull(sender);
|
||||
@ -48,6 +53,8 @@ public class SignedChatCommand implements KeySigned {
|
||||
this.expiry = Preconditions.checkNotNull(expiry);
|
||||
this.salt = Preconditions.checkNotNull(salt);
|
||||
this.isPreviewSigned = isPreviewSigned;
|
||||
this.previousSignatures = previousSignatures;
|
||||
this.lastSignature = lastSignature;
|
||||
|
||||
}
|
||||
|
||||
@ -83,4 +90,12 @@ public class SignedChatCommand implements KeySigned {
|
||||
public boolean isPreviewSigned() {
|
||||
return isPreviewSigned;
|
||||
}
|
||||
|
||||
public SignaturePair getLastSignature() {
|
||||
return lastSignature;
|
||||
}
|
||||
|
||||
public SignaturePair[] getPreviousSignatures() {
|
||||
return previousSignatures;
|
||||
}
|
||||
}
|
||||
|
@ -18,18 +18,13 @@
|
||||
package com.velocitypowered.proxy.crypto;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.velocitypowered.api.proxy.crypto.SignedMessage;
|
||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PublicKey;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAmount;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class SignedChatMessage implements SignedMessage {
|
||||
@ -46,13 +41,17 @@ public class SignedChatMessage implements SignedMessage {
|
||||
private final byte[] salt;
|
||||
private final UUID sender;
|
||||
//private final boolean isValid;
|
||||
private final SignaturePair[] previousSignatures;
|
||||
private final @Nullable SignaturePair previousSignature;
|
||||
private final boolean isPreviewSigned;
|
||||
|
||||
/**
|
||||
* Create a signed message from data.
|
||||
*/
|
||||
public SignedChatMessage(String message, PublicKey signer, UUID sender,
|
||||
Instant expiry, byte[] signature, byte[] salt, boolean isPreviewSigned) {
|
||||
Instant expiry, byte[] signature, byte[] salt,
|
||||
boolean isPreviewSigned, @Nullable SignaturePair[] previousSignatures,
|
||||
@Nullable SignaturePair previousSignature) {
|
||||
this.message = Preconditions.checkNotNull(message);
|
||||
this.signer = Preconditions.checkNotNull(signer);
|
||||
this.sender = Preconditions.checkNotNull(sender);
|
||||
@ -60,7 +59,8 @@ public class SignedChatMessage implements SignedMessage {
|
||||
this.expiry = Preconditions.checkNotNull(expiry);
|
||||
this.salt = Preconditions.checkNotNull(salt);
|
||||
this.isPreviewSigned = isPreviewSigned;
|
||||
|
||||
this.previousSignatures = previousSignatures;
|
||||
this.previousSignature = previousSignature;
|
||||
|
||||
//this.isValid = EncryptionUtils.verifySignature(EncryptionUtils.SHA1_WITH_RSA, signer,
|
||||
// signature, salt, EncryptionUtils.longToBigEndianByteArray(
|
||||
@ -83,6 +83,14 @@ public class SignedChatMessage implements SignedMessage {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public SignaturePair[] getPreviousSignatures() {
|
||||
return previousSignatures;
|
||||
}
|
||||
|
||||
public SignaturePair getPreviousSignature() {
|
||||
return previousSignature;
|
||||
}
|
||||
|
||||
//@Override
|
||||
//public boolean isSignatureValid() {
|
||||
// return isValid;
|
||||
|
@ -18,7 +18,6 @@
|
||||
package com.velocitypowered.proxy.plugin;
|
||||
|
||||
import com.velocitypowered.proxy.Velocity;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
@ -138,7 +138,7 @@ public class VelocityPluginManager implements PluginManager {
|
||||
for (PluginContainer container : pluginContainers.keySet()) {
|
||||
bind(PluginContainer.class)
|
||||
.annotatedWith(Names.named(container.getDescription().getId()))
|
||||
.toInstance(container);
|
||||
.toInstance(container);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -34,7 +34,6 @@ import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.handler.codec.CorruptedFrameException;
|
||||
import io.netty.handler.codec.DecoderException;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
@ -42,7 +41,6 @@ import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.kyori.adventure.nbt.BinaryTagIO;
|
||||
import net.kyori.adventure.nbt.CompoundBinaryTag;
|
||||
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
|
||||
@ -557,11 +555,13 @@ public enum ProtocolUtils {
|
||||
* @param buf the buffer
|
||||
* @return the key
|
||||
*/
|
||||
public static IdentifiedKey readPlayerKey(ByteBuf buf) {
|
||||
public static IdentifiedKey readPlayerKey(ProtocolVersion version, ByteBuf buf) {
|
||||
long expiry = buf.readLong();
|
||||
byte[] key = ProtocolUtils.readByteArray(buf);
|
||||
byte[] signature = ProtocolUtils.readByteArray(buf, 4096);
|
||||
return new IdentifiedKeyImpl(key, expiry, signature);
|
||||
IdentifiedKey.Revision revision = version.compareTo(ProtocolVersion.MINECRAFT_1_19) == 0
|
||||
? IdentifiedKey.Revision.GENERIC_V1 : IdentifiedKey.Revision.LINKED_V2;
|
||||
return new IdentifiedKeyImpl(revision, key, expiry, signature);
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
|
@ -30,6 +30,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_17;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_18_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_19_1;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_8;
|
||||
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
|
||||
@ -67,6 +68,7 @@ import com.velocitypowered.proxy.protocol.packet.TabCompleteRequest;
|
||||
import com.velocitypowered.proxy.protocol.packet.TabCompleteResponse;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.LegacyChat;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChat;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerChatCompletion;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.PlayerCommand;
|
||||
import com.velocitypowered.proxy.protocol.packet.chat.SystemChat;
|
||||
import com.velocitypowered.proxy.protocol.packet.title.LegacyTitlePacket;
|
||||
@ -121,7 +123,8 @@ public enum StateRegistry {
|
||||
map(0x01, MINECRAFT_1_12_1, false),
|
||||
map(0x05, MINECRAFT_1_13, false),
|
||||
map(0x06, MINECRAFT_1_14, false),
|
||||
map(0x08, MINECRAFT_1_19, false));
|
||||
map(0x08, MINECRAFT_1_19, false),
|
||||
map(0x09, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(LegacyChat.class, LegacyChat::new,
|
||||
map(0x01, MINECRAFT_1_7_2, false),
|
||||
map(0x02, MINECRAFT_1_9, false),
|
||||
@ -129,16 +132,19 @@ public enum StateRegistry {
|
||||
map(0x02, MINECRAFT_1_12_1, false),
|
||||
map(0x03, MINECRAFT_1_14, MINECRAFT_1_18_2, false));
|
||||
serverbound.register(PlayerCommand.class, PlayerCommand::new,
|
||||
map(0x03, MINECRAFT_1_19, false));
|
||||
map(0x03, MINECRAFT_1_19, false),
|
||||
map(0x04, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(PlayerChat.class, PlayerChat::new,
|
||||
map(0x04, MINECRAFT_1_19, false));
|
||||
map(0x04, MINECRAFT_1_19, false),
|
||||
map(0x05, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(ClientSettings.class, ClientSettings::new,
|
||||
map(0x15, MINECRAFT_1_7_2, false),
|
||||
map(0x04, MINECRAFT_1_9, false),
|
||||
map(0x05, MINECRAFT_1_12, false),
|
||||
map(0x04, MINECRAFT_1_12_1, false),
|
||||
map(0x05, MINECRAFT_1_14, false),
|
||||
map(0x07, MINECRAFT_1_19, false));
|
||||
map(0x07, MINECRAFT_1_19, false),
|
||||
map(0x08, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(PluginMessage.class, PluginMessage::new,
|
||||
map(0x17, MINECRAFT_1_7_2, false),
|
||||
map(0x09, MINECRAFT_1_9, false),
|
||||
@ -147,7 +153,8 @@ public enum StateRegistry {
|
||||
map(0x0A, MINECRAFT_1_13, false),
|
||||
map(0x0B, MINECRAFT_1_14, false),
|
||||
map(0x0A, MINECRAFT_1_17, false),
|
||||
map(0x0C, MINECRAFT_1_19, false));
|
||||
map(0x0C, MINECRAFT_1_19, false),
|
||||
map(0x0D, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(KeepAlive.class, KeepAlive::new,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x0B, MINECRAFT_1_9, false),
|
||||
@ -157,7 +164,8 @@ public enum StateRegistry {
|
||||
map(0x0F, MINECRAFT_1_14, false),
|
||||
map(0x10, MINECRAFT_1_16, false),
|
||||
map(0x0F, MINECRAFT_1_17, false),
|
||||
map(0x11, MINECRAFT_1_19, false));
|
||||
map(0x11, MINECRAFT_1_19, false),
|
||||
map(0x12, MINECRAFT_1_19_1, false));
|
||||
serverbound.register(ResourcePackResponse.class, ResourcePackResponse::new,
|
||||
map(0x19, MINECRAFT_1_8, false),
|
||||
map(0x16, MINECRAFT_1_9, false),
|
||||
@ -166,7 +174,8 @@ public enum StateRegistry {
|
||||
map(0x1F, MINECRAFT_1_14, false),
|
||||
map(0x20, MINECRAFT_1_16, false),
|
||||
map(0x21, MINECRAFT_1_16_2, false),
|
||||
map(0x23, MINECRAFT_1_19, false));
|
||||
map(0x23, MINECRAFT_1_19, false),
|
||||
map(0x24, MINECRAFT_1_19_1, false));
|
||||
|
||||
clientbound.register(BossBar.class, BossBar::new,
|
||||
map(0x0C, MINECRAFT_1_9, false),
|
||||
@ -206,7 +215,8 @@ public enum StateRegistry {
|
||||
map(0x18, MINECRAFT_1_16, false),
|
||||
map(0x17, MINECRAFT_1_16_2, false),
|
||||
map(0x18, MINECRAFT_1_17, false),
|
||||
map(0x15, MINECRAFT_1_19, false));
|
||||
map(0x15, MINECRAFT_1_19, false),
|
||||
map(0x16, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(Disconnect.class, Disconnect::new,
|
||||
map(0x40, MINECRAFT_1_7_2, false),
|
||||
map(0x1A, MINECRAFT_1_9, false),
|
||||
@ -216,7 +226,8 @@ public enum StateRegistry {
|
||||
map(0x1A, MINECRAFT_1_16, false),
|
||||
map(0x19, MINECRAFT_1_16_2, false),
|
||||
map(0x1A, MINECRAFT_1_17, false),
|
||||
map(0x17, MINECRAFT_1_19, false));
|
||||
map(0x17, MINECRAFT_1_19, false),
|
||||
map(0x19, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(KeepAlive.class, KeepAlive::new,
|
||||
map(0x00, MINECRAFT_1_7_2, false),
|
||||
map(0x1F, MINECRAFT_1_9, false),
|
||||
@ -226,7 +237,8 @@ public enum StateRegistry {
|
||||
map(0x20, MINECRAFT_1_16, false),
|
||||
map(0x1F, MINECRAFT_1_16_2, false),
|
||||
map(0x21, MINECRAFT_1_17, false),
|
||||
map(0x1E, MINECRAFT_1_19, false));
|
||||
map(0x1E, MINECRAFT_1_19, false),
|
||||
map(0x20, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(JoinGame.class, JoinGame::new,
|
||||
map(0x01, MINECRAFT_1_7_2, false),
|
||||
map(0x23, MINECRAFT_1_9, false),
|
||||
@ -236,7 +248,8 @@ public enum StateRegistry {
|
||||
map(0x25, MINECRAFT_1_16, false),
|
||||
map(0x24, MINECRAFT_1_16_2, false),
|
||||
map(0x26, MINECRAFT_1_17, false),
|
||||
map(0x23, MINECRAFT_1_19, false));
|
||||
map(0x23, MINECRAFT_1_19, false),
|
||||
map(0x25, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(Respawn.class, Respawn::new,
|
||||
map(0x07, MINECRAFT_1_7_2, true),
|
||||
map(0x33, MINECRAFT_1_9, true),
|
||||
@ -248,7 +261,8 @@ public enum StateRegistry {
|
||||
map(0x3A, MINECRAFT_1_16, true),
|
||||
map(0x39, MINECRAFT_1_16_2, true),
|
||||
map(0x3D, MINECRAFT_1_17, true),
|
||||
map(0x3B, MINECRAFT_1_19, true));
|
||||
map(0x3B, MINECRAFT_1_19, true),
|
||||
map(0x3E, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new,
|
||||
map(0x48, MINECRAFT_1_8, false),
|
||||
map(0x32, MINECRAFT_1_9, false),
|
||||
@ -260,7 +274,8 @@ public enum StateRegistry {
|
||||
map(0x39, MINECRAFT_1_16, false),
|
||||
map(0x38, MINECRAFT_1_16_2, false),
|
||||
map(0x3C, MINECRAFT_1_17, false),
|
||||
map(0x3A, MINECRAFT_1_19, false));
|
||||
map(0x3A, MINECRAFT_1_19, false),
|
||||
map(0x3D, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
|
||||
map(0x47, MINECRAFT_1_8, true),
|
||||
map(0x48, MINECRAFT_1_9, true),
|
||||
@ -273,7 +288,8 @@ public enum StateRegistry {
|
||||
map(0x53, MINECRAFT_1_16, true),
|
||||
map(0x5E, MINECRAFT_1_17, true),
|
||||
map(0x5F, MINECRAFT_1_18, true),
|
||||
map(0x60, MINECRAFT_1_19, true));
|
||||
map(0x60, MINECRAFT_1_19, true),
|
||||
map(0x63, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(LegacyTitlePacket.class, LegacyTitlePacket::new,
|
||||
map(0x45, MINECRAFT_1_8, true),
|
||||
map(0x45, MINECRAFT_1_9, true),
|
||||
@ -285,16 +301,20 @@ public enum StateRegistry {
|
||||
map(0x4F, MINECRAFT_1_16, MINECRAFT_1_16_4, true));
|
||||
clientbound.register(TitleSubtitlePacket.class, TitleSubtitlePacket::new,
|
||||
map(0x57, MINECRAFT_1_17, true),
|
||||
map(0x58, MINECRAFT_1_18, true));
|
||||
map(0x58, MINECRAFT_1_18, true),
|
||||
map(0x5B, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(TitleTextPacket.class, TitleTextPacket::new,
|
||||
map(0x59, MINECRAFT_1_17, true),
|
||||
map(0x5A, MINECRAFT_1_18, true));
|
||||
map(0x5A, MINECRAFT_1_18, true),
|
||||
map(0x5D, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(TitleActionbarPacket.class, TitleActionbarPacket::new,
|
||||
map(0x41, MINECRAFT_1_17, true),
|
||||
map(0x40, MINECRAFT_1_19, true));
|
||||
map(0x40, MINECRAFT_1_19, true),
|
||||
map(0x43, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(TitleTimesPacket.class, TitleTimesPacket::new,
|
||||
map(0x5A, MINECRAFT_1_17, true),
|
||||
map(0x5B, MINECRAFT_1_18, true));
|
||||
map(0x5B, MINECRAFT_1_18, true),
|
||||
map(0x5E, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(TitleClearPacket.class, TitleClearPacket::new,
|
||||
map(0x10, MINECRAFT_1_17, true),
|
||||
map(0x0D, MINECRAFT_1_19, true));
|
||||
@ -308,11 +328,16 @@ public enum StateRegistry {
|
||||
map(0x33, MINECRAFT_1_16, false),
|
||||
map(0x32, MINECRAFT_1_16_2, false),
|
||||
map(0x36, MINECRAFT_1_17, false),
|
||||
map(0x34, MINECRAFT_1_19, false));
|
||||
map(0x34, MINECRAFT_1_19, false),
|
||||
map(0x37, MINECRAFT_1_19_1, false));
|
||||
clientbound.register(SystemChat.class, SystemChat::new,
|
||||
map(0x5F, MINECRAFT_1_19, true));
|
||||
map(0x5F, MINECRAFT_1_19, true),
|
||||
map(0x62, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(PlayerChatCompletion.class, PlayerChatCompletion::new,
|
||||
StateRegistry.map(0x15, MINECRAFT_1_19_1, true));
|
||||
clientbound.register(ServerData.class, ServerData::new,
|
||||
map(0x3F, MINECRAFT_1_19, false));
|
||||
map(0x3F, MINECRAFT_1_19, false),
|
||||
map(0x42, MINECRAFT_1_19_1, false));
|
||||
}
|
||||
},
|
||||
LOGIN {
|
||||
|
@ -46,7 +46,6 @@ import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
public class GS4QueryHandler extends SimpleChannelInboundHandler<DatagramPacket> {
|
||||
|
@ -78,7 +78,7 @@ public class PlayerListItem implements MinecraftPacket {
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
if (buf.readBoolean()) {
|
||||
item.setPlayerKey(ProtocolUtils.readPlayerKey(buf));
|
||||
item.setPlayerKey(ProtocolUtils.readPlayerKey(version, buf));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -31,15 +31,17 @@ public class ServerData implements MinecraftPacket {
|
||||
private @Nullable Component description;
|
||||
private @Nullable Favicon favicon;
|
||||
private boolean previewsChat;
|
||||
private boolean secureChatEnforced; // Added in 1.19.1
|
||||
|
||||
public ServerData() {
|
||||
}
|
||||
|
||||
public ServerData(@Nullable Component description, @Nullable Favicon favicon,
|
||||
boolean previewsChat) {
|
||||
boolean previewsChat, boolean secureChatEnforced) {
|
||||
this.description = description;
|
||||
this.favicon = favicon;
|
||||
this.previewsChat = previewsChat;
|
||||
this.secureChatEnforced = secureChatEnforced;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,6 +55,9 @@ public class ServerData implements MinecraftPacket {
|
||||
this.favicon = new Favicon(ProtocolUtils.readString(buf));
|
||||
}
|
||||
this.previewsChat = buf.readBoolean();
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
this.secureChatEnforced = buf.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,6 +78,9 @@ public class ServerData implements MinecraftPacket {
|
||||
}
|
||||
|
||||
buf.writeBoolean(this.previewsChat);
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
buf.writeBoolean(this.secureChatEnforced);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,4 +99,8 @@ public class ServerData implements MinecraftPacket {
|
||||
public boolean isPreviewsChat() {
|
||||
return previewsChat;
|
||||
}
|
||||
|
||||
public boolean isSecureChatEnforced() {
|
||||
return secureChatEnforced;
|
||||
}
|
||||
}
|
||||
|
@ -28,12 +28,15 @@ import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ServerLogin implements MinecraftPacket {
|
||||
|
||||
private static final QuietDecoderException EMPTY_USERNAME = new QuietDecoderException("Empty username!");
|
||||
|
||||
private @Nullable String username;
|
||||
private @Nullable IdentifiedKey playerKey; // Introduced in 1.19
|
||||
private @Nullable UUID holderUuid; // Used for key revision 2
|
||||
|
||||
public ServerLogin() {
|
||||
}
|
||||
@ -58,6 +61,10 @@ public class ServerLogin implements MinecraftPacket {
|
||||
this.playerKey = playerKey;
|
||||
}
|
||||
|
||||
public UUID getHolderUuid() {
|
||||
return holderUuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ServerLogin{"
|
||||
@ -75,7 +82,13 @@ public class ServerLogin implements MinecraftPacket {
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
if (buf.readBoolean()) {
|
||||
playerKey = ProtocolUtils.readPlayerKey(buf);
|
||||
playerKey = ProtocolUtils.readPlayerKey(version, buf);
|
||||
}
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
if (buf.readBoolean()) {
|
||||
holderUuid = ProtocolUtils.readUuid(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,6 +107,15 @@ public class ServerLogin implements MinecraftPacket {
|
||||
} else {
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
if (playerKey != null && playerKey.getSignatureHolder() != null) {
|
||||
buf.writeBoolean(true);
|
||||
ProtocolUtils.writeUuid(buf, playerKey.getSignatureHolder());
|
||||
} else {
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,9 +124,20 @@ public class ServerLogin implements MinecraftPacket {
|
||||
// Accommodate the rare (but likely malicious) use of UTF-8 usernames, since it is technically
|
||||
// legal on the protocol level.
|
||||
int base = 1 + (16 * 4);
|
||||
// Adjustments for Key-authentication
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
return -1;
|
||||
//TODO: ## 19
|
||||
// + 1 for the boolean present/ not present
|
||||
// + 8 for the long expiry
|
||||
// + 2 len for varint key size
|
||||
// + 294 for the key
|
||||
// + 2 len for varint signature size
|
||||
// + 512 for signature
|
||||
base += 1 + 8 + 2 + 294 + 2 + 512;
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
// +1 boolean uuid optional
|
||||
// + 2 * 8 for the long msb/lsb
|
||||
base += 1 + 8 + 8;
|
||||
}
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
@ -23,7 +23,6 @@ import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
@ -19,7 +19,6 @@ package com.velocitypowered.proxy.protocol.packet.brigadier;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class RegistryKeyArgumentSerializer implements ArgumentPropertySerializer<RegistryKeyArgument> {
|
||||
|
@ -133,7 +133,7 @@ public class ChatBuilder {
|
||||
|
||||
if (version.compareTo(ProtocolVersion.MINECRAFT_1_19) >= 0) {
|
||||
// hard override chat > system for now
|
||||
return new SystemChat(msg, type == ChatType.CHAT ? ChatType.SYSTEM.getId() : type.getId());
|
||||
return new SystemChat(msg, type == ChatType.CHAT ? ChatType.SYSTEM : type);
|
||||
} else {
|
||||
return new LegacyChat(ProtocolUtils.getJsonChatSerializer(version).serialize(msg), type.getId(), identity);
|
||||
}
|
||||
|
@ -22,9 +22,11 @@ import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.crypto.EncryptionUtils;
|
||||
import com.velocitypowered.proxy.crypto.SignaturePair;
|
||||
import com.velocitypowered.proxy.crypto.SignedChatMessage;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.time.Instant;
|
||||
import java.util.UUID;
|
||||
@ -38,6 +40,13 @@ public class PlayerChat implements MinecraftPacket {
|
||||
private @Nullable Instant expiry;
|
||||
private @Nullable byte[] signature;
|
||||
private @Nullable byte[] salt;
|
||||
private SignaturePair[] previousMessages = new SignaturePair[0];
|
||||
private @Nullable SignaturePair lastMessage;
|
||||
|
||||
public static final int MAXIMUM_PREVIOUS_MESSAGE_COUNT = 5;
|
||||
|
||||
public static final QuietDecoderException INVALID_PREVIOUS_MESSAGES =
|
||||
new QuietDecoderException("Invalid previous messages");
|
||||
|
||||
public PlayerChat() {
|
||||
}
|
||||
@ -58,6 +67,8 @@ public class PlayerChat implements MinecraftPacket {
|
||||
this.salt = message.getSalt();
|
||||
this.signature = message.getSignature();
|
||||
this.signedPreview = message.isPreviewSigned();
|
||||
this.lastMessage = message.getPreviousSignature();
|
||||
this.previousMessages = message.getPreviousSignatures();
|
||||
}
|
||||
|
||||
public Instant getExpiry() {
|
||||
@ -98,6 +109,23 @@ public class PlayerChat implements MinecraftPacket {
|
||||
if (signedPreview && unsigned) {
|
||||
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
int size = ProtocolUtils.readVarInt(buf);
|
||||
if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) {
|
||||
throw INVALID_PREVIOUS_MESSAGES;
|
||||
}
|
||||
|
||||
SignaturePair[] lastSignatures = new SignaturePair[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
previousMessages = lastSignatures;
|
||||
|
||||
if (buf.readBoolean()) {
|
||||
lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,6 +137,23 @@ public class PlayerChat implements MinecraftPacket {
|
||||
ProtocolUtils.writeByteArray(buf, unsigned ? EncryptionUtils.EMPTY : signature);
|
||||
|
||||
buf.writeBoolean(signedPreview);
|
||||
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
ProtocolUtils.writeVarInt(buf, previousMessages.length);
|
||||
for (SignaturePair previousMessage : previousMessages) {
|
||||
ProtocolUtils.writeUuid(buf, previousMessage.getSigner());
|
||||
ProtocolUtils.writeByteArray(buf, previousMessage.getSignature());
|
||||
}
|
||||
|
||||
if (lastMessage != null) {
|
||||
buf.writeBoolean(true);
|
||||
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
|
||||
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
|
||||
} else {
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +174,8 @@ public class PlayerChat implements MinecraftPacket {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SignedChatMessage(message, signer.getSignedPublicKey(), sender, expiry, signature, salt, signedPreview);
|
||||
return new SignedChatMessage(message, signer.getSignedPublicKey(), sender, expiry, signature,
|
||||
salt, signedPreview, previousMessages, lastMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet.chat;
|
||||
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public class PlayerChatCompletion implements MinecraftPacket {
|
||||
|
||||
private String[] completions;
|
||||
private Action action;
|
||||
|
||||
|
||||
public String[] getCompletions() {
|
||||
return completions;
|
||||
}
|
||||
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setCompletions(String[] completions) {
|
||||
this.completions = completions;
|
||||
}
|
||||
|
||||
public void setAction(Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
action = Action.values()[ProtocolUtils.readVarInt(buf)];
|
||||
completions = ProtocolUtils.readStringArray(buf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeVarInt(buf, action.ordinal());
|
||||
ProtocolUtils.writeStringArray(buf, completions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(MinecraftSessionHandler handler) {
|
||||
return handler.handle(this);
|
||||
}
|
||||
|
||||
enum Action {
|
||||
ADD,
|
||||
REMOVE,
|
||||
ALTER
|
||||
}
|
||||
}
|
@ -17,12 +17,16 @@
|
||||
|
||||
package com.velocitypowered.proxy.protocol.packet.chat;
|
||||
|
||||
import static com.velocitypowered.proxy.protocol.packet.chat.PlayerChat.INVALID_PREVIOUS_MESSAGES;
|
||||
import static com.velocitypowered.proxy.protocol.packet.chat.PlayerChat.MAXIMUM_PREVIOUS_MESSAGE_COUNT;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
|
||||
import com.velocitypowered.proxy.crypto.EncryptionUtils;
|
||||
import com.velocitypowered.proxy.crypto.SignaturePair;
|
||||
import com.velocitypowered.proxy.crypto.SignedChatCommand;
|
||||
import com.velocitypowered.proxy.crypto.SignedChatMessage;
|
||||
import com.velocitypowered.proxy.protocol.MinecraftPacket;
|
||||
@ -30,9 +34,11 @@ import com.velocitypowered.proxy.protocol.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.util.except.QuietDecoderException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class PlayerCommand implements MinecraftPacket {
|
||||
|
||||
@ -46,6 +52,8 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
private Instant timestamp;
|
||||
private long salt;
|
||||
private boolean signedPreview; // Good god. Please no.
|
||||
private SignaturePair[] previousMessages = new SignaturePair[0];
|
||||
private @Nullable SignaturePair lastMessage;
|
||||
private Map<String, byte[]> arguments = ImmutableMap.of();
|
||||
|
||||
public boolean isSignedPreview() {
|
||||
@ -96,6 +104,8 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
this.timestamp = signedCommand.getExpiryTemporal();
|
||||
this.salt = Longs.fromByteArray(signedCommand.getSalt());
|
||||
this.signedPreview = signedCommand.isPreviewSigned();
|
||||
this.lastMessage = signedCommand.getLastSignature();
|
||||
this.previousMessages = signedCommand.getPreviousSignatures();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -124,6 +134,24 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
if (unsigned && signedPreview) {
|
||||
throw EncryptionUtils.PREVIEW_SIGNATURE_MISSING;
|
||||
}
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
int size = ProtocolUtils.readVarInt(buf);
|
||||
if (size < 0 || size > MAXIMUM_PREVIOUS_MESSAGE_COUNT) {
|
||||
throw INVALID_PREVIOUS_MESSAGES;
|
||||
}
|
||||
|
||||
SignaturePair[] lastSignatures = new SignaturePair[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
lastSignatures[i] = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
previousMessages = lastSignatures;
|
||||
|
||||
if (buf.readBoolean()) {
|
||||
lastMessage = new SignaturePair(ProtocolUtils.readUuid(buf), ProtocolUtils.readByteArray(buf));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,6 +174,22 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
|
||||
buf.writeBoolean(signedPreview);
|
||||
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
ProtocolUtils.writeVarInt(buf, previousMessages.length);
|
||||
for (SignaturePair previousMessage : previousMessages) {
|
||||
ProtocolUtils.writeUuid(buf, previousMessage.getSigner());
|
||||
ProtocolUtils.writeByteArray(buf, previousMessage.getSignature());
|
||||
}
|
||||
|
||||
if (lastMessage != null) {
|
||||
buf.writeBoolean(true);
|
||||
ProtocolUtils.writeUuid(buf, lastMessage.getSigner());
|
||||
ProtocolUtils.writeByteArray(buf, lastMessage.getSignature());
|
||||
} else {
|
||||
buf.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -158,8 +202,12 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
* @throws com.velocitypowered.proxy.util.except.QuietDecoderException when mustSign is {@code true} and the signature
|
||||
* is invalid.
|
||||
*/
|
||||
public SignedChatCommand signedContainer(IdentifiedKey signer, UUID sender, boolean mustSign) {
|
||||
if (unsigned) {
|
||||
public SignedChatCommand signedContainer(
|
||||
@Nullable IdentifiedKey signer, UUID sender, boolean mustSign) {
|
||||
// There's a certain mod that is very broken that still signs messages but
|
||||
// doesn't provide the player key. This is broken and wrong, but we need to
|
||||
// work around that.
|
||||
if (unsigned || signer == null) {
|
||||
if (mustSign) {
|
||||
throw EncryptionUtils.INVALID_SIGNATURE;
|
||||
}
|
||||
@ -167,7 +215,20 @@ public class PlayerCommand implements MinecraftPacket {
|
||||
}
|
||||
|
||||
return new SignedChatCommand(command, signer.getSignedPublicKey(), sender, timestamp,
|
||||
arguments, Longs.toByteArray(salt), signedPreview);
|
||||
arguments, Longs.toByteArray(salt), signedPreview, previousMessages, lastMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlayerCommand{"
|
||||
+ "unsigned=" + unsigned
|
||||
+ ", command='" + command + '\''
|
||||
+ ", timestamp=" + timestamp
|
||||
+ ", salt=" + salt
|
||||
+ ", signedPreview=" + signedPreview
|
||||
+ ", previousMessages=" + Arrays.toString(previousMessages)
|
||||
+ ", arguments=" + arguments
|
||||
+ '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,15 +28,15 @@ public class SystemChat implements MinecraftPacket {
|
||||
|
||||
public SystemChat() {}
|
||||
|
||||
public SystemChat(Component component, int type) {
|
||||
public SystemChat(Component component, ChatBuilder.ChatType type) {
|
||||
this.component = component;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private Component component;
|
||||
private int type;
|
||||
private ChatBuilder.ChatType type;
|
||||
|
||||
public int getType() {
|
||||
public ChatBuilder.ChatType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@ -47,13 +47,27 @@ public class SystemChat implements MinecraftPacket {
|
||||
@Override
|
||||
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
component = ProtocolUtils.getJsonChatSerializer(protocolVersion).deserialize(ProtocolUtils.readString(buf));
|
||||
type = ProtocolUtils.readVarInt(buf);
|
||||
// System chat is never decoded so this doesn't matter for now
|
||||
type = ChatBuilder.ChatType.values()[ProtocolUtils.readVarInt(buf)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion protocolVersion) {
|
||||
ProtocolUtils.writeString(buf, ProtocolUtils.getJsonChatSerializer(protocolVersion).serialize(component));
|
||||
ProtocolUtils.writeVarInt(buf, type);
|
||||
if (protocolVersion.compareTo(ProtocolVersion.MINECRAFT_1_19_1) >= 0) {
|
||||
switch (type) {
|
||||
case SYSTEM:
|
||||
buf.writeBoolean(false);
|
||||
break;
|
||||
case GAME_INFO:
|
||||
buf.writeBoolean(true);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid chat type");
|
||||
}
|
||||
} else {
|
||||
ProtocolUtils.writeVarInt(buf, type.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -38,7 +38,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
@ -18,6 +18,7 @@
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.crypto.IdentifiedKey;
|
||||
@ -226,8 +227,19 @@ public class VelocityTabList implements TabList {
|
||||
if (entries.containsKey(entry.getProfile().getId())) {
|
||||
PlayerListItem.Item packetItem = PlayerListItem.Item.from(entry);
|
||||
|
||||
IdentifiedKey selectedKey = packetItem.getPlayerKey();
|
||||
Optional<Player> existing = proxyServer.getPlayer(entry.getProfile().getId());
|
||||
existing.ifPresent(value -> packetItem.setPlayerKey(value.getIdentifiedKey()));
|
||||
if (existing.isPresent()) {
|
||||
selectedKey = existing.get().getIdentifiedKey();
|
||||
}
|
||||
|
||||
if (selectedKey != null
|
||||
&& selectedKey.getKeyRevision().getApplicableTo().contains(connection.getProtocolVersion())
|
||||
&& Objects.equals(selectedKey.getSignatureHolder(), entry.getProfile().getId())) {
|
||||
packetItem.setPlayerKey(selectedKey);
|
||||
} else {
|
||||
packetItem.setPlayerKey(null);
|
||||
}
|
||||
|
||||
connection.write(new PlayerListItem(action, Collections.singletonList(packetItem)));
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Config version. Do not change this
|
||||
config-version = "2.0"
|
||||
config-version = "2.5"
|
||||
|
||||
# What port should the proxy be bound to? By default, we'll bind to all addresses on port 25577.
|
||||
bind = "0.0.0.0:25577"
|
||||
|
@ -31,7 +31,6 @@ import com.velocitypowered.proxy.testutil.FakePluginManager;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DynamicNode;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
|
@ -38,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.proxy.protocol.packet.Handshake;
|
||||
import com.velocitypowered.proxy.protocol.packet.StatusPing;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PacketRegistryTest {
|
||||
|
@ -22,13 +22,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||
import com.velocitypowered.api.scheduler.TaskStatus;
|
||||
import com.velocitypowered.proxy.testutil.FakePluginManager;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class VelocitySchedulerTest {
|
||||
|
@ -22,7 +22,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import com.velocitypowered.proxy.crypto.EncryptionUtils;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class EncryptionUtilsTest {
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren