Handle forge caps in items properly (#143)
Maybe not covering all cases
This commit is contained in:
parent
3964235b8f
commit
41a70332a4
|
@ -27,3 +27,4 @@ public org/bukkit/craftbukkit/v/inventory/CraftMetaTropicalFishBucket/<init>(Lor
|
|||
public org/bukkit/craftbukkit/v/inventory/CraftMetaCrossbow/<init>(Lorg/bukkit/craftbukkit/v/inventory/CraftMetaItem;)V
|
||||
public org/bukkit/craftbukkit/v/inventory/CraftMetaSuspiciousStew/<init>(Lorg/bukkit/craftbukkit/v/inventory/CraftMetaItem;)V
|
||||
public org/spigotmc/ActivationRange$ActivationType/boundingBox
|
||||
public org/bukkit/craftbukkit/v/inventory/CraftMetaItem/<init>(Lnet/minecraft/nbt/CompoundNBT;)V
|
|
@ -0,0 +1,19 @@
|
|||
package io.izzel.arclight.common.bridge.bukkit;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.INBT;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface ItemMetaBridge {
|
||||
|
||||
CompoundNBT bridge$getForgeCaps();
|
||||
|
||||
void bridge$setForgeCaps(CompoundNBT nbt);
|
||||
|
||||
void bridge$offerUnhandledTags(CompoundNBT nbt);
|
||||
|
||||
Map<String, INBT> bridge$getUnhandledTags();
|
||||
|
||||
void bridge$setUnhandledTags(Map<String, INBT> tags);
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
package io.izzel.arclight.common.bridge.item;
|
||||
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
|
||||
public interface ItemStackBridge {
|
||||
|
||||
void bridge$convertStack(int version);
|
||||
|
||||
CompoundNBT bridge$getForgeCaps();
|
||||
|
||||
void bridge$setForgeCaps(CompoundNBT caps);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package io.izzel.arclight.common.mixin.bukkit;
|
||||
|
||||
import io.izzel.arclight.common.bridge.bukkit.ItemMetaBridge;
|
||||
import io.izzel.arclight.common.bridge.bukkit.MaterialBridge;
|
||||
import io.izzel.arclight.common.bridge.item.ItemStackBridge;
|
||||
import io.izzel.arclight.i18n.conf.MaterialPropertySpec;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.v.inventory.CraftItemFactory;
|
||||
import org.bukkit.craftbukkit.v.inventory.CraftItemStack;
|
||||
import org.bukkit.craftbukkit.v.inventory.CraftMetaItem;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = CraftItemStack.class, remap = false)
|
||||
public abstract class CraftItemStackMixin {
|
||||
|
||||
// @formatter:off
|
||||
@Shadow static Material getType(ItemStack item) { return null; }
|
||||
@Shadow static boolean hasItemMeta(ItemStack item) { return false; }
|
||||
// @formatter:on
|
||||
|
||||
@Inject(method = "getItemMeta(Lnet/minecraft/item/ItemStack;)Lorg/bukkit/inventory/meta/ItemMeta;",
|
||||
cancellable = true, at = @At("HEAD"))
|
||||
private static void arclight$offerCaps(ItemStack item, CallbackInfoReturnable<ItemMeta> cir) {
|
||||
Material type = getType(item);
|
||||
if (((MaterialBridge) (Object) type).bridge$getType() != MaterialPropertySpec.MaterialType.VANILLA) {
|
||||
if (hasItemMeta(item)) {
|
||||
CraftMetaItem metaItem = new CraftMetaItem(item.getTag());
|
||||
((ItemMetaBridge) metaItem).bridge$offerUnhandledTags(item.getTag());
|
||||
((ItemMetaBridge) metaItem).bridge$setForgeCaps(((ItemStackBridge) (Object) item).bridge$getForgeCaps());
|
||||
cir.setReturnValue(metaItem);
|
||||
} else {
|
||||
cir.setReturnValue(CraftItemFactory.instance().getItemMeta(getType(item)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setItemMeta(Lnet/minecraft/item/ItemStack;Lorg/bukkit/inventory/meta/ItemMeta;)Z", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ItemStack;convertStack(I)V"))
|
||||
private static void arclight$setCaps(ItemStack item, ItemMeta itemMeta, CallbackInfoReturnable<Boolean> cir) {
|
||||
CompoundNBT forgeCaps = ((ItemMetaBridge) itemMeta).bridge$getForgeCaps();
|
||||
if (forgeCaps != null) {
|
||||
((ItemStackBridge)(Object) item).bridge$setForgeCaps(forgeCaps.copy());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
package io.izzel.arclight.common.mixin.bukkit;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.izzel.arclight.common.bridge.bukkit.ItemMetaBridge;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.nbt.CompressedStreamTools;
|
||||
import net.minecraft.nbt.INBT;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.bukkit.craftbukkit.v.inventory.CraftMetaItem;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(value = CraftMetaItem.class, remap = false)
|
||||
public class CraftMetaItemMixin implements ItemMetaBridge {
|
||||
|
||||
// @formatter:off
|
||||
@Shadow(remap = false) @Final private Map<String, INBT> unhandledTags;
|
||||
// @formatter:on
|
||||
|
||||
private static final Set<String> EXTEND_TAGS = ImmutableSet.of(
|
||||
"map_is_scaling",
|
||||
"map",
|
||||
"CustomPotionEffects",
|
||||
"Potion",
|
||||
"CustomPotionColor",
|
||||
"SkullOwner",
|
||||
"SkullProfile",
|
||||
"EntityTag",
|
||||
"BlockEntityTag",
|
||||
"title",
|
||||
"author",
|
||||
"pages",
|
||||
"resolved",
|
||||
"generation",
|
||||
"Fireworks",
|
||||
"StoredEnchantments",
|
||||
"Explosion",
|
||||
"Recipes",
|
||||
"BucketVariantTag",
|
||||
"Charged",
|
||||
"ChargedProjectiles",
|
||||
"Effects",
|
||||
"LodestoneDimension",
|
||||
"LodestonePos",
|
||||
"LodestoneTracked"
|
||||
);
|
||||
private CompoundNBT forgeCaps;
|
||||
|
||||
@Override
|
||||
public CompoundNBT bridge$getForgeCaps() {
|
||||
return this.forgeCaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setForgeCaps(CompoundNBT nbt) {
|
||||
this.forgeCaps = nbt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$offerUnhandledTags(CompoundNBT nbt) {
|
||||
if (getClass().equals(CraftMetaItem.class)) {
|
||||
for (String s : nbt.keySet()) {
|
||||
if (EXTEND_TAGS.contains(s)) {
|
||||
this.unhandledTags.put(s, nbt.get(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, INBT> bridge$getUnhandledTags() {
|
||||
return this.unhandledTags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setUnhandledTags(Map<String, INBT> tags) {
|
||||
this.unhandledTags.putAll(tags);
|
||||
}
|
||||
|
||||
@Inject(method = "serialize(Lcom/google/common/collect/ImmutableMap$Builder;)Lcom/google/common/collect/ImmutableMap$Builder;", at = @At("RETURN"))
|
||||
private void arclight$serializeForgeCaps(ImmutableMap.Builder<String, Object> builder, CallbackInfoReturnable<ImmutableMap.Builder<String, Object>> cir) throws IOException {
|
||||
if (this.forgeCaps != null) {
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
CompressedStreamTools.writeCompressed(this.forgeCaps, buf);
|
||||
builder.put("forgeCaps", Base64.encodeBase64String(buf.toByteArray()));
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "clone", locals = LocalCapture.CAPTURE_FAILHARD, at = @At("RETURN"))
|
||||
private void arclight$cloneTags(CallbackInfoReturnable<CraftMetaItem> cir, CraftMetaItem clone) {
|
||||
if (this.unhandledTags != null) {
|
||||
((ItemMetaBridge) clone).bridge$getUnhandledTags().putAll(this.unhandledTags);
|
||||
}
|
||||
if (this.forgeCaps != null) {
|
||||
((ItemMetaBridge) clone).bridge$setForgeCaps(this.forgeCaps.copy());
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "applyHash", index = 1, at = @At("RETURN"))
|
||||
private int arclight$applyForgeCapsHash(int hash) {
|
||||
return 61 * hash + (this.forgeCaps != null ? this.forgeCaps.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Inject(method = "equalsCommon", cancellable = true, at = @At("HEAD"))
|
||||
private void arclight$forgeCapsEquals(CraftMetaItem that, CallbackInfoReturnable<Boolean> cir) {
|
||||
CompoundNBT forgeCaps = ((ItemMetaBridge) that).bridge$getForgeCaps();
|
||||
boolean ret;
|
||||
if (this.forgeCaps == null) {
|
||||
ret = forgeCaps != null && forgeCaps.size() != 0;
|
||||
} else {
|
||||
ret = forgeCaps == null ? this.forgeCaps.size() != 0 : !this.forgeCaps.equals(forgeCaps);
|
||||
}
|
||||
if (ret) {
|
||||
cir.setReturnValue(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "<init>(Ljava/util/Map;)V", at = @At("RETURN"))
|
||||
private void arclight$extractForgeCaps(Map<String, Object> map, CallbackInfo ci) {
|
||||
if (map.containsKey("forgeCaps")) {
|
||||
Object forgeCaps = map.get("forgeCaps");
|
||||
try {
|
||||
ByteArrayInputStream buf = new ByteArrayInputStream(Base64.decodeBase64(forgeCaps.toString()));
|
||||
this.forgeCaps = CompressedStreamTools.readCompressed(buf);
|
||||
} catch (IOException e) {
|
||||
LogManager.getLogger(getClass()).error("Reading forge caps", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "<init>*", locals = LocalCapture.CAPTURE_FAILSOFT, at = @At("RETURN"))
|
||||
private void arclight$copyForgeCaps(CallbackInfo ci, CraftMetaItem meta) {
|
||||
CompoundNBT forgeCaps = ((ItemMetaBridge) meta).bridge$getForgeCaps();
|
||||
if (forgeCaps != null) {
|
||||
this.forgeCaps = forgeCaps.copy();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,6 +17,7 @@ import org.bukkit.event.player.PlayerCommandSendEvent;
|
|||
import org.spigotmc.SpigotConfig;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Mutable;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
|
@ -28,12 +29,10 @@ public abstract class CommandsMixin {
|
|||
|
||||
// @formatter:off
|
||||
@Shadow public abstract int handleCommand(CommandSource source, String command);
|
||||
@Shadow @Final private CommandDispatcher<CommandSource> dispatcher;
|
||||
@Mutable @Shadow @Final private CommandDispatcher<CommandSource> dispatcher;
|
||||
@Shadow protected abstract void commandSourceNodesToSuggestionNodes(CommandNode<CommandSource> rootCommandSource, CommandNode<ISuggestionProvider> rootSuggestion, CommandSource source, Map<CommandNode<CommandSource>, CommandNode<ISuggestionProvider>> commandNodeToSuggestionNode);
|
||||
// @formatter:on
|
||||
|
||||
@Shadow
|
||||
protected abstract void commandSourceNodesToSuggestionNodes(CommandNode<CommandSource> rootCommandSource, CommandNode<ISuggestionProvider> rootSuggestion, CommandSource source, Map<CommandNode<CommandSource>, CommandNode<ISuggestionProvider>> commandNodeToSuggestionNode);
|
||||
|
||||
public void arclight$constructor() {
|
||||
this.dispatcher = new CommandDispatcher<>();
|
||||
this.dispatcher.setConsumer((context, b, i) -> context.getSource().onCommandComplete(context, b, i));
|
||||
|
|
|
@ -7,6 +7,8 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||
import net.minecraft.entity.player.ServerPlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraftforge.common.capabilities.CapabilityProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bukkit.craftbukkit.v.event.CraftEventFactory;
|
||||
|
@ -25,13 +27,31 @@ import java.util.Random;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
@Mixin(ItemStack.class)
|
||||
public abstract class ItemStackMixin implements ItemStackBridge {
|
||||
public abstract class ItemStackMixin extends CapabilityProvider<ItemStack> implements ItemStackBridge {
|
||||
|
||||
// @formatter:off
|
||||
@Shadow @Deprecated private Item item;
|
||||
@Shadow private int count;
|
||||
@Shadow(remap = false) private CompoundNBT capNBT;
|
||||
// @formatter:on
|
||||
|
||||
protected ItemStackMixin(Class<ItemStack> baseClass) {
|
||||
super(baseClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundNBT bridge$getForgeCaps() {
|
||||
return this.serializeCaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$setForgeCaps(CompoundNBT caps) {
|
||||
this.capNBT = caps;
|
||||
if (caps != null) {
|
||||
this.deserializeCaps(caps);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger("Arclight");
|
||||
|
||||
public void convertStack(int version) {
|
||||
|
|
|
@ -19,9 +19,11 @@
|
|||
"CraftHumanEntityMixin",
|
||||
"CraftInventoryMixin",
|
||||
"CraftItemFactoryMixin",
|
||||
"CraftItemStackMixin",
|
||||
"CraftLegacyLegacyMixin",
|
||||
"CraftLegacyUtilMixin",
|
||||
"CraftMagicNumbersMixin",
|
||||
"CraftMetaItemMixin",
|
||||
"CraftServerMixin",
|
||||
"CraftVillagerMixin",
|
||||
"CraftWorldMixin",
|
||||
|
|
Loading…
Reference in New Issue
Block a user