13
0
geforkt von Mirrors/Velocity

Merge pull request #351 from VelocityPowered/future/1.16.2-velocity-1.1.0

[Future] 1.16.2 for Velocity 1.1.0
Dieser Commit ist enthalten in:
Andrew Steinborn 2020-08-10 21:46:41 -04:00 committet von GitHub
Commit d83c5327c7
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 4AEE18F83AFDEB23
13 geänderte Dateien mit 462 neuen und 137 gelöschten Zeilen

Datei anzeigen

@ -38,7 +38,8 @@ public enum ProtocolVersion {
MINECRAFT_1_15_1(575, "1.15.1"), MINECRAFT_1_15_1(575, "1.15.1"),
MINECRAFT_1_15_2(578, "1.15.2"), MINECRAFT_1_15_2(578, "1.15.2"),
MINECRAFT_1_16(735, "1.16"), MINECRAFT_1_16(735, "1.16"),
MINECRAFT_1_16_1(736, "1.16.1"); MINECRAFT_1_16_1(736, "1.16.1"),
MINECRAFT_1_16_2(751, "1.16.2");
private final int protocol; private final int protocol;
private final String name; private final String name;

Datei anzeigen

@ -63,7 +63,7 @@ dependencies {
compile 'it.unimi.dsi:fastutil:8.2.3' compile 'it.unimi.dsi:fastutil:8.2.3'
compile 'net.kyori:event-method-asm:4.0.0-SNAPSHOT' compile 'net.kyori:event-method-asm:4.0.0-SNAPSHOT'
compile 'net.kyori:nbt:1.12-1.0.0-SNAPSHOT' compile 'net.kyori:adventure-nbt:4.0.0-SNAPSHOT'
compile 'com.mojang:brigadier:1.0.17' compile 'com.mojang:brigadier:1.0.17'

Datei anzeigen

@ -32,6 +32,7 @@ import com.velocitypowered.proxy.console.VelocityConsole;
import com.velocitypowered.proxy.network.ConnectionManager; import com.velocitypowered.proxy.network.ConnectionManager;
import com.velocitypowered.proxy.plugin.VelocityEventManager; import com.velocitypowered.proxy.plugin.VelocityEventManager;
import com.velocitypowered.proxy.plugin.VelocityPluginManager; import com.velocitypowered.proxy.plugin.VelocityPluginManager;
import com.velocitypowered.proxy.protocol.ProtocolUtils;
import com.velocitypowered.proxy.protocol.packet.Chat; import com.velocitypowered.proxy.protocol.packet.Chat;
import com.velocitypowered.proxy.protocol.util.FaviconSerializer; import com.velocitypowered.proxy.protocol.util.FaviconSerializer;
import com.velocitypowered.proxy.protocol.util.GameProfileSerializer; import com.velocitypowered.proxy.protocol.util.GameProfileSerializer;
@ -91,13 +92,14 @@ public class VelocityServer implements ProxyServer, ForwardingAudience {
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
.registerTypeHierarchyAdapter(GameProfile.class, new GameProfileSerializer()) .registerTypeHierarchyAdapter(GameProfile.class, new GameProfileSerializer())
.create(); .create();
private static final Gson PRE_1_16_PING_SERIALIZER = GsonComponentSerializer private static final Gson PRE_1_16_PING_SERIALIZER = ProtocolUtils
.colorDownsamplingGson() .getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_15_2)
.serializer() .serializer()
.newBuilder() .newBuilder()
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())
.create(); .create();
private static final Gson POST_1_16_PING_SERIALIZER = GsonComponentSerializer.gson() private static final Gson POST_1_16_PING_SERIALIZER = ProtocolUtils
.getJsonChatSerializer(ProtocolVersion.MINECRAFT_1_16)
.serializer() .serializer()
.newBuilder() .newBuilder()
.registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer()) .registerTypeHierarchyAdapter(Favicon.class, new FaviconSerializer())

Datei anzeigen

@ -336,15 +336,16 @@ public class ClientPlaySessionHandler implements MinecraftSessionHandler {
int tempDim = joinGame.getDimension() == 0 ? -1 : 0; int tempDim = joinGame.getDimension() == 0 ? -1 : 0;
player.getConnection().delayedWrite( player.getConnection().delayedWrite(
new Respawn(tempDim, joinGame.getPartialHashedSeed(), joinGame.getDifficulty(), new Respawn(tempDim, joinGame.getPartialHashedSeed(), joinGame.getDifficulty(),
joinGame.getGamemode(), joinGame.getLevelType(), joinGame.getGamemode(), joinGame.getLevelType(),
false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode())); false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(),
joinGame.getCurrentDimensionData()));
} }
player.getConnection().delayedWrite( player.getConnection().delayedWrite(
new Respawn(joinGame.getDimension(), joinGame.getPartialHashedSeed(), new Respawn(joinGame.getDimension(), joinGame.getPartialHashedSeed(),
joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(), joinGame.getDifficulty(), joinGame.getGamemode(), joinGame.getLevelType(),
false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode())); false, joinGame.getDimensionInfo(), joinGame.getPreviousGamemode(),
joinGame.getCurrentDimensionData()));
destination.setActiveDimensionRegistry(joinGame.getDimensionRegistry()); // 1.16 destination.setActiveDimensionRegistry(joinGame.getDimensionRegistry()); // 1.16
} }

Datei anzeigen

