Geyser/core/src/main/java/org/geysermc/geyser/item/type/BannerItem.java

203 lines
9.0 KiB
Java

/*
* Copyright (c) 2019-2023 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.item.type;
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;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType;
import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents;
import java.util.ArrayList;
import java.util.List;
public class BannerItem extends BlockItem {
/**
* Holds what a Java ominous banner pattern looks like.
* <p>
* Translating the patterns over to Bedrock does not work effectively, but Bedrock has a dedicated type for
* 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.
*/
private static final List<Pair<BannerPattern, DyeColor>> OMINOUS_BANNER_PATTERN;
private static final List<NbtMap> OMINOUS_BANNER_PATTERN_BLOCK;
static {
// Construct what an ominous banner is supposed to look like
OMINOUS_BANNER_PATTERN = List.of(
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 ArrayList<>();
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.getColorId() != pair.right().ordinal() ||
!patternLayer.getPattern().isId()) {
return false;
}
BannerPattern bannerPattern = session.getRegistryCache().bannerPatterns().get(patternLayer.getPattern().id());
if (bannerPattern != pair.left()) {
return false;
}
}
return true;
}
public static boolean isOminous(List<NbtMap> blockEntityPatterns) {
return OMINOUS_BANNER_PATTERN_BLOCK.equals(blockEntityPatterns);
}
/**
* Convert a list of patterns from Java nbt to Bedrock nbt
*
* @param patterns The patterns to convert
* @return The new converted patterns
*/
public static NbtList<NbtMap> convertBannerPattern(List<NbtMap> patterns) {
List<NbtMap> tagsList = new ArrayList<>();
for (NbtMap patternTag : patterns) {
NbtMap bedrockBannerPattern = getBedrockBannerPattern(patternTag);
if (bedrockBannerPattern != null) {
tagsList.add(bedrockBannerPattern);
}
}
return new NbtList<>(NbtType.COMPOUND, tagsList);
}
/**
* Convert the Java edition banner pattern nbt to Bedrock edition, null if the pattern doesn't exist
*
* @param pattern Java edition pattern nbt
* @return The Bedrock edition format pattern nbt
*/
private static NbtMap getBedrockBannerPattern(NbtMap pattern) {
BannerPattern bannerPattern = BannerPattern.getByJavaIdentifier(pattern.getString("pattern"));
DyeColor dyeColor = DyeColor.getByJavaIdentifier(pattern.getString("color"));
if (bannerPattern == null || dyeColor == null) {
return null;
}
return NbtMap.builder()
.putString("Pattern", bannerPattern.getBedrockIdentifier())
.putInt("Color", 15 - dyeColor.ordinal())
.build();
}
public static NbtMap getJavaBannerPatternTag(BannerPattern bannerPattern, DyeColor dyeColor) {
return NbtMap.builder()
.putString("pattern", bannerPattern.getJavaIdentifier())
.putString("color", dyeColor.getJavaIdentifier())
.build();
}
/**
* Convert the Bedrock edition banner pattern nbt to Java edition
*
* @param pattern Bedrock edition pattern nbt
* @return The Java edition format pattern layer
*/
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) {
super(javaIdentifier, builder);
}
@Override
public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) {
super.translateComponentsToBedrock(session, components, builder);
List<BannerPatternLayer> patterns = components.get(DataComponentType.BANNER_PATTERNS);
if (patterns != null) {
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 NbtMap bedrockTag, @NonNull DataComponents components, @NonNull ItemMapping mapping) { // TODO
super.translateNbtToJava(bedrockTag, components, mapping);
if (bedrockTag.getInt("Type") == 1) {
// Ominous banner pattern
// TODO more registry stuff
//components.put(DataComponentType.BANNER_PATTERNS);
}
// Bedrock's creative inventory does not support other patterns as of 1.20.5
}
}