mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-12-29 15:49:09 +01:00
Refactor Java registry storage; implement trim support
This commit is contained in:
parent
6a5efa3c9d
commit
11f79d4e2c
11 changed files with 253 additions and 172 deletions
|
@ -25,63 +25,68 @@
|
|||
|
||||
package org.geysermc.geyser.inventory.recipe;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.RegistryEntry;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemDescriptorWithCount;
|
||||
import org.cloudburstmc.protocol.bedrock.data.inventory.descriptor.ItemTagDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
|
||||
/**
|
||||
* Hardcoded recipe information about armor trims until further improvements can be made. This information was scraped
|
||||
* from BDS 1.19.81 with a world with the next_major_update and sniffer features enabled, using ProxyPass.
|
||||
* Stores information on trim materials and patterns, including smithing armor hacks for pre-1.20.
|
||||
*/
|
||||
public class TrimRecipe {
|
||||
|
||||
// For TrimDataPacket, which BDS sends just before the CraftingDataPacket
|
||||
public static final List<TrimPattern> PATTERNS;
|
||||
public static final List<TrimMaterial> MATERIALS;
|
||||
|
||||
public final class TrimRecipe {
|
||||
// For CraftingDataPacket
|
||||
public static final String ID = "minecraft:smithing_armor_trim";
|
||||
public static final ItemDescriptorWithCount BASE = tagDescriptor("minecraft:trimmable_armors");
|
||||
public static final ItemDescriptorWithCount ADDITION = tagDescriptor("minecraft:trim_materials");
|
||||
public static final ItemDescriptorWithCount TEMPLATE = tagDescriptor("minecraft:trim_templates");
|
||||
|
||||
static {
|
||||
List<TrimPattern> patterns = new ArrayList<>(16);
|
||||
patterns.add(new TrimPattern("minecraft:ward_armor_trim_smithing_template", "ward"));
|
||||
patterns.add(new TrimPattern("minecraft:sentry_armor_trim_smithing_template", "sentry"));
|
||||
patterns.add(new TrimPattern("minecraft:snout_armor_trim_smithing_template", "snout"));
|
||||
patterns.add(new TrimPattern("minecraft:dune_armor_trim_smithing_template", "dune"));
|
||||
patterns.add(new TrimPattern("minecraft:spire_armor_trim_smithing_template", "spire"));
|
||||
patterns.add(new TrimPattern("minecraft:tide_armor_trim_smithing_template", "tide"));
|
||||
patterns.add(new TrimPattern("minecraft:wild_armor_trim_smithing_template", "wild"));
|
||||
patterns.add(new TrimPattern("minecraft:rib_armor_trim_smithing_template", "rib"));
|
||||
patterns.add(new TrimPattern("minecraft:coast_armor_trim_smithing_template", "coast"));
|
||||
patterns.add(new TrimPattern("minecraft:shaper_armor_trim_smithing_template", "shaper"));
|
||||
patterns.add(new TrimPattern("minecraft:eye_armor_trim_smithing_template", "eye"));
|
||||
patterns.add(new TrimPattern("minecraft:vex_armor_trim_smithing_template", "vex"));
|
||||
patterns.add(new TrimPattern("minecraft:silence_armor_trim_smithing_template", "silence"));
|
||||
patterns.add(new TrimPattern("minecraft:wayfinder_armor_trim_smithing_template", "wayfinder"));
|
||||
patterns.add(new TrimPattern("minecraft:raiser_armor_trim_smithing_template", "raiser"));
|
||||
patterns.add(new TrimPattern("minecraft:host_armor_trim_smithing_template", "host"));
|
||||
PATTERNS = Collections.unmodifiableList(patterns);
|
||||
public static TrimMaterial readTrimMaterial(GeyserSession session, RegistryEntry entry) {
|
||||
String key = stripNamespace(entry.getId());
|
||||
|
||||
List<TrimMaterial> materials = new ArrayList<>(10);
|
||||
materials.add(new TrimMaterial("quartz", "§h", "minecraft:quartz"));
|
||||
materials.add(new TrimMaterial("iron", "§i", "minecraft:iron_ingot"));
|
||||
materials.add(new TrimMaterial("netherite", "§j", "minecraft:netherite_ingot"));
|
||||
materials.add(new TrimMaterial("redstone", "§m", "minecraft:redstone"));
|
||||
materials.add(new TrimMaterial("copper", "§n", "minecraft:copper_ingot"));
|
||||
materials.add(new TrimMaterial("gold", "§p", "minecraft:gold_ingot"));
|
||||
materials.add(new TrimMaterial("emerald", "§q", "minecraft:emerald"));
|
||||
materials.add(new TrimMaterial("diamond", "§s", "minecraft:diamond"));
|
||||
materials.add(new TrimMaterial("lapis", "§t", "minecraft:lapis_lazuli"));
|
||||
materials.add(new TrimMaterial("amethyst", "§u", "minecraft:amethyst_shard"));
|
||||
MATERIALS = Collections.unmodifiableList(materials);
|
||||
// Color is used when hovering over the item
|
||||
// Find the nearest legacy color from the RGB Java gives us to work with
|
||||
// Also yes this is a COMPLETE hack but it works ok!!!!!
|
||||
StringTag colorTag = ((CompoundTag) entry.getData().get("description")).get("color");
|
||||
TextColor color = TextColor.fromHexString(colorTag.getValue());
|
||||
String legacy = MessageTranslator.convertMessage(Component.space().color(color));
|
||||
|
||||
String itemIdentifier = ((StringTag) entry.getData().get("ingredient")).getValue();
|
||||
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
|
||||
if (itemMapping == null) {
|
||||
// This should never happen so not sure what to do here.
|
||||
itemMapping = ItemMapping.AIR;
|
||||
}
|
||||
// Just pick out the resulting color code, without RESET in front.
|
||||
return new TrimMaterial(key, legacy.substring(2).trim(), itemMapping.getBedrockIdentifier());
|
||||
}
|
||||
|
||||
public static TrimPattern readTrimPattern(GeyserSession session, RegistryEntry entry) {
|
||||
String key = stripNamespace(entry.getId());
|
||||
|
||||
String itemIdentifier = ((StringTag) entry.getData().get("template_item")).getValue();
|
||||
ItemMapping itemMapping = session.getItemMappings().getMapping(itemIdentifier);
|
||||
if (itemMapping == null) {
|
||||
// This should never happen so not sure what to do here.
|
||||
itemMapping = ItemMapping.AIR;
|
||||
}
|
||||
return new TrimPattern(itemMapping.getBedrockIdentifier(), key);
|
||||
}
|
||||
|
||||
// TODO find a good place for a stripNamespace util method
|
||||
private static String stripNamespace(String identifier) {
|
||||
int i = identifier.indexOf(':');
|
||||
if (i >= 0) {
|
||||
return identifier.substring(i + 1);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
|
||||
private TrimRecipe() {
|
||||
|
|
|
@ -33,6 +33,8 @@ import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
|||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||
import org.geysermc.geyser.item.ArmorMaterial;
|
||||
import org.geysermc.geyser.registry.type.ItemMapping;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
@ -57,14 +59,13 @@ public class ArmorItem extends Item {
|
|||
return;
|
||||
}
|
||||
|
||||
// TODO material IDs
|
||||
String material = trim.material().custom().assetName();
|
||||
String pattern = trim.pattern().custom().assetId();
|
||||
TrimMaterial material = session.getRegistryCache().trimMaterials().get(trim.material().id());
|
||||
TrimPattern pattern = session.getRegistryCache().trimPatterns().get(trim.pattern().id());
|
||||
|
||||
NbtMapBuilder trimBuilder = NbtMap.builder();
|
||||
// bedrock has an uppercase first letter key, and the value is not namespaced
|
||||
trimBuilder.put("Material", stripNamespace(material));
|
||||
trimBuilder.put("Pattern", stripNamespace(pattern));
|
||||
trimBuilder.put("Material", material.getMaterialId());
|
||||
trimBuilder.put("Pattern", pattern.getPatternId());
|
||||
builder.putCompound("Trim", trimBuilder.build());
|
||||
}
|
||||
}
|
||||
|
@ -86,12 +87,4 @@ public class ArmorItem extends Item {
|
|||
public boolean isValidRepairItem(Item other) {
|
||||
return material.getRepairIngredient() == other;
|
||||
}
|
||||
|
||||
private static String stripNamespace(String identifier) {
|
||||
int i = identifier.indexOf(':');
|
||||
if (i >= 0) {
|
||||
return identifier.substring(i + 1);
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,4 +55,18 @@ public record JavaDimension(int minY, int maxY, boolean piglinSafe, double world
|
|||
map.put(i, new JavaDimension(minY, maxY, piglinSafe, coordinateScale));
|
||||
}
|
||||
}
|
||||
|
||||
public static JavaDimension read(RegistryEntry entry) {
|
||||
CompoundTag dimension = entry.getData();
|
||||
int minY = ((IntTag) dimension.get("min_y")).getValue();
|
||||
int maxY = ((IntTag) dimension.get("height")).getValue();
|
||||
// Logical height can be ignored probably - seems to be for artificial limits like the Nether.
|
||||
|
||||
// Set if piglins/hoglins should shake
|
||||
boolean piglinSafe = ((Number) dimension.get("piglin_safe").getValue()).byteValue() != (byte) 0;
|
||||
// Load world coordinate scale for the world border
|
||||
double coordinateScale = ((Number) dimension.get("coordinate_scale").getValue()).doubleValue();
|
||||
|
||||
return new JavaDimension(minY, maxY, piglinSafe, coordinateScale);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,6 @@ import com.github.steveice10.mc.protocol.data.game.statistic.CustomStatistic;
|
|||
import com.github.steveice10.mc.protocol.data.game.statistic.Statistic;
|
||||
import com.github.steveice10.mc.protocol.packet.common.serverbound.ServerboundClientInformationPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.handshake.serverbound.ClientIntentionPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatCommandSignedPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.ServerboundChatPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosPacket;
|
||||
|
@ -54,11 +53,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.serverbound.player.Server
|
|||
import com.github.steveice10.mc.protocol.packet.login.serverbound.ServerboundCustomQueryAnswerPacket;
|
||||
import com.github.steveice10.packetlib.BuiltinFlags;
|
||||
import com.github.steveice10.packetlib.Session;
|
||||
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
|
||||
import com.github.steveice10.packetlib.event.session.PacketErrorEvent;
|
||||
import com.github.steveice10.packetlib.event.session.PacketSendingEvent;
|
||||
import com.github.steveice10.packetlib.event.session.SessionAdapter;
|
||||
import com.github.steveice10.packetlib.event.session.*;
|
||||
import com.github.steveice10.packetlib.packet.Packet;
|
||||
import com.github.steveice10.packetlib.tcp.TcpClientSession;
|
||||
import com.github.steveice10.packetlib.tcp.TcpSession;
|
||||
|
@ -66,8 +61,6 @@ import io.netty.channel.Channel;
|
|||
import io.netty.channel.EventLoop;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
|
@ -146,7 +139,6 @@ import org.geysermc.geyser.session.cache.*;
|
|||
import org.geysermc.geyser.skin.FloodgateSkinUploader;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.inventory.InventoryTranslator;
|
||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||
import org.geysermc.geyser.util.ChunkUtils;
|
||||
|
@ -158,16 +150,7 @@ import java.net.ConnectException;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
@ -214,6 +197,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
private final LodestoneCache lodestoneCache;
|
||||
private final PistonCache pistonCache;
|
||||
private final PreferencesCache preferencesCache;
|
||||
private final RegistryCache registryCache;
|
||||
private final SkullCache skullCache;
|
||||
private final StructureBlockCache structureBlockCache;
|
||||
private final TagCache tagCache;
|
||||
|
@ -263,12 +247,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
@Setter
|
||||
private ItemMappings itemMappings;
|
||||
|
||||
/**
|
||||
* Stores the map between Java and Bedrock biome network IDs.
|
||||
*/
|
||||
@Setter
|
||||
private int[] biomeTranslations = null;
|
||||
|
||||
/**
|
||||
* A map of Vector3i positions to Java entities.
|
||||
* Used for translating Bedrock block actions to Java entity actions.
|
||||
|
@ -360,12 +338,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
@MonotonicNonNull
|
||||
@Setter
|
||||
private JavaDimension dimensionType = null;
|
||||
/**
|
||||
* All dimensions that the client could possibly connect to.
|
||||
*/
|
||||
private final Int2ObjectMap<JavaDimension> dimensions = new Int2ObjectOpenHashMap<>(4);
|
||||
|
||||
private final Int2ObjectMap<TextDecoration> chatTypes = new Int2ObjectOpenHashMap<>(7);
|
||||
|
||||
@Setter
|
||||
private int breakingBlock;
|
||||
|
@ -619,6 +591,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
this.lodestoneCache = new LodestoneCache();
|
||||
this.pistonCache = new PistonCache(this);
|
||||
this.preferencesCache = new PreferencesCache(this);
|
||||
this.registryCache = new RegistryCache(this);
|
||||
this.skullCache = new SkullCache(this);
|
||||
this.structureBlockCache = new StructureBlockCache();
|
||||
this.tagCache = new TagCache();
|
||||
|
|
153
core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java
vendored
Normal file
153
core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java
vendored
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.RegistryEntry;
|
||||
import com.github.steveice10.mc.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimMaterial;
|
||||
import org.cloudburstmc.protocol.bedrock.data.TrimPattern;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.inventory.recipe.TrimRecipe;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
/**
|
||||
* Stores any information sent via Java registries. May not contain all data in a given registry - we'll strip what's
|
||||
* unneeded.
|
||||
*
|
||||
* Crafted as of 1.20.5 for easy "add new registry" in the future.
|
||||
*/
|
||||
@Accessors(fluent = true)
|
||||
@Getter
|
||||
public final class RegistryCache {
|
||||
private static final Map<String, BiConsumer<RegistryCache, List<RegistryEntry>>> REGISTRIES = new HashMap<>();
|
||||
|
||||
static {
|
||||
register("chat_type", cache -> cache.chatTypes, ($, entry) -> TextDecoration.readChatType(entry));
|
||||
register("dimension_type", cache -> cache.dimensions, ($, entry) -> JavaDimension.read(entry));
|
||||
register("trim_material", cache -> cache.trimMaterials, TrimRecipe::readTrimMaterial);
|
||||
register("trim_pattern", cache -> cache.trimPatterns, TrimRecipe::readTrimPattern);
|
||||
register("worldgen/biome", (cache, array) -> cache.biomeTranslations = array, BiomeTranslator::loadServerBiome);
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.NONE)
|
||||
private final GeyserSession session;
|
||||
|
||||
/**
|
||||
* Java -> Bedrock biome network IDs.
|
||||
*/
|
||||
private int[] biomeTranslations;
|
||||
private final Int2ObjectMap<TextDecoration> chatTypes = new Int2ObjectOpenHashMap<>(7);
|
||||
/**
|
||||
* All dimensions that the client could possibly connect to.
|
||||
*/
|
||||
private final Int2ObjectMap<JavaDimension> dimensions = new Int2ObjectOpenHashMap<>(4);
|
||||
private final Int2ObjectMap<TrimMaterial> trimMaterials = new Int2ObjectOpenHashMap<>();
|
||||
private final Int2ObjectMap<TrimPattern> trimPatterns = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public RegistryCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a registry in, if we are tracking it.
|
||||
*/
|
||||
public void load(ClientboundRegistryDataPacket packet) {
|
||||
var reader = REGISTRIES.get(packet.getRegistry());
|
||||
if (reader != null) {
|
||||
reader.accept(this, packet.getEntries());
|
||||
} else {
|
||||
GeyserImpl.getInstance().getLogger().debug("Ignoring registry of type " + packet.getRegistry());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registry the Java registry resource location, without the "minecraft:" prefix.
|
||||
* @param localCacheFunction which local field in RegistryCache are we caching entries for this registry?
|
||||
* @param reader converts the RegistryEntry NBT into a class file
|
||||
* @param <T> the class that represents these entries.
|
||||
*/
|
||||
private static <T> void register(String registry, Function<RegistryCache, Int2ObjectMap<T>> localCacheFunction, BiFunction<GeyserSession, RegistryEntry, T> reader) {
|
||||
REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> {
|
||||
Int2ObjectMap<T> localCache = localCacheFunction.apply(registryCache);
|
||||
// Clear each local cache every time a new registry entry is given to us
|
||||
localCache.clear();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
T cacheEntry = reader.apply(registryCache.session, entry);
|
||||
localCache.put(i, cacheEntry);
|
||||
}
|
||||
// Trim registry down to needed size
|
||||
if (localCache instanceof Int2ObjectOpenHashMap<T> hashMap) {
|
||||
hashMap.trim();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localCacheFunction the int array to set the final values to.
|
||||
*/
|
||||
private static void register(String registry, BiConsumer<RegistryCache, int[]> localCacheFunction, ToIntFunction<RegistryEntry> reader) {
|
||||
REGISTRIES.put("minecraft:" + registry, (registryCache, entries) -> {
|
||||
Int2IntMap temp = new Int2IntOpenHashMap();
|
||||
int greatestId = 0;
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
// This is what Geyser wants to keep as a value for this registry.
|
||||
int cacheEntry = reader.applyAsInt(entry);
|
||||
temp.put(i, cacheEntry);
|
||||
if (i > greatestId) {
|
||||
// Maximum registry ID, so far. Make sure the final array is at least this large.
|
||||
greatestId = i;
|
||||
}
|
||||
}
|
||||
|
||||
int[] array = new int[greatestId + 1];
|
||||
for (Int2IntMap.Entry entry : temp.int2IntEntrySet()) {
|
||||
array[entry.getIntKey()] = entry.getIntValue();
|
||||
}
|
||||
localCacheFunction.accept(registryCache, array);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
package org.geysermc.geyser.text;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.RegistryEntry;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.StringTag;
|
||||
|
@ -87,6 +88,17 @@ public final class TextDecoration {
|
|||
'}';
|
||||
}
|
||||
|
||||
public static TextDecoration readChatType(RegistryEntry entry) {
|
||||
// Note: The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
|
||||
CompoundTag tag = entry.getData();
|
||||
CompoundTag chat = tag.get("chat");
|
||||
TextDecoration textDecoration = null;
|
||||
if (chat != null) {
|
||||
textDecoration = new TextDecoration(chat);
|
||||
}
|
||||
return textDecoration;
|
||||
}
|
||||
|
||||
public enum Parameter {
|
||||
CONTENT,
|
||||
SENDER,
|
||||
|
|
|
@ -31,7 +31,9 @@ import com.github.steveice10.mc.protocol.data.game.chunk.DataPalette;
|
|||
import com.github.steveice10.mc.protocol.data.game.chunk.palette.GlobalPalette;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.palette.Palette;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.palette.SingletonPalette;
|
||||
import it.unimi.dsi.fastutil.ints.*;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.ints.IntLists;
|
||||
import org.geysermc.geyser.level.chunk.BlockStorage;
|
||||
import org.geysermc.geyser.level.chunk.bitarray.BitArray;
|
||||
import org.geysermc.geyser.level.chunk.bitarray.BitArrayVersion;
|
||||
|
@ -39,58 +41,20 @@ import org.geysermc.geyser.level.chunk.bitarray.SingletonBitArray;
|
|||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// Array index formula by https://wiki.vg/Chunk_Format
|
||||
public class BiomeTranslator {
|
||||
|
||||
public static void loadServerBiomes(GeyserSession session, List<RegistryEntry> entries) {
|
||||
Int2IntMap biomeTranslations = new Int2IntOpenHashMap();
|
||||
|
||||
int greatestBiomeId = 0;
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
RegistryEntry entry = entries.get(i);
|
||||
String javaIdentifier = entry.getId();
|
||||
int bedrockId = Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, 0);
|
||||
int javaId = i;
|
||||
if (javaId > greatestBiomeId) {
|
||||
greatestBiomeId = javaId;
|
||||
}
|
||||
|
||||
// TODO - the category tag no longer exists - find a better replacement option
|
||||
// if (bedrockId == -1) {
|
||||
// // There is no matching Bedrock variation for this biome; let's set the closest match based on biome category
|
||||
// String category = ((StringTag) ((CompoundTag) biomeTag.get("element")).get("category")).getValue();
|
||||
// String replacementBiome = switch (category) {
|
||||
// case "extreme_hills" -> "minecraft:mountains";
|
||||
// case "icy" -> "minecraft:ice_spikes";
|
||||
// case "mesa" -> "minecraft:badlands";
|
||||
// case "mushroom" -> "minecraft:mushroom_fields";
|
||||
// case "nether" -> "minecraft:nether_wastes";
|
||||
// default -> "minecraft:ocean"; // Typically ID 0 so a good default
|
||||
// case "taiga", "jungle", "plains", "savanna", "the_end", "beach", "ocean", "desert", "river", "swamp" -> "minecraft:" + category;
|
||||
// };
|
||||
// bedrockId = Registries.BIOME_IDENTIFIERS.get().getInt(replacementBiome);
|
||||
// }
|
||||
|
||||
// When we see the Java ID, we should instead apply the Bedrock ID
|
||||
biomeTranslations.put(javaId, bedrockId);
|
||||
|
||||
if (javaId == 0) {
|
||||
// Matches Java behavior when it sees an invalid biome - it just replaces it with ID 0
|
||||
biomeTranslations.defaultReturnValue(bedrockId);
|
||||
}
|
||||
}
|
||||
|
||||
int[] biomes = new int[greatestBiomeId + 1];
|
||||
for (Int2IntMap.Entry entry : biomeTranslations.int2IntEntrySet()) {
|
||||
biomes[entry.getIntKey()] = entry.getIntValue();
|
||||
}
|
||||
session.setBiomeTranslations(biomes);
|
||||
public static int loadServerBiome(RegistryEntry entry) {
|
||||
String javaIdentifier = entry.getId();
|
||||
return Registries.BIOME_IDENTIFIERS.get().getOrDefault(javaIdentifier, 0);
|
||||
// if (javaId == 0) {
|
||||
// // Matches Java behavior when it sees an invalid biome - it just replaces it with ID 0
|
||||
// biomeTranslations.defaultReturnValue(bedrockId);
|
||||
// }
|
||||
}
|
||||
|
||||
public static BlockStorage toNewBedrockBiome(GeyserSession session, DataPalette biomeData) {
|
||||
int[] biomeTranslations = session.getBiomeTranslations();
|
||||
int[] biomeTranslations = session.getRegistryCache().biomeTranslations();
|
||||
// As of 1.17.10: the client expects the same format as a chunk but filled with biomes
|
||||
// As of 1.18 this is the same as Java Edition
|
||||
|
||||
|
|
|
@ -25,49 +25,16 @@
|
|||
|
||||
package org.geysermc.geyser.translator.protocol.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.RegistryEntry;
|
||||
import com.github.steveice10.mc.protocol.packet.configuration.clientbound.ClientboundRegistryDataPacket;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import org.geysermc.geyser.level.JavaDimension;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.text.TextDecoration;
|
||||
import org.geysermc.geyser.translator.level.BiomeTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Translator(packet = ClientboundRegistryDataPacket.class)
|
||||
public class JavaRegistryDataTranslator extends PacketTranslator<ClientboundRegistryDataPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(GeyserSession session, ClientboundRegistryDataPacket packet) {
|
||||
if (packet.getRegistry().equals("minecraft:dimension_type")) {
|
||||
Int2ObjectMap<JavaDimension> dimensions = session.getDimensions();
|
||||
dimensions.clear();
|
||||
JavaDimension.load(packet.getEntries(), dimensions);
|
||||
}
|
||||
|
||||
if (packet.getRegistry().equals("minecraft:chat_type")) {
|
||||
Int2ObjectMap<TextDecoration> chatTypes = session.getChatTypes();
|
||||
chatTypes.clear();
|
||||
List<RegistryEntry> entries = packet.getEntries();
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
// The ID is NOT ALWAYS THE SAME! ViaVersion as of 1.19 adds two registry entries that do NOT match vanilla.
|
||||
RegistryEntry entry = entries.get(i);
|
||||
CompoundTag tag = entry.getData();
|
||||
CompoundTag chat = tag.get("chat");
|
||||
TextDecoration textDecoration = null;
|
||||
if (chat != null) {
|
||||
textDecoration = new TextDecoration(chat);
|
||||
}
|
||||
chatTypes.put(i, textDecoration);
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.getRegistry().equals("minecraft:worldgen/biome")) {
|
||||
BiomeTranslator.loadServerBiomes(session, packet.getEntries());
|
||||
}
|
||||
session.getRegistryCache().load(packet);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -269,8 +269,8 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
|
|||
if (sendTrimRecipes) {
|
||||
// BDS sends armor trim templates and materials before the CraftingDataPacket
|
||||
TrimDataPacket trimDataPacket = new TrimDataPacket();
|
||||
trimDataPacket.getPatterns().addAll(TrimRecipe.PATTERNS);
|
||||
trimDataPacket.getMaterials().addAll(TrimRecipe.MATERIALS);
|
||||
trimDataPacket.getPatterns().addAll(session.getRegistryCache().trimPatterns().values());
|
||||
trimDataPacket.getMaterials().addAll(session.getRegistryCache().trimMaterials().values());
|
||||
session.sendUpstreamPacket(trimDataPacket);
|
||||
|
||||
// Identical smithing_trim recipe sent by BDS that uses tag-descriptors, as the client seems to ignore the
|
||||
|
|
|
@ -312,7 +312,7 @@ public class MessageTranslator {
|
|||
|
||||
textPacket.setNeedsTranslation(false);
|
||||
|
||||
TextDecoration decoration = session.getChatTypes().get(chatType);
|
||||
TextDecoration decoration = session.getRegistryCache().chatTypes().get(chatType);
|
||||
if (decoration != null) {
|
||||
// As of 1.19 - do this to apply all the styling for signed messages
|
||||
// Though, Bedrock cannot care about the signed stuff.
|
||||
|
|
|
@ -280,7 +280,7 @@ public class ChunkUtils {
|
|||
* This must be done after the player has switched dimensions so we know what their dimension is
|
||||
*/
|
||||
public static void loadDimension(GeyserSession session) {
|
||||
JavaDimension dimension = session.getDimensions().get(session.getDimension());
|
||||
JavaDimension dimension = session.getRegistryCache().dimensions().get(session.getDimension());
|
||||
session.setDimensionType(dimension);
|
||||
int minY = dimension.minY();
|
||||
int maxY = dimension.maxY();
|
||||
|
|
Loading…
Reference in a new issue