@ -1,11 +1,15 @@
package com.velocitypowered.proxy.connection.registry; package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import net.kyori.nbt.CompoundTag; import com.velocitypowered.api.network.ProtocolVersion;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public final class DimensionData { public final class DimensionData {
private static final String UNKNOWN_DIMENSION_ID = "velocity:unknown_dimension";
private final String registryIdentifier; private final String registryIdentifier;
private final @Nullable Integer dimensionId;
private final boolean isNatural; private final boolean isNatural;
private final float ambientLight; private final float ambientLight;
private final boolean isShrunk; private final boolean isShrunk;
@ -20,10 +24,13 @@ public final class DimensionData {
private final String burningBehaviourIdentifier; private final String burningBehaviourIdentifier;
private final @Nullable Long fixedTime; private final @Nullable Long fixedTime;
private final @Nullable Boolean createDragonFight; private final @Nullable Boolean createDragonFight;
private final @Nullable Double coordinateScale;
private final @Nullable String effects;
/** /**
* Initializes a new {@link DimensionData} instance. * Initializes a new {@link DimensionData} instance.
* @param registryIdentifier the identifier for the dimension from the registry. * @param registryIdentifier the identifier for the dimension from the registry.
* @param dimensionId the dimension ID contained in the registry (the "id" tag)
* @param isNatural indicates if the dimension use natural world generation (e.g. overworld) * @param isNatural indicates if the dimension use natural world generation (e.g. overworld)
* @param ambientLight the light level the client sees without external lighting * @param ambientLight the light level the client sees without external lighting
* @param isShrunk indicates if the world is shrunk, aka not the full 256 blocks (e.g. nether) * @param isShrunk indicates if the world is shrunk, aka not the full 256 blocks (e.g. nether)
@ -38,24 +45,31 @@ public final class DimensionData {
* @param burningBehaviourIdentifier the identifier for how burning blocks work in the dimension * @param burningBehaviourIdentifier the identifier for how burning blocks work in the dimension
* @param fixedTime optional. If set to any game daytime value will deactivate time cycle * @param fixedTime optional. If set to any game daytime value will deactivate time cycle
* @param createDragonFight optional. Internal flag used in the end dimension * @param createDragonFight optional. Internal flag used in the end dimension
* @param coordinateScale optional, unknown purpose
* @param effects optional, unknown purpose
*/ */
public DimensionData(String registryIdentifier, boolean isNatural, public DimensionData(String registryIdentifier,
float ambientLight, boolean isShrunk, boolean isUltrawarm, @Nullable Integer dimensionId,
boolean hasCeiling, boolean hasSkylight, boolean isNatural,
boolean isPiglinSafe, boolean doBedsWork, float ambientLight, boolean isShrunk, boolean isUltrawarm,
boolean doRespawnAnchorsWork, boolean hasRaids, boolean hasCeiling, boolean hasSkylight,
int logicalHeight, String burningBehaviourIdentifier, boolean isPiglinSafe, boolean doBedsWork,
@Nullable Long fixedTime, @Nullable Boolean createDragonFight) { boolean doRespawnAnchorsWork, boolean hasRaids,
int logicalHeight, String burningBehaviourIdentifier,
@Nullable Long fixedTime, @Nullable Boolean createDragonFight,
@Nullable Double coordinateScale,
@Nullable String effects) {
Preconditions.checkNotNull( Preconditions.checkNotNull(
registryIdentifier, "registryIdentifier cannot be null"); registryIdentifier, "registryIdentifier cannot be null");
Preconditions.checkArgument(registryIdentifier.length() > 0, Preconditions.checkArgument(registryIdentifier.length() > 0,
"registryIdentifier cannot be empty"); "registryIdentifier cannot be empty");
Preconditions.checkArgument(logicalHeight >= 0, "localHeight must be >= 0"); Preconditions.checkArgument(logicalHeight >= 0, "localHeight must be >= 0");
Preconditions.checkNotNull( Preconditions.checkNotNull(
burningBehaviourIdentifier, "burningBehaviourIdentifier cannot be null"); burningBehaviourIdentifier, "burningBehaviourIdentifier cannot be null");
Preconditions.checkArgument(burningBehaviourIdentifier.length() > 0, Preconditions.checkArgument(burningBehaviourIdentifier.length() > 0,
"burningBehaviourIdentifier cannot be empty"); "burningBehaviourIdentifier cannot be empty");
this.registryIdentifier = registryIdentifier; this.registryIdentifier = registryIdentifier;
this.dimensionId = dimensionId;
this.isNatural = isNatural; this.isNatural = isNatural;
this.ambientLight = ambientLight; this.ambientLight = ambientLight;
this.isShrunk = isShrunk; this.isShrunk = isShrunk;
@ -70,12 +84,18 @@ public final class DimensionData {
this.burningBehaviourIdentifier = burningBehaviourIdentifier; this.burningBehaviourIdentifier = burningBehaviourIdentifier;
this.fixedTime = fixedTime; this.fixedTime = fixedTime;
this.createDragonFight = createDragonFight; this.createDragonFight = createDragonFight;
this.coordinateScale = coordinateScale;
this.effects = effects;
} }
public String getRegistryIdentifier() { public String getRegistryIdentifier() {
return registryIdentifier; return registryIdentifier;
} }
public @Nullable Integer getDimensionId() {
return dimensionId;
}
public boolean isNatural() { public boolean isNatural() {
return isNatural; return isNatural;
} }
@ -132,61 +152,143 @@ public final class DimensionData {
return createDragonFight; return createDragonFight;
} }
public @Nullable Double getCoordinateScale() {
return coordinateScale;
}
/** /**
* Parses a given CompoundTag to a DimensionData instance. * Returns a fresh {@link DimensionData} with the specified {@code registryIdentifier}
* @param toRead the compound from the registry to read * and {@code dimensionId}.
*
* @param registryIdentifier the identifier for the dimension from the registry
* @param dimensionId optional, dimension ID
* @return a new {@link DimensionData}
*/
public DimensionData annotateWith(String registryIdentifier,
@Nullable Integer dimensionId) {
return new DimensionData(registryIdentifier, dimensionId, isNatural, ambientLight, isShrunk,
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, createDragonFight,
coordinateScale, effects);
}
public boolean isUnannotated() {
return this.registryIdentifier.equalsIgnoreCase(UNKNOWN_DIMENSION_ID);
}
/**
* Parses a given CompoundTag to a DimensionData instance. Assumes the data only contains
* dimension details.
*
* @param details the compound from the registry to read
* @param version the protocol version
* @return game dimension data * @return game dimension data
*/ */
public static DimensionData decodeCompoundTag(CompoundTag toRead) { public static DimensionData decodeBaseCompoundTag(CompoundBinaryTag details,
Preconditions.checkNotNull(toRead, "CompoundTag cannot be null"); ProtocolVersion version) {
String registryIdentifier = toRead.getString("name"); boolean isNatural = details.getByte("natural") >= 1;
boolean isNatural = toRead.getBoolean("natural"); float ambientLight = details.getFloat("ambient_light");
float ambientLight = toRead.getFloat("ambient_light"); boolean isShrunk = details.getByte("shrunk") >= 1;
boolean isShrunk = toRead.getBoolean("shrunk"); boolean isUltrawarm = details.getByte("ultrawarm") >= 1;
boolean isUltrawarm = toRead.getBoolean("ultrawarm"); boolean hasCeiling = details.getByte("has_ceiling") >= 1;
boolean hasCeiling = toRead.getBoolean("has_ceiling"); boolean hasSkylight = details.getByte("has_skylight") >= 1;
boolean hasSkylight = toRead.getBoolean("has_skylight"); boolean isPiglinSafe = details.getByte("piglin_safe") >= 1;
boolean isPiglinSafe = toRead.getBoolean("piglin_safe"); boolean doBedsWork = details.getByte("bed_works") >= 1;
boolean doBedsWork = toRead.getBoolean("bed_works"); boolean doRespawnAnchorsWork = details.getByte("respawn_anchor_works") >= 1;
boolean doRespawnAnchorsWork = toRead.getBoolean("respawn_anchor_works"); boolean hasRaids = details.getByte("has_raids") >= 1;
boolean hasRaids = toRead.getBoolean("has_raids"); int logicalHeight = details.getInt("logical_height");
int logicalHeight = toRead.getInt("logical_height"); String burningBehaviourIdentifier = details.getString("infiniburn");
String burningBehaviourIdentifier = toRead.getString("infiniburn"); Long fixedTime = details.keySet().contains("fixed_time")
Long fixedTime = toRead.contains("fixed_time") ? details.getLong("fixed_time") : null;
? toRead.getLong("fixed_time") : null; Boolean hasEnderdragonFight = details.keySet().contains("has_enderdragon_fight")
Boolean hasEnderdragonFight = toRead.contains("has_enderdragon_fight") ? details.getByte("has_enderdragon_fight") >= 1 : null;
? toRead.getBoolean("has_enderdragon_fight") : null; Double coordinateScale = details.keySet().contains("coordinate_scale")
? details.getDouble("coordinate_scale") : null;
String effects = details.keySet().contains("effects") ? details.getString("effects")
: null;
return new DimensionData( return new DimensionData(
registryIdentifier, isNatural, ambientLight, isShrunk, UNKNOWN_DIMENSION_ID, null, isNatural, ambientLight, isShrunk,
isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork, isUltrawarm, hasCeiling, hasSkylight, isPiglinSafe, doBedsWork, doRespawnAnchorsWork,
hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, hasEnderdragonFight); hasRaids, logicalHeight, burningBehaviourIdentifier, fixedTime, hasEnderdragonFight,
coordinateScale, effects);
}
/**
* Parses a given CompoundTag to a DimensionData instance. Assumes the data is part of a
* dimension registry.
* @param dimTag the compound from the registry to read
* @param version the protocol version
* @return game dimension data
*/
public static DimensionData decodeRegistryEntry(CompoundBinaryTag dimTag,
ProtocolVersion version) {
String registryIdentifier = dimTag.getString("name");
CompoundBinaryTag details;
Integer dimensionId = null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
dimensionId = dimTag.getInt("id");
details = dimTag.getCompound("element");
} else {
details = dimTag;
}
DimensionData deserializedDetails = decodeBaseCompoundTag(details, version);
return deserializedDetails.annotateWith(registryIdentifier, dimensionId);
} }
/** /**
* Encodes the Dimension data as CompoundTag. * Encodes the Dimension data as CompoundTag.
* @param version the version to serialize as
* @return compound containing the dimension data * @return compound containing the dimension data
*/ */
public CompoundTag encodeAsCompundTag() { public CompoundBinaryTag encodeAsCompoundTag(ProtocolVersion version) {
CompoundTag ret = new CompoundTag(); CompoundBinaryTag details = serializeDimensionDetails();
ret.putString("name", registryIdentifier); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
ret.putBoolean("natural", isNatural); if (dimensionId == null) {
throw new IllegalStateException("Tried to serialize a 1.16.2+ dimension registry entry "
+ "without an ID");
}
return CompoundBinaryTag.builder()
.putString("name", registryIdentifier)
.putInt("id", dimensionId)
.put("element", details)
.build();
} else {
return details.putString("name", registryIdentifier);
}
}
/**
* Serializes details of this dimension.
* @return serialized details of this dimension
*/
public CompoundBinaryTag serializeDimensionDetails() {
CompoundBinaryTag.Builder ret = CompoundBinaryTag.builder();
ret.putByte("natural", (byte) (isNatural ? 1 : 0));
ret.putFloat("ambient_light", ambientLight); ret.putFloat("ambient_light", ambientLight);
ret.putBoolean("shrunk", isShrunk); ret.putByte("shrunk", (byte) (isShrunk ? 1 : 0));
ret.putBoolean("ultrawarm", isUltrawarm); ret.putByte("ultrawarm", (byte) (isUltrawarm ? 1 : 0));
ret.putBoolean("has_ceiling", hasCeiling); ret.putByte("has_ceiling", (byte) (hasCeiling ? 1 : 0));
ret.putBoolean("has_skylight", hasSkylight); ret.putByte("has_skylight", (byte) (hasSkylight ? 1 : 0));
ret.putBoolean("piglin_safe", isPiglinSafe); ret.putByte("piglin_safe", (byte) (isPiglinSafe ? 1 : 0));
ret.putBoolean("bed_works", doBedsWork); ret.putByte("bed_works", (byte) (doBedsWork ? 1 : 0));
ret.putBoolean("respawn_anchor_works", doRespawnAnchorsWork); ret.putByte("respawn_anchor_works", (byte) (doBedsWork ? 1 : 0));
ret.putBoolean("has_raids", hasRaids); ret.putByte("has_raids", (byte) (hasRaids ? 1 : 0));
ret.putInt("logical_height", logicalHeight); ret.putInt("logical_height", logicalHeight);
ret.putString("infiniburn", burningBehaviourIdentifier); ret.putString("infiniburn", burningBehaviourIdentifier);
if (fixedTime != null) { if (fixedTime != null) {
ret.putLong("fixed_time", fixedTime); ret.putLong("fixed_time", fixedTime);
} }
if (createDragonFight != null) { if (createDragonFight != null) {
ret.putBoolean("has_enderdragon_fight", createDragonFight); ret.putByte("has_enderdragon_fight", (byte) (createDragonFight ? 1 : 0));
} }
return ret; if (coordinateScale != null) {
ret.putDouble("coordinate_scale", coordinateScale);
}
if (effects != null) {
ret.putString("effects", effects);
}
return ret.build();
} }
} }

