Ability to get registry packets without MinecraftServer (#2469)

* Ability to get registry packets without MinecraftServer

* review fixes
This commit is contained in:
MelonHell 2024-11-07 01:46:09 +03:00 committed by Matt Worzala
parent 5f7ac6fb72
commit e6781cd3fa
5 changed files with 57 additions and 43 deletions

View File

@ -6,10 +6,7 @@ import net.minestom.server.MinecraftServer;
import net.minestom.server.entity.EntityType;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.registry.DynamicRegistry;
import net.minestom.server.registry.FluidRegistries;
import net.minestom.server.registry.ProtocolObject;
import net.minestom.server.registry.Registry;
import net.minestom.server.registry.*;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
@ -19,6 +16,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
@ -94,32 +92,32 @@ public final class Tag implements ProtocolObject, Keyed {
public enum BasicType {
BLOCKS("minecraft:block", Registry.Resource.BLOCK_TAGS,
name -> Objects.requireNonNull(Block.fromNamespaceId(name)).id()),
(name, registries) -> Objects.requireNonNull(Block.fromNamespaceId(name)).id()),
ITEMS("minecraft:item", Registry.Resource.ITEM_TAGS,
name -> Objects.requireNonNull(Material.fromNamespaceId(name)).id()),
(name, registries) -> Objects.requireNonNull(Material.fromNamespaceId(name)).id()),
FLUIDS("minecraft:fluid", Registry.Resource.FLUID_TAGS,
name -> FluidRegistries.getFluid(name).ordinal()),
(name, registries) -> FluidRegistries.getFluid(name).ordinal()),
ENTITY_TYPES("minecraft:entity_type", Registry.Resource.ENTITY_TYPE_TAGS,
name -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id()),
(name, registries) -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id()),
GAME_EVENTS("minecraft:game_event", Registry.Resource.GAMEPLAY_TAGS,
name -> FluidRegistries.getFluid(name).ordinal()),
(name, registries) -> FluidRegistries.getFluid(name).ordinal()),
SOUND_EVENTS("minecraft:sound_event", null, null), // Seems not to be included in server data
POTION_EFFECTS("minecraft:potion_effect", null, null), // Seems not to be included in server data
//todo this is cursed. it does not update as the registry changes. Fix later.
ENCHANTMENTS("minecraft:enchantment", Registry.Resource.ENCHANTMENT_TAGS,
name -> MinecraftServer.getEnchantmentRegistry().getId(DynamicRegistry.Key.of(name))),
(name, registries) -> registries.enchantment().getId(DynamicRegistry.Key.of(name))),
BIOMES("minecraft:worldgen/biome", Registry.Resource.BIOME_TAGS,
name -> MinecraftServer.getBiomeRegistry().getId(DynamicRegistry.Key.of(name)));
(name, registries) -> registries.biome().getId(DynamicRegistry.Key.of(name)));
private final static BasicType[] VALUES = values();
private final String identifier;
private final Registry.Resource resource;
private final Function<String, Integer> function;
private final BiFunction<String, Registries, Integer> function;
BasicType(@NotNull String identifier,
@Nullable Registry.Resource resource,
@Nullable Function<String, Integer> function) {
@Nullable BiFunction<String, Registries, Integer> function) {
this.identifier = identifier;
this.resource = resource;
this.function = function;
@ -133,7 +131,7 @@ public final class Tag implements ProtocolObject, Keyed {
return resource;
}
public Function<String, Integer> getFunction() {
public BiFunction<String, Registries, Integer> getFunction() {
return function;
}

View File

@ -1,6 +1,7 @@
package net.minestom.server.gamedata.tags;
import net.minestom.server.network.packet.server.common.TagsPacket;
import net.minestom.server.registry.Registries;
import net.minestom.server.utils.NamespaceID;
import org.jetbrains.annotations.Nullable;
@ -40,20 +41,20 @@ public final class TagManager {
return Collections.unmodifiableMap(tagMap);
}
public TagsPacket packet() {
List<TagsPacket.Registry> registries = new ArrayList<>();
public TagsPacket packet(Registries registries) {
List<TagsPacket.Registry> registryList = new ArrayList<>();
for (Map.Entry<Tag.BasicType, List<Tag>> entry : tagMap.entrySet()) {
final Tag.BasicType type = entry.getKey();
final String registry = type.getIdentifier();
List<TagsPacket.Tag> tags = new ArrayList<>();
for (Tag tag : entry.getValue()) {
final String identifier = tag.getName().asString();
final int[] values = tag.getValues().stream().mapToInt(value -> type.getFunction().apply(value.asString())).toArray();
final int[] values = tag.getValues().stream().mapToInt(value -> type.getFunction().apply(value.asString(), registries)).toArray();
tags.add(new TagsPacket.Tag(identifier, values));
}
registries.add(new TagsPacket.Registry(registry, tags));
registryList.add(new TagsPacket.Registry(registry, tags));
}
return new TagsPacket(registries);
return new TagsPacket(registryList);
}
private Set<NamespaceID> getValues(Map<String, Map<String, Object>> main, String value) {

View File

@ -4,6 +4,7 @@ import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerFlag;
import net.minestom.server.ServerProcess;
import net.minestom.server.entity.Player;
import net.minestom.server.event.EventDispatcher;
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
@ -24,6 +25,7 @@ import net.minestom.server.network.player.GameProfile;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import net.minestom.server.network.plugin.LoginPluginMessageProcessor;
import net.minestom.server.registry.Registries;
import net.minestom.server.registry.StaticProtocolObject;
import net.minestom.server.utils.StringUtils;
import net.minestom.server.utils.validate.Check;
@ -44,10 +46,10 @@ public final class ConnectionManager {
private static final Component TIMEOUT_TEXT = Component.text("Timeout", NamedTextColor.RED);
private CachedPacket defaultTags;
private CachedPacket getDefaultTags() {
private CachedPacket getDefaultTags(Registries registries) {
var defaultTags = this.defaultTags;
if (defaultTags == null) {
final TagsPacket packet = MinecraftServer.getTagManager().packet();
final TagsPacket packet = MinecraftServer.getTagManager().packet(registries);
this.defaultTags = defaultTags = new CachedPacket(packet);
}
return defaultTags;
@ -250,20 +252,20 @@ public final class ConnectionManager {
}
boolean excludeVanilla = knownPacks.contains(SelectKnownPacksPacket.MINECRAFT_CORE);
var serverProcess = MinecraftServer.process();
player.sendPacket(serverProcess.chatType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.dimensionType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.biome().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.damageType().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.trimMaterial().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.trimPattern().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.bannerPattern().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.wolfVariant().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.enchantment().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.paintingVariant().registryDataPacket(excludeVanilla));
player.sendPacket(serverProcess.jukeboxSong().registryDataPacket(excludeVanilla));
Registries registries = MinecraftServer.process();
player.sendPacket(registries.chatType().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.dimensionType().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.biome().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.damageType().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.trimMaterial().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.trimPattern().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.bannerPattern().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.wolfVariant().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.enchantment().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.paintingVariant().registryDataPacket(registries, excludeVanilla));
player.sendPacket(registries.jukeboxSong().registryDataPacket(registries, excludeVanilla));
player.sendPacket(getDefaultTags());
player.sendPacket(getDefaultTags(registries));
}
// Wait for pending resource packs if any

View File

@ -213,10 +213,11 @@ public sealed interface DynamicRegistry<T> permits DynamicRegistryImpl {
* <p>Returns a {@link SendablePacket} potentially excluding vanilla entries if possible. It is never possible to
* exclude vanilla entries if one has been overridden (e.g. via {@link #register(NamespaceID, T)}.</p>
*
* @param registries Registries provider
* @param excludeVanilla Whether to exclude vanilla entries
* @return A {@link SendablePacket} containing the registry data
*/
@ApiStatus.Internal
@NotNull SendablePacket registryDataPacket(boolean excludeVanilla);
@NotNull SendablePacket registryDataPacket(@NotNull Registries registries, boolean excludeVanilla);
}

View File

@ -3,7 +3,6 @@ package net.minestom.server.registry;
import net.kyori.adventure.nbt.BinaryTag;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.TagStringIOExt;
import net.minestom.server.MinecraftServer;
import net.minestom.server.ServerFlag;
import net.minestom.server.gamedata.DataPack;
import net.minestom.server.network.packet.server.CachedPacket;
@ -49,7 +48,8 @@ final class DynamicRegistryImpl<T> implements DynamicRegistry<T> {
}
}
private final CachedPacket vanillaRegistryDataPacket = new CachedPacket(() -> createRegistryDataPacket(true));
private Registries registries = null;
private CachedPacket vanillaRegistryDataPacket = new CachedPacket(() -> createRegistryDataPacket(registries, true));
private final ReentrantLock lock = new ReentrantLock(); // Protects writes
private final List<T> entryById = new CopyOnWriteArrayList<>();
@ -143,7 +143,9 @@ final class DynamicRegistryImpl<T> implements DynamicRegistry<T> {
entryByName.put(namespaceId, object);
idByName.add(namespaceId);
packById.add(id, pack);
vanillaRegistryDataPacket.invalidate();
if (vanillaRegistryDataPacket != null) {
vanillaRegistryDataPacket.invalidate();
}
return Key.of(namespaceId);
} finally {
lock.unlock();
@ -163,7 +165,9 @@ final class DynamicRegistryImpl<T> implements DynamicRegistry<T> {
entryByName.remove(namespaceId);
idByName.remove(id);
packById.remove(id);
vanillaRegistryDataPacket.invalidate();
if (vanillaRegistryDataPacket != null) {
vanillaRegistryDataPacket.invalidate();
}
return true;
} finally {
lock.unlock();
@ -171,15 +175,23 @@ final class DynamicRegistryImpl<T> implements DynamicRegistry<T> {
}
@Override
public @NotNull SendablePacket registryDataPacket(boolean excludeVanilla) {
public @NotNull SendablePacket registryDataPacket(@NotNull Registries registries, boolean excludeVanilla) {
// We cache the vanilla packet because that is by far the most common case. If some client claims not to have
// the vanilla datapack we can compute the entire thing.
return excludeVanilla ? vanillaRegistryDataPacket : createRegistryDataPacket(false);
if (excludeVanilla) {
if (this.registries != registries) {
vanillaRegistryDataPacket.invalidate();
this.registries = registries;
}
return vanillaRegistryDataPacket;
}
return createRegistryDataPacket(registries, false);
}
private @NotNull RegistryDataPacket createRegistryDataPacket(boolean excludeVanilla) {
private @NotNull RegistryDataPacket createRegistryDataPacket(@NotNull Registries registries, boolean excludeVanilla) {
Check.notNull(nbtType, "Cannot create registry data packet for server-only registry");
BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(MinecraftServer.process(), true);
BinaryTagSerializer.Context context = new BinaryTagSerializer.ContextWithRegistries(registries, true);
List<RegistryDataPacket.Entry> entries = new ArrayList<>(entryById.size());
for (int i = 0; i < entryById.size(); i++) {
CompoundBinaryTag data = null;