mirror of
https://github.com/italicsjenga/valence.git
synced 2024-12-23 14:31:30 +11:00
tag registry (#350)
- add Tags extractor - add tags.json to extracted - send `SynchronizeTagsS2c` packet on join - fix encode ## Description Adds a `TagsRegistry` resource that contains all the information needed to build and send `SynchronizeTagsS2c` on join. closes #349
This commit is contained in:
parent
c5557e744d
commit
975014e76b
|
@ -67,7 +67,7 @@ use valence_instance::packet::{
|
|||
ChunkLoadDistanceS2c, ChunkRenderDistanceCenterS2c, UnloadChunkS2c,
|
||||
};
|
||||
use valence_instance::{ClearInstanceChangesSet, Instance, WriteUpdatePacketsToInstancesSet};
|
||||
use valence_registry::{RegistryCodec, RegistryCodecSet};
|
||||
use valence_registry::{RegistryCodec, RegistryCodecSet, TagsRegistry};
|
||||
|
||||
pub mod action;
|
||||
pub mod chat;
|
||||
|
@ -678,6 +678,7 @@ struct ClientJoinQuery {
|
|||
|
||||
fn initial_join(
|
||||
codec: Res<RegistryCodec>,
|
||||
tags: Res<TagsRegistry>,
|
||||
mut clients: Query<ClientJoinQuery, Added<Client>>,
|
||||
instances: Query<&Instance>,
|
||||
mut commands: Commands,
|
||||
|
@ -724,6 +725,8 @@ fn initial_join(
|
|||
last_death_location,
|
||||
});
|
||||
|
||||
q.client.enc.append_bytes(tags.sync_tags_packet());
|
||||
|
||||
/*
|
||||
// TODO: enable all the features?
|
||||
q.client.write_packet(&FeatureFlags {
|
||||
|
|
|
@ -142,7 +142,7 @@ impl Default for CoreSettings {
|
|||
}
|
||||
|
||||
/// Contains global server state accessible as a [`Resource`].
|
||||
#[derive(Resource)]
|
||||
#[derive(Resource, Default)]
|
||||
pub struct Server {
|
||||
/// Incremented on every tick.
|
||||
current_tick: i64,
|
||||
|
|
|
@ -1372,26 +1372,3 @@ pub mod map {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Move this to valence_registry?
|
||||
pub mod synchronize_tags {
|
||||
use super::*;
|
||||
|
||||
#[derive(Clone, Debug, Encode, Decode, Packet)]
|
||||
#[packet(id = packet_id::SYNCHRONIZE_TAGS_S2C)]
|
||||
pub struct SynchronizeTagsS2c<'a> {
|
||||
pub tags: Vec<TagGroup<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct TagGroup<'a> {
|
||||
pub kind: Ident<Cow<'a, str>>,
|
||||
pub tags: Vec<Tag<'a>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Encode, Decode)]
|
||||
pub struct Tag<'a> {
|
||||
pub name: Ident<Cow<'a, str>>,
|
||||
pub entries: Vec<VarInt>,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,13 @@ use std::io::{Read, Write};
|
|||
|
||||
use anyhow::bail;
|
||||
use byteorder::ReadBytesExt;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::protocol::{Decode, Encode};
|
||||
|
||||
/// An `i32` encoded with variable length.
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Deserialize)]
|
||||
#[repr(transparent)]
|
||||
pub struct VarInt(pub i32);
|
||||
|
||||
|
|
|
@ -8,4 +8,6 @@ tracing.workspace = true
|
|||
valence_core.workspace = true
|
||||
valence_nbt.workspace = true
|
||||
bevy_ecs.workspace = true
|
||||
bevy_app.workspace = true
|
||||
bevy_app.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
|
@ -25,6 +25,10 @@ use tracing::error;
|
|||
use valence_core::ident::Ident;
|
||||
use valence_nbt::{compound, Compound, List, Value};
|
||||
|
||||
mod tags;
|
||||
|
||||
pub use tags::*;
|
||||
|
||||
pub struct RegistryPlugin;
|
||||
|
||||
/// The [`SystemSet`] where the [`RegistryCodec`] cache is rebuilt. Systems that
|
||||
|
@ -35,8 +39,11 @@ pub struct RegistryCodecSet;
|
|||
impl Plugin for RegistryPlugin {
|
||||
fn build(&self, app: &mut bevy_app::App) {
|
||||
app.init_resource::<RegistryCodec>()
|
||||
.init_resource::<TagsRegistry>()
|
||||
.configure_set(RegistryCodecSet.in_base_set(CoreSet::PostUpdate))
|
||||
.add_system(cache_registry_codec.in_set(RegistryCodecSet));
|
||||
.add_startup_system(init_tags_registry.in_set(RegistryCodecSet))
|
||||
.add_system(cache_registry_codec.in_set(RegistryCodecSet))
|
||||
.add_system(cache_tags_packet.in_set(RegistryCodecSet));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
84
crates/valence_registry/src/tags.rs
Normal file
84
crates/valence_registry/src/tags.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use bevy_ecs::prelude::*;
|
||||
use serde::Deserialize;
|
||||
use valence_core::ident::Ident;
|
||||
use valence_core::protocol::encode::{PacketWriter, WritePacket};
|
||||
use valence_core::protocol::var_int::VarInt;
|
||||
use valence_core::protocol::{packet_id, Decode, Encode, Packet};
|
||||
use valence_core::Server;
|
||||
|
||||
#[derive(Clone, Debug, Encode, Decode, Packet)]
|
||||
#[packet(id = packet_id::SYNCHRONIZE_TAGS_S2C)]
|
||||
pub struct SynchronizeTagsS2c<'a> {
|
||||
pub registries: Cow<'a, [Registry]>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Resource, Default)]
|
||||
pub struct TagsRegistry {
|
||||
pub registries: Vec<Registry>,
|
||||
cached_packet: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Encode, Decode)]
|
||||
pub struct Registry {
|
||||
pub registry: Ident<String>,
|
||||
pub tags: Vec<TagEntry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, Encode, Decode)]
|
||||
pub struct TagEntry {
|
||||
pub name: Ident<String>,
|
||||
pub entries: Vec<VarInt>,
|
||||
}
|
||||
|
||||
impl<'a> TagsRegistry {
|
||||
pub(crate) fn build_synchronize_tags(&'a self) -> SynchronizeTagsS2c<'a> {
|
||||
SynchronizeTagsS2c {
|
||||
registries: Cow::Borrowed(&self.registries),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sync_tags_packet(&self) -> &Vec<u8> {
|
||||
&self.cached_packet
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_tags_registry(mut tags: ResMut<TagsRegistry>) {
|
||||
let registries =
|
||||
serde_json::from_str::<Vec<Registry>>(include_str!("../../../extracted/tags.json"))
|
||||
.expect("tags.json is invalid");
|
||||
tags.registries = registries;
|
||||
}
|
||||
|
||||
pub(crate) fn cache_tags_packet(server: Res<Server>, tags: ResMut<TagsRegistry>) {
|
||||
if tags.is_changed() {
|
||||
let tags = tags.into_inner();
|
||||
let packet = tags.build_synchronize_tags();
|
||||
let mut bytes = vec![];
|
||||
let mut scratch = vec![];
|
||||
let mut writer =
|
||||
PacketWriter::new(&mut bytes, server.compression_threshold(), &mut scratch);
|
||||
writer.write_packet(&packet);
|
||||
tags.cached_packet = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::RegistryPlugin;
|
||||
|
||||
#[test]
|
||||
fn smoke_test() {
|
||||
let mut app = bevy_app::App::new();
|
||||
app.add_plugin(RegistryPlugin);
|
||||
app.insert_resource(Server::default());
|
||||
app.update();
|
||||
|
||||
let tags_registry = app.world.get_resource::<TagsRegistry>().unwrap();
|
||||
let packet = tags_registry.build_synchronize_tags();
|
||||
assert!(!packet.registries.is_empty());
|
||||
assert!(!tags_registry.cached_packet.is_empty());
|
||||
}
|
||||
}
|
7235
extracted/tags.json
Normal file
7235
extracted/tags.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -74,9 +74,9 @@ public class Main implements ModInitializer {
|
|||
}
|
||||
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(server -> {
|
||||
LOGGER.info("Server starting, Extracting registry codec...");
|
||||
LOGGER.info("Server starting, Running startup extractors...");
|
||||
// TODO: make `Codec` implement `Extractor`
|
||||
var codecExtractor = new Codec(server);
|
||||
|
||||
try {
|
||||
var out = outputDirectory.resolve(codecExtractor.fileName());
|
||||
var compound = codecExtractor.extract();
|
||||
|
@ -92,6 +92,22 @@ public class Main implements ModInitializer {
|
|||
LOGGER.error("Extractor for \"" + codecExtractor.fileName() + "\" failed.", e);
|
||||
}
|
||||
|
||||
var startupExtractors = new Extractor[]{
|
||||
new Tags(server),
|
||||
};
|
||||
|
||||
for (var ext : startupExtractors) {
|
||||
try {
|
||||
var out = outputDirectory.resolve(ext.fileName());
|
||||
var fileWriter = new FileWriter(out.toFile(), StandardCharsets.UTF_8);
|
||||
gson.toJson(ext.extract(), fileWriter);
|
||||
fileWriter.close();
|
||||
LOGGER.info("Wrote " + out.toAbsolutePath());
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Extractor for \"" + ext.fileName() + "\" failed.", e);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("Done.");
|
||||
server.shutdown();
|
||||
});
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package rs.valence.extractor.extractors;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.entity.SpawnGroup;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.collection.Weighted;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.registry.RegistryWrapper;
|
||||
import net.minecraft.registry.BuiltinRegistries;
|
||||
import net.minecraft.registry.DynamicRegistryManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import rs.valence.extractor.Main;
|
||||
import net.minecraft.registry.tag.TagKey;
|
||||
import net.minecraft.registry.CombinedDynamicRegistries;
|
||||
import net.minecraft.registry.ServerDynamicRegistryType;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.registry.entry.RegistryEntryList;
|
||||
import net.minecraft.registry.SerializableRegistries;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Tags implements Main.Extractor {
|
||||
private CombinedDynamicRegistries<ServerDynamicRegistryType> dynamicRegistryManager;
|
||||
|
||||
public Tags(MinecraftServer server) {
|
||||
this.dynamicRegistryManager = server.getCombinedDynamicRegistries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fileName() {
|
||||
return "tags.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement extract() {
|
||||
var tagsJson = new JsonArray();
|
||||
|
||||
Map<RegistryKey<? extends Registry<?>>, Map<Identifier, JsonArray>> registryTags =
|
||||
SerializableRegistries.streamRegistryManagerEntries(this.dynamicRegistryManager)
|
||||
.map(registry -> Pair.of(registry.key(), serializeTags(registry.value())))
|
||||
.filter(pair -> !(pair.getSecond()).isEmpty())
|
||||
.collect(Collectors.toMap(Pair::getFirst, Pair::getSecond));
|
||||
|
||||
for (var registry : registryTags.entrySet()) {
|
||||
var registryIdent = registry.getKey().getValue().toString();
|
||||
var tagGroupJson = new JsonObject();
|
||||
var tagGroupTagsJson = new JsonArray();
|
||||
|
||||
for (var tag : registry.getValue().entrySet()) {
|
||||
var tagJson = new JsonObject();
|
||||
var ident = tag.getKey().toString();
|
||||
var raw_ids = tag.getValue();
|
||||
|
||||
tagJson.addProperty("name", ident);
|
||||
tagJson.add("entries", raw_ids);
|
||||
tagGroupTagsJson.add(tagJson);
|
||||
}
|
||||
|
||||
tagGroupJson.addProperty("registry", registryIdent.toString());
|
||||
tagGroupJson.add("tags", tagGroupTagsJson);
|
||||
tagsJson.add(tagGroupJson);
|
||||
}
|
||||
|
||||
return tagsJson;
|
||||
}
|
||||
|
||||
private static <T> Map<Identifier, JsonArray> serializeTags(Registry<T> registry) {
|
||||
HashMap<Identifier, JsonArray> map = new HashMap<Identifier, JsonArray>();
|
||||
registry.streamTagsAndEntries().forEach(pair -> {
|
||||
RegistryEntryList<T> registryEntryList = (RegistryEntryList<T>)pair.getSecond();
|
||||
JsonArray intList = new JsonArray(registryEntryList.size());
|
||||
for (RegistryEntry<T> registryEntry : registryEntryList) {
|
||||
if (registryEntry.getType() != RegistryEntry.Type.REFERENCE) {
|
||||
throw new IllegalStateException("Can't serialize unregistered value " + registryEntry);
|
||||
}
|
||||
intList.add(registry.getRawId(registryEntry.value()));
|
||||
}
|
||||
map.put(((TagKey)pair.getFirst()).id(), intList);
|
||||
});
|
||||
return map;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue