This commit is contained in:
AJ Ferguson 2024-04-22 22:49:46 -04:00 committed by Camotoy
parent 11f79d4e2c
commit d3f902ae34
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
7 changed files with 295 additions and 65 deletions

View file

@ -0,0 +1,104 @@
/*
* 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.inventory.item;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
@Getter
public enum BannerPattern {
BASE("b"),
SQUARE_BOTTOM_LEFT("bl"),
SQUARE_BOTTOM_RIGHT("br"),
SQUARE_TOP_LEFT("tl"),
SQUARE_TOP_RIGHT("tr"),
STRIPE_BOTTOM("bs"),
STRIPE_TOP("ts"),
STRIPE_LEFT("ls"),
STRIPE_RIGHT("rs"),
STRIPE_CENTER("cs"),
STRIPE_MIDDLE("ms"),
STRIPE_DOWNRIGHT("drs"),
STRIPE_DOWNLEFT("dls"),
SMALL_STRIPES("ss"),
CROSS("cr"),
STRAIGHT_CROSS("sc"),
TRIANGLE_BOTTOM("bt"),
TRIANGLE_TOP("tt"),
TRIANGLES_BOTTOM("bts"),
TRIANGLES_TOP("tts"),
DIAGONAL_LEFT("ld"),
DIAGONAL_UP_RIGHT("rd"),
DIAGONAL_UP_LEFT("lud"),
DIAGONAL_RIGHT("rud"),
CIRCLE("mc"),
RHOMBUS("mr"),
HALF_VERTICAL("vh"),
HALF_HORIZONTAL("hh"),
HALF_VERTICAL_RIGHT("vhr"),
HALF_HORIZONTAL_BOTTOM("hhb"),
BORDER("bo"),
CURLY_BORDER("cbo"),
GRADIENT("gra"),
GRADIENT_UP("gru"),
BRICKS("bri"),
GLOBE("glb"),
CREEPER("cre"),
SKULL("sku"),
FLOWER("flo"),
MOJANG("moj"),
PIGLIN("pig");
private static final BannerPattern[] VALUES = values();
private final String javaIdentifier;
private final String bedrockIdentifier;
BannerPattern(String bedrockIdentifier) {
this.javaIdentifier = "minecraft:" + this.name().toLowerCase(Locale.ROOT);
this.bedrockIdentifier = bedrockIdentifier;
}
public static @Nullable BannerPattern getByJavaIdentifier(String javaIdentifier) {
for (BannerPattern bannerPattern : VALUES) {
if (bannerPattern.javaIdentifier.equals(javaIdentifier)) {
return bannerPattern;
}
}
return null;
}
public static @Nullable BannerPattern getByBedrockIdentifier(String bedrockIdentifier) {
for (BannerPattern bannerPattern : VALUES) {
if (bannerPattern.bedrockIdentifier.equals(bedrockIdentifier)) {
return bannerPattern;
}
}
return null;
}
}

View file

@ -0,0 +1,75 @@
/*
* 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.inventory.item;
import lombok.Getter;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.Locale;
@Getter
public enum DyeColor {
WHITE,
ORANGE,
MAGENTA,
LIGHT_BLUE,
YELLOW,
LIME,
PINK,
GRAY,
LIGHT_GRAY,
CYAN,
PURPLE,
BLUE,
BROWN,
GREEN,
RED,
BLACK;
private static final DyeColor[] VALUES = values();
private final String javaIdentifier;
DyeColor() {
this.javaIdentifier = this.name().toLowerCase(Locale.ROOT);
}
public static @Nullable DyeColor getById(int id) {
if (id >= 0 && id < VALUES.length) {
return VALUES[id];
}
return null;
}
public static @Nullable DyeColor getByJavaIdentifier(String javaIdentifier) {
for (DyeColor dyeColor : VALUES) {
if (dyeColor.javaIdentifier.equals(javaIdentifier)) {
return dyeColor;
}
}
return null;
}
}

View file

@ -28,14 +28,14 @@ package org.geysermc.geyser.item.type;
import com.github.steveice10.mc.protocol.data.game.item.component.BannerPatternLayer;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponentType;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.github.steveice10.opennbt.tag.builtin.*;
import it.unimi.dsi.fastutil.Pair;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.nbt.NbtList;
import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.geyser.inventory.item.BannerPattern;
import org.geysermc.geyser.inventory.item.DyeColor;
import org.geysermc.geyser.registry.type.ItemMapping;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.item.BedrockItemBuilder;
@ -51,20 +51,48 @@ public class BannerItem extends BlockItem {
* ominous banners that we set instead. This variable is used to detect Java ominous banner patterns, and apply
* the correct ominous banner pattern if Bedrock pulls the item from creative.
*/
public static final List<BannerPatternLayer> OMINOUS_BANNER_PATTERN;
private static final List<Pair<BannerPattern, DyeColor>> OMINOUS_BANNER_PATTERN;
private static final ListTag OMINOUS_BANNER_PATTERN_BLOCK;
static {
// Construct what an ominous banner is supposed to look like
OMINOUS_BANNER_PATTERN = List.of(
// new BannerPatternLayer("mr", 9),
// new BannerPatternLayer("bs", 8),
// new BannerPatternLayer("cs", 7),
// new BannerPatternLayer("bo", 8),
// new BannerPatternLayer("ms", 15),
// new BannerPatternLayer("hh", 8),
// new BannerPatternLayer("mc", 8),
// new BannerPatternLayer("bo", 15)
Pair.of(BannerPattern.RHOMBUS, DyeColor.CYAN),
Pair.of(BannerPattern.STRIPE_BOTTOM, DyeColor.LIGHT_GRAY),
Pair.of(BannerPattern.STRIPE_CENTER, DyeColor.GRAY),
Pair.of(BannerPattern.BORDER, DyeColor.LIGHT_GRAY),
Pair.of(BannerPattern.STRIPE_MIDDLE, DyeColor.BLACK),
Pair.of(BannerPattern.HALF_HORIZONTAL, DyeColor.LIGHT_GRAY),
Pair.of(BannerPattern.CIRCLE, DyeColor.LIGHT_GRAY),
Pair.of(BannerPattern.BORDER, DyeColor.BLACK)
);
OMINOUS_BANNER_PATTERN_BLOCK = new ListTag("patterns");
for (Pair<BannerPattern, DyeColor> pair : OMINOUS_BANNER_PATTERN) {
OMINOUS_BANNER_PATTERN_BLOCK.add(getJavaBannerPatternTag(pair.left(), pair.right()));
}
}
public static boolean isOminous(GeyserSession session, List<BannerPatternLayer> patternLayers) {
if (OMINOUS_BANNER_PATTERN.size() != patternLayers.size()) {
return false;
}
for (int i = 0; i < OMINOUS_BANNER_PATTERN.size(); i++) {
BannerPatternLayer patternLayer = patternLayers.get(i);
Pair<BannerPattern, DyeColor> pair = OMINOUS_BANNER_PATTERN.get(i);
if (!patternLayer.getPattern().isId() || patternLayer.getColorId() != pair.right().ordinal()) {
return false;
}
BannerPattern bannerPattern = session.getRegistryCache().bannerPatterns().get(patternLayer.getPattern().id());
if (bannerPattern != pair.left()) {
return false;
}
}
return true;
}
public static boolean isOminous(ListTag blockEntityPatterns) {
return OMINOUS_BANNER_PATTERN_BLOCK.equals(blockEntityPatterns);
}
/**
@ -76,7 +104,10 @@ public class BannerItem extends BlockItem {
public static NbtList<NbtMap> convertBannerPattern(ListTag patterns) {
List<NbtMap> tagsList = new ArrayList<>();
for (Tag patternTag : patterns.getValue()) {
tagsList.add(getBedrockBannerPattern((CompoundTag) patternTag));
NbtMap bedrockBannerPattern = getBedrockBannerPattern((CompoundTag) patternTag);
if (bedrockBannerPattern != null) {
tagsList.add(bedrockBannerPattern);
}
}
return new NbtList<>(NbtType.COMPOUND, tagsList);
@ -88,35 +119,41 @@ public class BannerItem extends BlockItem {
* @param pattern Java edition pattern nbt
* @return The Bedrock edition format pattern nbt
*/
@NonNull
private static NbtMap getBedrockBannerPattern(CompoundTag pattern) {
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier((String) pattern.get("pattern").getValue());
DyeColor dyeColor = DyeColor.getByJavaIdentifier((String) pattern.get("color").getValue());
if (bannerPattern == null || dyeColor == null) {
return null;
}
return NbtMap.builder()
.putInt("Color", 15 - (int) pattern.get("Color").getValue())
.putString("Pattern", (String) pattern.get("Pattern").getValue())
.putString("Pattern", bannerPattern.getBedrockIdentifier())
.putInt("Color", 15 - dyeColor.ordinal())
.build();
}
public static CompoundTag getJavaBannerPatternTag(BannerPattern bannerPattern, DyeColor dyeColor) {
CompoundTag tag = new CompoundTag("");
tag.put(new StringTag("pattern", bannerPattern.getJavaIdentifier()));
tag.put(new StringTag("color", dyeColor.getJavaIdentifier()));
return tag;
}
/**
* Convert the Bedrock edition banner pattern nbt to Java edition
*
* @param pattern Bedrock edition pattern nbt
* @return The Java edition format pattern nbt
* @return The Java edition format pattern layer
*/
public static CompoundTag getJavaBannerPattern(NbtMap pattern) {
//return new BannerPatternLayer(0/*pattern.getString("Pattern")*/, 15 - pattern.getInt("Color"));
return null;
}
/**
* Convert a list of patterns from Java nbt to Bedrock nbt, or vice versa (we just need to invert the color)
*
* @param patterns The patterns to convert
*/
private void invertBannerColors(ListTag patterns) {
for (Tag patternTag : patterns.getValue()) {
IntTag color = ((CompoundTag) patternTag).get("Color");
color.setValue(15 - color.getValue());
public static BannerPatternLayer getJavaBannerPattern(GeyserSession session, NbtMap pattern) {
return null; // TODO
/*Int2ObjectBiMap<BannerPattern> registry = session.getRegistryCache().bannerPatterns();
BannerPattern bannerPattern = BannerPattern.getByBedrockIdentifier(pattern.getString("Pattern"));
DyeColor dyeColor = DyeColor.getById(15 - pattern.getInt("Color"));
if (bannerPattern != null && dyeColor != null && registry.containsValue(bannerPattern)) {
return new BannerPatternLayer(Holder.ofId(registry.get(bannerPattern)), dyeColor.ordinal());
}
return null;*/
}
public BannerItem(String javaIdentifier, Builder builder) {
@ -129,31 +166,51 @@ public class BannerItem extends BlockItem {
List<BannerPatternLayer> patterns = components.get(DataComponentType.BANNER_PATTERNS);
if (patterns != null) {
// if (patterns.equals(OMINOUS_BANNER_PATTERN)) {
// // Remove the current patterns and set the ominous banner type
// builder.putInt("Type", 1);
// } else {
// invertBannerColors(patterns);
// tag.put(patterns);
// }
if (isOminous(session, patterns)) {
// Remove the current patterns and set the ominous banner type
builder.putInt("Type", 1);
} else {
List<NbtMap> patternList = new ArrayList<>(patterns.size());
for (BannerPatternLayer patternLayer : patterns) {
patternLayer.getPattern().ifId(holder -> {
BannerPattern bannerPattern = session.getRegistryCache().bannerPatterns().get(holder.id());
if (bannerPattern != null) {
NbtMap tag = NbtMap.builder()
.putString("Pattern", bannerPattern.getBedrockIdentifier())
.putInt("Color", 15 - patternLayer.getColorId())
.build();
patternList.add(tag);
}
});
}
builder.putList("Patterns", NbtType.COMPOUND, patternList);
}
}
}
@Override
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) {
public void translateNbtToJava(@NonNull CompoundTag tag, @NonNull ItemMapping mapping) { // TODO
super.translateNbtToJava(tag, mapping);
if (tag.get("Type") instanceof IntTag type && type.getValue() == 1) {
// Ominous banner pattern
tag.remove("Type");
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
//blockEntityTag.put(OMINOUS_BANNER_PATTERN);
blockEntityTag.put(OMINOUS_BANNER_PATTERN_BLOCK);
tag.put(blockEntityTag);
} else if (tag.get("Patterns") instanceof ListTag patterns) {
} else if (tag.get("Patterns") instanceof ListTag patterns && patterns.getElementType() == CompoundTag.class) {
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
invertBannerColors(patterns);
blockEntityTag.put(patterns);
ListTag javaPatterns = new ListTag("patterns");
for (Tag pattern : patterns.getValue()) {
BannerPattern bannerPattern = BannerPattern.getByBedrockIdentifier((String) ((CompoundTag) pattern).get("Pattern").getValue());
DyeColor dyeColor = DyeColor.getById((int) ((CompoundTag) pattern).get("Color").getValue());
if (bannerPattern != null && dyeColor != null) {
javaPatterns.add(getJavaBannerPatternTag(bannerPattern, dyeColor));
}
}
blockEntityTag.put(javaPatterns);
tag.put(blockEntityTag);
tag.remove("Patterns"); // Remove the old Bedrock patterns list

View file

@ -37,6 +37,7 @@ 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.item.BannerPattern;
import org.geysermc.geyser.inventory.recipe.TrimRecipe;
import org.geysermc.geyser.level.JavaDimension;
import org.geysermc.geyser.session.GeyserSession;
@ -68,6 +69,7 @@ public final class RegistryCache {
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);
register("banner_pattern", cache -> cache.bannerPatterns, ($, entry) -> BannerPattern.getByJavaIdentifier(entry.getId()));
}
@Getter(AccessLevel.NONE)
@ -85,6 +87,8 @@ public final class RegistryCache {
private final Int2ObjectMap<TrimMaterial> trimMaterials = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<TrimPattern> trimPatterns = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<BannerPattern> bannerPatterns = new Int2ObjectOpenHashMap<>();
public RegistryCache(GeyserSession session) {
this.session = session;
}

View file

@ -25,10 +25,10 @@
package org.geysermc.geyser.translator.inventory;
import com.github.steveice10.mc.protocol.data.game.item.component.BannerPatternLayer;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponentType;
import com.github.steveice10.mc.protocol.data.game.item.component.DataComponents;
import com.github.steveice10.mc.protocol.packet.ingame.serverbound.inventory.ServerboundContainerButtonClickPacket;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.cloudburstmc.nbt.NbtMap;
@ -51,7 +51,7 @@ import org.geysermc.geyser.item.type.BannerItem;
import org.geysermc.geyser.item.type.DyeItem;
import org.geysermc.geyser.session.GeyserSession;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -159,23 +159,12 @@ public class LoomInventoryTranslator extends AbstractBlockInventoryTranslator {
if (inputCopy.getComponents() == null) {
inputCopy.setComponents(new DataComponents(new HashMap<>()));
}
//TODO
CompoundTag blockEntityTag = inputCopy.getNbt().get("BlockEntityTag");
CompoundTag javaBannerPattern = BannerItem.getJavaBannerPattern(pattern);
if (blockEntityTag != null) {
ListTag patternsList = blockEntityTag.get("Patterns");
if (patternsList != null) {
patternsList.add(javaBannerPattern);
} else {
patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern));
blockEntityTag.put(patternsList);
}
} else {
blockEntityTag = new CompoundTag("BlockEntityTag");
ListTag patternsList = new ListTag("Patterns", Collections.singletonList(javaBannerPattern));
blockEntityTag.put(patternsList);
inputCopy.getNbt().put(blockEntityTag);
BannerPatternLayer bannerPatternLayer = BannerItem.getJavaBannerPattern(session, pattern); // TODO
if (bannerPatternLayer != null) {
List<BannerPatternLayer> patternsList = inputCopy.getComponents().getOrDefault(DataComponentType.BANNER_PATTERNS, new ArrayList<>());
patternsList.add(bannerPatternLayer);
inputCopy.getComponents().put(DataComponentType.BANNER_PATTERNS, patternsList);
}
// Set the new item as the output

View file

@ -46,8 +46,8 @@ public class BannerBlockEntityTranslator extends BlockEntityTranslator implement
return;
}
if (tag.get("Patterns") instanceof ListTag patterns) {
if (patterns.equals(BannerItem.OMINOUS_BANNER_PATTERN)) {
if (tag.get("patterns") instanceof ListTag patterns) {
if (BannerItem.isOminous(patterns)) {
// This is an ominous banner; don't try to translate the raw patterns (it doesn't translate correctly)
// and tell the Bedrock client that this is an ominous banner
builder.putInt("Type", 1);

View file

@ -105,6 +105,7 @@ public class BedrockBookEditTranslator extends PacketTranslator<BookEditPacket>
break;
}
case SIGN_BOOK: {
// As of JE 1.20.5, client no longer adds title and author on its own
break;
}
default: