2019-07-29 22:20:48 +00:00
/ *
2022-01-01 19:03:05 +00:00
* Copyright ( c ) 2019 - 2022 GeyserMC . http : //geysermc.org
2019-07-29 22:20:48 +00:00
*
2020-07-30 20:10:15 +00:00
* 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 :
2019-07-29 22:20:48 +00:00
*
2020-07-30 20:10:15 +00:00
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
2019-07-29 22:20:48 +00:00
*
2020-07-30 20:10:15 +00:00
* 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 .
2019-07-29 22:20:48 +00:00
*
2020-07-30 20:10:15 +00:00
* @author GeyserMC
* @link https : //github.com/GeyserMC/Geyser
2019-07-29 22:20:48 +00:00
* /
2021-11-20 23:29:46 +00:00
package org.geysermc.geyser.translator.inventory.item ;
2019-07-29 22:20:48 +00:00
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack ;
2020-07-06 22:36:04 +00:00
import com.github.steveice10.opennbt.tag.builtin.* ;
2020-07-05 20:58:43 +00:00
import com.nukkitx.nbt.NbtList ;
import com.nukkitx.nbt.NbtMap ;
import com.nukkitx.nbt.NbtMapBuilder ;
import com.nukkitx.nbt.NbtType ;
2020-06-23 00:11:09 +00:00
import com.nukkitx.protocol.bedrock.data.inventory.ItemData ;
2020-04-22 21:26:16 +00:00
import it.unimi.dsi.fastutil.ints.Int2ObjectMap ;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap ;
2022-07-02 16:50:16 +00:00
import it.unimi.dsi.fastutil.objects.Object2IntMap ;
2021-12-19 20:54:31 +00:00
import net.kyori.adventure.text.Component ;
import net.kyori.adventure.text.format.NamedTextColor ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.GeyserImpl ;
2022-07-02 16:50:16 +00:00
import org.geysermc.geyser.api.item.custom.CustomItemOptions ;
import org.geysermc.geyser.api.util.TriState ;
2022-01-30 16:05:29 +00:00
import org.geysermc.geyser.inventory.GeyserItemStack ;
2021-11-20 21:34:30 +00:00
import org.geysermc.geyser.registry.BlockRegistries ;
import org.geysermc.geyser.registry.type.ItemMapping ;
import org.geysermc.geyser.registry.type.ItemMappings ;
2021-12-19 20:54:31 +00:00
import org.geysermc.geyser.session.GeyserSession ;
2021-11-20 23:29:46 +00:00
import org.geysermc.geyser.text.MinecraftLocale ;
2021-12-19 20:54:31 +00:00
import org.geysermc.geyser.translator.text.MessageTranslator ;
import org.geysermc.geyser.util.FileUtils ;
2019-07-29 22:20:48 +00:00
2021-07-22 13:16:19 +00:00
import javax.annotation.Nonnull ;
2022-03-23 17:57:25 +00:00
import java.lang.reflect.InvocationTargetException ;
2020-06-15 18:24:52 +00:00
import java.util.* ;
2020-04-22 21:26:16 +00:00
import java.util.stream.Collectors ;
2019-07-29 22:20:48 +00:00
2020-05-25 01:07:05 +00:00
public abstract class ItemTranslator {
private static final Int2ObjectMap < ItemTranslator > ITEM_STACK_TRANSLATORS = new Int2ObjectOpenHashMap < > ( ) ;
private static final List < NbtItemStackTranslator > NBT_TRANSLATORS ;
2020-04-29 15:58:29 +00:00
2020-05-25 01:07:05 +00:00
protected ItemTranslator ( ) {
}
2019-12-31 03:55:17 +00:00
2020-05-25 01:07:05 +00:00
public static void init ( ) {
// no-op
}
2019-12-31 03:55:17 +00:00
2020-05-25 01:07:05 +00:00
static {
/* Load item translators */
2020-04-22 21:26:16 +00:00
Map < NbtItemStackTranslator , Integer > loadedNbtItemTranslators = new HashMap < > ( ) ;
2021-07-17 18:36:04 +00:00
for ( Class < ? > clazz : FileUtils . getGeneratedClassesForAnnotation ( ItemRemapper . class ) ) {
2020-04-22 21:26:16 +00:00
int priority = clazz . getAnnotation ( ItemRemapper . class ) . priority ( ) ;
2021-11-20 21:34:30 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . debug ( " Found annotated item translator: " + clazz . getCanonicalName ( ) ) ;
2020-04-22 21:26:16 +00:00
try {
if ( NbtItemStackTranslator . class . isAssignableFrom ( clazz ) ) {
2022-03-23 17:57:25 +00:00
NbtItemStackTranslator nbtItemTranslator = ( NbtItemStackTranslator ) clazz . getDeclaredConstructor ( ) . newInstance ( ) ;
2020-04-22 21:26:16 +00:00
loadedNbtItemTranslators . put ( nbtItemTranslator , priority ) ;
continue ;
}
2022-03-23 17:57:25 +00:00
ItemTranslator itemStackTranslator = ( ItemTranslator ) clazz . getDeclaredConstructor ( ) . newInstance ( ) ;
2021-07-13 01:19:40 +00:00
List < ItemMapping > appliedItems = itemStackTranslator . getAppliedItems ( ) ;
for ( ItemMapping item : appliedItems ) {
2020-05-25 01:07:05 +00:00
ItemTranslator registered = ITEM_STACK_TRANSLATORS . get ( item . getJavaId ( ) ) ;
2020-04-22 21:26:16 +00:00
if ( registered ! = null ) {
2021-11-20 21:34:30 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Could not instantiate annotated item translator " +
2021-06-06 13:35:56 +00:00
clazz . getCanonicalName ( ) + " . Item translator " + registered . getClass ( ) . getCanonicalName ( ) +
" is already registered for the item " + item . getJavaIdentifier ( ) ) ;
2020-04-22 21:26:16 +00:00
continue ;
}
2020-05-25 01:07:05 +00:00
ITEM_STACK_TRANSLATORS . put ( item . getJavaId ( ) , itemStackTranslator ) ;
2020-04-22 21:26:16 +00:00
}
2022-03-23 17:57:25 +00:00
} catch ( InstantiationException | InvocationTargetException | IllegalAccessException | NoSuchMethodException e ) {
2021-11-20 21:34:30 +00:00
GeyserImpl . getInstance ( ) . getLogger ( ) . error ( " Could not instantiate annotated item translator " + clazz . getCanonicalName ( ) ) ;
2020-04-22 21:26:16 +00:00
}
}
2020-05-25 01:07:05 +00:00
NBT_TRANSLATORS = loadedNbtItemTranslators . keySet ( ) . stream ( ) . sorted ( Comparator . comparingInt ( loadedNbtItemTranslators : : get ) ) . collect ( Collectors . toList ( ) ) ;
2020-04-22 21:26:16 +00:00
}
2021-07-13 01:19:40 +00:00
/ * *
* @param mappings item mappings to use while translating . This can ' t just be a Geyser session as this method is used
* when loading recipes .
* /
public static ItemStack translateToJava ( ItemData data , ItemMappings mappings ) {
2020-04-22 21:26:16 +00:00
if ( data = = null ) {
return new ItemStack ( 0 ) ;
}
2021-07-13 01:19:40 +00:00
ItemMapping javaItem = mappings . getMapping ( data ) ;
2019-07-29 22:20:48 +00:00
2020-04-22 21:26:16 +00:00
ItemStack itemStack ;
2020-05-25 01:07:05 +00:00
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS . get ( javaItem . getJavaId ( ) ) ;
2020-04-22 21:26:16 +00:00
if ( itemStackTranslator ! = null ) {
2021-07-13 01:19:40 +00:00
itemStack = itemStackTranslator . translateToJava ( data , javaItem , mappings ) ;
2020-04-22 21:26:16 +00:00
} else {
2021-07-13 01:19:40 +00:00
itemStack = DEFAULT_TRANSLATOR . translateToJava ( data , javaItem , mappings ) ;
2020-04-22 21:26:16 +00:00
}
if ( itemStack ! = null & & itemStack . getNbt ( ) ! = null ) {
2020-05-25 01:07:05 +00:00
for ( NbtItemStackTranslator translator : NBT_TRANSLATORS ) {
2020-04-22 21:26:16 +00:00
if ( translator . acceptItem ( javaItem ) ) {
translator . translateToJava ( itemStack . getNbt ( ) , javaItem ) ;
}
2019-12-03 00:53:46 +00:00
}
2020-11-30 15:55:35 +00:00
if ( itemStack . getNbt ( ) . isEmpty ( ) ) {
2022-07-02 16:50:16 +00:00
// Otherwise, seems to cause issues with villagers accepting books, and I don't see how this will break anything else. - Camotoy
2020-11-30 15:55:35 +00:00
itemStack = new ItemStack ( itemStack . getId ( ) , itemStack . getAmount ( ) , null ) ;
}
2019-08-04 22:23:19 +00:00
}
2020-04-22 21:26:16 +00:00
return itemStack ;
2019-07-29 22:20:48 +00:00
}
2021-07-22 13:16:19 +00:00
@Nonnull
2021-11-22 19:52:26 +00:00
public static ItemData translateToBedrock ( GeyserSession session , ItemStack stack ) {
2019-07-29 22:20:48 +00:00
if ( stack = = null ) {
2020-01-31 01:05:57 +00:00
return ItemData . AIR ;
2019-07-29 22:20:48 +00:00
}
2021-07-13 01:19:40 +00:00
ItemMapping bedrockItem = session . getItemMappings ( ) . getMapping ( stack ) ;
2021-03-03 00:10:17 +00:00
if ( bedrockItem = = null ) {
2021-11-20 21:34:30 +00:00
session . getGeyser ( ) . getLogger ( ) . debug ( " No matching ItemMapping for " + stack ) ;
2021-03-03 00:10:17 +00:00
return ItemData . AIR ;
}
2020-04-22 21:26:16 +00:00
2021-03-03 00:10:17 +00:00
CompoundTag nbt = stack . getNbt ( ) ! = null ? stack . getNbt ( ) . clone ( ) : null ;
2020-06-15 18:24:52 +00:00
// This is a fallback for maps with no nbt
if ( nbt = = null & & bedrockItem . getJavaIdentifier ( ) . equals ( " minecraft:filled_map " ) ) {
2020-11-30 15:55:35 +00:00
nbt = new CompoundTag ( " " ) ;
2020-06-15 18:24:52 +00:00
nbt . put ( new IntTag ( " map " , 0 ) ) ;
}
if ( nbt ! = null ) {
2020-05-25 01:07:05 +00:00
for ( NbtItemStackTranslator translator : NBT_TRANSLATORS ) {
2020-04-22 21:26:16 +00:00
if ( translator . acceptItem ( bedrockItem ) ) {
2020-08-25 02:05:39 +00:00
translator . translateToBedrock ( session , nbt , bedrockItem ) ;
2019-12-02 09:48:27 +00:00
}
}
2019-08-04 22:23:19 +00:00
}
2020-04-04 20:23:02 +00:00
2021-04-26 18:44:16 +00:00
nbt = translateDisplayProperties ( session , nbt , bedrockItem ) ;
2021-11-26 15:49:28 +00:00
if ( session . isAdvancedTooltips ( ) ) {
2022-02-10 14:17:27 +00:00
nbt = addAdvancedTooltips ( nbt , bedrockItem , session . locale ( ) ) ;
2021-11-26 15:49:28 +00:00
}
2021-04-26 18:44:16 +00:00
ItemStack itemStack = new ItemStack ( stack . getId ( ) , stack . getAmount ( ) , nbt ) ;
2020-08-25 02:05:39 +00:00
2022-01-30 16:05:29 +00:00
ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS . getOrDefault ( bedrockItem . getJavaId ( ) , DEFAULT_TRANSLATOR ) ;
ItemData . Builder builder = itemStackTranslator . translateToBedrock ( itemStack , bedrockItem , session . getItemMappings ( ) ) ;
2021-04-06 04:14:06 +00:00
if ( bedrockItem . isBlock ( ) ) {
builder . blockRuntimeId ( bedrockItem . getBedrockBlockId ( ) ) ;
2020-04-12 00:45:31 +00:00
}
2020-06-02 20:33:37 +00:00
2022-07-02 16:50:16 +00:00
translateCustomItem ( nbt , builder , bedrockItem ) ;
2020-09-15 00:50:07 +00:00
if ( nbt ! = null ) {
// Translate the canDestroy and canPlaceOn Java NBT
ListTag canDestroy = nbt . get ( " CanDestroy " ) ;
String [ ] canBreak = new String [ 0 ] ;
ListTag canPlaceOn = nbt . get ( " CanPlaceOn " ) ;
String [ ] canPlace = new String [ 0 ] ;
2021-07-13 01:19:40 +00:00
canBreak = getCanModify ( canDestroy , canBreak ) ;
canPlace = getCanModify ( canPlaceOn , canPlace ) ;
2021-04-06 04:14:06 +00:00
builder . canBreak ( canBreak ) ;
builder . canPlace ( canPlace ) ;
2020-09-15 00:50:07 +00:00
}
2021-04-06 04:14:06 +00:00
return builder . build ( ) ;
2019-07-29 22:20:48 +00:00
}
2021-11-26 15:49:28 +00:00
private static CompoundTag addAdvancedTooltips ( CompoundTag nbt , ItemMapping mapping , String language ) {
CompoundTag newNbt = nbt ;
if ( newNbt = = null ) {
newNbt = new CompoundTag ( " nbt " ) ;
CompoundTag display = new CompoundTag ( " display " ) ;
display . put ( new ListTag ( " Lore " ) ) ;
newNbt . put ( display ) ;
}
CompoundTag compoundTag = newNbt . get ( " display " ) ;
if ( compoundTag = = null ) {
compoundTag = new CompoundTag ( " display " ) ;
}
ListTag listTag = compoundTag . get ( " Lore " ) ;
if ( listTag = = null ) {
listTag = new ListTag ( " Lore " ) ;
}
int maxDurability = mapping . getMaxDamage ( ) ;
if ( maxDurability ! = 0 ) {
2021-12-19 20:54:31 +00:00
Tag durabilityTag = newNbt . get ( " Damage " ) ;
if ( durabilityTag instanceof IntTag ) {
int durability = maxDurability - ( ( IntTag ) durabilityTag ) . getValue ( ) ;
if ( durability ! = maxDurability ) {
Component component = Component . text ( )
. resetStyle ( )
. color ( NamedTextColor . WHITE )
. append ( Component . translatable ( " item.durability " ,
Component . text ( durability ) ,
Component . text ( maxDurability ) ) )
. build ( ) ;
listTag . add ( new StringTag ( " " , MessageTranslator . convertMessage ( component , language ) ) ) ;
}
2021-11-26 15:49:28 +00:00
}
}
listTag . add ( new StringTag ( " " , " §r§8 " + mapping . getJavaIdentifier ( ) ) ) ;
if ( nbt ! = null ) {
2021-12-19 20:54:31 +00:00
Component component = Component . text ( )
. resetStyle ( )
. color ( NamedTextColor . DARK_GRAY )
. append ( Component . translatable ( " item.nbt_tags " ,
Component . text ( nbt . size ( ) ) ) )
. build ( ) ;
listTag . add ( new StringTag ( " " , MessageTranslator . convertMessage ( component , language ) ) ) ;
2021-11-26 15:49:28 +00:00
}
compoundTag . put ( listTag ) ;
newNbt . put ( compoundTag ) ;
return newNbt ;
}
2020-09-15 00:50:07 +00:00
/ * *
* Translates the Java NBT of canDestroy and canPlaceOn to its Bedrock counterparts .
* In Java , this is treated as normal NBT , but in Bedrock , these arguments are extra parts of the item data itself .
2021-03-09 17:51:48 +00:00
*
2020-09-15 00:50:07 +00:00
* @param canModifyJava the list of items in Java
* @param canModifyBedrock the empty list of items in Bedrock
* @return the new list of items in Bedrock
* /
2021-07-13 01:19:40 +00:00
private static String [ ] getCanModify ( ListTag canModifyJava , String [ ] canModifyBedrock ) {
2020-09-15 00:50:07 +00:00
if ( canModifyJava ! = null & & canModifyJava . size ( ) > 0 ) {
canModifyBedrock = new String [ canModifyJava . size ( ) ] ;
for ( int i = 0 ; i < canModifyBedrock . length ; i + + ) {
// Get the Java identifier of the block that can be placed
String block = ( ( StringTag ) canModifyJava . get ( i ) ) . getValue ( ) ;
// Sometimes this is done but it's still valid
if ( ! block . startsWith ( " minecraft: " ) ) block = " minecraft: " + block ;
// Get the Bedrock identifier of the item and replace it.
// This will unfortunately be limited - for example, beds and banners will be translated weirdly
2021-07-24 01:28:28 +00:00
canModifyBedrock [ i ] = BlockRegistries . JAVA_TO_BEDROCK_IDENTIFIERS . getOrDefault ( block , block ) . replace ( " minecraft: " , " " ) ;
2020-09-15 00:50:07 +00:00
}
}
return canModifyBedrock ;
}
2022-01-30 16:05:29 +00:00
/ * *
* Given an item stack , determine the item mapping that should be applied to Bedrock players .
* /
@Nonnull
public static ItemMapping getBedrockItemMapping ( GeyserSession session , @Nonnull GeyserItemStack itemStack ) {
if ( itemStack . isEmpty ( ) ) {
return ItemMapping . AIR ;
}
int javaId = itemStack . getJavaId ( ) ;
return ITEM_STACK_TRANSLATORS . getOrDefault ( javaId , DEFAULT_TRANSLATOR )
. getItemMapping ( javaId , itemStack . getNbt ( ) , session . getItemMappings ( ) ) ;
}
2020-05-25 01:07:05 +00:00
private static final ItemTranslator DEFAULT_TRANSLATOR = new ItemTranslator ( ) {
@Override
2021-07-13 01:19:40 +00:00
public List < ItemMapping > getAppliedItems ( ) {
2020-05-25 01:07:05 +00:00
return null ;
}
} ;
2022-01-30 16:05:29 +00:00
protected ItemData . Builder translateToBedrock ( ItemStack itemStack , ItemMapping mapping , ItemMappings mappings ) {
2020-05-25 01:07:05 +00:00
if ( itemStack = = null ) {
2021-04-06 04:14:06 +00:00
// Return, essentially, air
return ItemData . builder ( ) ;
2020-05-25 01:07:05 +00:00
}
2021-04-06 04:14:06 +00:00
ItemData . Builder builder = ItemData . builder ( )
2021-07-13 01:19:40 +00:00
. id ( mapping . getBedrockId ( ) )
. damage ( mapping . getBedrockData ( ) )
2021-04-06 04:14:06 +00:00
. count ( itemStack . getAmount ( ) ) ;
if ( itemStack . getNbt ( ) ! = null ) {
builder . tag ( this . translateNbtToBedrock ( itemStack . getNbt ( ) ) ) ;
2020-04-12 00:45:31 +00:00
}
2022-07-02 16:50:16 +00:00
CompoundTag nbt = itemStack . getNbt ( ) ;
translateCustomItem ( nbt , builder , mapping ) ;
2021-04-06 04:14:06 +00:00
return builder ;
2019-07-29 22:20:48 +00:00
}
2021-07-13 01:19:40 +00:00
public ItemStack translateToJava ( ItemData itemData , ItemMapping mapping , ItemMappings mappings ) {
2020-05-25 01:07:05 +00:00
if ( itemData = = null ) return null ;
if ( itemData . getTag ( ) = = null ) {
2021-07-13 01:19:40 +00:00
return new ItemStack ( mapping . getJavaId ( ) , itemData . getCount ( ) , new CompoundTag ( " " ) ) ;
2020-05-25 01:07:05 +00:00
}
2021-07-13 01:19:40 +00:00
return new ItemStack ( mapping . getJavaId ( ) , itemData . getCount ( ) , this . translateToJavaNBT ( " " , itemData . getTag ( ) ) ) ;
2019-07-29 22:20:48 +00:00
}
2022-03-23 20:21:04 +00:00
/ * *
* Used for initialization only and only called once .
* /
2021-07-13 01:19:40 +00:00
public abstract List < ItemMapping > getAppliedItems ( ) ;
2020-05-25 01:07:05 +00:00
2022-01-30 16:05:29 +00:00
protected ItemMapping getItemMapping ( int javaId , CompoundTag nbt , ItemMappings mappings ) {
return mappings . getMapping ( javaId ) ;
}
2022-03-23 20:21:04 +00:00
protected NbtMap translateNbtToBedrock ( CompoundTag tag ) {
2020-11-05 21:36:22 +00:00
NbtMapBuilder builder = NbtMap . builder ( ) ;
2020-05-25 01:07:05 +00:00
if ( tag . getValue ( ) ! = null & & ! tag . getValue ( ) . isEmpty ( ) ) {
for ( String str : tag . getValue ( ) . keySet ( ) ) {
2020-11-30 15:55:35 +00:00
Tag javaTag = tag . get ( str ) ;
2020-07-05 20:58:43 +00:00
Object translatedTag = translateToBedrockNBT ( javaTag ) ;
2020-05-25 01:07:05 +00:00
if ( translatedTag = = null )
continue ;
2020-11-05 21:36:22 +00:00
builder . put ( javaTag . getName ( ) , translatedTag ) ;
2019-10-10 00:11:50 +00:00
}
2019-07-31 19:59:23 +00:00
}
2020-07-05 20:58:43 +00:00
return builder . build ( ) ;
2020-05-25 01:07:05 +00:00
}
2021-06-21 22:51:43 +00:00
private Object translateToBedrockNBT ( Tag tag ) {
2020-05-25 01:07:05 +00:00
if ( tag instanceof ByteArrayTag ) {
2020-07-05 20:58:43 +00:00
return ( ( ByteArrayTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof ByteTag ) {
2020-07-05 20:58:43 +00:00
return ( ( ByteTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof DoubleTag ) {
2020-07-05 20:58:43 +00:00
return ( ( DoubleTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof FloatTag ) {
2020-07-05 20:58:43 +00:00
return ( ( FloatTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof IntArrayTag ) {
2020-07-05 20:58:43 +00:00
return ( ( IntArrayTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof IntTag ) {
2020-07-05 20:58:43 +00:00
return ( ( IntTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof LongArrayTag ) {
2020-06-29 18:59:51 +00:00
//Long array tag does not exist in BE
//LongArrayTag longArrayTag = (LongArrayTag) tag;
//return new com.nukkitx.nbt.tag.LongArrayTag(longArrayTag.getName(), longArrayTag.getValue());
return null ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof LongTag ) {
2020-07-05 20:58:43 +00:00
return ( ( LongTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof ShortTag ) {
2020-07-05 20:58:43 +00:00
return ( ( ShortTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
if ( tag instanceof StringTag ) {
2020-07-05 20:58:43 +00:00
return ( ( StringTag ) tag ) . getValue ( ) ;
2020-05-25 01:07:05 +00:00
}
2021-09-10 18:10:56 +00:00
if ( tag instanceof ListTag listTag ) {
2020-05-25 01:07:05 +00:00
2020-07-05 20:58:43 +00:00
List < Object > tagList = new ArrayList < > ( ) ;
2021-06-21 01:42:22 +00:00
for ( Tag value : listTag ) {
2020-05-25 01:07:05 +00:00
tagList . add ( translateToBedrockNBT ( value ) ) ;
2019-10-10 00:11:50 +00:00
}
2020-07-05 20:58:43 +00:00
NbtType < ? > type = NbtType . COMPOUND ;
2020-05-25 01:07:05 +00:00
if ( ! tagList . isEmpty ( ) ) {
2020-07-05 20:58:43 +00:00
type = NbtType . byClass ( tagList . get ( 0 ) . getClass ( ) ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
return new NbtList ( type , tagList ) ;
2020-05-25 01:07:05 +00:00
}
2021-09-10 18:10:56 +00:00
if ( tag instanceof CompoundTag compoundTag ) {
2020-05-25 01:07:05 +00:00
return translateNbtToBedrock ( compoundTag ) ;
2019-07-31 19:59:23 +00:00
}
2019-07-29 22:20:48 +00:00
2020-05-25 01:07:05 +00:00
return null ;
2019-07-29 22:20:48 +00:00
}
2022-03-23 20:21:04 +00:00
private CompoundTag translateToJavaNBT ( String name , NbtMap tag ) {
2020-11-30 15:55:35 +00:00
CompoundTag javaTag = new CompoundTag ( name ) ;
Map < String , Tag > javaValue = javaTag . getValue ( ) ;
2020-07-05 20:58:43 +00:00
if ( tag ! = null & & ! tag . isEmpty ( ) ) {
2021-02-16 21:25:46 +00:00
for ( Map . Entry < String , Object > entry : tag . entrySet ( ) ) {
Tag translatedTag = translateToJavaNBT ( entry . getKey ( ) , entry . getValue ( ) ) ;
2020-05-25 01:07:05 +00:00
if ( translatedTag = = null )
continue ;
javaValue . put ( translatedTag . getName ( ) , translatedTag ) ;
2020-04-15 19:24:57 +00:00
}
}
2019-07-29 22:20:48 +00:00
2020-05-25 01:07:05 +00:00
javaTag . setValue ( javaValue ) ;
return javaTag ;
2019-07-29 22:20:48 +00:00
}
2020-11-30 15:55:35 +00:00
private Tag translateToJavaNBT ( String name , Object object ) {
2020-07-05 20:58:43 +00:00
if ( object instanceof int [ ] ) {
return new IntArrayTag ( name , ( int [ ] ) object ) ;
2019-07-29 22:20:48 +00:00
}
2020-05-25 01:07:05 +00:00
2020-07-05 20:58:43 +00:00
if ( object instanceof byte [ ] ) {
return new ByteArrayTag ( name , ( byte [ ] ) object ) ;
2020-05-25 01:07:05 +00:00
}
2022-07-02 16:50:16 +00:00
2020-07-05 20:58:43 +00:00
if ( object instanceof Byte ) {
return new ByteTag ( name , ( byte ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof Float ) {
return new FloatTag ( name , ( float ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof Double ) {
return new DoubleTag ( name , ( double ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof Integer ) {
return new IntTag ( name , ( int ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof long [ ] ) {
return new LongArrayTag ( name , ( long [ ] ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof Long ) {
return new LongTag ( name , ( long ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof Short ) {
return new ShortTag ( name , ( short ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof String ) {
return new StringTag ( name , ( String ) object ) ;
2020-05-25 01:07:05 +00:00
}
2020-07-05 20:58:43 +00:00
if ( object instanceof List ) {
2020-11-30 15:55:35 +00:00
List < Tag > tags = new ArrayList < > ( ) ;
2020-05-25 01:07:05 +00:00
2020-07-05 20:58:43 +00:00
for ( Object value : ( List < ? > ) object ) {
2020-11-30 15:55:35 +00:00
Tag javaTag = translateToJavaNBT ( " " , value ) ;
2020-05-25 01:07:05 +00:00
if ( javaTag ! = null )
tags . add ( javaTag ) ;
}
2020-07-05 20:58:43 +00:00
return new ListTag ( name , tags ) ;
2020-05-25 01:07:05 +00:00
}
2021-09-10 18:10:56 +00:00
if ( object instanceof NbtMap map ) {
2020-07-14 07:18:11 +00:00
return translateToJavaNBT ( name , map ) ;
2020-05-25 01:07:05 +00:00
}
return null ;
2019-12-31 03:55:17 +00:00
}
2020-08-25 02:05:39 +00:00
/ * *
* Translates the display name of the item
* @param session the Bedrock client ' s session
* @param tag the tag to translate
2021-07-13 01:19:40 +00:00
* @param mapping the item entry , in case it requires translation
2021-04-26 18:44:16 +00:00
*
* @return the new tag to use , should the current one be null
* /
2021-11-22 19:52:26 +00:00
public static CompoundTag translateDisplayProperties ( GeyserSession session , CompoundTag tag , ItemMapping mapping ) {
2021-07-13 01:19:40 +00:00
return translateDisplayProperties ( session , tag , mapping , 'f' ) ;
2021-04-26 18:44:16 +00:00
}
/ * *
* @param translationColor if this item is not available on Java , the color that the new name should be .
* Normally , this should just be white , but for shulker boxes this should be gray .
2020-08-25 02:05:39 +00:00
* /
2021-11-22 19:52:26 +00:00
public static CompoundTag translateDisplayProperties ( GeyserSession session , CompoundTag tag , ItemMapping mapping , char translationColor ) {
2021-04-26 18:44:16 +00:00
boolean hasCustomName = false ;
2020-08-25 02:05:39 +00:00
if ( tag ! = null ) {
2022-01-20 00:44:46 +00:00
if ( tag . get ( " display " ) instanceof CompoundTag display & & display . get ( " Name " ) instanceof StringTag tagName ) {
String name = tagName . getValue ( ) ;
2020-08-25 02:05:39 +00:00
2020-11-16 23:57:57 +00:00
// Get the translated name and prefix it with a reset char
2022-01-16 21:09:53 +00:00
name = MessageTranslator . convertMessageLenient ( name , session . locale ( ) ) ;
2020-08-25 02:05:39 +00:00
2020-11-16 23:57:57 +00:00
// Add the new name tag
display . put ( new StringTag ( " Name " , name ) ) ;
2021-04-26 18:44:16 +00:00
// Indicate that a custom name is present
hasCustomName = true ;
2020-08-25 02:05:39 +00:00
2020-11-16 23:57:57 +00:00
// Add to the new root tag
tag . put ( display ) ;
2020-08-25 02:05:39 +00:00
}
}
2021-04-26 18:44:16 +00:00
2021-07-13 01:19:40 +00:00
if ( ! hasCustomName & & mapping . hasTranslation ( ) ) {
2021-04-26 18:44:16 +00:00
// No custom name, but we need to localize the item's name
if ( tag = = null ) {
tag = new CompoundTag ( " " ) ;
}
2022-01-20 00:44:46 +00:00
CompoundTag display ;
if ( tag . get ( " display " ) instanceof CompoundTag oldDisplay ) {
display = oldDisplay ;
} else {
2021-04-26 18:44:16 +00:00
display = new CompoundTag ( " display " ) ;
// Add to the new root tag
tag . put ( display ) ;
}
2021-07-13 01:19:40 +00:00
String translationKey = mapping . getTranslationString ( ) ;
2021-04-26 18:44:16 +00:00
// Reset formatting since Bedrock defaults to italics
2022-01-16 21:09:53 +00:00
display . put ( new StringTag ( " Name " , " §r§ " + translationColor + MinecraftLocale . getLocaleString ( translationKey , session . locale ( ) ) ) ) ;
2021-04-26 18:44:16 +00:00
}
return tag ;
2020-08-25 02:05:39 +00:00
}
2022-07-02 16:50:16 +00:00
/ * *
* Translates the custom model data of an item
* /
private static void translateCustomItem ( CompoundTag nbt , ItemData . Builder builder , ItemMapping mapping ) {
int bedrockId = getCustomItem ( nbt , mapping ) ;
if ( bedrockId ! = - 1 ) {
builder . id ( bedrockId ) ;
}
}
private static int getCustomItem ( CompoundTag nbt , ItemMapping mapping ) {
if ( nbt = = null ) {
return - 1 ;
}
Object2IntMap < CustomItemOptions > customMappings = mapping . getCustomItemOptions ( ) ;
if ( customMappings . isEmpty ( ) ) {
return - 1 ;
}
int customModelData = nbt . get ( " CustomModelData " ) instanceof IntTag customModelDataTag ? customModelDataTag . getValue ( ) : 0 ;
TriState unbreakable = TriState . fromBoolean ( nbt . get ( " Unbreakable " ) instanceof ByteTag unbreakableTag & & unbreakableTag . getValue ( ) = = 1 ) ;
int damage = nbt . get ( " Damage " ) instanceof IntTag damageTag ? damageTag . getValue ( ) : 0 ;
for ( Object2IntMap . Entry < CustomItemOptions > mappingTypes : customMappings . object2IntEntrySet ( ) ) {
CustomItemOptions options = mappingTypes . getKey ( ) ;
TriState unbreakableOption = options . unbreakable ( ) ;
if ( unbreakableOption = = unbreakable ) { // Implementation note: if the option is NOT_SET then this comparison will always be false because of how the item unbreaking TriState is created
return mappingTypes . getIntValue ( ) ;
}
OptionalInt customModelDataOption = options . customModelData ( ) ;
if ( customModelDataOption . isPresent ( ) & & customModelDataOption . getAsInt ( ) = = customModelData ) {
return mappingTypes . getIntValue ( ) ;
}
OptionalInt damagePredicate = options . damagePredicate ( ) ;
if ( damagePredicate . isPresent ( ) & & damagePredicate . getAsInt ( ) = = damage ) {
return mappingTypes . getIntValue ( ) ;
}
}
return - 1 ;
}
2020-05-17 04:52:39 +00:00
/ * *
* Checks if an { @link ItemStack } is equal to another item stack
*
* @param itemStack the item stack to check
* @param equalsItemStack the item stack to check if equal to
* @param checkAmount if the amount should be taken into account
* @param trueIfAmountIsGreater if this should return true if the amount of the
* first item stack is greater than that of the second
* @param checkNbt if NBT data should be checked
* @return if an item stack is equal to another item stack
* /
public boolean equals ( ItemStack itemStack , ItemStack equalsItemStack , boolean checkAmount , boolean trueIfAmountIsGreater , boolean checkNbt ) {
if ( itemStack . getId ( ) ! = equalsItemStack . getId ( ) ) {
return false ;
}
if ( checkAmount ) {
if ( trueIfAmountIsGreater ) {
if ( itemStack . getAmount ( ) < equalsItemStack . getAmount ( ) ) {
return false ;
}
} else {
if ( itemStack . getAmount ( ) ! = equalsItemStack . getAmount ( ) ) {
return false ;
}
}
}
if ( ! checkNbt ) {
return true ;
}
if ( ( itemStack . getNbt ( ) = = null | | itemStack . getNbt ( ) . isEmpty ( ) ) & & ( equalsItemStack . getNbt ( ) ! = null & & ! equalsItemStack . getNbt ( ) . isEmpty ( ) ) ) {
return false ;
}
if ( ( itemStack . getNbt ( ) ! = null & & ! itemStack . getNbt ( ) . isEmpty ( ) & & ( equalsItemStack . getNbt ( ) = = null | | ! equalsItemStack . getNbt ( ) . isEmpty ( ) ) ) ) {
return false ;
}
if ( itemStack . getNbt ( ) ! = null & & equalsItemStack . getNbt ( ) ! = null ) {
return itemStack . getNbt ( ) . equals ( equalsItemStack . getNbt ( ) ) ;
}
return true ;
}
2019-07-29 22:20:48 +00:00
}