Datei anzeigen

@ -1,6 +1,7 @@
package com.velocitypowered.proxy.connection.registry; package com.velocitypowered.proxy.connection.registry;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class DimensionInfo { public final class DimensionInfo {
@ -16,18 +17,13 @@ public final class DimensionInfo {
* @param isFlat if true will set world lighting below surface-level to not display fog * @param isFlat if true will set world lighting below surface-level to not display fog
* @param isDebugType if true constrains the world to the very limited debug-type world * @param isDebugType if true constrains the world to the very limited debug-type world
*/ */
public DimensionInfo(String registryIdentifier, String levelName, public DimensionInfo(String registryIdentifier, @Nullable String levelName,
boolean isFlat, boolean isDebugType) { boolean isFlat, boolean isDebugType) {
this.registryIdentifier = Preconditions.checkNotNull( this.registryIdentifier = Preconditions.checkNotNull(
registryIdentifier, "registryIdentifier cannot be null"); registryIdentifier, "registryIdentifier cannot be null");
Preconditions.checkArgument( Preconditions.checkArgument(registryIdentifier.length() > 0,
registryIdentifier.length() > 0, "registryIdentifier cannot be empty");
"registryIdentifier cannot be empty"); this.levelName = levelName;
this.levelName = Preconditions.checkNotNull(
levelName, "levelName cannot be null");
Preconditions.checkArgument(
levelName.length() > 0,
"registryIdentifier cannot be empty");
this.isFlat = isFlat; this.isFlat = isFlat;
this.isDebugType = isDebugType; this.isDebugType = isDebugType;
} }
@ -40,7 +36,7 @@ public final class DimensionInfo {
return isFlat; return isFlat;
} }
public String getLevelName() { public @Nullable String getLevelName() {
return levelName; return levelName;
} }

Datei anzeigen

@ -4,16 +4,16 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.velocitypowered.api.network.ProtocolVersion;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import net.kyori.nbt.CompoundTag; import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.nbt.ListTag; import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.nbt.Tag; import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.nbt.TagType; import net.kyori.adventure.nbt.ListBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public final class DimensionRegistry { public final class DimensionRegistry {
private final Map<String, DimensionData> registeredDimensions; private final Map<String, DimensionData> registeredDimensions;
@ -31,17 +31,17 @@ public final class DimensionRegistry {
* @param levelNames a populated {@link ImmutableSet} of the level (world) names the server offers * @param levelNames a populated {@link ImmutableSet} of the level (world) names the server offers
*/ */
public DimensionRegistry(ImmutableSet<DimensionData> registeredDimensions, public DimensionRegistry(ImmutableSet<DimensionData> registeredDimensions,
ImmutableSet<String> levelNames) { ImmutableSet<String> levelNames) {
Preconditions.checkNotNull(registeredDimensions, Preconditions.checkNotNull(registeredDimensions,
"registeredDimensions cannot be null"); "registeredDimensions cannot be null");
Preconditions.checkNotNull(levelNames, Preconditions.checkNotNull(levelNames,
"levelNames cannot be null"); "levelNames cannot be null");
Preconditions.checkArgument(registeredDimensions.size() > 0, Preconditions.checkArgument(registeredDimensions.size() > 0,
"registeredDimensions needs to be populated"); "registeredDimensions needs to be populated");
Preconditions.checkArgument(levelNames.size() > 0, Preconditions.checkArgument(levelNames.size() > 0,
"levelNames needs to populated"); "levelNames needs to populated");
this.registeredDimensions = Maps.uniqueIndex( this.registeredDimensions = Maps.uniqueIndex(
registeredDimensions, DimensionData::getRegistryIdentifier); registeredDimensions, DimensionData::getRegistryIdentifier);
this.levelNames = levelNames; this.levelNames = levelNames;
} }
@ -72,36 +72,33 @@ public final class DimensionRegistry {
return false; return false;
} }
return registeredDimensions.containsKey(toValidate.getRegistryIdentifier()) return registeredDimensions.containsKey(toValidate.getRegistryIdentifier())
&& levelNames.contains(toValidate.getLevelName()); && levelNames.contains(toValidate.getLevelName());
} }
/** /**
* Encodes the stored Dimension registry as CompoundTag. * Encodes the stored Dimension registry as CompoundTag.
* @return the CompoundTag containing identifier:type mappings * @return the CompoundTag containing identifier:type mappings
*/ */
public CompoundTag encodeRegistry() { public ListBinaryTag encodeRegistry(ProtocolVersion version) {
CompoundTag ret = new CompoundTag(); ListBinaryTag.Builder<CompoundBinaryTag> listBuilder = ListBinaryTag
ListTag list = new ListTag(TagType.COMPOUND); .builder(BinaryTagTypes.COMPOUND);
for (DimensionData iter : registeredDimensions.values()) { for (DimensionData iter : registeredDimensions.values()) {
list.add(iter.encodeAsCompundTag()); listBuilder.add(iter.encodeAsCompoundTag(version));
} }
ret.put("dimension", list); return listBuilder.build();
return ret;
} }
/** /**
* Decodes a CompoundTag storing a dimension registry. * Decodes a CompoundTag storing a dimension registry.
* @param toParse CompoundTag containing a dimension registry * @param toParse CompoundTag containing a dimension registry
*/ */
public static ImmutableSet<DimensionData> fromGameData(CompoundTag toParse) { public static ImmutableSet<DimensionData> fromGameData(ListBinaryTag toParse,
Preconditions.checkNotNull(toParse, "CompoundTag cannot be null"); ProtocolVersion version) {
Preconditions.checkArgument(toParse.contains("dimension", TagType.LIST), Preconditions.checkNotNull(toParse, "ListTag cannot be null");
"CompoundTag does not contain a dimension list");
ListTag dimensions = toParse.getList("dimension");
ImmutableSet.Builder<DimensionData> mappings = ImmutableSet.builder(); ImmutableSet.Builder<DimensionData> mappings = ImmutableSet.builder();
for (Tag iter : dimensions) { for (BinaryTag iter : toParse) {
if (iter instanceof CompoundTag) { if (iter instanceof CompoundBinaryTag) {
mappings.add(DimensionData.decodeCompoundTag((CompoundTag) iter)); mappings.add(DimensionData.decodeRegistryEntry((CompoundBinaryTag) iter, version));
} }
} }
return mappings.build(); return mappings.build();

Datei anzeigen

@ -3,10 +3,10 @@ package com.velocitypowered.proxy.protocol;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
import static com.velocitypowered.proxy.protocol.util.NettyPreconditions.checkFrame; import static com.velocitypowered.proxy.protocol.util.NettyPreconditions.checkFrame;
import com.google.common.base.Preconditions;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.api.util.GameProfile; import com.velocitypowered.api.util.GameProfile;
import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder; import com.velocitypowered.proxy.protocol.netty.MinecraftDecoder;
import com.velocitypowered.proxy.protocol.util.VelocityLegacyHoverEventSerializer;
import com.velocitypowered.proxy.util.except.QuietDecoderException; import com.velocitypowered.proxy.util.except.QuietDecoderException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
@ -22,12 +22,24 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import net.kyori.adventure.nbt.BinaryTagIO;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer; import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.nbt.CompoundTag;
import net.kyori.nbt.TagIO;
public enum ProtocolUtils { public enum ProtocolUtils {
; ;
private static final GsonComponentSerializer PRE_1_16_SERIALIZER =
GsonComponentSerializer.builder()
.downsampleColors()
.emitLegacyHoverEvent()
.legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE)
.build();
private static final GsonComponentSerializer MODERN_SERIALIZER =
GsonComponentSerializer.builder()
.legacyHoverEventSerializer(VelocityLegacyHoverEventSerializer.INSTANCE)
.build();
private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB private static final int DEFAULT_MAX_STRING_SIZE = 65536; // 64KiB
private static final QuietDecoderException BAD_VARINT_CACHED = private static final QuietDecoderException BAD_VARINT_CACHED =
new QuietDecoderException("Bad varint decoded"); new QuietDecoderException("Bad varint decoded");
@ -219,11 +231,11 @@ public enum ProtocolUtils {
} }
/** /**
* Reads a {@link net.kyori.nbt.CompoundTag} from the {@code buf}. * Reads a {@link net.kyori.adventure.nbt.CompoundBinaryTag} from the {@code buf}.
* @param buf the buffer to read from * @param buf the buffer to read from
* @return {@link net.kyori.nbt.CompoundTag} the CompoundTag from the buffer * @return {@link net.kyori.adventure.nbt.CompoundBinaryTag} the CompoundTag from the buffer
*/ */
public static CompoundTag readCompoundTag(ByteBuf buf) { public static CompoundBinaryTag readCompoundTag(ByteBuf buf) {
int indexBefore = buf.readerIndex(); int indexBefore = buf.readerIndex();
byte startType = buf.readByte(); byte startType = buf.readByte();
if (startType == 0) { if (startType == 0) {
@ -231,7 +243,7 @@ public enum ProtocolUtils {
} }
buf.readerIndex(indexBefore); buf.readerIndex(indexBefore);
try { try {
return TagIO.readDataInput(new ByteBufInputStream(buf)); return BinaryTagIO.readDataInput(new ByteBufInputStream(buf));
} catch (IOException thrown) { } catch (IOException thrown) {
throw new DecoderException( throw new DecoderException(
"Unable to parse NBT CompoundTag, full error: " + thrown.getMessage()); "Unable to parse NBT CompoundTag, full error: " + thrown.getMessage());
@ -243,13 +255,13 @@ public enum ProtocolUtils {
* @param buf the buffer to write to * @param buf the buffer to write to
* @param compoundTag the CompoundTag to write * @param compoundTag the CompoundTag to write
*/ */
public static void writeCompoundTag(ByteBuf buf, CompoundTag compoundTag) { public static void writeCompoundTag(ByteBuf buf, CompoundBinaryTag compoundTag) {
if (compoundTag == null) { if (compoundTag == null) {
buf.writeByte(0); buf.writeByte(0);
return; return;
} }
try { try {
TagIO.writeDataOutput(compoundTag, new ByteBufOutputStream(buf)); BinaryTagIO.writeDataOutput(compoundTag, new ByteBufOutputStream(buf));
} catch (IOException e) { } catch (IOException e) {
throw new EncoderException("Unable to encode NBT CompoundTag"); throw new EncoderException("Unable to encode NBT CompoundTag");
} }
@ -468,9 +480,9 @@ public enum ProtocolUtils {
*/ */
public static GsonComponentSerializer getJsonChatSerializer(ProtocolVersion version) { public static GsonComponentSerializer getJsonChatSerializer(ProtocolVersion version) {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) {
return GsonComponentSerializer.gson(); return MODERN_SERIALIZER;
} }
return GsonComponentSerializer.colorDownsamplingGson(); return PRE_1_16_SERIALIZER;
} }
public enum Direction { public enum Direction {

Datei anzeigen

@ -7,6 +7,7 @@ import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_13;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_14;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_15;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_16_2;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_7_2; 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_8;
import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9; import static com.velocitypowered.api.network.ProtocolVersion.MINECRAFT_1_9;
@ -121,7 +122,8 @@ public enum StateRegistry {
map(0x18, MINECRAFT_1_12, false), map(0x18, MINECRAFT_1_12, false),
map(0x1D, MINECRAFT_1_13, false), map(0x1D, MINECRAFT_1_13, false),
map(0x1F, MINECRAFT_1_14, false), map(0x1F, MINECRAFT_1_14, false),
map(0x20, MINECRAFT_1_16, false)); map(0x20, MINECRAFT_1_16, false),
map(0x21, MINECRAFT_1_16_2, false));
clientbound.register(BossBar.class, BossBar::new, clientbound.register(BossBar.class, BossBar::new,
map(0x0C, MINECRAFT_1_9, false), map(0x0C, MINECRAFT_1_9, false),
@ -138,39 +140,45 @@ public enum StateRegistry {
map(0x0E, MINECRAFT_1_9, false), map(0x0E, MINECRAFT_1_9, false),
map(0x10, MINECRAFT_1_13, false), map(0x10, MINECRAFT_1_13, false),
map(0x11, MINECRAFT_1_15, false), map(0x11, MINECRAFT_1_15, false),
map(0x10, MINECRAFT_1_16, false)); map(0x10, MINECRAFT_1_16, false),
map(0x0F, MINECRAFT_1_16_2, false));
clientbound.register(AvailableCommands.class, AvailableCommands::new, clientbound.register(AvailableCommands.class, AvailableCommands::new,
map(0x11, MINECRAFT_1_13, false), map(0x11, MINECRAFT_1_13, false),
map(0x12, MINECRAFT_1_15, false), map(0x12, MINECRAFT_1_15, false),
map(0x11, MINECRAFT_1_16, false)); map(0x11, MINECRAFT_1_16, false),
map(0x10, MINECRAFT_1_16_2, false));
clientbound.register(PluginMessage.class, PluginMessage::new, clientbound.register(PluginMessage.class, PluginMessage::new,
map(0x3F, MINECRAFT_1_7_2, false), map(0x3F, MINECRAFT_1_7_2, false),
map(0x18, MINECRAFT_1_9, false), map(0x18, MINECRAFT_1_9, false),
map(0x19, MINECRAFT_1_13, false), map(0x19, MINECRAFT_1_13, false),
map(0x18, MINECRAFT_1_14, false), map(0x18, MINECRAFT_1_14, false),
map(0x19, MINECRAFT_1_15, false), map(0x19, MINECRAFT_1_15, false),
map(0x18, MINECRAFT_1_16, false)); map(0x18, MINECRAFT_1_16, false),
map(0x17, MINECRAFT_1_16_2, false));
clientbound.register(Disconnect.class, Disconnect::new, clientbound.register(Disconnect.class, Disconnect::new,
map(0x40, MINECRAFT_1_7_2, false), map(0x40, MINECRAFT_1_7_2, false),
map(0x1A, MINECRAFT_1_9, false), map(0x1A, MINECRAFT_1_9, false),
map(0x1B, MINECRAFT_1_13, false), map(0x1B, MINECRAFT_1_13, false),
map(0x1A, MINECRAFT_1_14, false), map(0x1A, MINECRAFT_1_14, false),
map(0x1B, MINECRAFT_1_15, false), map(0x1B, MINECRAFT_1_15, false),
map(0x1A, MINECRAFT_1_16, false)); map(0x1A, MINECRAFT_1_16, false),
map(0x19, MINECRAFT_1_16_2, false));
clientbound.register(KeepAlive.class, KeepAlive::new, clientbound.register(KeepAlive.class, KeepAlive::new,
map(0x00, MINECRAFT_1_7_2, false), map(0x00, MINECRAFT_1_7_2, false),
map(0x1F, MINECRAFT_1_9, false), map(0x1F, MINECRAFT_1_9, false),
map(0x21, MINECRAFT_1_13, false), map(0x21, MINECRAFT_1_13, false),
map(0x20, MINECRAFT_1_14, false), map(0x20, MINECRAFT_1_14, false),
map(0x21, MINECRAFT_1_15, false), map(0x21, MINECRAFT_1_15, false),
map(0x20, MINECRAFT_1_16, false)); map(0x20, MINECRAFT_1_16, false),
map(0x1F, MINECRAFT_1_16_2, false));
clientbound.register(JoinGame.class, JoinGame::new, clientbound.register(JoinGame.class, JoinGame::new,
map(0x01, MINECRAFT_1_7_2, false), map(0x01, MINECRAFT_1_7_2, false),
map(0x23, MINECRAFT_1_9, false), map(0x23, MINECRAFT_1_9, false),
map(0x25, MINECRAFT_1_13, false), map(0x25, MINECRAFT_1_13, false),
map(0x25, MINECRAFT_1_14, false), map(0x25, MINECRAFT_1_14, false),
map(0x26, MINECRAFT_1_15, false), map(0x26, MINECRAFT_1_15, false),
map(0x25, MINECRAFT_1_16, false)); map(0x25, MINECRAFT_1_16, false),
map(0x24, MINECRAFT_1_16_2, false));
clientbound.register(Respawn.class, Respawn::new, clientbound.register(Respawn.class, Respawn::new,
map(0x07, MINECRAFT_1_7_2, true), map(0x07, MINECRAFT_1_7_2, true),
map(0x33, MINECRAFT_1_9, true), map(0x33, MINECRAFT_1_9, true),
@ -179,7 +187,8 @@ public enum StateRegistry {
map(0x38, MINECRAFT_1_13, true), map(0x38, MINECRAFT_1_13, true),
map(0x3A, MINECRAFT_1_14, true), map(0x3A, MINECRAFT_1_14, true),
map(0x3B, MINECRAFT_1_15, true), map(0x3B, MINECRAFT_1_15, true),
map(0x3A, MINECRAFT_1_16, true)); map(0x3A, MINECRAFT_1_16, true),
map(0x39, MINECRAFT_1_16_2, true));
clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new, clientbound.register(ResourcePackRequest.class, ResourcePackRequest::new,
map(0x48, MINECRAFT_1_8, true), map(0x48, MINECRAFT_1_8, true),
map(0x32, MINECRAFT_1_9, true), map(0x32, MINECRAFT_1_9, true),
@ -188,7 +197,8 @@ public enum StateRegistry {
map(0x37, MINECRAFT_1_13, true), map(0x37, MINECRAFT_1_13, true),
map(0x39, MINECRAFT_1_14, true), map(0x39, MINECRAFT_1_14, true),
map(0x3A, MINECRAFT_1_15, true), map(0x3A, MINECRAFT_1_15, true),
map(0x39, MINECRAFT_1_16, true)); map(0x39, MINECRAFT_1_16, true),
map(0x38, MINECRAFT_1_16_2, true));
clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new, clientbound.register(HeaderAndFooter.class, HeaderAndFooter::new,
map(0x47, MINECRAFT_1_8, true), map(0x47, MINECRAFT_1_8, true),
map(0x48, MINECRAFT_1_9, true), map(0x48, MINECRAFT_1_9, true),
@ -215,7 +225,8 @@ public enum StateRegistry {
map(0x30, MINECRAFT_1_13, false), map(0x30, MINECRAFT_1_13, false),
map(0x33, MINECRAFT_1_14, false), map(0x33, MINECRAFT_1_14, false),
map(0x34, MINECRAFT_1_15, false), map(0x34, MINECRAFT_1_15, false),
map(0x33, MINECRAFT_1_16, false)); map(0x33, MINECRAFT_1_16, false),
map(0x32, MINECRAFT_1_16_2, false));
} }
}, },
LOGIN { LOGIN {

Datei anzeigen

@ -8,6 +8,9 @@ import com.velocitypowered.proxy.connection.registry.DimensionInfo;
import com.velocitypowered.proxy.connection.registry.DimensionRegistry; import com.velocitypowered.proxy.connection.registry.DimensionRegistry;
import com.velocitypowered.proxy.protocol.*; import com.velocitypowered.proxy.protocol.*;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.nbt.BinaryTagTypes;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
public class JoinGame implements MinecraftPacket { public class JoinGame implements MinecraftPacket {
@ -17,14 +20,17 @@ public class JoinGame implements MinecraftPacket {
private int dimension; private int dimension;
private long partialHashedSeed; // 1.15+ private long partialHashedSeed; // 1.15+
private short difficulty; private short difficulty;
private short maxPlayers; private boolean isHardcore;
private int maxPlayers;
private @Nullable String levelType; private @Nullable String levelType;
private int viewDistance; // 1.14+ private int viewDistance; // 1.14+
private boolean reducedDebugInfo; private boolean reducedDebugInfo;
private boolean showRespawnScreen; private boolean showRespawnScreen;
private DimensionRegistry dimensionRegistry; // 1.16+ private DimensionRegistry dimensionRegistry; // 1.16+
private DimensionInfo dimensionInfo; // 1.16+ private DimensionInfo dimensionInfo; // 1.16+
private DimensionData currentDimensionData; // 1.16.2+
private short previousGamemode; // 1.16+ private short previousGamemode; // 1.16+
private CompoundBinaryTag biomeRegistry; // 1.16.2+
public int getEntityId() { public int getEntityId() {
return entityId; return entityId;
@ -62,11 +68,11 @@ public class JoinGame implements MinecraftPacket {
this.difficulty = difficulty; this.difficulty = difficulty;
} }
public short getMaxPlayers() { public int getMaxPlayers() {
return maxPlayers; return maxPlayers;
} }
public void setMaxPlayers(short maxPlayers) { public void setMaxPlayers(int maxPlayers) {
this.maxPlayers = maxPlayers; this.maxPlayers = maxPlayers;
} }
@ -118,6 +124,26 @@ public class JoinGame implements MinecraftPacket {
this.previousGamemode = previousGamemode; this.previousGamemode = previousGamemode;
} }
public boolean getIsHardcore() {
return isHardcore;
}
public void setIsHardcore(boolean isHardcore) {
this.isHardcore = isHardcore;
}
public CompoundBinaryTag getBiomeRegistry() {
return biomeRegistry;
}
public void setBiomeRegistry(CompoundBinaryTag biomeRegistry) {
this.biomeRegistry = biomeRegistry;
}
public DimensionData getCurrentDimensionData() {
return currentDimensionData;
}
@Override @Override
public String toString() { public String toString() {
return "JoinGame{" return "JoinGame{"
@ -139,16 +165,41 @@ public class JoinGame implements MinecraftPacket {
@Override @Override
public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void decode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
this.entityId = buf.readInt(); this.entityId = buf.readInt();
this.gamemode = buf.readUnsignedByte(); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
this.isHardcore = buf.readBoolean();
this.gamemode = buf.readByte();
} else {
this.gamemode = buf.readByte();
this.isHardcore = (this.gamemode & 0x08) != 0;
this.gamemode &= ~0x08;
}
String dimensionIdentifier = null; String dimensionIdentifier = null;
String levelName = null; String levelName = null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) {
this.previousGamemode = buf.readByte(); this.previousGamemode = buf.readByte();
ImmutableSet<String> levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf)); ImmutableSet<String> levelNames = ImmutableSet.copyOf(ProtocolUtils.readStringArray(buf));
ImmutableSet<DimensionData> readData = DimensionRegistry.fromGameData(ProtocolUtils.readCompoundTag(buf)); CompoundBinaryTag registryContainer = ProtocolUtils.readCompoundTag(buf);
ListBinaryTag dimensionRegistryContainer = null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
dimensionRegistryContainer = registryContainer.getCompound("minecraft:dimension_type")
.getList("value", BinaryTagTypes.COMPOUND);
this.biomeRegistry = registryContainer.getCompound("minecraft:worldgen/biome");
} else {
dimensionRegistryContainer = registryContainer.getList("dimension",
BinaryTagTypes.COMPOUND);
}
ImmutableSet<DimensionData> readData =
DimensionRegistry.fromGameData(dimensionRegistryContainer, version);
this.dimensionRegistry = new DimensionRegistry(readData, levelNames); this.dimensionRegistry = new DimensionRegistry(readData, levelNames);
dimensionIdentifier = ProtocolUtils.readString(buf); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
levelName = ProtocolUtils.readString(buf); CompoundBinaryTag currentDimDataTag = ProtocolUtils.readCompoundTag(buf);
dimensionIdentifier = ProtocolUtils.readString(buf);
this.currentDimensionData = DimensionData.decodeBaseCompoundTag(currentDimDataTag, version)
.annotateWith(dimensionIdentifier, null);
} else {
dimensionIdentifier = ProtocolUtils.readString(buf);
levelName = ProtocolUtils.readString(buf);
}
} else if (version.compareTo(ProtocolVersion.MINECRAFT_1_9_1) >= 0) { } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_9_1) >= 0) {
this.dimension = buf.readInt(); this.dimension = buf.readInt();
} else { } else {
@ -160,7 +211,11 @@ public class JoinGame implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_15) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_15) >= 0) {
this.partialHashedSeed = buf.readLong(); this.partialHashedSeed = buf.readLong();
} }
this.maxPlayers = buf.readUnsignedByte(); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
this.maxPlayers = ProtocolUtils.readVarInt(buf);
} else {
this.maxPlayers = buf.readUnsignedByte();
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) < 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) < 0) {
this.levelType = ProtocolUtils.readString(buf, 16); this.levelType = ProtocolUtils.readString(buf, 16);
} }
@ -183,14 +238,35 @@ public class JoinGame implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
buf.writeInt(entityId); buf.writeInt(entityId);
buf.writeByte(gamemode); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
buf.writeBoolean(isHardcore);
buf.writeByte(gamemode);
} else {
buf.writeByte(isHardcore ? gamemode | 0x8 : gamemode);
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) {
buf.writeByte(previousGamemode); buf.writeByte(previousGamemode);
ProtocolUtils.writeStringArray(buf, dimensionRegistry.getLevelNames().toArray( ProtocolUtils.writeStringArray(buf, dimensionRegistry.getLevelNames().toArray(
new String[dimensionRegistry.getLevelNames().size()])); new String[dimensionRegistry.getLevelNames().size()]));
ProtocolUtils.writeCompoundTag(buf, dimensionRegistry.encodeRegistry()); CompoundBinaryTag.Builder registryContainer = CompoundBinaryTag.builder();
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); ListBinaryTag encodedDimensionRegistry = dimensionRegistry.encodeRegistry(version);
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName()); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
CompoundBinaryTag.Builder dimensionRegistryDummy = CompoundBinaryTag.builder();
dimensionRegistryDummy.putString("type", "minecraft:dimension_type");
dimensionRegistryDummy.put("value", encodedDimensionRegistry);
registryContainer.put("minecraft:dimension_type", dimensionRegistryDummy.build());
registryContainer.put("minecraft:worldgen/biome", biomeRegistry);
} else {
registryContainer.put("dimension", encodedDimensionRegistry);
}
ProtocolUtils.writeCompoundTag(buf, registryContainer.build());
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
ProtocolUtils.writeCompoundTag(buf, currentDimensionData.serializeDimensionDetails());
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
}
} else if (version.compareTo(ProtocolVersion.MINECRAFT_1_9_1) >= 0) { } else if (version.compareTo(ProtocolVersion.MINECRAFT_1_9_1) >= 0) {
buf.writeInt(dimension); buf.writeInt(dimension);
} else { } else {
@ -202,7 +278,11 @@ public class JoinGame implements MinecraftPacket {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_15) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_15) >= 0) {
buf.writeLong(partialHashedSeed); buf.writeLong(partialHashedSeed);
} }
buf.writeByte(maxPlayers); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
ProtocolUtils.writeVarInt(buf, maxPlayers);
} else {
buf.writeByte(maxPlayers);
}
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) < 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) < 0) {
if (levelType == null) { if (levelType == null) {
throw new IllegalStateException("No level type specified."); throw new IllegalStateException("No level type specified.");

Datei anzeigen

@ -2,10 +2,12 @@ package com.velocitypowered.proxy.protocol.packet;
import com.velocitypowered.api.network.ProtocolVersion; import com.velocitypowered.api.network.ProtocolVersion;
import com.velocitypowered.proxy.connection.MinecraftSessionHandler; import com.velocitypowered.proxy.connection.MinecraftSessionHandler;
import com.velocitypowered.proxy.connection.registry.DimensionData;
import com.velocitypowered.proxy.connection.registry.DimensionInfo; import com.velocitypowered.proxy.connection.registry.DimensionInfo;
import com.velocitypowered.proxy.protocol.MinecraftPacket; import com.velocitypowered.proxy.protocol.MinecraftPacket;
import com.velocitypowered.proxy.protocol.ProtocolUtils; import com.velocitypowered.proxy.protocol.ProtocolUtils;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.kyori.adventure.nbt.CompoundBinaryTag;
public class Respawn implements MinecraftPacket { public class Respawn implements MinecraftPacket {
@ -15,15 +17,16 @@ public class Respawn implements MinecraftPacket {
private short gamemode; private short gamemode;
private String levelType = ""; private String levelType = "";
private boolean shouldKeepPlayerData; // 1.16+ private boolean shouldKeepPlayerData; // 1.16+
private DimensionInfo dimensionInfo; // 1.16+ private DimensionInfo dimensionInfo; // 1.16-1.16.1
private short previousGamemode; // 1.16+ private short previousGamemode; // 1.16+
private DimensionData currentDimensionData; // 1.16.2+
public Respawn() { public Respawn() {
} }
public Respawn(int dimension, long partialHashedSeed, short difficulty, short gamemode, public Respawn(int dimension, long partialHashedSeed, short difficulty, short gamemode,
String levelType, boolean shouldKeepPlayerData, DimensionInfo dimensionInfo, String levelType, boolean shouldKeepPlayerData, DimensionInfo dimensionInfo,
short previousGamemode) { short previousGamemode, DimensionData currentDimensionData) {
this.dimension = dimension; this.dimension = dimension;
this.partialHashedSeed = partialHashedSeed; this.partialHashedSeed = partialHashedSeed;
this.difficulty = difficulty; this.difficulty = difficulty;
@ -32,6 +35,7 @@ public class Respawn implements MinecraftPacket {
this.shouldKeepPlayerData = shouldKeepPlayerData; this.shouldKeepPlayerData = shouldKeepPlayerData;
this.dimensionInfo = dimensionInfo; this.dimensionInfo = dimensionInfo;
this.previousGamemode = previousGamemode; this.previousGamemode = previousGamemode;
this.currentDimensionData = currentDimensionData;
} }
public int getDimension() { public int getDimension() {
@ -100,7 +104,9 @@ public class Respawn implements MinecraftPacket {
+ ", levelType='" + levelType + '\'' + ", levelType='" + levelType + '\''
+ ", shouldKeepPlayerData=" + shouldKeepPlayerData + ", shouldKeepPlayerData=" + shouldKeepPlayerData
+ ", dimensionRegistryName='" + dimensionInfo.toString() + '\'' + ", dimensionRegistryName='" + dimensionInfo.toString() + '\''
+ ", dimensionInfo=" + dimensionInfo
+ ", previousGamemode=" + previousGamemode + ", previousGamemode=" + previousGamemode
+ ", dimensionData=" + currentDimensionData
+ '}'; + '}';
} }
@ -109,8 +115,15 @@ public class Respawn implements MinecraftPacket {
String dimensionIdentifier = null; String dimensionIdentifier = null;
String levelName = null; String levelName = null;
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) {
dimensionIdentifier = ProtocolUtils.readString(buf); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
levelName = ProtocolUtils.readString(buf); CompoundBinaryTag dimDataTag = ProtocolUtils.readCompoundTag(buf);
dimensionIdentifier = ProtocolUtils.readString(buf);
this.currentDimensionData = DimensionData.decodeBaseCompoundTag(dimDataTag, version)
.annotateWith(dimensionIdentifier, null);
} else {
dimensionIdentifier = ProtocolUtils.readString(buf);
levelName = ProtocolUtils.readString(buf);
}
} else { } else {
this.dimension = buf.readInt(); this.dimension = buf.readInt();
} }
@ -135,8 +148,13 @@ public class Respawn implements MinecraftPacket {
@Override @Override
public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) { public void encode(ByteBuf buf, ProtocolUtils.Direction direction, ProtocolVersion version) {
if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) { if (version.compareTo(ProtocolVersion.MINECRAFT_1_16) >= 0) {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier()); if (version.compareTo(ProtocolVersion.MINECRAFT_1_16_2) >= 0) {
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName()); ProtocolUtils.writeCompoundTag(buf, currentDimensionData.serializeDimensionDetails());
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
} else {
ProtocolUtils.writeString(buf, dimensionInfo.getRegistryIdentifier());
ProtocolUtils.writeString(buf, dimensionInfo.getLevelName());
}
} else { } else {
buf.writeInt(dimension); buf.writeInt(dimension);
} }

Datei anzeigen

@ -135,5 +135,6 @@ public class ArgumentPropertyRegistry {
dummy("minecraft:float_range", DUMMY); dummy("minecraft:float_range", DUMMY);
dummy("minecraft:time", DUMMY); // added in 1.14 dummy("minecraft:time", DUMMY); // added in 1.14
dummy("minecraft:uuid", DUMMY); // added in 1.16 dummy("minecraft:uuid", DUMMY); // added in 1.16
dummy("minecraft:angle", DUMMY); // added in 1.16.2
} }
} }

Datei anzeigen

@ -0,0 +1,104 @@
package com.velocitypowered.proxy.protocol.util;
import java.io.IOException;
import java.util.UUID;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIO;
import net.kyori.adventure.nbt.api.BinaryTagHolder;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.event.HoverEvent.ShowEntity;
import net.kyori.adventure.text.event.HoverEvent.ShowItem;
import net.kyori.adventure.text.serializer.gson.LegacyHoverEventSerializer;
import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer;
import net.kyori.adventure.util.Codec.Decoder;
import net.kyori.adventure.util.Codec.Encoder;
import org.checkerframework.checker.nullness.qual.NonNull;
/**
* An implementation of {@link LegacyHoverEventSerializer} that implements the interface in the
* most literal, albeit "incompatible" way possible.
*/
public class VelocityLegacyHoverEventSerializer implements LegacyHoverEventSerializer {
public static final LegacyHoverEventSerializer INSTANCE =
new VelocityLegacyHoverEventSerializer();
private VelocityLegacyHoverEventSerializer() {
}
private static Key legacyIdToFakeKey(byte id) {
return Key.of("velocity", "legacy_hover/id_" + id);
}
@Override
public HoverEvent.@NonNull ShowItem deserializeShowItem(@NonNull Component input)
throws IOException {
String snbt = PlainComponentSerializer.plain().serialize(input);
CompoundBinaryTag item = TagStringIO.get().asCompound(snbt);
Key key;
String idIfString = item.getString("id", "");
if (idIfString.isEmpty()) {
key = legacyIdToFakeKey(item.getByte("id"));
} else {
key = Key.of(idIfString);
}
byte count = item.getByte("Count", (byte) 1);
return new ShowItem(key, count, BinaryTagHolder.of(snbt));
}
@Override
public HoverEvent.@NonNull ShowEntity deserializeShowEntity(@NonNull Component input,
Decoder<Component, String, ? extends RuntimeException> componentDecoder) throws IOException {
String snbt = PlainComponentSerializer.plain().serialize(input);
CompoundBinaryTag item = TagStringIO.get().asCompound(snbt);
Component name;
try {
name = componentDecoder.decode(item.getString("name"));
} catch (Exception e) {
name = TextComponent.of(item.getString("name"));
}
return new ShowEntity(Key.of(item.getString("type")),
UUID.fromString(item.getString("id")),
name);
}
@Override
public @NonNull Component serializeShowItem(HoverEvent.@NonNull ShowItem input)
throws IOException {
final CompoundBinaryTag.Builder builder = CompoundBinaryTag.builder()
.putByte("Count", (byte) input.count());
String keyAsString = input.item().asString();
if (keyAsString.startsWith("velocity:legacy_hover/id_")) {
builder.putByte("id", Byte.parseByte(keyAsString
.substring("velocity:legacy_hover/id_".length())));
} else {
builder.putString("id", keyAsString);
}
if (input.nbt() != null) {
builder.put("tag", TagStringIO.get().asCompound(input.nbt().string()));
}
return TextComponent.of(TagStringIO.get().asString(builder.build()));
}
@Override
public @NonNull Component serializeShowEntity(HoverEvent.@NonNull ShowEntity input,
Encoder<Component, String, ? extends RuntimeException> componentEncoder) throws IOException {
CompoundBinaryTag.Builder tag = CompoundBinaryTag.builder()
.putString("id", input.id().toString())
.putString("type", input.type().asString());
if (input.name() != null) {
tag.putString("name", componentEncoder.encode(input.name()));
}
return TextComponent.of(TagStringIO.get().asString(tag.build()));
}
}