Merge branch '2.0' into feat/1.21.5-support

This commit is contained in:
booky10 committed 2025-03-29 20:36:56 +01:00
commit 0018d60f9c
190 files changed
+2136213 -2135450

No files matched your search

+13 -66
View File
@@ -1,6 +1,6 @@
import com.github.retrooper.compression.strategy.JsonArrayCompressionStrategy
import com.github.retrooper.compression.strategy.JsonObjectCompressionStrategy
import com.github.retrooper.compression.strategy.JsonToNbtStrategy
import com.github.retrooper.compression.strategy.dir.JsonBase64DataDirStrategy
import com.github.retrooper.compression.strategy.dir.JsonRegistryCompressionDirStrategy
import com.github.retrooper.compression.strategy.dir.JsonToNbtDirStrategy
import com.github.retrooper.excludeAdventure
plugins {
@@ -39,76 +39,21 @@ dependencies {
testImplementation(testlibs.slf4j)
testImplementation(testlibs.bundles.junit)
testImplementation(libs.netty)
testImplementation(libs.classgraph)
}
mappingCompression {
mappingDirectory = rootDir.resolve("mappings")
outDirectory = project.layout.buildDirectory.dir("mappings/generated/assets/mappings")
with<JsonToNbtStrategy> {
compress("block/legacy_block_mappings.json")
compress("block/modern_block_mappings.json")
compress("enchantment/enchantment_type_data.json")
compress("item/item_base_components.json")
compress("stats/statistics.json")
compress("world/biome_data.json")
with<JsonToNbtDirStrategy> {
compress("data")
}
with<JsonArrayCompressionStrategy> {
compress("attribute/attribute_mappings.json")
compress("block/block_entity_type_mappings.json")
compress("block/block_type_mappings.json")
compress("chat/chat_type_mappings.json")
compress("command/argument_parser_mappings.json")
compress("damage/damagetype_mappings.json")
compress("enchantment/effect_component_type.json")
compress("entity/entity_data_type_mappings.json")
compress("entity/painting_mappings.json")
compress("entity/wolf_variant_mappings.json")
compress("item/consume_effect_type_mappings.json")
compress("item/equipment_asset_mappings.json")
compress("item/item_banner_pattern_mappings.json")
compress("item/item_component_mappings.json")
compress("item/item_instrument_mappings.json")
compress("item/item_jukebox_song_mappings.json")
compress("item/item_map_decoration_type_mappings.json")
compress("item/item_potion_mappings.json")
compress("item/item_trim_material_mappings.json")
compress("item/item_trim_pattern_mappings.json")
compress("item/recipe_book_category.json")
compress("item/recipe_display_types.json")
compress("item/recipe_serializer_mappings.json")
compress("item/recipe_slot_display_types.json")
compress("particle/particle_type_mappings.json")
compress("sound/sound_mappings.json")
compress("world/biome_mappings.json")
compress("world/world_position_source_mappings.json")
with<JsonBase64DataDirStrategy> {
compress("item_base_components")
}
with<JsonObjectCompressionStrategy> {
compress("enchantment/enchantment_type_mappings.json")
compress("entity/entity_effect_mappings.json")
compress("entity/entity_type_mappings.json")
compress("entity/legacy_entity_type_mappings.json")
compress("item/item_type_mappings.json")
compress("world/dimension_type_mappings.json")
with<JsonRegistryCompressionDirStrategy> {
compress("registries")
}
}
@@ -145,7 +90,9 @@ tasks {
shadowJar {
exclude {
val path = it.path
path.startsWith("net/kyori") && !path.startsWith("net/kyori/adventure/text/serializer") && !path.startsWith("net/kyori/option")
path.startsWith("net/kyori") && !path.startsWith("net/kyori/adventure/text/serializer") && !path.startsWith(
"net/kyori/option"
)
}
}
}
@@ -31,4 +31,6 @@ public interface ByteBufAllocationOperator {
Object compositeBuffer();
Object compositeBuffer(int maxNumComponents);
Object emptyBuffer();
}
@@ -52,4 +52,8 @@ public class UnpooledByteBufAllocationHelper {
public static Object compositeBuffer(int maxNumComponents) {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().compositeBuffer(maxNumComponents);
}
public static Object emptyBuffer() {
return PacketEvents.getAPI().getNettyManager().getByteBufAllocationOperator().emptyBuffer();
}
}
@@ -26,8 +26,7 @@ import org.jetbrains.annotations.Nullable;
public final class Attributes {
private static final VersionedRegistry<Attribute> REGISTRY = new VersionedRegistry<>(
"attribute", "attribute/attribute_mappings");
private static final VersionedRegistry<Attribute> REGISTRY = new VersionedRegistry<>("attribute");
private Attributes() {
}
@@ -22,6 +22,7 @@ import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticAttribute extends AbstractMappedEntity implements Attribute {
@@ -31,21 +32,24 @@ public class StaticAttribute extends AbstractMappedEntity implements Attribute {
private final double minValue;
private final double maxValue;
StaticAttribute(
TypesBuilderData data, String legacyPrefix,
@ApiStatus.Internal
public StaticAttribute(
@Nullable TypesBuilderData data, String legacyPrefix,
double defaultValue, double minValue, double maxValue
) {
super(data);
this.defaultValue = defaultValue;
this.minValue = minValue;
this.maxValue = maxValue;
this.legacyName = legacyPrefix == null ? null : new ResourceLocation(
this.legacyName = legacyPrefix == null || data == null ? null : new ResourceLocation(
data.getName().getNamespace(), legacyPrefix + "." + data.getName().getKey());
}
@Override
public ResourceLocation getName(ClientVersion version) {
assert this.data != null; // not marked as nullable in ctor
if (this.data == null) {
throw new UnsupportedOperationException();
}
return version.isNewerThanOrEquals(ClientVersion.V_1_21_2) || this.legacyName == null
? this.data.getName() : this.legacyName;
}
@@ -31,8 +31,7 @@ import static com.github.retrooper.packetevents.protocol.chat.ChatTypeDecoration
public final class ChatTypes {
private static final VersionedRegistry<ChatType> REGISTRY = new VersionedRegistry<>(
"chat_type", "chat/chat_type_mappings");
private static final VersionedRegistry<ChatType> REGISTRY = new VersionedRegistry<>("chat_type");
private ChatTypes() {
}
@@ -19,58 +19,54 @@
package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class Parsers {
public final class Parsers {
private static final List<Parser> ALL_PARSERS = new ArrayList<>(); // support for old methods
private static final Map<String, Parser> PARSER_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, Parser>> PARSER_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("command/argument_parser_mappings");
private static final VersionedRegistry<Parser> REGISTRY = new VersionedRegistry<>("argument_type");
private Parsers() {
}
@ApiStatus.Internal
public static Parser define(String key) {
return define(key, null, null);
}
@ApiStatus.Internal
public static Parser define(String key, @Nullable Reader reader, @Nullable Writer writer) {
TypesBuilderData data = TYPES_BUILDER.define(key);
Parser parser = new Parser(data, reader, writer);
ALL_PARSERS.add(parser);
MappingHelper.registerMapping(TYPES_BUILDER, PARSER_MAP, PARSER_ID_MAP, parser);
return parser;
return REGISTRY.define(key, data -> new Parser(data, reader, writer));
}
// with key
public static Parser getByName(String name) {
return PARSER_MAP.get(name);
return REGISTRY.getByName(name);
}
public static Parser getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, Parser> idMap = PARSER_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
public static List<Parser> getParsers() {
return Collections.unmodifiableList(ALL_PARSERS);
return new ArrayList<>(REGISTRY.getEntries());
}
public static VersionedRegistry<Parser> getRegistry() {
return REGISTRY;
}
public static final Parser BRIGADIER_BOOL = define("brigadier:bool", null, null);
@@ -223,7 +219,7 @@ public class Parsers {
);
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
@FunctionalInterface
@@ -232,9 +228,8 @@ public class Parsers {
@FunctionalInterface
public interface Writer extends BiConsumer<PacketWrapper<?>, List<Object>> {}
public static final class Parser implements MappedEntity {
public static final class Parser extends AbstractMappedEntity {
private final TypesBuilderData data;
private final Reader reader;
private final Writer writer;
@@ -247,8 +242,9 @@ public class Parsers {
);
}
private Parser(TypesBuilderData data, @Nullable Reader reader, Writer writer) {
this.data = data;
@ApiStatus.Internal
public Parser(@Nullable TypesBuilderData data, @Nullable Reader reader, @Nullable Writer writer) {
super(data);
this.reader = reader;
this.writer = writer;
}
@@ -265,24 +261,5 @@ public class Parsers {
this.writer.accept(wrapper, properties);
}
}
@Override
public ResourceLocation getName() {
return this.data.getName();
}
@Override
public int getId(ClientVersion version) {
int index = TYPES_BUILDER.getDataIndex(version);
return this.data.getData()[index];
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Parser) {
return this.getName().equals(((Parser) obj).getName());
}
return false;
}
}
}
@@ -20,6 +20,7 @@ package com.github.retrooper.packetevents.protocol.chat;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
@@ -39,6 +40,7 @@ public class StaticChatType extends AbstractMappedEntity implements ChatType {
this(null, chatDecoration, null, narrationDecoration, null);
}
@ApiStatus.Internal
public StaticChatType(
@Nullable TypesBuilderData data,
ChatTypeDecoration chatDecoration,
@@ -56,6 +58,7 @@ public class StaticChatType extends AbstractMappedEntity implements ChatType {
this(null, chatDecoration, overlayDecoration, narrationDecoration, narrationPriority);
}
@ApiStatus.Internal
public StaticChatType(
@Nullable TypesBuilderData data,
@UnknownNullability("only nullable for 1.19") ChatTypeDecoration chatDecoration,
@@ -84,8 +84,7 @@ import java.util.Collection;
*/
public final class ComponentTypes {
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>(
"data_component_type", "item/item_component_mappings");
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>("data_component_type");
private ComponentTypes() {
}
@@ -32,8 +32,7 @@ import org.jetbrains.annotations.Nullable;
*/
public final class EnchantEffectComponentTypes {
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>(
"enchantment_effect_component_type", "enchantment/effect_component_type");
private static final VersionedRegistry<ComponentType<?>> REGISTRY = new VersionedRegistry<>("enchantment_effect_component_type");
private EnchantEffectComponentTypes() {
}
@@ -25,6 +25,7 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.function.Function;
@@ -36,6 +37,7 @@ public class StaticComponentType<T> extends AbstractMappedEntity implements Comp
private final @Nullable Decoder<T> decoder;
private final @Nullable Encoder<T> encoder;
@ApiStatus.Internal
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Reader<T> reader,
@@ -44,6 +46,7 @@ public class StaticComponentType<T> extends AbstractMappedEntity implements Comp
this(data, reader, writer, null, null);
}
@ApiStatus.Internal
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Decoder<T> decoder,
@@ -52,6 +55,7 @@ public class StaticComponentType<T> extends AbstractMappedEntity implements Comp
this(data, null, null, decoder, encoder);
}
@ApiStatus.Internal
public StaticComponentType(
@Nullable TypesBuilderData data,
@Nullable Reader<T> reader,
@@ -18,39 +18,46 @@
package com.github.retrooper.packetevents.protocol.entity.data;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class EntityDataType<T> {
private final String name;
private final int[] ids;
private final Function<PacketWrapper<?>, T> dataDeserializer;
private final BiConsumer<PacketWrapper<?>, Object> dataSerializer;
public class EntityDataType<T> extends AbstractMappedEntity {
public EntityDataType(String name, int[] ids, Function<PacketWrapper<?>, T> dataDeserializer, BiConsumer<PacketWrapper<?>, Object> dataSerializer) {
this.name = name;
this.ids = ids;
this.dataDeserializer = dataDeserializer;
this.dataSerializer = dataSerializer;
private final PacketWrapper.Reader<? extends T> reader;
private final PacketWrapper.Writer<T> writer;
@ApiStatus.Internal
public EntityDataType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<? extends T> reader,
PacketWrapper.Writer<T> writer
) {
super(data);
this.reader = reader;
this.writer = writer;
}
public String getName() {
return name;
public T read(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
public int getId(ClientVersion version) {
int index = EntityDataTypes.TYPES_BUILDER.getDataIndex(version);
return ids[index];
public void write(PacketWrapper<?> wrapper, T serializer) {
this.writer.accept(wrapper, serializer);
}
@Deprecated
public Function<PacketWrapper<?>, T> getDataDeserializer() {
return dataDeserializer;
return this.reader::apply;
}
public BiConsumer<PacketWrapper<?>, Object> getDataSerializer() {
return dataSerializer;
@Deprecated
public BiConsumer<PacketWrapper<?>, T> getDataSerializer() {
return this.writer;
}
}
@@ -18,7 +18,6 @@
package com.github.retrooper.packetevents.protocol.entity.data;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.entity.armadillo.ArmadilloState;
import com.github.retrooper.packetevents.protocol.entity.pose.EntityPose;
@@ -34,27 +33,23 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Quaternion4f;
import com.github.retrooper.packetevents.util.Vector3f;
import com.github.retrooper.packetevents.util.Vector3i;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public class EntityDataTypes {
//1.7 -> 1.18 block_position is just 3 ints, not serialized with a long
//short was removed in 1.9+
//boolean was added in 1.9
//nbt was added in 1.12
public final class EntityDataTypes {
private static final Map<String, EntityDataType<?>> ENTITY_DATA_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, EntityDataType<?>>> ENTITY_DATA_TYPE_ID_MAP = new HashMap<>();
protected static final TypesBuilder TYPES_BUILDER = new TypesBuilder("entity/entity_data_type_mappings");
private static final VersionedRegistry<EntityDataType<?>> REGISTRY = new VersionedRegistry<>("entity_data_serializer");
public static final EntityDataType<Byte> BYTE = define("byte", PacketWrapper::readByte, PacketWrapper::writeByte);
// short was removed in 1.9+
public static final EntityDataType<Short> SHORT = define("short", PacketWrapper::readShort, PacketWrapper::writeShort);
public static final EntityDataType<Integer> INT = define("int", wrapper -> {
@@ -91,6 +86,7 @@ public class EntityDataTypes {
(PacketWrapper<?> wrapper) -> Optional.of(wrapper.readItemStack()),
(PacketWrapper<?> wrapper, Optional<ItemStack> value) -> wrapper.writeItemStack(value.orElse(null)));
// boolean was added in 1.9
public static final EntityDataType<Boolean> BOOLEAN = define("boolean", PacketWrapper::readBoolean, PacketWrapper::writeBoolean);
public static final EntityDataType<Vector3f> ROTATION = define("rotation",
@@ -101,6 +97,7 @@ public class EntityDataTypes {
wrapper.writeFloat(value.z);
});
// 1.7 -> 1.18 block_position is just 3 ints, not serialized with a long
public static final EntityDataType<Vector3i> BLOCK_POSITION = define("block_position", (PacketWrapper<?> wrapper) -> {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
return wrapper.readBlockPosition();
@@ -139,6 +136,7 @@ public class EntityDataTypes {
public static final EntityDataType<Integer> OPTIONAL_BLOCK_STATE = define("optional_block_state", readIntDeserializer(), writeIntSerializer());
// nbt was added in 1.12
public static final EntityDataType<NBTCompound> NBT = define("nbt", PacketWrapper::readNBT, PacketWrapper::writeNBT);
public static final EntityDataType<Particle<?>> PARTICLE = define("particle", Particle::read, Particle::write);
@@ -206,171 +204,95 @@ public class EntityDataTypes {
public static final EntityDataType<Integer> WOLF_VARIANT =
define("wolf_variant_type", readIntDeserializer(), writeIntSerializer());
private EntityDataTypes() {
}
public static VersionedRegistry<EntityDataType<?>> getRegistry() {
return REGISTRY;
}
/**
* Returns an immutable view of the entity-data types.
*
* @return Entity-Data Types
*/
public static Collection<EntityDataType<?>> values() {
return Collections.unmodifiableCollection(ENTITY_DATA_TYPE_MAP.values());
return REGISTRY.getEntries();
}
public static EntityDataType<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, EntityDataType<?>> typeIdMap = ENTITY_DATA_TYPE_ID_MAP.get((byte) index);
return typeIdMap.get(id);
return REGISTRY.getById(version, id);
}
public static EntityDataType<?> getByName(String name) {
return ENTITY_DATA_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static <T> EntityDataType<T> define(String name, Function<PacketWrapper<?>, T> deserializer, BiConsumer<PacketWrapper<?>, T> serializer) {
TypesBuilderData data = TYPES_BUILDER.define(name);
EntityDataType<T> type = new EntityDataType<>(name, data.getData(), deserializer,
(BiConsumer<PacketWrapper<?>, Object>) serializer);
ENTITY_DATA_TYPE_MAP.put(type.getName(), type);
for (ClientVersion version : TYPES_BUILDER.getVersions()) {
int index = TYPES_BUILDER.getDataIndex(version);
if (index == -1) continue;
Map<Integer, EntityDataType<?>> typeIdMap = ENTITY_DATA_TYPE_ID_MAP
.computeIfAbsent((byte) index, k -> new HashMap<>());
typeIdMap.put(type.getId(version), type);
}
return type;
@ApiStatus.Internal
public static <T, Z extends T> EntityDataType<Z> define(
String name, PacketWrapper.Reader<Z> reader, PacketWrapper.Writer<T> writer
) {
return REGISTRY.define(name, data -> new EntityDataType<>(data, reader, writer::accept));
}
private static <T> Function<PacketWrapper<?>, T> readIntDeserializer() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
return (PacketWrapper<?> wrapper) -> (T) ((Object) wrapper.readVarInt());
} else {
return (PacketWrapper<?> wrapper) -> (T) ((Object) wrapper.readInt());
}
private static PacketWrapper.Reader<Integer> readIntDeserializer() {
return wrapper -> wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9) ?
wrapper.readVarInt() : wrapper.readInt();
}
private static <T> BiConsumer<PacketWrapper<?>, T> writeIntSerializer() {
return (PacketWrapper<?> wrapper, T value) -> {
int output = 0;
if (value instanceof Byte) {
output = ((Byte) value).intValue();
} else if (value instanceof Short) {
output = ((Short) value).intValue();
} else if (value instanceof Integer) {
output = (Integer) value;
} else if (value instanceof Long) {
output = ((Long) value).intValue();
}
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
wrapper.writeVarInt(output);
private static PacketWrapper.Writer<Number> writeIntSerializer() {
return (wrapper, value) -> {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
wrapper.writeVarInt(value.intValue());
} else {
wrapper.writeInt(output);
wrapper.writeInt(value.intValue());
}
};
}
@Deprecated
private static Function<PacketWrapper<?>, Optional<String>> readOptionalComponentJSONDeserializer() {
return (PacketWrapper<?> wrapper) -> {
if (wrapper.readBoolean()) {
return Optional.of(wrapper.readComponentJSON());
} else {
return Optional.empty();
}
};
private static PacketWrapper.Reader<Optional<String>> readOptionalComponentJSONDeserializer() {
return wrapper -> wrapper.readJavaOptional(PacketWrapper::readComponentJSON);
}
@Deprecated
private static BiConsumer<PacketWrapper<?>, Optional<String>> writeOptionalComponentJSONSerializer() {
return (PacketWrapper<?> wrapper, Optional<String> value) -> {
if (value != null && value.isPresent()) {
wrapper.writeBoolean(true);
wrapper.writeComponentJSON(value.get());
} else {
wrapper.writeBoolean(false);
private static PacketWrapper.Writer<Optional<String>> writeOptionalComponentJSONSerializer() {
return (wrapper, value) -> wrapper.writeJavaOptional(value, PacketWrapper::writeComponentJSON);
}
private static PacketWrapper.Reader<Optional<Component>> readOptionalComponentDeserializer() {
return wrapper -> wrapper.readJavaOptional(PacketWrapper::readComponent);
}
private static PacketWrapper.Writer<Optional<Component>> writeOptionalComponentSerializer() {
return (wrapper, value) -> wrapper.writeJavaOptional(value, PacketWrapper::writeComponent);
}
private static PacketWrapper.Reader<Optional<Vector3i>> readOptionalBlockPositionDeserializer() {
return wrapper -> {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
return wrapper.readJavaOptional(PacketWrapper::readBlockPosition);
}
return wrapper.readJavaOptional(ew ->
new Vector3i(ew.readInt(), ew.readInt(), ew.readInt()));
};
}
private static Function<PacketWrapper<?>, Optional<Component>> readOptionalComponentDeserializer() {
return (PacketWrapper<?> wrapper) -> {
if (wrapper.readBoolean()) {
return Optional.of(wrapper.readComponent());
private static PacketWrapper.Writer<Optional<Vector3i>> writeOptionalBlockPositionSerializer() {
return (wrapper, value) -> {
if (wrapper.getServerVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
wrapper.writeJavaOptional(value, PacketWrapper::writeBlockPosition);
} else {
return Optional.empty();
wrapper.writeJavaOptional(value, (ew, valuee) -> {
wrapper.writeInt(valuee.getX());
wrapper.writeInt(valuee.getY());
wrapper.writeInt(valuee.getZ());
});
}
};
}
private static BiConsumer<PacketWrapper<?>, Optional<Component>> writeOptionalComponentSerializer() {
return (PacketWrapper<?> wrapper, Optional<Component> value) -> {
if (value != null && value.isPresent()) {
wrapper.writeBoolean(true);
wrapper.writeComponent(value.get());
} else {
wrapper.writeBoolean(false);
}
};
}
private static <T> Function<PacketWrapper<?>, T> readOptionalBlockPositionDeserializer() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
return (PacketWrapper<?> wrapper) -> {
if (wrapper.readBoolean()) {
return (T) Optional.of(wrapper.readBlockPosition());
} else {
return (T) Optional.empty();
}
};
} else {
return (PacketWrapper<?> wrapper) -> {
if (wrapper.readBoolean()) {
int x = wrapper.readInt();
int y = wrapper.readInt();
int z = wrapper.readInt();
return (T) Optional.of(new Vector3i(x, y, z));
} else {
return (T) Optional.empty();
}
};
}
}
private static <T> BiConsumer<PacketWrapper<?>, T> writeOptionalBlockPositionSerializer() {
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_9)) {
return (PacketWrapper<?> wrapper, T value) -> {
if (value instanceof Optional) {
Optional<?> optional = (Optional<?>) value;
if (optional.isPresent()) {
wrapper.writeBoolean(true);
wrapper.writeBlockPosition((Vector3i) optional.get());
} else {
wrapper.writeBoolean(false);
}
} else {
wrapper.writeBoolean(false);
}
};
} else {
return (PacketWrapper<?> wrapper, T value) -> {
if (value instanceof Optional) {
Optional<?> optional = (Optional<?>) value;
if (optional.isPresent()) {
wrapper.writeBoolean(true);
Vector3i position = (Vector3i) optional.get();
wrapper.writeInt(position.getX());
wrapper.writeInt(position.getY());
wrapper.writeInt(position.getZ());
} else {
wrapper.writeBoolean(false);
}
} else {
wrapper.writeBoolean(false);
}
};
}
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -24,5 +24,8 @@ import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import java.util.Optional;
public interface EntityType extends MappedEntity, LegacyMappedEntity {
boolean isInstanceOf(EntityType parent);
Optional<EntityType> getParent();
}
@@ -19,105 +19,54 @@
package com.github.retrooper.packetevents.protocol.entity.type;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class EntityTypes {
private static final Map<String, EntityType> ENTITY_TYPE_MAP = new HashMap<>();
//Key - mappings version, value - map with entity type ids and entity types
private static final Map<Byte, Map<Integer, EntityType>> ENTITY_TYPE_ID_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, EntityType>> LEGACY_ENTITY_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("entity/entity_type_mappings");
private static final TypesBuilder LEGACY_TYPES_BUILDER = new TypesBuilder("entity/legacy_entity_type_mappings");
public final class EntityTypes {
public static EntityType define(String key, @Nullable EntityType parent) {
TypesBuilderData data = TYPES_BUILDER.define(key);
TypesBuilderData legacyData = LEGACY_TYPES_BUILDER.define(key);
Optional<EntityType> optParent = Optional.ofNullable(parent);
EntityType entityType = new EntityType() {
private final int[] ids = data.getData();
private final int[] legacyIds = legacyData.getData();
private static final VersionedRegistry<EntityType> REGISTRY = new VersionedRegistry<>("entity_type");
private static final VersionedRegistry<EntityType> LEGACY_SPAWN_REGISTRY = new VersionedRegistry<>("legacy_spawn_entity_type");
@Override
public Optional<EntityType> getParent() {
return optParent;
}
private EntityTypes() {
}
@Override
public int getLegacyId(ClientVersion version) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_14)) {
return -1;
}
int index = LEGACY_TYPES_BUILDER.getDataIndex(version);
return legacyIds[index];
}
public static VersionedRegistry<EntityType> getRegistry() {
return REGISTRY;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@ApiStatus.Obsolete
public static VersionedRegistry<EntityType> getLegacySpawnRegistry() {
return LEGACY_SPAWN_REGISTRY;
}
@Override
public int getId(ClientVersion version) {
int index = TYPES_BUILDER.getDataIndex(version);
return ids[index];
}
};
ENTITY_TYPE_MAP.put(entityType.getName().toString(), entityType);
for (ClientVersion version : TYPES_BUILDER.getVersions()) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, EntityType> typeIdMap = ENTITY_TYPE_ID_MAP.computeIfAbsent((byte) index, k -> new HashMap<>());
typeIdMap.put(entityType.getId(version), entityType);
}
for (ClientVersion version : LEGACY_TYPES_BUILDER.getVersions()) {
int index = LEGACY_TYPES_BUILDER.getDataIndex(version);
Map<Integer, EntityType> legacyTypeIdMap = LEGACY_ENTITY_TYPE_ID_MAP.computeIfAbsent((byte) index, k -> new HashMap<>());
legacyTypeIdMap.put(entityType.getLegacyId(version), entityType);
}
return entityType;
@ApiStatus.Internal
public static EntityType define(String name, @Nullable EntityType parent) {
StaticEntityType type = REGISTRY.define(name, data ->
new StaticEntityType(data, parent));
return LEGACY_SPAWN_REGISTRY.define(name, type::setLegacyData);
}
public static boolean isTypeInstanceOf(EntityType type, EntityType parent) {
while (type != null) {
if (type == parent) {
return true;
}
if (type.getParent().isPresent()) {
type = type.getParent().get();
} else {
return false;
}
}
return false;
return type != null && type.isInstanceOf(parent);
}
//with minecraft:key
public static EntityType getByName(String name) {
return ENTITY_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static EntityType getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
return ENTITY_TYPE_ID_MAP.get((byte) index).get(id);
return REGISTRY.getById(version, id);
}
@ApiStatus.Obsolete
public static EntityType getByLegacyId(ClientVersion version, int id) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_14)) {
return null;
}
int index = LEGACY_TYPES_BUILDER.getDataIndex(version);
return LEGACY_ENTITY_TYPE_ID_MAP.get((byte) index).get(id);
return LEGACY_SPAWN_REGISTRY.getById(version, id);
}
// Credit to ViaVersion for these categories
@@ -340,11 +289,11 @@ public class EntityTypes {
* @return Entity Types
*/
public static Collection<EntityType> values() {
return Collections.unmodifiableCollection(ENTITY_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
LEGACY_TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
LEGACY_SPAWN_REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,76 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.entity.type;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Optional;
public class StaticEntityType extends AbstractMappedEntity implements EntityType {
private final Optional<EntityType> parent;
private final Map<EntityType, Boolean> parents;
private @Nullable TypesBuilderData legacyData;
@ApiStatus.Internal
public StaticEntityType(@Nullable TypesBuilderData data, @Nullable EntityType parent) {
super(data);
this.parent = Optional.ofNullable(parent);
// iterate through all parents and save them for faster access
this.parents = new IdentityHashMap<>();
this.parents.put(this, true);
while (parent != null) {
this.parents.put(parent, true);
parent = parent.getParent().orElse(null);
}
}
StaticEntityType setLegacyData(@Nullable TypesBuilderData legacyData) {
this.legacyData = legacyData;
return this;
}
@Override
public boolean isInstanceOf(EntityType parent) {
return parent != null && this.parents.containsKey(parent);
}
@Override
public Optional<EntityType> getParent() {
return this.parent;
}
@Override
public int getLegacyId(ClientVersion version) {
if (version.isNewerThanOrEquals(ClientVersion.V_1_14)) {
return -1;
} else if (this.legacyData != null) {
return this.legacyData.getId(version);
}
throw new UnsupportedOperationException();
}
}
@@ -41,6 +41,7 @@ public class VillagerData {
this.level = level;
}
@Deprecated
public VillagerData(int typeId, int professionId, int level) {
this(VillagerTypes.getById(typeId), VillagerProfessions.getById(professionId), level);
}
@@ -0,0 +1,40 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.entity.villager.profession;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticVillagerProfession extends AbstractMappedEntity implements VillagerProfession {
@ApiStatus.Internal
public StaticVillagerProfession(@Nullable TypesBuilderData data) {
super(data);
}
@Override
public int getId() {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
return this.getId(version.toClientVersion());
}
}
@@ -22,4 +22,7 @@ import com.github.retrooper.packetevents.protocol.mapper.StaticMappedEntity;
public interface VillagerProfession extends StaticMappedEntity {
@Deprecated
@Override
int getId();
}
@@ -18,54 +18,65 @@
package com.github.retrooper.packetevents.protocol.entity.villager.profession;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.util.HashMap;
import java.util.Map;
public final class VillagerProfessions {
public class VillagerProfessions {
private static final Map<String, VillagerProfession> VILLAGER_PROFESSION_MAP = new HashMap<>();
private static final Map<Byte, VillagerProfession> VILLAGER_PROFESSION_ID_MAP = new HashMap<>();
private static final VersionedRegistry<VillagerProfession> REGISTRY = new VersionedRegistry<>("villager_profession");
public static VillagerProfession define(int id, String name) {
ResourceLocation location = new ResourceLocation(name);
VillagerProfession type = new VillagerProfession() {
@Override
public ResourceLocation getName() {
return location;
}
@Override
public int getId() {
return id;
}
};
VILLAGER_PROFESSION_MAP.put(type.getName().toString(), type);
VILLAGER_PROFESSION_ID_MAP.put((byte) type.getId(), type);
return type;
private VillagerProfessions() {
}
public static VersionedRegistry<VillagerProfession> getRegistry() {
return REGISTRY;
}
@Deprecated
@ApiStatus.Internal
public static VillagerProfession define(int id, String name) {
return define(name);
}
@ApiStatus.Internal
public static VillagerProfession define(String name) {
return REGISTRY.define(name, StaticVillagerProfession::new);
}
@Deprecated
public static VillagerProfession getById(int id) {
return VILLAGER_PROFESSION_ID_MAP.get((byte)id);
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
return getById(version.toClientVersion(), id);
}
public static VillagerProfession getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static VillagerProfession getByName(String name) {
return VILLAGER_PROFESSION_MAP.get(name);
return REGISTRY.getByName(name);
}
public static final VillagerProfession NONE = define(0, "minecraft:none");
public static final VillagerProfession ARMORER = define(1, "minecraft:armorer");
public static final VillagerProfession BUTCHER = define(2, "minecraft:butcher");
public static final VillagerProfession CARTOGRAPHER = define(3, "minecraft:cartographer");
public static final VillagerProfession CLERIC = define(4, "minecraft:cleric");
public static final VillagerProfession FARMER = define(5, "minecraft:farmer");
public static final VillagerProfession FISHERMAN = define(6, "minecraft:fisherman");
public static final VillagerProfession FLETCHER = define(7, "minecraft:fletcher");
public static final VillagerProfession LEATHERWORKER = define(8, "minecraft:leatherworker");
public static final VillagerProfession LIBRARIAN = define(9, "minecraft:librarian");
public static final VillagerProfession MASON = define(10, "minecraft:mason");
public static final VillagerProfession NITWIT = define(11, "minecraft:nitwit");
public static final VillagerProfession SHEPHERD = define(12, "minecraft:shepherd");
public static final VillagerProfession TOOLSMITH = define(13, "minecraft:toolsmith");
public static final VillagerProfession WEAPONSMITH = define(14, "minecraft:weaponsmith");
public static final VillagerProfession NONE = define("none");
public static final VillagerProfession ARMORER = define("armorer");
public static final VillagerProfession BUTCHER = define("butcher");
public static final VillagerProfession CARTOGRAPHER = define("cartographer");
public static final VillagerProfession CLERIC = define("cleric");
public static final VillagerProfession FARMER = define("farmer");
public static final VillagerProfession FISHERMAN = define("fisherman");
public static final VillagerProfession FLETCHER = define("fletcher");
public static final VillagerProfession LEATHERWORKER = define("leatherworker");
public static final VillagerProfession LIBRARIAN = define("librarian");
public static final VillagerProfession MASON = define("mason");
public static final VillagerProfession NITWIT = define("nitwit");
public static final VillagerProfession SHEPHERD = define("shepherd");
public static final VillagerProfession TOOLSMITH = define("toolsmith");
public static final VillagerProfession WEAPONSMITH = define("weaponsmith");
static {
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,40 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.entity.villager.type;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticVillagerType extends AbstractMappedEntity implements VillagerType {
@ApiStatus.Internal
public StaticVillagerType(@Nullable TypesBuilderData data) {
super(data);
}
@Override
public int getId() {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
return this.getId(version.toClientVersion());
}
}
@@ -22,4 +22,7 @@ import com.github.retrooper.packetevents.protocol.mapper.StaticMappedEntity;
public interface VillagerType extends StaticMappedEntity {
@Deprecated
@Override
int getId();
}
@@ -18,58 +18,68 @@
package com.github.retrooper.packetevents.protocol.entity.villager.type;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class VillagerTypes {
private static final Map<String, VillagerType> VILLAGER_TYPE_MAP = new HashMap<>();
private static final Map<Byte, VillagerType> VILLAGER_TYPE_ID_MAP = new HashMap<>();
public final class VillagerTypes {
private static final VersionedRegistry<VillagerType> REGISTRY = new VersionedRegistry<>("villager_type");
private VillagerTypes() {
}
public static VersionedRegistry<VillagerType> getRegistry() {
return REGISTRY;
}
@Deprecated
@ApiStatus.Internal
public static VillagerType define(int id, String name) {
ResourceLocation location = new ResourceLocation(name);
VillagerType type = new VillagerType() {
@Override
public ResourceLocation getName() {
return location;
}
@Override
public int getId() {
return id;
}
};
VILLAGER_TYPE_MAP.put(type.getName().toString(), type);
VILLAGER_TYPE_ID_MAP.put((byte)type.getId(), type);
return type;
return define(name);
}
@ApiStatus.Internal
public static VillagerType define(String name) {
return REGISTRY.define(name, StaticVillagerType::new);
}
@Deprecated
public static VillagerType getById(int id) {
return VILLAGER_TYPE_ID_MAP.get((byte)id);
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
return getById(version.toClientVersion(), id);
}
public static VillagerType getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static VillagerType getByName(String name) {
return VILLAGER_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static final VillagerType DESERT = define(0, "minecraft:desert");
public static final VillagerType JUNGLE = define(1, "minecraft:jungle");
public static final VillagerType PLAINS = define(2, "minecraft:plains");
public static final VillagerType SAVANNA = define(3, "minecraft:savanna");
public static final VillagerType SNOW = define(4, "minecraft:snow");
public static final VillagerType SWAMP = define(5, "minecraft:swamp");
public static final VillagerType TAIGA = define(6, "minecraft:taiga");
public static final VillagerType DESERT = define("desert");
public static final VillagerType JUNGLE = define("jungle");
public static final VillagerType PLAINS = define("plains");
public static final VillagerType SAVANNA = define("savanna");
public static final VillagerType SNOW = define("snow");
public static final VillagerType SWAMP = define("swamp");
public static final VillagerType TAIGA = define("taiga");
/**
* Returns an immutable view of the villager types.
*
* @return Villager Types
*/
public static Collection<VillagerType> values() {
return Collections.unmodifiableCollection(VILLAGER_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
REGISTRY.unloadMappings();
}
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.protocol.world.biome.Biome;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -43,6 +44,7 @@ public class StaticWolfVariant extends AbstractMappedEntity implements WolfVaria
this(null, wildTexture, tameTexture, angryTexture, biomes);
}
@ApiStatus.Internal
public StaticWolfVariant(
@Nullable TypesBuilderData data,
ResourceLocation wildTexture,
@@ -25,37 +25,36 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import java.util.Arrays;
import java.util.Collections;
public final class WolfVariants {
private static final VersionedRegistry<WolfVariant> REGISTRY = new VersionedRegistry<>(
"wolf_variant", "entity/wolf_variant_mappings");
private static final VersionedRegistry<WolfVariant> REGISTRY = new VersionedRegistry<>("wolf_variant");
private WolfVariants() {
}
@ApiStatus.Internal
public static WolfVariant define(String key, MappedEntitySet<Biome> biomes) {
return define(key, "wolf_" + key, biomes);
public static WolfVariant define(String name, MappedEntitySet<Biome> biomes) {
return define(name, "wolf_" + name, biomes);
}
@ApiStatus.Internal
public static WolfVariant define(String key, String assetId, MappedEntitySet<Biome> biomes) {
return define(key, ResourceLocation.minecraft("entity/wolf/" + assetId),
public static WolfVariant define(String name, String assetId, MappedEntitySet<Biome> biomes) {
return define(name, ResourceLocation.minecraft("entity/wolf/" + assetId),
ResourceLocation.minecraft("entity/wolf/" + assetId + "_tame"),
ResourceLocation.minecraft("entity/wolf/" + assetId + "_angry"), biomes);
}
@ApiStatus.Internal
public static WolfVariant define(
String key,
String name,
ResourceLocation wildTexture,
ResourceLocation tameTexture,
ResourceLocation angryTexture,
MappedEntitySet<Biome> biomes
) {
return REGISTRY.define(key, data -> new StaticWolfVariant(
return REGISTRY.define(name, data -> new StaticWolfVariant(
data, wildTexture, tameTexture, angryTexture, biomes));
}
@@ -64,21 +63,21 @@ public final class WolfVariants {
}
public static final WolfVariant PALE = define("pale", "wolf",
new MappedEntitySet<>(Arrays.asList(Biomes.TAIGA)));
new MappedEntitySet<>(Collections.singletonList(Biomes.TAIGA)));
public static final WolfVariant SPOTTED = define("spotted",
new MappedEntitySet<>(ResourceLocation.minecraft("is_savanna")));
public static final WolfVariant SNOWY = define("snowy",
new MappedEntitySet<>(Arrays.asList(Biomes.GROVE)));
new MappedEntitySet<>(Collections.singletonList(Biomes.GROVE)));
public static final WolfVariant BLACK = define("black",
new MappedEntitySet<>(Arrays.asList(Biomes.OLD_GROWTH_PINE_TAIGA)));
new MappedEntitySet<>(Collections.singletonList(Biomes.OLD_GROWTH_PINE_TAIGA)));
public static final WolfVariant ASHEN = define("ashen",
new MappedEntitySet<>(Arrays.asList(Biomes.SNOWY_TAIGA)));
new MappedEntitySet<>(Collections.singletonList(Biomes.SNOWY_TAIGA)));
public static final WolfVariant RUSTY = define("rusty",
new MappedEntitySet<>(ResourceLocation.minecraft("is_jungle")));
public static final WolfVariant WOODS = define("woods",
new MappedEntitySet<>(Arrays.asList(Biomes.FOREST)));
new MappedEntitySet<>(Collections.singletonList(Biomes.FOREST)));
public static final WolfVariant CHESTNUT = define("chestnut",
new MappedEntitySet<>(Arrays.asList(Biomes.OLD_GROWTH_SPRUCE_TAIGA)));
new MappedEntitySet<>(Collections.singletonList(Biomes.OLD_GROWTH_SPRUCE_TAIGA)));
public static final WolfVariant STRIPED = define("striped",
new MappedEntitySet<>(ResourceLocation.minecraft("is_badlands")));
@@ -40,8 +40,7 @@ public final class ArmorMaterials {
DFU.put("minecraft:armadillo", "minecraft:armadillo_scute");
}
private static final VersionedRegistry<ArmorMaterial> REGISTRY = new VersionedRegistry<>(
"equipment_asset", "item/equipment_asset_mappings");
private static final VersionedRegistry<ArmorMaterial> REGISTRY = new VersionedRegistry<>("equipment_asset");
private ArmorMaterials() {
}
@@ -20,10 +20,12 @@ package com.github.retrooper.packetevents.protocol.item.armormaterial;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticArmorMaterial extends AbstractMappedEntity implements ArmorMaterial {
@ApiStatus.Internal
public StaticArmorMaterial(@Nullable TypesBuilderData data) {
super(data);
}
@@ -27,8 +27,7 @@ import java.util.Collection;
public final class BannerPatterns {
private static final VersionedRegistry<BannerPattern> REGISTRY = new VersionedRegistry<>(
"banner_pattern", "item/item_banner_pattern_mappings");
private static final VersionedRegistry<BannerPattern> REGISTRY = new VersionedRegistry<>("banner_pattern");
private BannerPatterns() {
}
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.protocol.item.banner;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -34,6 +35,7 @@ public class StaticBannerPattern extends AbstractMappedEntity implements BannerP
this(null, assetId, translationKey);
}
@ApiStatus.Internal
public StaticBannerPattern(@Nullable TypesBuilderData data, ResourceLocation assetId, String translationKey) {
super(data);
this.assetId = assetId;
@@ -30,8 +30,7 @@ import org.jetbrains.annotations.ApiStatus;
public final class ConsumeEffectTypes {
private static final VersionedRegistry<ConsumeEffectType<?>> REGISTRY = new VersionedRegistry<>(
"consume_effect_type", "item/consume_effect_type_mappings");
private static final VersionedRegistry<ConsumeEffectType<?>> REGISTRY = new VersionedRegistry<>("consume_effect_type");
private ConsumeEffectTypes() {
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticConsumeEffectType<T extends ConsumeEffect<?>> extends AbstractMappedEntity implements ConsumeEffectType<T> {
@@ -30,6 +31,7 @@ public class StaticConsumeEffectType<T extends ConsumeEffect<?>> extends Abstrac
private final Reader<T> reader;
private final Writer<T> writer;
@ApiStatus.Internal
public StaticConsumeEffectType(
@Nullable TypesBuilderData data,
Reader<T> reader,
@@ -46,12 +46,9 @@ public final class EnchantmentTypes {
static {
ENCHANTMENT_DATA = new HashMap<>();
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/enchantment/enchantment_type_data")) {
while (dataTag.hasNext()) {
Map.Entry<String, NBT> entry = dataTag.next();
if (entry.getKey().equals("version")) {
continue; // skip version field
}
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/data/enchantment")) {
dataTag.skipOne(); // skip version
for (Map.Entry<String, NBT> entry : (SequentialNBTReader.Compound) dataTag.next().getValue()) {
ResourceLocation enchantKey = new ResourceLocation(entry.getKey());
ENCHANTMENT_DATA.put(enchantKey, ((SequentialNBTReader.Compound) entry.getValue()).readFully());
}
@@ -60,8 +57,10 @@ public final class EnchantmentTypes {
}
}
private static final VersionedRegistry<EnchantmentType> REGISTRY = new VersionedRegistry<>(
"enchantment", "enchantment/enchantment_type_mappings");
private static final VersionedRegistry<EnchantmentType> REGISTRY = new VersionedRegistry<>("enchantment");
private EnchantmentTypes() {
}
@ApiStatus.Internal
public static EnchantmentType define(String key) {
@@ -24,6 +24,7 @@ import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntitySet;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -44,6 +45,7 @@ public class StaticEnchantmentType extends AbstractMappedEntity implements Encha
this(null, description, definition, exclusiveSet, effects);
}
@ApiStatus.Internal
public StaticEnchantmentType(
@Nullable TypesBuilderData data,
Component description,
@@ -27,12 +27,12 @@ import org.jetbrains.annotations.ApiStatus;
public final class Instruments {
private static final VersionedRegistry<Instrument> REGISTRY = new VersionedRegistry<>(
"instrument", "item/item_instrument_mappings");
private static final VersionedRegistry<Instrument> REGISTRY = new VersionedRegistry<>("instrument");
private Instruments() {
}
@ApiStatus.Internal
public static Instrument define(String key, Sound sound) {
// vanilla defaults for goat horns
return define(key, sound, 20 * 7, 256f);
@@ -22,6 +22,7 @@ import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -42,6 +43,7 @@ public class StaticInstrument extends AbstractMappedEntity implements Instrument
this(null, sound, useSeconds, range, description);
}
@ApiStatus.Internal
public StaticInstrument(@Nullable TypesBuilderData data, Sound sound, float useSeconds, float range, Component description) {
super(data);
this.sound = sound;
@@ -23,11 +23,13 @@ import com.github.retrooper.packetevents.protocol.sound.Sound;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class JukeboxSong extends AbstractMappedEntity implements IJukeboxSong {
private Sound sound;
private Component description;
private float lengthInSeconds;
@@ -37,8 +39,14 @@ public class JukeboxSong extends AbstractMappedEntity implements IJukeboxSong {
this(null, sound, description, lengthInSeconds, comparatorOutput);
}
public JukeboxSong(@Nullable TypesBuilderData data, Sound sound, Component description, float lengthInSeconds,
int comparatorOutput) {
@ApiStatus.Internal
public JukeboxSong(
@Nullable TypesBuilderData data,
Sound sound,
Component description,
float lengthInSeconds,
int comparatorOutput
) {
super(data);
this.sound = sound;
this.description = description;
@@ -29,9 +29,9 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class JukeboxSongs {
private static final VersionedRegistry<IJukeboxSong> REGISTRY = new VersionedRegistry<>("jukebox_song",
"item/item_jukebox_song_mappings");
public final class JukeboxSongs {
private static final VersionedRegistry<IJukeboxSong> REGISTRY = new VersionedRegistry<>("jukebox_song");
private JukeboxSongs() {
}
@@ -21,21 +21,26 @@ package com.github.retrooper.packetevents.protocol.item.mapdecoration;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import static com.github.retrooper.packetevents.resources.ResourceLocation.minecraft;
public class MapDecorationTypes {
public final class MapDecorationTypes {
private static final VersionedRegistry<MapDecorationType> REGISTRY =
new VersionedRegistry<>("map_decoration_type", "item/item_map_decoration_type_mappings");
private static final VersionedRegistry<MapDecorationType> REGISTRY = new VersionedRegistry<>("map_decoration_type");
private MapDecorationTypes() {
}
@ApiStatus.Internal
public static MapDecorationType define(String key, boolean showOnItemFrame, boolean trackCount) {
return define(key, minecraft(key), showOnItemFrame, trackCount);
}
@ApiStatus.Internal
public static MapDecorationType define(
String key, ResourceLocation assetId,
boolean showOnItemFrame, boolean trackCount
@@ -44,6 +49,7 @@ public class MapDecorationTypes {
-1, false, trackCount);
}
@ApiStatus.Internal
public static MapDecorationType define(
String key,
boolean showOnItemFrame, int mapColor,
@@ -53,55 +59,18 @@ public class MapDecorationTypes {
showOnItemFrame, mapColor, explorationMapElement, trackCount);
}
@ApiStatus.Internal
public static MapDecorationType define(
String key, ResourceLocation assetId,
boolean showOnItemFrame, int mapColor,
boolean explorationMapElement, boolean trackCount
) {
return REGISTRY.define(key, data -> new MapDecorationType() {
@Override
public ResourceLocation getAssetId() {
return assetId;
}
return REGISTRY.define(key, data -> new StaticMapDecorationType(data,
assetId, showOnItemFrame, mapColor, explorationMapElement, trackCount));
}
@Override
public boolean isShowOnItemFrame() {
return showOnItemFrame;
}
@Override
public int getMapColor() {
return mapColor;
}
@Override
public boolean isExplorationMapElement() {
return explorationMapElement;
}
@Override
public boolean isTrackCount() {
return trackCount;
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return data.getId(version);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MapDecorationType) {
return this.getName().equals(((MapDecorationType) obj).getName());
}
return false;
}
});
public static VersionedRegistry<MapDecorationType> getRegistry() {
return REGISTRY;
}
public static @Nullable MapDecorationType getByName(String name) {
@@ -0,0 +1,73 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.item.mapdecoration;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticMapDecorationType extends AbstractMappedEntity implements MapDecorationType {
private final ResourceLocation assetId;
private final boolean showOnItemFrame;
private final int mapColor;
private final boolean explorationMapElement;
private final boolean trackCount;
@ApiStatus.Internal
public StaticMapDecorationType(
@Nullable TypesBuilderData data,
ResourceLocation assetId, boolean showOnItemFrame, int mapColor,
boolean explorationMapElement, boolean trackCount
) {
super(data);
this.assetId = assetId;
this.showOnItemFrame = showOnItemFrame;
this.mapColor = mapColor;
this.explorationMapElement = explorationMapElement;
this.trackCount = trackCount;
}
@Override
public ResourceLocation getAssetId() {
return this.assetId;
}
@Override
public boolean isShowOnItemFrame() {
return this.showOnItemFrame;
}
@Override
public int getMapColor() {
return this.mapColor;
}
@Override
public boolean isExplorationMapElement() {
return this.explorationMapElement;
}
@Override
public boolean isTrackCount() {
return this.trackCount;
}
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
@@ -43,6 +44,7 @@ public class StaticTrimMaterial extends AbstractMappedEntity implements TrimMate
this(null, assetName, ingredient, FALLBACK_ITEM_MODEL_INDEX, overrideArmorMaterials, description);
}
@ApiStatus.Internal
public StaticTrimMaterial(
@Nullable TypesBuilderData data,
String assetName, ItemType ingredient,
@@ -58,6 +60,7 @@ public class StaticTrimMaterial extends AbstractMappedEntity implements TrimMate
this(null, assetName, ingredient, itemModelIndex, overrideArmorMaterials, description);
}
@ApiStatus.Internal
public StaticTrimMaterial(
@Nullable TypesBuilderData data,
String assetName, ItemType ingredient, float itemModelIndex,
@@ -37,8 +37,7 @@ import static net.kyori.adventure.text.format.TextColor.color;
public final class TrimMaterials {
private static final VersionedRegistry<TrimMaterial> REGISTRY = new VersionedRegistry<>(
"trim_material", "item/item_trim_material_mappings");
private static final VersionedRegistry<TrimMaterial> REGISTRY = new VersionedRegistry<>("trim_material");
private TrimMaterials() {
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -41,6 +42,7 @@ public class StaticTrimPattern extends AbstractMappedEntity implements TrimPatte
this(null, assetId, templateItem, description, decal);
}
@ApiStatus.Internal
public StaticTrimPattern(
@Nullable TypesBuilderData data,
ResourceLocation assetId, ItemType templateItem,
@@ -28,27 +28,26 @@ import org.jetbrains.annotations.ApiStatus;
public final class TrimPatterns {
private static final VersionedRegistry<TrimPattern> REGISTRY = new VersionedRegistry<>(
"trim_pattern", "item/item_trim_pattern_mappings");
private static final VersionedRegistry<TrimPattern> REGISTRY = new VersionedRegistry<>("trim_pattern");
private TrimPatterns() {
}
@ApiStatus.Internal
public static TrimPattern define(String key) {
ResourceLocation assetId = ResourceLocation.minecraft(key);
public static TrimPattern define(String name) {
ResourceLocation assetId = ResourceLocation.minecraft(name);
ItemType templateItem = ItemTypes.getByName(assetId + "_armor_trim_smithing_template");
Component description = Component.translatable("trim_pattern.minecraft." + key);
Component description = Component.translatable("trim_pattern.minecraft." + name);
boolean decal = false;
return define(key, assetId, templateItem, description, decal);
return define(name, assetId, templateItem, description, decal);
}
@ApiStatus.Internal
public static TrimPattern define(
String key, ResourceLocation assetId, ItemType templateItem,
String name, ResourceLocation assetId, ItemType templateItem,
Component description, boolean decal
) {
return REGISTRY.define(key, data ->
return REGISTRY.define(name, data ->
new StaticTrimPattern(data, assetId, templateItem, description, decal));
}
@@ -49,6 +49,7 @@ public interface ItemType extends MappedEntity {
return this.getAttributes().contains(attribute);
}
@Deprecated
default StaticComponentMap getComponents() {
return this.getComponents(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
}
@@ -18,12 +18,13 @@
package com.github.retrooper.packetevents.protocol.item.type;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.netty.buffer.UnpooledByteBufAllocationHelper;
import com.github.retrooper.packetevents.protocol.component.ComponentType;
import com.github.retrooper.packetevents.protocol.component.ComponentTypes;
import com.github.retrooper.packetevents.protocol.component.StaticComponentMap;
import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.NBTByteArray;
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
@@ -32,11 +33,11 @@ import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -46,10 +47,9 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
public class ItemTypes {
public final class ItemTypes {
private static final VersionedRegistry<ItemType> REGISTRY = new VersionedRegistry<>(
"item", "item/item_type_mappings");
private static final VersionedRegistry<ItemType> REGISTRY = new VersionedRegistry<>("item");
private static final Map<StateType, ItemType> HELD_TO_PLACED_MAP = new HashMap<>();
// <editor-fold desc="item type definitions" defaultstate="collapsed">
@@ -1504,6 +1504,9 @@ public class ItemTypes {
// </editor-fold>
private ItemTypes() {
}
@SuppressWarnings("unchecked")
private static StaticComponentMap.Builder parseComponents(
ClientVersion version,
@@ -1514,52 +1517,73 @@ public class ItemTypes {
if (base != null) {
components.setAll(base);
}
// TODO release buffer helper
// allocate a buffer once and use it for parsing everything
Object byteBuf = UnpooledByteBufAllocationHelper.buffer();
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(byteBuf, version.toServerVersion());
for (Map.Entry<String, NBT> entry : nbt) {
ComponentType<?> compType = ComponentTypes.getByName(entry.getKey());
if (compType == null) {
continue;
}
byte[] encodedValue = Base64.getDecoder().decode(
((NBTString) entry.getValue()).getValue());
Object byteBuf = UnpooledByteBufAllocationHelper.wrappedBuffer(encodedValue);
PacketWrapper<?> wrapper = PacketWrapper.createUniversalPacketWrapper(byteBuf);
wrapper.setClientVersion(version);
wrapper.setServerVersion(version.toServerVersion());
// reset to start of buffer
ByteBufHelper.resetReaderIndex(byteBuf);
// empty values are serialized as a single byte (smaller than an empty byte array),
// so just parse byte array tag values
if (entry.getValue() instanceof NBTByteArray) {
byte[] bytes = ((NBTByteArray) entry.getValue()).getValue();
// write bytes at beginning of buffer
ByteBufHelper.resetWriterIndex(byteBuf);
ByteBufHelper.writeBytes(byteBuf, bytes);
}
// read from shared buffer
Object compValue = compType.read(wrapper);
components.set((ComponentType<Object>) compType, compValue);
}
return components;
}
static {
try (SequentialNBTReader.Compound compound = MappingHelper.decompress("mappings/item/item_base_components")) {
private static void parseAllComponents(ClientVersion version) {
String path = "mappings/item_base_components/" + version.name();
try (SequentialNBTReader.Compound compound = MappingHelper.decompress(path)) {
compound.skipOne(); // skip version
for (Map.Entry<String, NBT> entry : compound) {
ClientVersion version = ClientVersion.valueOf(entry.getKey());
SequentialNBTReader.Compound items = (SequentialNBTReader.Compound) entry.getValue();
StaticComponentMap defaults = parseComponents(
version, null, (SequentialNBTReader.Compound) items.next().getValue()).build();
SequentialNBTReader.Compound items = (SequentialNBTReader.Compound) compound.next().getValue();
StaticComponentMap defaults = parseComponents(
version, null, (SequentialNBTReader.Compound) items.next().getValue()).build();
for (Map.Entry<String, NBT> item : items) {
ItemType itemType = REGISTRY.getByName(new ResourceLocation(item.getKey()));
if (!(itemType instanceof StaticItemType)) {
((SequentialNBTReader.Compound) item.getValue()).skip();
continue; // somehow unknown item
}
StaticComponentMap.Builder components = parseComponents(version, defaults,
(SequentialNBTReader.Compound) item.getValue());
((StaticItemType) itemType).setComponents(version, components.build());
for (Map.Entry<String, NBT> item : items) {
ItemType itemType = REGISTRY.getByName(new ResourceLocation(item.getKey()));
if (!(itemType instanceof StaticItemType)) {
((SequentialNBTReader.Compound) item.getValue()).skip();
continue; // somehow unknown item
}
for (ItemType type : REGISTRY.getEntries()) {
if (type instanceof StaticItemType && !((StaticItemType) type).hasComponents(version)) {
((StaticItemType) type).setComponents(version, defaults);
}
StaticComponentMap.Builder components = parseComponents(version, defaults,
(SequentialNBTReader.Compound) item.getValue());
((StaticItemType) itemType).setComponents(version, components.build());
}
for (ItemType type : REGISTRY.getEntries()) {
if (type instanceof StaticItemType && !((StaticItemType) type).hasComponents(version)) {
((StaticItemType) type).setComponents(version, defaults);
}
}
} catch (IOException exception) {
throw new RuntimeException("Error while parsing item base component data", exception);
}
}
static {
// all versions where base components were changed TODO UPDATE
ClientVersion[] versions = new ClientVersion[]{
ClientVersion.V_1_20_5, ClientVersion.V_1_21, ClientVersion.V_1_21_2,
ClientVersion.V_1_21_4,
};
for (ClientVersion version : versions) {
parseAllComponents(version);
}
for (ItemType type : REGISTRY.getEntries()) {
if (type instanceof StaticItemType) {
((StaticItemType) type).fillComponents();
@@ -1579,6 +1603,7 @@ public class ItemTypes {
return new Builder(key.toLowerCase());
}
@ApiStatus.Internal
public static ItemType define(int maxAmount, String key, ItemType craftRemainder, StateType placedType, int maxDurability, List<ItemAttribute> attributesArr) {
// Creates an immutable set
Set<ItemAttribute> attributes = attributesArr == null ? Collections.emptySet() :
@@ -24,6 +24,7 @@ import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.EnumMap;
@@ -39,8 +40,9 @@ public class StaticItemType extends AbstractMappedEntity implements ItemType {
private final Set<ItemAttribute> attributes;
private final Map<ClientVersion, StaticComponentMap> components;
StaticItemType(
TypesBuilderData data,
@ApiStatus.Internal
public StaticItemType(
@Nullable TypesBuilderData data,
int maxAmount,
int maxDurability,
ItemType craftRemainder,
@@ -22,6 +22,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@Deprecated
public class GenericMappedEntity implements StaticMappedEntity {
private final int id;
@@ -35,7 +35,7 @@ public interface ParticleType<T extends ParticleData> extends MappedEntity {
T decodeData(NBTCompound compound, ClientVersion version);
void encodeData(T data, ClientVersion version, NBTCompound compound);
void encodeData(T value, ClientVersion version, NBTCompound compound);
@Deprecated
default Function<PacketWrapper<?>, ParticleData> readDataFunction() {
@@ -30,101 +30,50 @@ import com.github.retrooper.packetevents.protocol.particle.data.ParticleShriekDa
import com.github.retrooper.packetevents.protocol.particle.data.ParticleTrailData;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleVibrationData;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class ParticleTypes {
public final class ParticleTypes {
private static final Map<String, ParticleType<?>> PARTICLE_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, ParticleType<?>>> PARTICLE_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("particle/particle_type_mappings");
private static final VersionedRegistry<ParticleType<?>> REGISTRY = new VersionedRegistry<>("particle_type");
public static ParticleType<ParticleData> define(String key) {
private ParticleTypes() {
}
public static VersionedRegistry<ParticleType<?>> getRegistry() {
return REGISTRY;
}
@ApiStatus.Internal
public static ParticleType<ParticleData> define(String name) {
return define(
key,
name,
wrapper -> ParticleData.emptyData(), null,
(nbt, version) -> ParticleData.emptyData(), null
);
}
@ApiStatus.Internal
public static <T extends ParticleData> ParticleType<T> define(
String key,
String name,
Reader<T> reader, @Nullable Writer<T> writer,
Decoder<T> decoder, @Nullable Encoder<T> encoder
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
ParticleType<T> particleType = new ParticleType<T>() {
@Override
public T readData(PacketWrapper<?> wrapper) {
return reader.apply(wrapper);
}
@Override
public void writeData(PacketWrapper<?> wrapper, T data) {
if (writer != null) {
writer.accept(wrapper, data);
} else if (!data.isEmpty()) {
throw new UnsupportedOperationException("Trying to write non-empty data for " + this.getName());
}
}
@Override
public T decodeData(NBTCompound compound, ClientVersion version) {
return decoder.decode(compound, version);
}
@Override
public void encodeData(T data, ClientVersion version, NBTCompound compound) {
if (encoder != null) {
encoder.encode(data, version, compound);
} else if (!data.isEmpty()) {
throw new UnsupportedOperationException("Trying to encode non-empty data for " + this.getName());
}
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof ParticleType<?>) {
return this.getName().equals(((ParticleType<?>) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PARTICLE_TYPE_MAP, PARTICLE_TYPE_ID_MAP, particleType);
return particleType;
return REGISTRY.define(name, data ->
new StaticParticleType<>(data, reader, writer, decoder, encoder));
}
//with minecraft:key
public static ParticleType<?> getByName(String name) {
return PARTICLE_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static ParticleType<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, ParticleType<?>> typeIdMap = PARTICLE_TYPE_ID_MAP.get((byte) index);
return typeIdMap.get(id);
return REGISTRY.getById(version, id);
}
@Deprecated // Removed in 1.20.5
@@ -291,11 +240,11 @@ public class ParticleTypes {
* @return Particle Types
*/
public static Collection<ParticleType<?>> values() {
return Collections.unmodifiableCollection(PARTICLE_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
@ApiStatus.Internal
@@ -0,0 +1,79 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.particle.type;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.particle.data.ParticleData;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticParticleType<T extends ParticleData> extends AbstractMappedEntity implements ParticleType<T> {
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
private final ParticleTypes.Decoder<T> decoder;
private final ParticleTypes.Encoder<T> encoder;
@ApiStatus.Internal
public StaticParticleType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer,
ParticleTypes.Decoder<T> decoder,
ParticleTypes.Encoder<T> encoder
) {
super(data);
this.reader = reader;
this.writer = writer;
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T readData(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
@Override
public void writeData(PacketWrapper<?> wrapper, T data) {
if (this.writer != null) {
this.writer.accept(wrapper, data);
} else if (!data.isEmpty()) {
throw new UnsupportedOperationException("Trying to write non-empty data for " + this.getName());
}
}
@Override
public T decodeData(NBTCompound compound, ClientVersion version) {
return this.decoder.decode(compound, version);
}
@Override
public void encodeData(T value, ClientVersion version, NBTCompound compound) {
if (this.encoder != null) {
this.encoder.encode(value, version, compound);
} else if (!value.isEmpty()) {
throw new UnsupportedOperationException("Trying to encode non-empty data for " + this.getName());
}
}
}
@@ -21,16 +21,11 @@ package com.github.retrooper.packetevents.protocol.potion;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Potion types are the individually applied potion effects.<br>
@@ -38,46 +33,29 @@ import java.util.Map;
* <p>
* For potions brewable in survival, see {@link Potions}.
*/
public class PotionTypes {
public final class PotionTypes {
private static final Map<String, PotionType> POTION_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, PotionType>> POTION_TYPE_ID_MAP = new HashMap<>();
private static final VersionedRegistry<PotionType> REGISTRY = new VersionedRegistry<>("mob_effect");
// initial mappings based upon https://minecraft.wiki/w/Effect#History
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("entity/entity_effect_mappings");
@Deprecated
public static PotionType define(String key, int ignoredId) {
return define(key);
private PotionTypes() {
}
public static PotionType define(String key) {
TypesBuilderData data = TYPES_BUILDER.define(key);
PotionType potionType = new PotionType() {
@Override
public ResourceLocation getName() {
return data.getName();
}
public static VersionedRegistry<PotionType> getRegistry() {
return REGISTRY;
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Deprecated
public static PotionType define(String name, int ignoredId) {
return define(name);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PotionType) {
return this.getName().equals(((PotionType) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, POTION_TYPE_MAP, POTION_TYPE_ID_MAP, potionType);
return potionType;
@ApiStatus.Internal
public static PotionType define(String name) {
return REGISTRY.define(name, StaticPotionType::new);
}
public static @Nullable PotionType getByName(String name) {
return POTION_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
@Deprecated
@@ -85,20 +63,21 @@ public class PotionTypes {
return getById(id, PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
}
@Deprecated
public static @Nullable PotionType getById(int id, ServerVersion version) {
return getById(id, version.toClientVersion());
}
public static @Nullable PotionType getById(ClientVersion version, int id) {
return getById(id, version);
}
@Deprecated
public static @Nullable PotionType getById(int id, ClientVersion version) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, PotionType> idMap = POTION_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return getById(version, id);
}
public static @Nullable PotionType getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
// initial mappings based upon https://minecraft.wiki/w/Effect#History
// Added in b1.8
public static final PotionType SPEED = define("speed");
public static final PotionType SLOWNESS = define("slowness");
@@ -156,13 +135,14 @@ public class PotionTypes {
/**
* Returns an immutable view of the potion types.
*
* @return Potion Types
*/
public static Collection<PotionType> values() {
return Collections.unmodifiableCollection(POTION_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -19,65 +19,42 @@
package com.github.retrooper.packetevents.protocol.potion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Potions are all possible brewable drinks.<br>
* Some examples are the potion of leaping, the extended potion of leaping
* or the strong potion of leaping.
* <p>
* For individual potion effects, see {@link PotionTypes}.
* For individual potion effects (also called "mob effects"), see {@link PotionTypes}.
*/
public class Potions {
public final class Potions {
private static final Map<String, Potion> POTION_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, Potion>> POTION_ID_MAP = new HashMap<>();
private static final VersionedRegistry<Potion> REGISTRY = new VersionedRegistry<>("potion");
// initial mappings based upon https://minecraft.wiki/w/Potion#History
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/item_potion_mappings");
private Potions() {
}
public static Potion define(String key) {
TypesBuilderData data = TYPES_BUILDER.define(key);
Potion potion = new Potion() {
@Override
public ResourceLocation getName() {
return data.getName();
}
public static VersionedRegistry<Potion> getRegistry() {
return REGISTRY;
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PotionType) {
return getName().equals(((PotionType) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, POTION_MAP, POTION_ID_MAP, potion);
return potion;
@ApiStatus.Internal
public static Potion define(String name) {
return REGISTRY.define(name, StaticPotion::new);
}
public static @Nullable Potion getByName(String name) {
return POTION_MAP.get(name);
return REGISTRY.getByName(name);
}
public static @Nullable Potion getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, Potion> idMap = POTION_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
// initial mappings based upon https://minecraft.wiki/w/Potion#History
public static final Potion WATER = define("water");
public static final Potion MUNDANE = define("mundane");
public static final Potion THICK = define("thick");
@@ -126,6 +103,6 @@ public class Potions {
public static final Potion INFESTED = define("infested");
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.potion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticPotion extends AbstractMappedEntity implements Potion {
@ApiStatus.Internal
public StaticPotion(@Nullable TypesBuilderData data) {
super(data);
}
}
@@ -0,0 +1,32 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.potion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticPotionType extends AbstractMappedEntity implements PotionType {
@ApiStatus.Internal
public StaticPotionType(@Nullable TypesBuilderData data) {
super(data);
}
}
@@ -27,94 +27,54 @@ import com.github.retrooper.packetevents.protocol.recipe.data.SimpleRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.SmithingRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.SmithingTrimRecipeData;
import com.github.retrooper.packetevents.protocol.recipe.data.StoneCuttingRecipeData;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* <strong>WARNING:</strong> No longer exists since 1.21.2, network recipe data was rewritten.
*/
@ApiStatus.Obsolete
public class RecipeSerializers {
public final class RecipeSerializers {
private static final Map<String, RecipeSerializer<?>> PATTERN_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, RecipeSerializer<?>>> PATTERN_TYPE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("item/recipe_serializer_mappings");
private static final VersionedRegistry<RecipeSerializer<?>> REGISTRY = new VersionedRegistry<>("legacy_recipe_serializer");
private RecipeSerializers() {
}
public static VersionedRegistry<RecipeSerializer<?>> getRegistry() {
return REGISTRY;
}
@ApiStatus.Internal
public static <T extends RecipeData> RecipeSerializer<T> define(
String key,
String name,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer
) {
return define(key, reader, writer, null);
return define(name, reader, writer, null);
}
@ApiStatus.Internal
public static <T extends RecipeData> RecipeSerializer<T> define(
String key,
String name,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer,
@Nullable RecipeType recipeType
@Nullable RecipeType legacyType
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
RecipeSerializer<T> pattern = new RecipeSerializer<T>() {
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public RecipeType getLegacyType() {
if (recipeType == null) {
throw new UnsupportedOperationException("No legacy type found for " + this.getName());
}
return recipeType;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T data) {
writer.accept(wrapper, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof RecipeSerializer<?>) {
return this.getName().equals(((RecipeSerializer<?>) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, PATTERN_TYPE_MAP, PATTERN_TYPE_ID_MAP, pattern);
return pattern;
return REGISTRY.define(name, data -> new StaticRecipeSerializer<>(
data, reader, writer, legacyType));
}
// with key
public static RecipeSerializer<?> getByName(String name) {
return PATTERN_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static RecipeSerializer<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, RecipeSerializer<?>> idMap = PATTERN_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
public static final RecipeSerializer<ShapedRecipeData> CRAFTING_SHAPED = define("crafting_shaped",
@@ -175,10 +135,10 @@ public class RecipeSerializers {
* @return Banner Patterns
*/
public static Collection<RecipeSerializer<?>> values() {
return Collections.unmodifiableCollection(PATTERN_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -0,0 +1,61 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.recipe;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.recipe.data.RecipeData;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticRecipeSerializer<T extends RecipeData> extends AbstractMappedEntity implements RecipeSerializer<T> {
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
private final @Nullable RecipeType legacyType;
@ApiStatus.Internal
public StaticRecipeSerializer(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer,
@Nullable RecipeType legacyType
) {
super(data);
this.reader = reader;
this.writer = writer;
this.legacyType = legacyType;
}
@Override
public RecipeType getLegacyType() {
return this.legacyType;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T data) {
this.writer.accept(wrapper, data);
}
}
@@ -22,8 +22,7 @@ import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
public final class RecipeBookCategories {
private static final VersionedRegistry<RecipeBookCategory> REGISTRY = new VersionedRegistry<>(
"recipe_book_category", "item/recipe_book_category");
private static final VersionedRegistry<RecipeBookCategory> REGISTRY = new VersionedRegistry<>("recipe_book_category");
private RecipeBookCategories() {
}
@@ -20,10 +20,13 @@ package com.github.retrooper.packetevents.protocol.recipe.category;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticRecipeBookCategory extends AbstractMappedEntity implements RecipeBookCategory {
public StaticRecipeBookCategory(TypesBuilderData data) {
@ApiStatus.Internal
public StaticRecipeBookCategory(@Nullable TypesBuilderData data) {
super(data);
}
}
@@ -23,8 +23,7 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public final class RecipeDisplayTypes {
private static final VersionedRegistry<RecipeDisplayType<?>> REGISTRY = new VersionedRegistry<>(
"recipe_display", "item/recipe_display_types");
private static final VersionedRegistry<RecipeDisplayType<?>> REGISTRY = new VersionedRegistry<>("recipe_display");
private RecipeDisplayTypes() {
}
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.protocol.recipe.display;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticRecipeDisplayType<T extends RecipeDisplay<?>> extends AbstractMappedEntity implements RecipeDisplayType<T> {
@@ -28,6 +29,7 @@ public class StaticRecipeDisplayType<T extends RecipeDisplay<?>> extends Abstrac
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
@ApiStatus.Internal
public StaticRecipeDisplayType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
@@ -23,8 +23,7 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public final class SlotDisplayTypes {
private static final VersionedRegistry<SlotDisplayType<?>> REGISTRY = new VersionedRegistry<>(
"slot_display", "item/recipe_slot_display_types");
private static final VersionedRegistry<SlotDisplayType<?>> REGISTRY = new VersionedRegistry<>("slot_display");
private SlotDisplayTypes() {
}
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.protocol.recipe.display.slot;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticSlotDisplayType<T extends SlotDisplay<?>> extends AbstractMappedEntity implements SlotDisplayType<T> {
@@ -28,6 +29,7 @@ public class StaticSlotDisplayType<T extends SlotDisplay<?>> extends AbstractMap
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
@ApiStatus.Internal
public StaticSlotDisplayType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
public final class BlankScoreFormat implements ScoreFormat {
@@ -27,13 +28,21 @@ public final class BlankScoreFormat implements ScoreFormat {
private BlankScoreFormat() {
}
public static BlankScoreFormat read(PacketWrapper<?> wrapper) {
return INSTANCE;
}
public static void write(PacketWrapper<?> wrapper, BlankScoreFormat format) {
// NO-OP
}
@Override
public Component format(int score) {
return Component.empty();
}
@Override
public ScoreFormatType getType() {
public ScoreFormatType<BlankScoreFormat> getType() {
return ScoreFormatTypes.BLANK;
}
}
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
public final class FixedScoreFormat implements ScoreFormat {
@@ -28,9 +29,12 @@ public final class FixedScoreFormat implements ScoreFormat {
this.value = value;
}
@Override
public ScoreFormatType getType() {
return ScoreFormatTypes.FIXED;
public static FixedScoreFormat read(PacketWrapper<?> wrapper) {
return new FixedScoreFormat(wrapper.readComponent());
}
public static void write(PacketWrapper<?> wrapper, FixedScoreFormat format) {
wrapper.writeComponent(format.value);
}
@Override
@@ -38,6 +42,11 @@ public final class FixedScoreFormat implements ScoreFormat {
return this.value;
}
@Override
public ScoreFormatType<FixedScoreFormat> getType() {
return ScoreFormatTypes.FIXED;
}
public Component getValue() {
return this.value;
}
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
import org.jetbrains.annotations.ApiStatus;
@@ -37,7 +38,17 @@ public interface ScoreFormat {
return new FixedScoreFormat(value);
}
static ScoreFormat readTyped(PacketWrapper<?> wrapper) {
return wrapper.readMappedEntity(ScoreFormatTypes.getRegistry()).read(wrapper);
}
@SuppressWarnings("unchecked") // no
static <T extends ScoreFormat> void writeTyped(PacketWrapper<?> wrapper, T format) {
wrapper.writeMappedEntity(format.getType());
((ScoreFormatType<T>) format.getType()).write(wrapper, format);
}
Component format(int score);
ScoreFormatType getType();
ScoreFormatType<?> getType();
}
@@ -21,9 +21,13 @@ package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.protocol.mapper.StaticMappedEntity;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
public interface ScoreFormatType extends StaticMappedEntity {
public interface ScoreFormatType<T extends ScoreFormat> extends StaticMappedEntity {
ScoreFormat read(PacketWrapper<?> wrapper);
@Deprecated
@Override
int getId();
void write(PacketWrapper<?> wrapper, ScoreFormat format);
T read(PacketWrapper<?> wrapper);
void write(PacketWrapper<?> wrapper, T format);
}
@@ -20,97 +20,83 @@ package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
public final class ScoreFormatTypes {
private static final Map<String, ScoreFormatType> SCORE_FORMAT_TYPE_MAP = new HashMap<>();
private static final Map<Byte, ScoreFormatType> SCORE_FORMAT_TYPE_ID_MAP = new HashMap<>();
private static final VersionedRegistry<ScoreFormatType<?>> REGISTRY = new VersionedRegistry<>("number_format_type");
public static final ScoreFormatType BLANK = define(0, "blank", BlankScoreFormat.class,
wrapper -> ScoreFormat.blankScore(),
(wrapper, format) -> { /**/ });
public static final ScoreFormatType STYLED = define(1, "styled", StyledScoreFormat.class,
wrapper -> ScoreFormat.styledScore(wrapper.readStyle()),
(wrapper, format) -> wrapper.writeStyle(format.getStyle()));
public static final ScoreFormatType FIXED = define(2, "fixed", FixedScoreFormat.class,
wrapper -> ScoreFormat.fixedScore(wrapper.readComponent()),
(wrapper, format) -> wrapper.writeComponent(format.getValue()));
public static final ScoreFormatType<BlankScoreFormat> BLANK = define("blank",
BlankScoreFormat::read, BlankScoreFormat::write);
public static final ScoreFormatType<StyledScoreFormat> STYLED = define("styled",
StyledScoreFormat::read, StyledScoreFormat::write);
public static final ScoreFormatType<FixedScoreFormat> FIXED = define("fixed",
FixedScoreFormat::read, FixedScoreFormat::write);
/**
* Returns an immutable view of the score format types.
* @return Score Format Types
*/
public static Collection<ScoreFormatType> values() {
return Collections.unmodifiableCollection(SCORE_FORMAT_TYPE_MAP.values());
static {
REGISTRY.unloadMappings();
}
private ScoreFormatTypes() {
}
public static VersionedRegistry<ScoreFormatType<?>> getRegistry() {
return REGISTRY;
}
/**
* Returns an immutable view of the score format types.
*
* @return Score Format Types
*/
public static Collection<ScoreFormatType<?>> values() {
return REGISTRY.getEntries();
}
@Deprecated
public static ScoreFormat read(PacketWrapper<?> wrapper) {
int formatTypeId = wrapper.readVarInt();
ScoreFormatType formatType = getById(wrapper.getServerVersion().toClientVersion(), formatTypeId);
if (formatType == null) {
throw new NullPointerException("Can't resolve format type " + formatTypeId);
}
return formatType.read(wrapper);
return ScoreFormat.readTyped(wrapper);
}
@Deprecated
public static void write(PacketWrapper<?> wrapper, ScoreFormat format) {
int formatTypeId = format.getType().getId(wrapper.getServerVersion().toClientVersion());
wrapper.writeVarInt(formatTypeId);
format.getType().write(wrapper, format);
ScoreFormat.writeTyped(wrapper, format);
}
public static <T extends ScoreFormat> ScoreFormatType define(int id, String name,
Class<T> formatClass,
Function<PacketWrapper<?>, T> reader,
BiConsumer<PacketWrapper<?>, T> writer) {
ResourceLocation location = new ResourceLocation(name);
ScoreFormatType type = new ScoreFormatType() {
@Override
public ScoreFormat read(PacketWrapper<?> wrapper) {
return reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, ScoreFormat format) {
writer.accept(wrapper, formatClass.cast(format));
}
@Override
public ResourceLocation getName() {
return location;
}
@Override
public int getId() {
return id;
}
};
SCORE_FORMAT_TYPE_MAP.put(location.toString(), type);
SCORE_FORMAT_TYPE_ID_MAP.put((byte) id, type);
return type;
@Deprecated
@ApiStatus.Internal
public static <T extends ScoreFormat> ScoreFormatType<T> define(
int id, String name, Class<T> formatClass,
Function<PacketWrapper<?>, T> reader,
BiConsumer<PacketWrapper<?>, T> writer
) {
return define(name, reader::apply, writer::accept);
}
public static @Nullable ScoreFormatType getById(ClientVersion version, int id) {
return SCORE_FORMAT_TYPE_ID_MAP.get((byte) id);
@ApiStatus.Internal
public static <T extends ScoreFormat> ScoreFormatType<T> define(
String name, PacketWrapper.Reader<T> reader, PacketWrapper.Writer<T> writer
) {
return REGISTRY.define(name, data ->
new StaticScoreFormatType<>(data, reader, writer));
}
public static @Nullable ScoreFormatType getByName(String name) {
return getByName(new ResourceLocation(name));
public static @Nullable ScoreFormatType<?> getById(ClientVersion version, int id) {
return REGISTRY.getById(version, id);
}
public static @Nullable ScoreFormatType getByName(ResourceLocation name) {
return SCORE_FORMAT_TYPE_MAP.get(name.toString());
public static @Nullable ScoreFormatType<?> getByName(String name) {
return REGISTRY.getByName(name);
}
public static @Nullable ScoreFormatType<?> getByName(ResourceLocation name) {
return REGISTRY.getByName(name);
}
}
@@ -0,0 +1,60 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticScoreFormatType<T extends ScoreFormat> extends AbstractMappedEntity implements ScoreFormatType<T> {
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
@ApiStatus.Internal
public StaticScoreFormatType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer
) {
super(data);
this.reader = reader;
this.writer = writer;
}
@Override
public int getId() {
ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion();
return this.getId(version.toClientVersion());
}
@Override
public T read(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T format) {
this.writer.accept(wrapper, format);
}
}
@@ -18,6 +18,7 @@
package com.github.retrooper.packetevents.protocol.score;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.Style;
@@ -29,16 +30,21 @@ public final class StyledScoreFormat implements ScoreFormat {
this.style = style;
}
@Override
public Component format(int score) {
return Component.text()
.content(Integer.toString(score))
.style(this.style)
.build();
public static StyledScoreFormat read(PacketWrapper<?> wrapper) {
return new StyledScoreFormat(wrapper.readStyle());
}
public static void write(PacketWrapper<?> wrapper, StyledScoreFormat format) {
wrapper.writeStyle(format.style);
}
@Override
public ScoreFormatType getType() {
public Component format(int score) {
return Component.text(Integer.toString(score), this.style);
}
@Override
public ScoreFormatType<StyledScoreFormat> getType() {
return ScoreFormatTypes.STYLED;
}
@@ -20,61 +20,31 @@ package com.github.retrooper.packetevents.protocol.sound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class Sounds {
public final class Sounds {
private static final Map<String, Sound> SOUND_TYPE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, Sound>> SOUND_TYPE_ID_MAP = new HashMap<>();
private static final VersionedRegistry<Sound> REGISTRY = new VersionedRegistry<>("sound_event");
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("sound/sound_mappings");
public static Sound define(String key) {
return define(key, new ResourceLocation(key), null);
private Sounds() {
}
public static Sound define(String key, ResourceLocation soundId, @Nullable Float range) {
TypesBuilderData data = TYPES_BUILDER.define(key);
Sound soundType = new Sound() {
@Override
public ResourceLocation getSoundId() {
return soundId;
}
public static VersionedRegistry<Sound> getRegistry() {
return REGISTRY;
}
@Override
public @Nullable Float getRange() {
return range;
}
@ApiStatus.Internal
public static Sound define(String name) {
return define(name, new ResourceLocation(name), null);
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Sound) {
return getName().equals(((Sound) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, SOUND_TYPE_MAP, SOUND_TYPE_ID_MAP, soundType);
return soundType;
@ApiStatus.Internal
public static Sound define(String name, ResourceLocation soundId, @Nullable Float range) {
return REGISTRY.define(name, data -> new StaticSound(data, soundId, range));
}
public static Sound getByNameOrCreate(String name) {
@@ -86,13 +56,11 @@ public class Sounds {
}
public static @Nullable Sound getByName(String name) {
return SOUND_TYPE_MAP.get(name);
return REGISTRY.getByName(name);
}
public static @Nullable Sound getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, Sound> idMap = SOUND_TYPE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
public static final Sound ENTITY_ALLAY_AMBIENT_WITH_ITEM = define("entity.allay.ambient_with_item");
@@ -1779,10 +1747,10 @@ public class Sounds {
* @return Sounds
*/
public static Collection<Sound> values() {
return Collections.unmodifiableCollection(SOUND_TYPE_MAP.values());
return REGISTRY.getEntries();
}
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
}
@@ -18,18 +18,26 @@
package com.github.retrooper.packetevents.protocol.sound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticSound implements Sound {
public class StaticSound extends AbstractMappedEntity implements Sound {
private final ResourceLocation soundId;
private final @Nullable Float range;
public StaticSound(ResourceLocation soundId, @Nullable Float range) {
this(null, soundId, range);
}
@ApiStatus.Internal
public StaticSound(@Nullable TypesBuilderData data, ResourceLocation soundId, @Nullable Float range) {
super(data);
this.soundId = soundId;
this.range = range;
}
@@ -44,32 +52,20 @@ public class StaticSound implements Sound {
return this.range;
}
@Override
public ResourceLocation getName() {
throw new UnsupportedOperationException();
}
@Override
public int getId(ClientVersion version) {
throw new UnsupportedOperationException();
}
@Override
public boolean isRegistered() {
return false;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof StaticSound)) return false;
StaticSound that = (StaticSound) obj;
if (this.isRegistered()) {
return super.equals(obj);
}
if (!this.soundId.equals(that.soundId)) return false;
return Objects.equals(this.range, that.range);
}
@Override
public int hashCode() {
return Objects.hash(this.soundId, this.range);
return this.isRegistered() ? super.hashCode() : Objects.hash(this.soundId, this.range);
}
}
@@ -44,14 +44,15 @@ public class Statistics {
if (version.isOlderThan(ServerVersion.V_1_12_2)) {
try (final SequentialNBTReader.Compound mapping = MappingHelper.decompress("mappings/stats/statistics")) {
mapping.skipOne(); //Skip version
try (final SequentialNBTReader.Compound rootMapping = MappingHelper.decompress("mappings/data/statistics")) {
rootMapping.skipOne(); //Skip version
SequentialNBTReader.Compound mapping = (SequentialNBTReader.Compound) rootMapping.next().getValue();
if (version.isOlderThanOrEquals(ServerVersion.V_1_8_3)) {
mapping.skipOne(); // Skip to version 1.8
}
SequentialNBTReader.Compound toLoad = (SequentialNBTReader.Compound) mapping.next();
SequentialNBTReader.Compound toLoad = (SequentialNBTReader.Compound) mapping.next().getValue();
for (Map.Entry<String, NBT> entry : toLoad) {
String value = ((NBTString) entry.getValue()).getValue();
@@ -101,16 +101,16 @@ public class Dimension {
public com.github.retrooper.packetevents.protocol.world.dimension.DimensionType asDimensionType(
@Nullable User user, @Nullable ClientVersion version) {
IRegistry<com.github.retrooper.packetevents.protocol.world.dimension.DimensionType> registry = user != null
? user.getRegistryOr(DimensionTypes.getRegistry()) : DimensionTypes.getRegistry();
String dimName = this.getDimensionName();
if (!dimName.isEmpty()) {
return registry.getByName(new ResourceLocation(dimName));
}
if (version == null) {
version = user != null && PacketEvents.getAPI().getInjector().isProxy() ? user.getClientVersion() :
PacketEvents.getAPI().getServerManager().getVersion().toClientVersion();
}
IRegistry<com.github.retrooper.packetevents.protocol.world.dimension.DimensionType> registry = user != null
? user.getRegistryOr(DimensionTypes.getRegistry(), version) : DimensionTypes.getRegistry();
String dimName = this.getDimensionName();
if (!dimName.isEmpty()) {
return registry.getByName(new ResourceLocation(dimName));
}
return registry.getById(version, this.id);
}
@@ -33,20 +33,16 @@ import java.util.Map;
public final class Biomes {
private static final VersionedRegistry<Biome> REGISTRY = new VersionedRegistry<>(
"worldgen/biome", "world/biome_mappings");
private static final VersionedRegistry<Biome> REGISTRY = new VersionedRegistry<>("worldgen/biome");
// load data from file, biomes are too complex to define in code here
private static final Map<ResourceLocation, NBTCompound> BIOME_DATA;
static {
BIOME_DATA = new HashMap<>();
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/world/biome_data")) {
while (dataTag.hasNext()) {
Map.Entry<String, NBT> entry = dataTag.next();
if (entry.getKey().equals("version")) {
continue; // skip version field
}
try (SequentialNBTReader.Compound dataTag = MappingHelper.decompress("mappings/data/worldgen/biome")) {
dataTag.skipOne(); // skip version
for (Map.Entry<String, NBT> entry : (SequentialNBTReader.Compound) dataTag.next().getValue()) {
ResourceLocation biomeKey = new ResourceLocation(entry.getKey());
BIOME_DATA.put(biomeKey, ((SequentialNBTReader.Compound) entry.getValue()).readFully());
}
@@ -43,12 +43,14 @@ public class StaticBiome extends AbstractMappedEntity implements Biome {
this(null, precipitation, temperature, temperatureModifier, downfall, effects);
}
@ApiStatus.Internal
@ApiStatus.Obsolete
public StaticBiome(@Nullable TypesBuilderData data, Precipitation precipitation, float temperature,
TemperatureModifier temperatureModifier, float downfall, BiomeEffects effects) {
this(data, precipitation != Precipitation.NONE, temperature, temperatureModifier, downfall, effects);
}
@ApiStatus.Internal
public StaticBiome(
@Nullable TypesBuilderData data, boolean precipitation,
float temperature, TemperatureModifier temperatureModifier,
@@ -67,6 +69,7 @@ public class StaticBiome extends AbstractMappedEntity implements Biome {
category, depth, scale, effects);
}
@ApiStatus.Internal
@ApiStatus.Obsolete
public StaticBiome(@Nullable TypesBuilderData data, Precipitation precipitation, float temperature,
TemperatureModifier temperatureModifier, float downfall, @Nullable Category category,
@@ -75,6 +78,7 @@ public class StaticBiome extends AbstractMappedEntity implements Biome {
downfall, category, depth, scale, effects);
}
@ApiStatus.Internal
public StaticBiome(
@Nullable TypesBuilderData data, boolean precipitation,
float temperature, TemperatureModifier temperatureModifier,
@@ -25,8 +25,7 @@ import java.util.Collection;
public final class BlockEntityTypes {
private static final VersionedRegistry<BlockEntityType> REGISTRY = new VersionedRegistry<>(
"block_entity_type", "block/block_entity_type_mappings");
private static final VersionedRegistry<BlockEntityType> REGISTRY = new VersionedRegistry<>("block_entity_type");
private BlockEntityTypes() {
}
@@ -20,10 +20,13 @@ package com.github.retrooper.packetevents.protocol.world.blockentity;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticBlockEntityType extends AbstractMappedEntity implements BlockEntityType {
StaticBlockEntityType(TypesBuilderData data) {
@ApiStatus.Internal
public StaticBlockEntityType(@Nullable TypesBuilderData data) {
super(data);
}
}
@@ -24,9 +24,9 @@ import org.jetbrains.annotations.ApiStatus;
import java.util.Collection;
public class DamageTypes {
private static final VersionedRegistry<DamageType> REGISTRY = new VersionedRegistry<>("damage_type",
"damage/damagetype_mappings");
public final class DamageTypes {
private static final VersionedRegistry<DamageType> REGISTRY = new VersionedRegistry<>("damage_type");
private DamageTypes() {
}
@@ -20,21 +20,25 @@ package com.github.retrooper.packetevents.protocol.world.damagetype;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StaticDamageType extends AbstractMappedEntity implements DamageType {
private String messageId;
private DamageScaling scaling;
private float exhaustion;
private DamageEffects effects;
private DeathMessageType deathMessageType;
protected StaticDamageType(@Nullable TypesBuilderData data, String messageId, DamageScaling scaling,
float exhaustion, DamageEffects effects, DeathMessageType deathMessageType) {
private final String messageId;
private final DamageScaling scaling;
private final float exhaustion;
private final DamageEffects effects;
private final DeathMessageType deathMessageType;
@ApiStatus.Internal
public StaticDamageType(
@Nullable TypesBuilderData data, String messageId, DamageScaling scaling,
float exhaustion, DamageEffects effects, DeathMessageType deathMessageType
) {
super(data);
this.messageId = messageId;
this.scaling = scaling;
this.exhaustion = exhaustion;
@@ -29,8 +29,7 @@ import java.util.OptionalLong;
public final class DimensionTypes {
private static final VersionedRegistry<DimensionType> REGISTRY = new VersionedRegistry<>(
"dimension_type", "world/dimension_type_mappings");
private static final VersionedRegistry<DimensionType> REGISTRY = new VersionedRegistry<>("dimension_type");
private DimensionTypes() {
}
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -61,6 +62,7 @@ public class StaticDimensionType extends AbstractMappedEntity implements Dimensi
monsterSpawnLightLevel, monsterSpawnBlockLightLimit);
}
@ApiStatus.Internal
public StaticDimensionType(
@Nullable TypesBuilderData data, OptionalLong fixedTime, boolean hasSkyLight, boolean hasCeiling,
boolean ultraWarm, boolean natural, double coordinateScale, boolean bedWorking, boolean respawnAnchorWorking,
@@ -26,8 +26,7 @@ import org.jetbrains.annotations.Nullable;
public final class PaintingVariants {
private static final VersionedRegistry<PaintingVariant> REGISTRY = new VersionedRegistry<>(
"painting_variant", "entity/painting_mappings");
private static final VersionedRegistry<PaintingVariant> REGISTRY = new VersionedRegistry<>("painting_variant");
private PaintingVariants() {
}
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.protocol.world.painting;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
@@ -35,6 +36,7 @@ public class StaticPaintingVariant extends AbstractMappedEntity implements Paint
this(null, width, height, assetId);
}
@ApiStatus.Internal
public StaticPaintingVariant(@Nullable TypesBuilderData data, int width, int height, ResourceLocation assetId) {
super(data);
this.width = width;
@@ -22,85 +22,39 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.positionsource.builtin.BlockPositionSource;
import com.github.retrooper.packetevents.protocol.world.positionsource.builtin.EntityPositionSource;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Reader;
import com.github.retrooper.packetevents.wrapper.PacketWrapper.Writer;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public final class PositionSourceTypes {
public class PositionSourceTypes {
private static final VersionedRegistry<PositionSourceType<?>> REGISTRY = new VersionedRegistry<>("position_source_type");
private static final Map<String, PositionSourceType<?>> POS_SOURCE_MAP = new HashMap<>();
private static final Map<Byte, Map<Integer, PositionSourceType<?>>> POS_SOURCE_ID_MAP = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("world/world_position_source_mappings");
private PositionSourceTypes() {
}
public static VersionedRegistry<PositionSourceType<?>> getRegistry() {
return REGISTRY;
}
@ApiStatus.Internal
public static <T extends PositionSource> PositionSourceType<T> define(
String key,
String name,
Reader<T> reader, Writer<T> writer,
Decoder<T> decoder, Encoder<T> encoder
) {
TypesBuilderData data = TYPES_BUILDER.define(key);
PositionSourceType<T> sourceType = new PositionSourceType<T>() {
@Override
public T read(PacketWrapper<?> wrapper) {
return reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T source) {
writer.accept(wrapper, source);
}
@Override
public T decode(NBTCompound compound, ClientVersion version) {
return decoder.decode(compound, version);
}
@Override
public void encode(T source, ClientVersion version, NBTCompound compound) {
encoder.encode(source, version, compound);
}
@Override
public ResourceLocation getName() {
return data.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, TYPES_BUILDER, data);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof PositionSourceType<?>) {
return this.getName().equals(((PositionSourceType<?>) obj).getName());
}
return false;
}
};
MappingHelper.registerMapping(TYPES_BUILDER, POS_SOURCE_MAP, POS_SOURCE_ID_MAP, sourceType);
return sourceType;
return REGISTRY.define(name, data -> new StaticPositionSourceType<>(
data, reader, writer, decoder, encoder));
}
// with minecraft:key
@Nullable
public static PositionSourceType<?> getByName(String name) {
return POS_SOURCE_MAP.get(name);
public static @Nullable PositionSourceType<?> getByName(String name) {
return REGISTRY.getByName(name);
}
public static PositionSourceType<?> getById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, PositionSourceType<?>> idMap = POS_SOURCE_ID_MAP.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
public static final PositionSourceType<BlockPositionSource> BLOCK = define("block",
@@ -111,7 +65,7 @@ public class PositionSourceTypes {
EntityPositionSource::decodeSource, EntityPositionSource::encodeSource);
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
}
@FunctionalInterface
@@ -0,0 +1,70 @@
/*
* This file is part of packetevents - https://github.com/retrooper/packetevents
* Copyright (C) 2025 retrooper and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.github.retrooper.packetevents.protocol.world.positionsource;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
public class StaticPositionSourceType<T extends PositionSource> extends AbstractMappedEntity implements PositionSourceType<T> {
private final PacketWrapper.Reader<T> reader;
private final PacketWrapper.Writer<T> writer;
private final PositionSourceTypes.Decoder<T> decoder;
private final PositionSourceTypes.Encoder<T> encoder;
@ApiStatus.Internal
public StaticPositionSourceType(
@Nullable TypesBuilderData data,
PacketWrapper.Reader<T> reader,
PacketWrapper.Writer<T> writer,
PositionSourceTypes.Decoder<T> decoder,
PositionSourceTypes.Encoder<T> encoder
) {
super(data);
this.reader = reader;
this.writer = writer;
this.decoder = decoder;
this.encoder = encoder;
}
@Override
public T read(PacketWrapper<?> wrapper) {
return this.reader.apply(wrapper);
}
@Override
public void write(PacketWrapper<?> wrapper, T source) {
this.writer.accept(wrapper, source);
}
@Override
public T decode(NBTCompound compound, ClientVersion version) {
return this.decoder.decode(compound, version);
}
@Override
public void encode(T source, ClientVersion version, NBTCompound compound) {
this.encoder.encode(source, version, compound);
}
}
@@ -5,10 +5,8 @@ import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTByte;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.nbt.NBTInt;
import com.github.retrooper.packetevents.protocol.nbt.NBTLimiter;
import com.github.retrooper.packetevents.protocol.nbt.NBTNumber;
import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.serializer.DefaultNBTSerializer;
import com.github.retrooper.packetevents.protocol.nbt.serializer.SequentialNBTReader;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.BlockFace;
@@ -38,20 +36,19 @@ import com.github.retrooper.packetevents.protocol.world.states.enums.West;
import com.github.retrooper.packetevents.protocol.world.states.type.StateType;
import com.github.retrooper.packetevents.protocol.world.states.type.StateTypes;
import com.github.retrooper.packetevents.protocol.world.states.type.StateValue;
import com.github.retrooper.packetevents.util.BinaryNBTCompound;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUtil.indexValueOrThrow;
@@ -68,60 +65,68 @@ import static com.github.retrooper.packetevents.util.adventure.AdventureIndexUti
*/
public class WrappedBlockState {
// all versions where block state mappings were changed TODO UPDATE
private static final ClientVersion[] MAPPING_VERSION_STEPS = new ClientVersion[]{
ClientVersion.V_1_13, ClientVersion.V_1_13_2, ClientVersion.V_1_14, ClientVersion.V_1_15,
ClientVersion.V_1_16, ClientVersion.V_1_16_2, ClientVersion.V_1_17, ClientVersion.V_1_19,
ClientVersion.V_1_19_3, ClientVersion.V_1_19_4, ClientVersion.V_1_20, ClientVersion.V_1_20_2,
ClientVersion.V_1_20_3, ClientVersion.V_1_20_5, ClientVersion.V_1_21_2, ClientVersion.V_1_21_4,
};
private static final byte[] MAPPING_INDEXES;
private static final byte LEGACY_MAPPING_INDEX = 0;
private static final ClientVersion[] MAPPING_VERSIONS;
private static final byte AIR_MAPPING_INDEX = 0;
private static final byte LEGACY_MAPPING_INDEX = 1;
private static final byte HIGHEST_MAPPING_INDEX;
private static final String MAPPINGS_ASSETS_PREFIX = "mappings/data/block_state/";
private static final String MAPPINGS_ASSETS_LEGACY = MAPPINGS_ASSETS_PREFIX + "legacy";
private static final boolean PRELOAD_BLOCK_STATE_MAPPINGS = Boolean.getBoolean("packetevents.mappings.preload");
static {
// all versions where block state mappings were changed TODO UPDATE
ClientVersion[] mappingSteps = new ClientVersion[]{
ClientVersion.V_1_13, ClientVersion.V_1_13_2, ClientVersion.V_1_14, ClientVersion.V_1_15,
ClientVersion.V_1_16, ClientVersion.V_1_16_2, ClientVersion.V_1_17, ClientVersion.V_1_19,
ClientVersion.V_1_19_3, ClientVersion.V_1_19_4, ClientVersion.V_1_20, ClientVersion.V_1_20_2,
ClientVersion.V_1_20_3, ClientVersion.V_1_20_5, ClientVersion.V_1_21_2, ClientVersion.V_1_21_4,
};
ClientVersion[] versions = ClientVersion.values();
MAPPING_INDEXES = new byte[versions.length];
MAPPING_VERSIONS = new ClientVersion[versions.length];
ClientVersion mappingVersion = versions[0];
for (int i = 0, j = 0; i < versions.length; i++) {
ClientVersion version = versions[i];
if (j < mappingSteps.length
&& version == mappingSteps[j]) {
if (j < MAPPING_VERSION_STEPS.length && version == MAPPING_VERSION_STEPS[j]) {
j++;
mappingVersion = version;
}
MAPPING_INDEXES[version.ordinal()] = (byte) (LEGACY_MAPPING_INDEX + j);
MAPPING_VERSIONS[version.ordinal()] = mappingVersion;
}
HIGHEST_MAPPING_INDEX = MAPPING_INDEXES[versions.length - 1];
}
private static final WrappedBlockState AIR = new WrappedBlockState(StateTypes.AIR, new EnumMap<>(StateValue.class), 0, (byte) 0);
private static final Map<Byte, Map<String, WrappedBlockState>> BY_STRING = new HashMap<>();
private static final Map<Byte, Map<Integer, WrappedBlockState>> BY_ID = new HashMap<>();
private static final Map<Byte, Map<WrappedBlockState, String>> INTO_STRING = new HashMap<>();
private static final Map<Byte, Map<WrappedBlockState, Integer>> INTO_ID = new HashMap<>();
private static final Map<Byte, Map<StateType, WrappedBlockState>> DEFAULT_STATES = new HashMap<>();
private static final WrappedBlockState AIR = new WrappedBlockState(StateTypes.AIR,
new EnumMap<>(StateValue.class), 0, AIR_MAPPING_INDEX);
private static final Map<String, WrappedBlockState>[] BY_STRING = new Map[HIGHEST_MAPPING_INDEX + 1];
private static final Map<Integer, WrappedBlockState>[] BY_ID = new Map[HIGHEST_MAPPING_INDEX + 1];
private static final Map<WrappedBlockState, String>[] INTO_STRING = new Map[HIGHEST_MAPPING_INDEX + 1];
private static final Map<WrappedBlockState, Integer>[] INTO_ID = new Map[HIGHEST_MAPPING_INDEX + 1];
private static final Map<StateType, WrappedBlockState>[] DEFAULT_STATES = new Map[HIGHEST_MAPPING_INDEX + 1];
private static final Map<String, String> STRING_UPDATER = new HashMap<>();
static {
STRING_UPDATER.put("grass_path", "dirt_path"); // 1.16 -> 1.17
// Try to reduce memory footprint by re-using hashmaps when they are equal
// We do this by setting the key to the NBTCompound of the data and the value to the data
// this.data = cache.computeIfAbsent(dataContent, (key) -> { // NBTCompound to data });
// This will get an equal value if present, otherwise it will compute the value
// Once this is done, we remove this cache to save memory
// A HashMap is used instead of another data type because a hashmap is o(1)
//
// 6160 total combinations, last updated with 1.20.5
// This brings total memory usage from 62 MB to 34 MB, a 28 MB reduction
// Using a HashMap reduces memory usage to less than a megabyte, I can't get precise numbers because it is hard to see on a heapdump
Map<BinaryNBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache = new HashMap<>(6160, 70);
Arrays.fill(BY_STRING, Collections.emptyMap());
Arrays.fill(BY_ID, Collections.emptyMap());
Arrays.fill(INTO_STRING, Collections.emptyMap());
Arrays.fill(INTO_ID, Collections.emptyMap());
Arrays.fill(DEFAULT_STATES, Collections.emptyMap());
loadLegacy(cache);
loadModern(cache);
cache.clear();
cache = null;
// because AIR is a constant, preload all data for this into a separate mapping index
String airName = AIR.getType().getMapped().getName().getKey();
BY_STRING[AIR_MAPPING_INDEX] = Collections.singletonMap(airName, AIR);
BY_ID[AIR_MAPPING_INDEX] = Collections.singletonMap(AIR.getGlobalId(), AIR);
INTO_STRING[AIR_MAPPING_INDEX] = Collections.singletonMap(AIR, airName);
INTO_ID[AIR_MAPPING_INDEX] = Collections.singletonMap(AIR, AIR.getGlobalId());
DEFAULT_STATES[AIR_MAPPING_INDEX] = Collections.singletonMap(AIR.getType(), AIR);
}
int globalID;
@@ -158,6 +163,57 @@ public class WrappedBlockState {
this.mappingsIndex = mappingsIndex;
}
private static byte loadMappings(ClientVersion version) {
byte mappingsIndex = getMappingsIndex(version);
if (!PRELOAD_BLOCK_STATE_MAPPINGS && BY_ID[mappingsIndex].isEmpty()) {
loadMappings0(getMappingsVersion(version), mappingsIndex); // try to load mappings
}
return mappingsIndex;
}
private static synchronized void loadMappings0(ClientVersion version, byte mappingsIndex) {
if (!BY_ID[mappingsIndex].isEmpty()) {
return; // already loaded
}
PacketEvents.getAPI().getLogger().info("Loading block mappings for " + version + "/" + mappingsIndex + "...");
long start = System.nanoTime();
if (mappingsIndex == LEGACY_MAPPING_INDEX) {
loadLegacy(buildStateDataCache());
} else {
loadModern(buildStateDataCache(), version);
}
double timeDiff = (System.nanoTime() - start) / 1_000_000d;
PacketEvents.getAPI().getLogger().info("Finished loading block mappings for "
+ version + "/" + mappingsIndex + " in " + timeDiff + "ms");
}
private static Map<Map<StateValue, Object>, StateCacheValue> buildStateDataCache() {
// Try to reduce memory footprint by re-using hashmaps when they are equal
// We do this by setting the key to the NBTCompound of the data and the value to the data
// this.data = cache.computeIfAbsent(dataContent, (key) -> { // NBTCompound to data });
// This will get an equal value if present, otherwise it will compute the value
// Once this is done, we remove this cache to save memory
// A HashMap is used instead of another data type because a hashmap is o(1)
//
// 6160 total combinations, last updated with 1.20.5
// This brings total memory usage from 62 MB to 34 MB, a 28 MB reduction
// Using a HashMap reduces memory usage to less than a megabyte, I can't get precise numbers because it is hard to see on a heapdump
Map<Map<StateValue, Object>, StateCacheValue> cache = new HashMap<>();
for (byte i = 0; i < HIGHEST_MAPPING_INDEX; i++) {
Map<Integer, WrappedBlockState> map = BY_ID[i];
if (map == null) {
continue; // not loaded yet
}
for (WrappedBlockState state : map.values()) {
cache.computeIfAbsent(state.data, StateCacheValue::new);
}
}
return cache;
}
public static WrappedBlockState decode(NBT nbt, ClientVersion version) {
if (nbt instanceof NBTString) {
StateType type = StateTypes.getByName(((NBTString) nbt).getValue());
@@ -241,8 +297,8 @@ public class WrappedBlockState {
@NotNull
public static WrappedBlockState getByGlobalId(ClientVersion version, int globalID, boolean clone) {
if (globalID == 0) return AIR; // Hardcode for performance
byte mappingsIndex = getMappingsIndex(version);
final WrappedBlockState state = BY_ID.get(mappingsIndex).getOrDefault(globalID, AIR);
byte mappingsIndex = loadMappings(version);
final WrappedBlockState state = BY_ID[mappingsIndex].getOrDefault(globalID, AIR);
return clone ? state.clone() : state;
}
@@ -258,8 +314,8 @@ public class WrappedBlockState {
@NotNull
public static WrappedBlockState getByString(ClientVersion version, String string, boolean clone) {
byte mappingsIndex = getMappingsIndex(version);
final WrappedBlockState state = BY_STRING.get(mappingsIndex).getOrDefault(string.replace("minecraft:", ""), AIR);
byte mappingsIndex = loadMappings(version);
final WrappedBlockState state = BY_STRING[mappingsIndex].getOrDefault(string.replace("minecraft:", ""), AIR);
return clone ? state.clone() : state;
}
@@ -276,8 +332,8 @@ public class WrappedBlockState {
@NotNull
public static WrappedBlockState getDefaultState(ClientVersion version, StateType type, boolean clone) {
if (type == StateTypes.AIR) return AIR;
byte mappingsIndex = getMappingsIndex(version);
WrappedBlockState state = DEFAULT_STATES.get(mappingsIndex).get(type);
byte mappingsIndex = loadMappings(version);
WrappedBlockState state = DEFAULT_STATES[mappingsIndex].get(type);
if (state == null) {
PacketEvents.getAPI().getLogger().config("Default state for " + type.getName() + " is null. Returning AIR");
return AIR;
@@ -289,17 +345,21 @@ public class WrappedBlockState {
return MAPPING_INDEXES[version.ordinal()];
}
private static void loadLegacy(Map<BinaryNBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache) {
private static ClientVersion getMappingsVersion(ClientVersion version) {
return MAPPING_VERSIONS[version.ordinal()];
}
private static void loadLegacy(Map<Map<StateValue, Object>, StateCacheValue> cache) {
Map<Integer, WrappedBlockState> stateByIdMap = new HashMap<>();
Map<WrappedBlockState, Integer> stateToIdMap = new HashMap<>();
Map<String, WrappedBlockState> stateByStringMap = new HashMap<>();
Map<WrappedBlockState, String> stateToStringMap = new HashMap<>();
Map<StateType, WrappedBlockState> stateTypeToBlockStateMap = new HashMap<>();
try (final SequentialNBTReader.Compound compound = MappingHelper.decompress("mappings/block/legacy_block_mappings")) {
try (final SequentialNBTReader.Compound compound = MappingHelper.decompress(MAPPINGS_ASSETS_LEGACY)) {
compound.skipOne(); // Skip version
for (Map.Entry<String, NBT> entry : compound) {
for (Map.Entry<String, NBT> entry : (SequentialNBTReader.Compound) compound.next().getValue()) {
SequentialNBTReader.Compound inner = (SequentialNBTReader.Compound) entry.getValue();
StateType type = StateTypes.getByName(entry.getKey());
@@ -310,31 +370,23 @@ public class WrappedBlockState {
}
for (Map.Entry<String, NBT> element : inner) {
String[] ids = element.getKey().split(":");
int id = Integer.parseInt(ids[0]);
int data = Integer.parseInt(ids[1]);
String elementName = element.getKey();
int idIndex = elementName.indexOf(':');
int id = Integer.parseInt(elementName.substring(0, idIndex));
int data = Integer.parseInt(elementName.substring(idIndex + 1));
int combinedID = (id << 4) | data;
SequentialNBTReader.Compound dataContent = (SequentialNBTReader.Compound) element.getValue();
Map.Entry<Map<StateValue, Object>, String> dataEntry = cache.computeIfAbsent(new BinaryNBTCompound(dataContent.readFullyAsBytes()), (bin) -> {
NBTCompound key;
try (ByteArrayInputStream stream = new ByteArrayInputStream(bin.getData()); DataInputStream in = new DataInputStream(stream)) {
key = (NBTCompound) DefaultNBTSerializer.INSTANCE.deserializeTag(NBTLimiter.noop(), in, false);
} catch (IOException e) {
throw new RuntimeException("Failed to load legacy block mappings", e);
}
StringBuilder dataStringBuilder = new StringBuilder();
Map<StateValue, Object> dataMap = new HashMap<>(key.size());
for (Map.Entry<String, NBT> props : key.getTags().entrySet()) {
StateCacheValue stateCache;
if (dataContent.hasNext()) { // only try to read if there is data available
Map<StateValue, Object> dataMap = new HashMap<>(3);
for (Map.Entry<String, NBT> props : dataContent) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
}
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
@@ -345,26 +397,17 @@ public class WrappedBlockState {
v = ((NBTString) value).getValue();
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT type in legacy mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
stateCache = cache.computeIfAbsent(dataMap, StateCacheValue::new);
} else {
stateCache = StateCacheValue.EMPTY;
}
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
return new AbstractMap.SimpleEntry<>(dataMap, dataString);
});
String fullString = entry.getKey() + dataEntry.getValue();
WrappedBlockState state = new WrappedBlockState(type, dataEntry.getKey(), combinedID, (byte) 0);
String fullString = entry.getKey() + stateCache.getString();
WrappedBlockState state = new WrappedBlockState(type, stateCache.map, combinedID, LEGACY_MAPPING_INDEX);
stateByIdMap.put(combinedID, state);
stateToStringMap.put(state, fullString);
@@ -380,134 +423,113 @@ public class WrappedBlockState {
}
}
BY_ID.put(LEGACY_MAPPING_INDEX, stateByIdMap);
INTO_ID.put(LEGACY_MAPPING_INDEX, stateToIdMap);
BY_STRING.put(LEGACY_MAPPING_INDEX, stateByStringMap);
INTO_STRING.put(LEGACY_MAPPING_INDEX, stateToStringMap);
DEFAULT_STATES.put(LEGACY_MAPPING_INDEX, stateTypeToBlockStateMap);
BY_ID[LEGACY_MAPPING_INDEX] = stateByIdMap;
INTO_ID[LEGACY_MAPPING_INDEX] = stateToIdMap;
BY_STRING[LEGACY_MAPPING_INDEX] = stateByStringMap;
INTO_STRING[LEGACY_MAPPING_INDEX] = stateToStringMap;
DEFAULT_STATES[LEGACY_MAPPING_INDEX] = stateTypeToBlockStateMap;
} catch (IOException e) {
throw new RuntimeException("Failed to load legacy block mappings", e);
}
}
private static void loadModern(Map<BinaryNBTCompound, Map.Entry<Map<StateValue, Object>, String>> cache) {
try (final SequentialNBTReader.Compound compound = MappingHelper.decompress("mappings/block/modern_block_mappings")) {
private static void loadModern(Map<Map<StateValue, Object>, StateCacheValue> cache, ClientVersion version) {
try (final SequentialNBTReader.Compound compound = MappingHelper.decompress(MAPPINGS_ASSETS_PREFIX + version.name())) {
compound.skipOne(); // Skip version
for (Map.Entry<String, NBT> versionEntry : compound) {
ClientVersion version = ClientVersion.valueOf(versionEntry.getKey());
byte mappingIndex = getMappingsIndex(version);
SequentialNBTReader.List list = (SequentialNBTReader.List) versionEntry.getValue();
byte mappingIndex = getMappingsIndex(version);
SequentialNBTReader.List list = (SequentialNBTReader.List) compound.next().getValue();
Map<Integer, WrappedBlockState> stateByIdMap = new HashMap<>();
Map<WrappedBlockState, Integer> stateToIdMap = new HashMap<>();
Map<String, WrappedBlockState> stateByStringMap = new HashMap<>();
Map<WrappedBlockState, String> stateToStringMap = new HashMap<>();
Map<StateType, WrappedBlockState> stateTypeToBlockStateMap = new HashMap<>();
Map<Integer, WrappedBlockState> stateByIdMap = new HashMap<>();
Map<WrappedBlockState, Integer> stateToIdMap = new HashMap<>();
Map<String, WrappedBlockState> stateByStringMap = new HashMap<>();
Map<WrappedBlockState, String> stateToStringMap = new HashMap<>();
Map<StateType, WrappedBlockState> stateTypeToBlockStateMap = new HashMap<>();
int id = 0;
for (NBT e : list) {
SequentialNBTReader.Compound element = (SequentialNBTReader.Compound) e;
String typeString = ((NBTString) element.next().getValue()).getValue(); // type
StateType type = StateTypes.getByName(typeString);
if (type == null) {
// Let's update the state type to a modern version
for (Map.Entry<String, String> stringEntry : STRING_UPDATER.entrySet()) {
typeString = typeString.replace(stringEntry.getKey(), stringEntry.getValue());
}
type = StateTypes.getByName(typeString);
int id = 0;
for (NBT e : list) {
SequentialNBTReader.Compound element = (SequentialNBTReader.Compound) e;
String typeString = ((NBTString) element.next().getValue()).getValue(); // type
StateType type = StateTypes.getByName(typeString);
if (type == null) {
// Let's update the state type to a modern version
for (Map.Entry<String, String> stringEntry : STRING_UPDATER.entrySet()) {
typeString = typeString.replace(stringEntry.getKey(), stringEntry.getValue());
}
type = StateTypes.getByName(typeString);
if (type == null) {
PacketEvents.getAPI().getLogger().warning("Unknown block type: " + typeString);
element.skip();
continue;
}
}
Map.Entry<String, NBT> next = element.next(); // def
int defaultIdx = 0;
if (!next.getKey().equals("def")) {
PacketEvents.getAPI().getLogger().warning("No default state for " + type + " using 0");
} else {
defaultIdx = ((NBTNumber) next.getValue()).getAsInt();
next = element.next(); // entries
}
int index = 0;
for (NBT nbt : ((SequentialNBTReader.List) next.getValue())) {
SequentialNBTReader.Compound dataContent = (SequentialNBTReader.Compound) nbt;
Map.Entry<Map<StateValue, Object>, String> dataEntry = cache.computeIfAbsent(new BinaryNBTCompound(dataContent.readFullyAsBytes()), (bin) -> {
NBTCompound key;
try (ByteArrayInputStream stream = new ByteArrayInputStream(bin.getData()); DataInputStream in = new DataInputStream(stream)) {
key = (NBTCompound) DefaultNBTSerializer.INSTANCE.deserializeTag(NBTLimiter.noop(), in, false);
} catch (IOException ex) {
throw new RuntimeException("Failed to load legacy block mappings", ex);
}
StringBuilder dataStringBuilder = new StringBuilder();
Map<StateValue, Object> dataMap = new HashMap<>(key.size());
for (Map.Entry<String, NBT> props : key.getTags().entrySet()) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
}
dataStringBuilder.append(props.getKey()).append("=");
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT typeString in modern mapping: " + value.getClass().getSimpleName());
dataStringBuilder = new StringBuilder(dataStringBuilder.substring(0, dataStringBuilder.length() - props.getKey().length() - 1));
continue;
}
dataStringBuilder.append(v).append(",");
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
String dataString;
if (dataStringBuilder.length() == 0) {
dataString = "";
} else {
dataString = "[" + dataStringBuilder.substring(0, dataStringBuilder.length() - 1) + "]";
}
return new AbstractMap.SimpleEntry<>(dataMap, dataString);
});
String fullString = typeString + dataEntry.getValue();
WrappedBlockState state = new WrappedBlockState(type, dataEntry.getKey(), id, mappingIndex);
if (defaultIdx == index) {
stateTypeToBlockStateMap.put(type, state);
}
stateByStringMap.put(fullString, state);
stateByIdMap.put(id, state);
stateToStringMap.put(state, fullString);
stateToIdMap.put(state, id);
id++;
index++;
PacketEvents.getAPI().getLogger().warning("Unknown block type: " + typeString);
element.skip();
continue;
}
}
BY_ID.put(mappingIndex, stateByIdMap);
INTO_ID.put(mappingIndex, stateToIdMap);
BY_STRING.put(mappingIndex, stateByStringMap);
INTO_STRING.put(mappingIndex, stateToStringMap);
DEFAULT_STATES.put(mappingIndex, stateTypeToBlockStateMap);
Map.Entry<String, NBT> next = element.next(); // def
int defaultIdx = 0;
if (!next.getKey().equals("def")) {
PacketEvents.getAPI().getLogger().warning("No default state for " + type + " using 0");
} else {
defaultIdx = ((NBTNumber) next.getValue()).getAsInt();
next = element.next(); // entries
}
int index = 0;
for (NBT nbt : ((SequentialNBTReader.List) next.getValue())) {
SequentialNBTReader.Compound dataContent = (SequentialNBTReader.Compound) nbt;
StateCacheValue stateCache;
if (dataContent.hasNext()) { // only try to read if there is data available
Map<StateValue, Object> dataMap = new HashMap<>(3);
for (Map.Entry<String, NBT> props : dataContent) {
StateValue state = StateValue.byName(props.getKey());
if (state == null) {
PacketEvents.getAPI().getLogger().warning("Could not find value for " + props.getKey());
continue;
}
NBT value = props.getValue();
Object v;
if (value instanceof NBTByte) {
v = ((NBTByte) value).getAsInt() == 1;
} else if (value instanceof NBTNumber) {
v = ((NBTNumber) value).getAsInt();
} else if (value instanceof NBTString) {
v = ((NBTString) value).getValue();
} else {
PacketEvents.getAPI().getLogger().warning("Unknown NBT typeString in modern mapping: " + value.getClass().getSimpleName());
continue;
}
dataMap.put(state, state.getParser().apply(v.toString().toUpperCase(Locale.ROOT)));
}
stateCache = cache.computeIfAbsent(dataMap, StateCacheValue::new);
} else {
stateCache = StateCacheValue.EMPTY;
}
String fullString = typeString + stateCache.getString();
WrappedBlockState state = new WrappedBlockState(type, stateCache.map, id, mappingIndex);
if (defaultIdx == index) {
stateTypeToBlockStateMap.put(type, state);
}
stateByStringMap.put(fullString, state);
stateByIdMap.put(id, state);
stateToStringMap.put(state, fullString);
stateToIdMap.put(state, id);
id++;
index++;
}
}
BY_ID[mappingIndex] = stateByIdMap;
INTO_ID[mappingIndex] = stateToIdMap;
BY_STRING[mappingIndex] = stateByStringMap;
INTO_STRING[mappingIndex] = stateToStringMap;
DEFAULT_STATES[mappingIndex] = stateTypeToBlockStateMap;
} catch (IOException e) {
throw new RuntimeException("Failed to load modern block mappings", e);
}
@@ -1451,7 +1473,7 @@ public class WrappedBlockState {
int oldGlobalID = globalID;
globalID = getGlobalIdNoCache();
if (globalID == -1) { // -1 maps to no block as negative ID are impossible
WrappedBlockState blockState = BY_ID.get(mappingsIndex).getOrDefault(oldGlobalID, AIR).clone();
WrappedBlockState blockState = BY_ID[this.mappingsIndex].getOrDefault(oldGlobalID, AIR).clone();
this.type = blockState.type;
this.globalID = blockState.globalID;
this.data = new HashMap<>(blockState.data);
@@ -1495,13 +1517,55 @@ public class WrappedBlockState {
* Internal method for determining if the block state is still valid
*/
private int getGlobalIdNoCache() {
return INTO_ID.get(mappingsIndex).getOrDefault(this, -1);
return INTO_ID[this.mappingsIndex].getOrDefault(this, -1);
}
@Override
public String toString() {
return INTO_STRING.get(mappingsIndex).get(this);
return INTO_STRING[this.mappingsIndex].get(this);
}
public static void ensureLoad() { /**/ }
@ApiStatus.Internal
public static void ensureLoad() {
if (!PRELOAD_BLOCK_STATE_MAPPINGS) {
return;
}
Logger logger = PacketEvents.getAPI().getLogger();
logger.info("Preloading block mappings...");
long start = System.nanoTime();
{
Map<Map<StateValue, Object>, StateCacheValue> cache = new HashMap<>();
loadLegacy(cache);
for (ClientVersion version : MAPPING_VERSION_STEPS) {
loadModern(cache, version);
}
}
double timeDiff = (System.nanoTime() - start) / 1_000_000d;
logger.info("Finish preloading block mappings in " + timeDiff + "ms");
}
private static final class StateCacheValue {
public static final StateCacheValue EMPTY = new StateCacheValue(Collections.emptyMap());
private final Map<StateValue, Object> map;
private String string;
public StateCacheValue(Map<StateValue, Object> map) {
this.map = map;
}
public String getString() {
if (this.string == null) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<StateValue, Object> entry : this.map.entrySet()) {
builder.append(entry.getKey()).append('=').append(entry.getValue()).append(',');
}
this.string = builder.length() == 0 ? "" : '[' + builder.substring(0, builder.length() - 1) + ']';
}
return this.string;
}
}
}
@@ -19,22 +19,19 @@
package com.github.retrooper.packetevents.protocol.world.states.type;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.MaterialType;
import com.github.retrooper.packetevents.protocol.world.states.WrappedBlockState;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class StateType {
private final TypesBuilder typesBuilder;
private final TypesBuilderData typeData;
private final Mapped mapped = new Mapped();
private final Mapped mapped;
private final float blastResistance;
private final float hardness;
@@ -45,14 +42,14 @@ public class StateType {
private final boolean exceedsCube;
private final MaterialType materialType;
@ApiStatus.Internal
public StateType(
TypesBuilder typesBuilder, TypesBuilderData typeData,
TypesBuilderData typeData,
float blastResistance, float hardness, boolean isSolid,
boolean isBlocking, boolean isAir, boolean requiresCorrectTool,
boolean isShapeExceedsCube, MaterialType materialType
) {
this.typesBuilder = typesBuilder;
this.typeData = typeData;
this.mapped = new Mapped(typeData);
this.blastResistance = blastResistance;
this.hardness = hardness;
this.isSolid = isSolid;
@@ -76,7 +73,7 @@ public class StateType {
}
public String getName() {
return typeData.getName().getKey();
return this.mapped.getName().getKey();
}
public float getBlastResistance() {
@@ -155,20 +152,15 @@ public class StateType {
return Objects.hash(getName(), blastResistance, hardness, isSolid, isBlocking, isAir, requiresCorrectTool, exceedsCube, materialType);
}
public final class Mapped implements MappedEntity {
public final class Mapped extends AbstractMappedEntity {
@ApiStatus.Internal
public Mapped(@Nullable TypesBuilderData data) {
super(data);
}
public StateType getStateType() {
return StateType.this;
}
@Override
public ResourceLocation getName() {
return typeData.getName();
}
@Override
public int getId(ClientVersion version) {
return MappingHelper.getId(version, typesBuilder, typeData);
}
}
}
@@ -21,28 +21,29 @@ package com.github.retrooper.packetevents.protocol.world.states.type;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.world.MaterialType;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.mappings.MappingHelper;
import com.github.retrooper.packetevents.util.mappings.TypesBuilder;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class StateTypes {
public final class StateTypes {
private static final List<StateType> ALL_STATE_TYPES = new ArrayList<>();
private static final Map<String, StateType.Mapped> BY_NAME = new HashMap<>();
private static final Map<Byte, Map<Integer, StateType.Mapped>> BY_ID = new HashMap<>();
private static final TypesBuilder TYPES_BUILDER = new TypesBuilder("block/block_type_mappings");
private static final VersionedRegistry<StateType.Mapped> REGISTRY = new VersionedRegistry<>("block");
private static final List<StateType> STATE_TYPE_VALUES;
private StateTypes() {
}
public static VersionedRegistry<StateType.Mapped> getRegistry() {
return REGISTRY;
}
public static Collection<StateType> values() {
return Collections.unmodifiableCollection(ALL_STATE_TYPES);
return STATE_TYPE_VALUES;
}
public static @Nullable StateType getByName(String blockString) {
@@ -51,7 +52,7 @@ public class StateTypes {
}
public static StateType.@Nullable Mapped getMappedByName(String blockString) {
return getMappedByName(new ResourceLocation(blockString));
return REGISTRY.getByName(blockString);
}
public static @Nullable StateType getByName(ResourceLocation blockKey) {
@@ -60,7 +61,7 @@ public class StateTypes {
}
public static StateType.@Nullable Mapped getMappedByName(ResourceLocation blockKey) {
return BY_NAME.get(blockKey.toString());
return REGISTRY.getByName(blockKey);
}
public static StateType getById(ClientVersion version, int id) {
@@ -68,11 +69,10 @@ public class StateTypes {
}
public static StateType.Mapped getMappedById(ClientVersion version, int id) {
int index = TYPES_BUILDER.getDataIndex(version);
Map<Integer, StateType.Mapped> idMap = BY_ID.get((byte) index);
return idMap.get(id);
return REGISTRY.getById(version, id);
}
// <editor-fold desc="state type definitions" defaultstate="collapsed">
public static StateType AIR = StateTypes.builder().name("AIR").blastResistance(0.0f).hardness(0.0f).isBlocking(false).requiresCorrectTool(false).isSolid(false).isAir(true).setMaterial(MaterialType.AIR).build();
public static StateType STONE = StateTypes.builder().name("STONE").blastResistance(6.0f).hardness(1.5f).isBlocking(true).requiresCorrectTool(true).isSolid(true).setMaterial(MaterialType.STONE).build();
public static StateType GRANITE = StateTypes.builder().name("GRANITE").blastResistance(6.0f).hardness(1.5f).isBlocking(true).requiresCorrectTool(true).isSolid(true).setMaterial(MaterialType.STONE).build();
@@ -1196,9 +1196,16 @@ public class StateTypes {
public static StateType CLOSED_EYEBLOSSOM = StateTypes.builder().name("CLOSED_EYEBLOSSOM").blastResistance(0.0f).hardness(0.0f).isBlocking(false).requiresCorrectTool(false).isSolid(false).setMaterial(MaterialType.PLANT).build();
public static StateType POTTED_OPEN_EYEBLOSSOM = StateTypes.builder().name("POTTED_OPEN_EYEBLOSSOM").blastResistance(0.0f).hardness(0.0f).isBlocking(false).requiresCorrectTool(false).isSolid(true).setMaterial(MaterialType.DECORATION).build();
public static StateType POTTED_CLOSED_EYEBLOSSOM = StateTypes.builder().name("POTTED_CLOSED_EYEBLOSSOM").blastResistance(0.0f).hardness(0.0f).isBlocking(false).requiresCorrectTool(false).isSolid(true).setMaterial(MaterialType.DECORATION).build();
// </editor-fold>
static {
TYPES_BUILDER.unloadFileMappings();
REGISTRY.unloadMappings();
List<StateType> stateTypes = new ArrayList<>(REGISTRY.size());
for (StateType.Mapped type : REGISTRY.getEntries()) {
stateTypes.add(type.getStateType());
}
STATE_TYPE_VALUES = Collections.unmodifiableList(stateTypes);
}
public static Builder builder() {
@@ -1262,13 +1269,11 @@ public class StateTypes {
}
public StateType build() {
TypesBuilderData data = TYPES_BUILDER.define(this.name.getKey().toLowerCase(Locale.ROOT));
StateType type = new StateType(
TYPES_BUILDER, data, blastResistance, hardness, isSolid,
isBlocking, isAir, requiresCorrectTool, isShapeExceedsCube, materialType);
ALL_STATE_TYPES.add(type);
MappingHelper.registerMapping(TYPES_BUILDER, BY_NAME, BY_ID, type.getMapped());
return type;
String definedName = this.name.getKey().toLowerCase(Locale.ROOT);
return REGISTRY.define(definedName, data -> new StateType(
data, this.blastResistance, this.hardness, this.isSolid, this.isBlocking, this.isAir,
this.requiresCorrectTool, this.isShapeExceedsCube, this.materialType
).getMapped()).getStateType();
}
}
}
@@ -57,6 +57,7 @@ public class Vector3i {
this.z = 0;
}
@Deprecated
public Vector3i(long val) {
this(val, PacketEvents.getAPI().getServerManager().getVersion());
}
@@ -156,6 +157,7 @@ public class Vector3i {
return ((long) (getX() & 0x3FFFFFF) << 38) | ((long) (getY() & 0xFFF) << 26) | (getZ() & 0x3FFFFFF);
}
@Deprecated
public long getSerializedPosition() {
return getSerializedPosition(PacketEvents.getAPI().getServerManager().getVersion());
}
@@ -51,6 +51,8 @@ public interface IRegistry<T extends MappedEntity> extends BiFunction<ClientVers
*/
Collection<T> getEntries();
int size();
ResourceLocation getRegistryKey();
@Override
@@ -86,6 +86,11 @@ public final class SimpleRegistry<T extends MappedEntity> implements IRegistry<T
return Collections.unmodifiableCollection(this.typeMap.values());
}
@Override
public int size() {
return this.typeMap.size();
}
@Override
public ResourceLocation getRegistryKey() {
return this.registryKey;
@@ -58,8 +58,10 @@ public class TypesBuilder {
}
public void load() {
try (final SequentialNBTReader.Compound compound = MappingHelper.decompress("mappings/" + mapPath)) {
compound.skipOne(); // skip version tag for now
try (final SequentialNBTReader.Compound rootCompound = MappingHelper.decompress("mappings/" + this.mapPath)) {
rootCompound.skipOne(); // skip version tag for now
SequentialNBTReader.Compound compound = (SequentialNBTReader.Compound) rootCompound.next().getValue();
int length = ((NBTNumber) compound.next().getValue()).getAsInt(); // Second tag is the length
final SequentialNBTReader.Compound entries = (SequentialNBTReader.Compound) compound.next().getValue(); // Third tag are the entries
@@ -157,6 +159,11 @@ public class TypesBuilder {
return versionMapper.getIndex(rawVersion);
}
boolean isMappingDataLoaded() {
return this.entries != null;
}
@ApiStatus.Internal
public void unloadFileMappings() {
entries.clear();
entries = null;
@@ -23,6 +23,7 @@ import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.VisibleForTesting;
import java.util.Collection;
import java.util.Collections;
@@ -38,6 +39,10 @@ public final class VersionedRegistry<T extends MappedEntity> implements IRegistr
private final Map<String, T> typeMap = new HashMap<>();
private final Map<Byte, Map<Integer, T>> typeIdMap = new HashMap<>();
public VersionedRegistry(String registry) {
this(registry, "registries/" + registry);
}
public VersionedRegistry(String registry, String mappingsPath) {
this(new ResourceLocation(registry), mappingsPath);
}
@@ -55,6 +60,12 @@ public final class VersionedRegistry<T extends MappedEntity> implements IRegistr
return instance;
}
@VisibleForTesting
@ApiStatus.Internal
public boolean isMappingDataLoaded() {
return this.typesBuilder.isMappingDataLoaded();
}
@ApiStatus.Internal
public void unloadMappings() {
this.typesBuilder.unloadFileMappings();
@@ -91,6 +102,11 @@ public final class VersionedRegistry<T extends MappedEntity> implements IRegistr
return Collections.unmodifiableCollection(this.typeMap.values());
}
@Override
public int size() {
return this.typeMap.size();
}
@Override
public ResourceLocation getRegistryKey() {
return this.registryKey;
@@ -70,6 +70,10 @@ import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityMetadataProvider;
import com.github.retrooper.packetevents.protocol.entity.villager.VillagerData;
import com.github.retrooper.packetevents.protocol.entity.villager.profession.VillagerProfession;
import com.github.retrooper.packetevents.protocol.entity.villager.profession.VillagerProfessions;
import com.github.retrooper.packetevents.protocol.entity.villager.type.VillagerType;
import com.github.retrooper.packetevents.protocol.entity.villager.type.VillagerTypes;
import com.github.retrooper.packetevents.protocol.item.ItemStack;
import com.github.retrooper.packetevents.protocol.item.type.ItemType;
import com.github.retrooper.packetevents.protocol.item.type.ItemTypes;
@@ -201,12 +205,16 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
this.clientVersion = ClientVersion.UNKNOWN;
this.serverVersion = PacketEvents.getAPI().getServerManager().getVersion();
this.buffer = null;
int id = packetType.getId(PacketEvents.getAPI().getServerManager().getVersion().toClientVersion());
int id = packetType.getId(this.serverVersion.toClientVersion());
this.packetTypeData = new PacketTypeData(packetType, id);
}
public static PacketWrapper<?> createUniversalPacketWrapper(Object byteBuf) {
PacketWrapper<?> wrapper = new PacketWrapper<>(ClientVersion.UNKNOWN, PacketEvents.getAPI().getServerManager().getVersion(), -2);
return createUniversalPacketWrapper(byteBuf, PacketEvents.getAPI().getServerManager().getVersion());
}
public static PacketWrapper<?> createUniversalPacketWrapper(Object byteBuf, ServerVersion version) {
PacketWrapper<?> wrapper = new PacketWrapper<>(ClientVersion.UNKNOWN, version, -2);
wrapper.buffer = byteBuf;
return wrapper;
}
@@ -448,15 +456,15 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
}
public VillagerData readVillagerData() {
int villagerTypeId = readVarInt();
int villagerProfessionId = readVarInt();
VillagerType type = this.readMappedEntity(VillagerTypes.getRegistry());
VillagerProfession profession = this.readMappedEntity(VillagerProfessions.getRegistry());
int level = readVarInt();
return new VillagerData(villagerTypeId, villagerProfessionId, level);
return new VillagerData(type, profession, level);
}
public void writeVillagerData(VillagerData data) {
writeVarInt(data.getType().getId());
writeVarInt(data.getProfession().getId());
this.writeMappedEntity(data.getType());
this.writeMappedEntity(data.getProfession());
writeVarInt(data.getLevel());
}
@@ -477,7 +485,8 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
}
PatchableComponentMap components = new PatchableComponentMap(
itemType.getComponents(), new HashMap<>(4));
itemType.getComponents(this.serverVersion.toClientVersion()),
new HashMap<>(4));
for (int i = 0; i < presentCount; i++) {
ComponentType<?> type = this.readMappedEntity(ComponentTypes.getRegistry());
components.set((ComponentType<Object>) type, type.read(this));
@@ -967,16 +976,14 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
if (type == null) {
throw new IllegalStateException("Unknown entity metadata type id: " + typeID + " version " + serverVersion.toClientVersion());
}
Object value = type.getDataDeserializer().apply(this);
list.add(new EntityData(index, type, value));
list.add(new EntityData(index, type, type.read(this)));
}
} else {
for (byte data = readByte(); data != Byte.MAX_VALUE; data = readByte()) {
int typeID = (data & 0xE0) >> 5;
int index = data & 0x1F;
EntityDataType<?> type = EntityDataTypes.getById(serverVersion.toClientVersion(), typeID);
Object value = type.getDataDeserializer().apply(this);
EntityData entityData = new EntityData(index, type, value);
EntityData entityData = new EntityData(index, type, type.read(this));
list.add(entityData);
}
}
@@ -996,7 +1003,7 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
} else {
writeByte(entityData.getType().getId(serverVersion.toClientVersion()));
}
entityData.getType().getDataSerializer().accept(this, entityData.getValue());
((EntityDataType<? super Object>) entityData.getType()).write(this, entityData.getValue());
}
writeByte(255); // End of metadata array
} else {
@@ -1005,7 +1012,7 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
int index = entityData.getIndex();
int data = (typeID << 5 | index & 31) & 255;
writeByte(data);
entityData.getType().getDataSerializer().accept(this, entityData.getValue());
((EntityDataType<? super Object>) entityData.getType()).write(this, entityData.getValue());
}
writeByte(127); // End of metadata array
}
@@ -1437,6 +1444,18 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
}
}
public <R> Optional<R> readJavaOptional(Reader<R> reader) {
return this.readBoolean() ? Optional.of(reader.apply(this)) : Optional.empty();
}
public <V> void writeJavaOptional(Optional<V> value, Writer<V> writer) {
if (value.isPresent()) {
this.writeBoolean(true);
writer.accept(this, value.get());
} else {
this.writeBoolean(false);
}
}
public <K, C extends Collection<K>> C readCollection(IntFunction<C> function, Reader<K> reader) {
int size = this.readVarInt();
@@ -1524,12 +1543,12 @@ public class PacketWrapper<T extends PacketWrapper<T>> {
}
public <Z extends MappedEntity> Z readMappedEntity(IRegistry<Z> registry) {
IRegistry<Z> replacedRegistry = this.getRegistryHolder().getRegistryOr(registry);
IRegistry<Z> replacedRegistry = this.getRegistryHolder().getRegistryOr(registry, this.serverVersion.toClientVersion());
return this.readMappedEntity((BiFunction<ClientVersion, Integer, Z>) replacedRegistry);
}
public <Z extends MappedEntity> Z readMappedEntityOrDirect(IRegistry<Z> registry, Reader<Z> directReader) {
IRegistry<Z> replacedRegistry = this.getRegistryHolder().getRegistryOr(registry);
IRegistry<Z> replacedRegistry = this.getRegistryHolder().getRegistryOr(registry, this.serverVersion.toClientVersion());
return this.readMappedEntityOrDirect((BiFunction<ClientVersion, Integer, Z>) replacedRegistry, directReader);
}
@@ -44,14 +44,14 @@ public class WrapperPlayClientGenerateStructure extends PacketWrapper<WrapperPla
@Override
public void read() {
this.blockPosition = new Vector3i(readLong());
this.blockPosition = new Vector3i(readLong(), this.serverVersion);
this.levels = readVarInt();
this.keepJigsaws = readBoolean();
}
@Override
public void write() {
writeLong(this.blockPosition.getSerializedPosition());
writeLong(this.blockPosition.getSerializedPosition(this.serverVersion));
writeVarInt(this.levels);
writeBoolean(this.keepJigsaws);
}
@@ -54,7 +54,7 @@ public class WrapperPlayClientUpdateCommandBlock extends PacketWrapper<WrapperPl
@Override
public void read() {
this.position = new Vector3i(readLong());
this.position = new Vector3i(readLong(), this.serverVersion);
this.command = readString();
this.mode = CommandBlockMode.getById(readVarInt());
this.flags = readUnsignedByte();
@@ -65,7 +65,7 @@ public class WrapperPlayClientUpdateCommandBlock extends PacketWrapper<WrapperPl
@Override
public void write() {
writeLong(position.getSerializedPosition());
writeLong(position.getSerializedPosition(this.serverVersion));
writeString(command);
writeVarInt(mode.ordinal());
if (this.doesTrackOutput) {
@@ -46,7 +46,7 @@ public class WrapperPlayClientUpdateSign extends PacketWrapper<WrapperPlayClient
@Override
public void read() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
this.blockPosition = new Vector3i(readLong());
this.blockPosition = new Vector3i(readLong(), this.serverVersion);
} else {
int x = readInt();
int y = readShort();
@@ -67,7 +67,7 @@ public class WrapperPlayClientUpdateSign extends PacketWrapper<WrapperPlayClient
@Override
public void write() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
long positionVector = blockPosition.getSerializedPosition();
long positionVector = blockPosition.getSerializedPosition(this.serverVersion);
writeLong(positionVector);
} else {
writeInt(blockPosition.x);
@@ -548,7 +548,8 @@ public class WrapperPlayServerJoinGame extends PacketWrapper<WrapperPlayServerJo
}
public DimensionType getDimensionType() {
IRegistry<DimensionType> registry = this.getRegistryHolder().getRegistryOr(DimensionTypes.getRegistry());
IRegistry<DimensionType> registry = this.getRegistryHolder().getRegistryOr(
DimensionTypes.getRegistry(), this.serverVersion.toClientVersion());
return this.dimensionTypeRef.resolve(registry, this.serverVersion.toClientVersion());
}
@@ -41,7 +41,7 @@ public class WrapperPlayServerOpenSignEditor extends PacketWrapper<WrapperPlaySe
@Override
public void read() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
this.position = new Vector3i(readLong());
this.position = new Vector3i(readLong(), this.serverVersion);
} else {
int x = readInt();
int y = readInt();
@@ -58,7 +58,7 @@ public class WrapperPlayServerOpenSignEditor extends PacketWrapper<WrapperPlaySe
@Override
public void write() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_8)) {
long positionVector = position.getSerializedPosition();
long positionVector = position.getSerializedPosition(this.serverVersion);
writeLong(positionVector);
} else {
writeInt(position.x);
@@ -298,7 +298,8 @@ public class WrapperPlayServerRespawn extends PacketWrapper<WrapperPlayServerRes
}
public DimensionType getDimensionType() {
IRegistry<DimensionType> registry = this.getRegistryHolder().getRegistryOr(DimensionTypes.getRegistry());
IRegistry<DimensionType> registry = this.getRegistryHolder().getRegistryOr(
DimensionTypes.getRegistry(), this.serverVersion.toClientVersion());
return this.dimensionTypeRef.resolve(registry, this.serverVersion.toClientVersion());
}
Loaded 100 of 190 files, more files were not shown because too many files have changed in this diff. Show more