Compare commits

...

7 commits

28 changed files with 749 additions and 544 deletions

View file

@ -48,7 +48,8 @@ dependencies {
modImplementation libs.quilted.fabric.api modImplementation libs.quilted.fabric.api
modApi include("dev.emi:trinkets:${libs.versions.trinkets.get()}") // not JiJ because it causes its mixins to fail to load
modApi "dev.emi:trinkets:${libs.versions.trinkets.get()}"
modApi include("com.unascribed:lib39-core:${libs.versions.lib39.get()}+${libs.versions.minecraft.get()}") modApi include("com.unascribed:lib39-core:${libs.versions.lib39.get()}+${libs.versions.minecraft.get()}")
modApi include("com.unascribed:lib39-dessicant:${libs.versions.lib39.get()}+${libs.versions.minecraft.get()}") modApi include("com.unascribed:lib39-dessicant:${libs.versions.lib39.get()}+${libs.versions.minecraft.get()}")
@ -57,10 +58,22 @@ dependencies {
} }
processResources { processResources {
inputs.properties 'version': version, 'group': project.group inputs.property 'version', version
inputs.property 'group', project.group
inputs.property 'minecraft_version', libs.versions.minecraft.get()
inputs.property 'trinkets_version', libs.versions.trinkets.get()
inputs.property 'lib39_version', libs.versions.lib39.get()
inputs.property 'emi_version', libs.versions.emi.get()
filesMatching('quilt.mod.json') { filesMatching('quilt.mod.json') {
expand 'version': version, 'group': project.group expand (
"version": version,
"group": project.group,
"minecraft_version": libs.versions.minecraft.get(),
"trinkets_version": libs.versions.trinkets.get(),
"lib39_version": libs.versions.lib39.get(),
"emi_version": libs.versions.emi.get()
)
} }
} }

View file

@ -12,7 +12,6 @@ import net.minecraft.util.Identifier;
import org.quiltmc.loader.api.ModContainer; import org.quiltmc.loader.api.ModContainer;
import org.quiltmc.qsl.base.api.entrypoint.ModInitializer; import org.quiltmc.qsl.base.api.entrypoint.ModInitializer;
import pm.c7.scout.config.ScoutConfigHandler;
import pm.c7.scout.registry.ScoutItems; import pm.c7.scout.registry.ScoutItems;
public class Scout implements ModInitializer { public class Scout implements ModInitializer {
@ -32,7 +31,6 @@ public class Scout implements ModInitializer {
@Override @Override
public void onInitialize(ModContainer mod) { public void onInitialize(ModContainer mod) {
new ScoutConfigHandler();
ScoutItems.init(); ScoutItems.init();
Registry.register(Registries.ITEM_GROUP, new Identifier(ScoutUtil.MOD_ID, "itemgroup"), ITEM_GROUP); Registry.register(Registries.ITEM_GROUP, new Identifier(ScoutUtil.MOD_ID, "itemgroup"), ITEM_GROUP);
} }

View file

@ -0,0 +1,16 @@
package pm.c7.scout;
import org.quiltmc.config.api.ReflectiveConfig;
import org.quiltmc.config.api.annotations.Comment;
import org.quiltmc.config.api.values.TrackedValue;
import org.quiltmc.loader.api.config.v2.QuiltConfig;
public class ScoutConfig extends ReflectiveConfig {
public static final ScoutConfig CONFIG = QuiltConfig.create("", ScoutUtil.MOD_ID, ScoutConfig.class);
@Comment("Allow shulker boxes to be placed in bags. Bags are already blacklisted from shulker boxes with no toggle.")
public final TrackedValue<Boolean> allowShulkers = this.value(true);
@Comment("Allow bags to act as a quiver and pull arrows.")
public final TrackedValue<Boolean> useArrows = this.value(true);
}

View file

@ -16,6 +16,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtList;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.Pair; import net.minecraft.util.Pair;
import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.collection.DefaultedList;

View file

@ -1,12 +0,0 @@
package pm.c7.scout.config;
import org.quiltmc.config.api.WrappedConfig;
import org.quiltmc.config.api.annotations.Comment;
public class ScoutConfig extends WrappedConfig {
@Comment("Allow shulker boxes to be placed in bags. Bags are already blacklisted from shulker boxes with no toggle.")
public final boolean allowShulkers = true;
@Comment("Allow bags to act as a quiver and pull arrows.")
public final boolean useArrows = true;
}

View file

@ -1,17 +0,0 @@
package pm.c7.scout.config;
import org.quiltmc.config.api.values.TrackedValue;
import org.quiltmc.loader.api.config.QuiltConfig;
import pm.c7.scout.ScoutUtil;
import java.util.List;
public class ScoutConfigHandler {
public static final ScoutConfig CONFIG = QuiltConfig.create("", ScoutUtil.MOD_ID, ScoutConfig.class);
public ScoutConfigHandler() {}
public static TrackedValue<?> getConfigValue(String key) {
return CONFIG.getValue(List.of(key));
}
}

View file

@ -12,14 +12,14 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import pm.c7.scout.ScoutUtil; import pm.c7.scout.ScoutUtil;
import pm.c7.scout.config.ScoutConfigHandler; import pm.c7.scout.ScoutConfig;
import pm.c7.scout.item.BaseBagItem; import pm.c7.scout.item.BaseBagItem;
@Mixin(BowItem.class) @Mixin(BowItem.class)
public class BowItemMixin { public class BowItemMixin {
@Inject(method = "onStoppedUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FF)V"), locals = LocalCapture.CAPTURE_FAILHARD) @Inject(method = "onStoppedUsing", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FF)V"), locals = LocalCapture.CAPTURE_FAILHARD)
public void scout$arrowsFromBags(ItemStack stack, World world, LivingEntity user, int remainingUseTicks, CallbackInfo ci, PlayerEntity playerEntity, boolean bl, ItemStack itemStack, int maxTime, float f) { public void scout$arrowsFromBags(ItemStack stack, World world, LivingEntity user, int remainingUseTicks, CallbackInfo ci, PlayerEntity playerEntity, boolean bl, ItemStack itemStack, int maxTime, float f) {
if ((boolean) ScoutConfigHandler.getConfigValue("useArrows").value()) { if (ScoutConfig.CONFIG.useArrows.value()) {
boolean infinity = bl && itemStack.isOf(Items.ARROW); boolean infinity = bl && itemStack.isOf(Items.ARROW);
boolean hasRan = false; boolean hasRan = false;

View file

@ -9,14 +9,12 @@ import pm.c7.scout.item.BaseBagItem;
@Mixin(ItemStack.class) @Mixin(ItemStack.class)
public class ItemStackMixin { public class ItemStackMixin {
// Trinkets calls areEqual to check whether it should unequip old and equip new (https://github.com/emilyploszaj/trinkets/blob/7cb63ce0/src/main/java/dev/emi/trinkets/mixin/LivingEntityMixin.java#L155-L158) // Trinkets calls areEqual to check whether it should unequip old and equip new (https://github.com/emilyploszaj/trinkets/blob/7cb63ce0/src/main/java/dev/emi/trinkets/mixin/LivingEntityMixin.java#L155-L158)
// Excluding ourselves from this check to force unequip/equip when switching bag items fixes a duplication bug // Excluding ourselves from this check to force unequip/equip when switching bag items fixes a duplication bug
// Gross and hacky but oh well, can't mixin mixins. // Gross and hacky but oh well, can't mixin mixins.
@Inject(method = "areEqual", at = @At("HEAD"), cancellable = true) @Inject(method = "areEqual", at = @At("HEAD"), cancellable = true)
private void scout$grossTrinketsEquipFix(ItemStack newStack, CallbackInfoReturnable<Boolean> callbackInfo) { private static void scout$grossTrinketsEquipFix(ItemStack stack, ItemStack otherStack, CallbackInfoReturnable<Boolean> callbackInfo) {
ItemStack self = (ItemStack) (Object) this; if (stack.getItem() instanceof BaseBagItem && otherStack.getItem() instanceof BaseBagItem) {
if (self.getItem() instanceof BaseBagItem && newStack.getItem() instanceof BaseBagItem) {
callbackInfo.setReturnValue(false); callbackInfo.setReturnValue(false);
} }
} }

View file

@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import pm.c7.scout.ScoutUtil; import pm.c7.scout.ScoutUtil;
import pm.c7.scout.config.ScoutConfigHandler; import pm.c7.scout.ScoutConfig;
import pm.c7.scout.item.BaseBagItem; import pm.c7.scout.item.BaseBagItem;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -17,7 +17,7 @@ import java.util.function.Predicate;
public class PlayerEntityMixin { public class PlayerEntityMixin {
@Inject(method = "getArrowType", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/item/RangedWeaponItem;getProjectiles()Ljava/util/function/Predicate;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) @Inject(method = "getArrowType", at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/item/RangedWeaponItem;getProjectiles()Ljava/util/function/Predicate;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
public void scout$arrowsFromBags(ItemStack stack, CallbackInfoReturnable<ItemStack> cir, Predicate<ItemStack> predicate, ItemStack itemStack) { public void scout$arrowsFromBags(ItemStack stack, CallbackInfoReturnable<ItemStack> cir, Predicate<ItemStack> predicate, ItemStack itemStack) {
if ((boolean) ScoutConfigHandler.getConfigValue("useArrows").value()) { if (ScoutConfig.CONFIG.useArrows.value()) {
var self = (PlayerEntity) (Object) this; var self = (PlayerEntity) (Object) this;
var leftPouch = ScoutUtil.findBagItem(self, BaseBagItem.BagType.POUCH, false); var leftPouch = ScoutUtil.findBagItem(self, BaseBagItem.BagType.POUCH, false);
var rightPouch = ScoutUtil.findBagItem(self, BaseBagItem.BagType.POUCH, true); var rightPouch = ScoutUtil.findBagItem(self, BaseBagItem.BagType.POUCH, true);

View file

@ -12,9 +12,11 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import pm.c7.scout.ScoutScreenHandler; import pm.c7.scout.ScoutScreenHandler;
import pm.c7.scout.ScoutUtil; import pm.c7.scout.ScoutUtil;
import pm.c7.scout.ScoutMixin.Transformer;
import pm.c7.scout.screen.BagSlot; import pm.c7.scout.screen.BagSlot;
@Mixin(value = PlayerScreenHandler.class, priority = 950) @Mixin(value = PlayerScreenHandler.class, priority = 950)
@Transformer(PlayerScreenHandlerTransformer.class)
public abstract class PlayerScreenHandlerMixin extends ScreenHandler implements ScoutScreenHandler { public abstract class PlayerScreenHandlerMixin extends ScreenHandler implements ScoutScreenHandler {
protected PlayerScreenHandlerMixin() { protected PlayerScreenHandlerMixin() {
super(null, 0); super(null, 0);

View file

@ -0,0 +1,95 @@
package pm.c7.scout.mixin;
import org.objectweb.asm.tree.*;
import org.quiltmc.loader.api.QuiltLoader;
import pm.c7.scout.mixinsupport.ClassNodeTransformer;
import static org.objectweb.asm.Opcodes.*;
public class PlayerScreenHandlerTransformer implements ClassNodeTransformer {
@Override
public void transform(String name, ClassNode node) {
var resolver = QuiltLoader.getMappingResolver();
var namespace = "intermediary";
var LPlayerScreenHandler = L(slash(name));
var quickMove = resolver.mapMethodName(namespace, name, "method_7601", "(Lnet/minecraft/class_1657;I)Lnet/minecraft/class_1799;");
var Slot = slash(resolver.mapClassName(namespace, "net.minecraft.class_1735"));
var LSlot = L(Slot);
var DefaultedList = slash(resolver.mapClassName(namespace, "net.minecraft.class_2371"));
for (var mn : node.methods) {
// that other comment was a half truth, you can transform mixins :^)
if (mn.name.endsWith("trinkets$quickMove") || mn.name.equals(quickMove)) {
for (var insn : mn.instructions) {
if (insn instanceof VarInsnNode vin) {
if (vin.getOpcode() == ASTORE && vin.var == 4) {
if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) {
if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) {
if(prevPrevInsn.owner.equals(DefaultedList)) {
LabelNode LnotBag = new LabelNode();
LabelNode Lend = new LabelNode();
mn.instructions.insertBefore(prevPrevInsn.getPrevious().getPrevious().getPrevious(), insns(
ILOAD(2),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFEQ(LnotBag),
ILOAD(2),
ALOAD(0),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "getBagSlot", "(I" + LPlayerScreenHandler + ")" + LSlot),
CHECKCAST(Slot),
ASTORE(vin.var),
GOTO(Lend),
LnotBag
));
mn.instructions.insert(vin, insns(
Lend
));
}
}
}
}
}
}
}
}
}
private String slash(String clazz) {
return clazz.replaceAll("\\.", "/");
}
private String L(String clazz) {
return "L" + clazz + ";";
}
private InsnList insns(AbstractInsnNode... insns) {
var li = new InsnList();
for (var i : insns) li.add(i);
return li;
}
private static VarInsnNode ILOAD(int v) {
return new VarInsnNode(ILOAD, v);
}
private static MethodInsnNode INVOKESTATIC(String owner, String name, String desc) {
return new MethodInsnNode(INVOKESTATIC, owner, name, desc);
}
private static JumpInsnNode IFEQ(LabelNode v) {
return new JumpInsnNode(IFEQ, v);
}
private static VarInsnNode ALOAD(int v) {
return new VarInsnNode(ALOAD, v);
}
private static VarInsnNode ASTORE(int v) {
return new VarInsnNode(ASTORE, v);
}
private static TypeInsnNode CHECKCAST(String desc) {
return new TypeInsnNode(CHECKCAST, desc);
}
private static JumpInsnNode GOTO(LabelNode v) {
return new JumpInsnNode(GOTO, v);
}
}

View file

@ -6,18 +6,21 @@ import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import net.minecraft.screen.slot.SlotActionType; import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.collection.DefaultedList;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import pm.c7.scout.ScoutMixin.Transformer; import pm.c7.scout.ScoutMixin.Transformer;
import pm.c7.scout.ScoutUtil; import pm.c7.scout.ScoutUtil;
@Mixin(ScreenHandler.class) @Mixin(value = ScreenHandler.class, priority = 950)
@Transformer(ScreenHandlerTransformer.class) @Transformer(ScreenHandlerTransformer.class)
public abstract class ScreenHandlerMixin { public abstract class ScreenHandlerMixin {
@Inject(method = "getSlot", at = @At("HEAD"), cancellable = true) @Inject(method = "getSlot", at = @At("HEAD"), cancellable = true)
@ -55,6 +58,15 @@ public abstract class ScreenHandlerMixin {
} }
} }
@Redirect(method = "internalOnSlotClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/collection/DefaultedList;get(I)Ljava/lang/Object;", ordinal = 5))
public Object scout$fixSlotIndexing(DefaultedList<Slot> self, int index, int slotIndex, int button, SlotActionType actionType, PlayerEntity player) {
if (ScoutUtil.isBagSlot(index)) {
return ScoutUtil.getBagSlot(index, player.playerScreenHandler);
} else {
return self.get(index);
}
}
@Shadow @Shadow
public static boolean canInsertItemIntoSlot(@Nullable Slot slot, ItemStack stack, boolean allowOverflow) { public static boolean canInsertItemIntoSlot(@Nullable Slot slot, ItemStack stack, boolean allowOverflow) {
return false; return false;

View file

@ -2,58 +2,91 @@ package pm.c7.scout.mixin;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
import org.quiltmc.loader.api.QuiltLoader; import org.quiltmc.loader.api.QuiltLoader;
import pm.c7.scout.mixinsupport.ClassNodeTransformer; import pm.c7.scout.mixinsupport.ClassNodeTransformer;
import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Opcodes.*;
public class ScreenHandlerTransformer implements ClassNodeTransformer { public class ScreenHandlerTransformer implements ClassNodeTransformer {
//private static final Logger LOGGER = LoggerFactory.getLogger("Scout:ScreenHandlerTransformer");
@Override @Override
public void transform(String name, ClassNode node) { public void transform(String name, ClassNode node) {
var internalOnSlotClick = "m_nqfgpzfl"; var resolver = QuiltLoader.getMappingResolver();
var insertItem = "m_jpjdgbxy"; var namespace = "intermediary";
var PlayerEntity = "net/minecraft/unmapped/C_jzrpycqo"; var internalOnSlotClick = resolver.mapMethodName(namespace, name, "method_30010", "(IILnet/minecraft/class_1713;Lnet/minecraft/class_1657;)V");
var playerScreenHandler = "f_xvlfpipb";
var PlayerScreenHandler = "net/minecraft/unmapped/C_wgehrbdx";
var Slot = "net/minecraft/unmapped/C_nhvqfffd";
var DefaultedList = "net/minecraft/unmapped/C_rnrfftze";
if (QuiltLoader.isDevelopmentEnvironment()) {
internalOnSlotClick = "internalOnSlotClick";
insertItem = "insertItem";
PlayerEntity = "net/minecraft/entity/player/PlayerEntity";
playerScreenHandler = "playerScreenHandler";
PlayerScreenHandler = "net/minecraft/screen/PlayerScreenHandler";
Slot = "net/minecraft/screen/slot/Slot";
DefaultedList = "net/minecraft/util/collection/DefaultedList";
}
var PlayerEntity = resolver.mapClassName(namespace, "net.minecraft.class_1657");
var PlayerScreenHandler = slash(resolver.mapClassName(namespace, "net.minecraft.class_1723"));
var LPlayerScreenHandler = L(PlayerScreenHandler); var LPlayerScreenHandler = L(PlayerScreenHandler);
String playerScreenHandler = resolver.mapFieldName(namespace, PlayerEntity, "field_7498", LPlayerScreenHandler);
PlayerEntity = slash(PlayerEntity);
var Slot = slash(resolver.mapClassName(namespace, "net.minecraft.class_1735"));
var LSlot = L(Slot); var LSlot = L(Slot);
var DefaultedList = slash(resolver.mapClassName(namespace, "net.minecraft.class_2371"));
//var LDefaultedList = L(DefaultedList);
//var slots = resolver.mapFieldName(namespace, name, "field_7761", LDefaultedList);
int ordinal = 0;
for (var mn : node.methods) { for (var mn : node.methods) {
if (mn.name.equals(internalOnSlotClick)) { if (mn.name.equals(internalOnSlotClick)) {
for (var insn : mn.instructions) { for (var insn : mn.instructions) {
if (insn instanceof VarInsnNode vin) { if (insn instanceof VarInsnNode vin) {
if (vin.getOpcode() == ILOAD && vin.var == 1) { if (vin.getOpcode() == ILOAD && vin.var == 1) {
if (insn.getNext() instanceof JumpInsnNode nextInsn && nextInsn.getOpcode() == IFGE) { if (insn.getNext() instanceof JumpInsnNode nextInsn && nextInsn.getOpcode() == IFGE) {
// `if (slotIndex < 0) return` -> `if (slotIndex < 0 && !isBagSlot(slotIndex)) return`
var jumpTo = nextInsn.label; var jumpTo = nextInsn.label;
mn.instructions.insert(nextInsn, insns( mn.instructions.insert(nextInsn, insns(
ILOAD(1), ILOAD(1),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFNE(jumpTo) IFNE(jumpTo)
)); ));
} else if (insn.getPrevious() instanceof JumpInsnNode prevInsn && prevInsn.getOpcode() == IFEQ && insn.getNext() instanceof JumpInsnNode nextInsn && nextInsn.getOpcode() == IFLT) {
// skip creative duping, it uses same signature and i dont feel like overcomplicating the check
if (ordinal != 1) {
ordinal++;
continue;
}
// fix dropping from bags not working
LabelNode Lcheck = new LabelNode();
nextInsn.label = Lcheck;
nextInsn.setOpcode(IFGE);
mn.instructions.insert(nextInsn, insns(
ILOAD(1),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFNE(Lcheck),
RETURN(),
Lcheck
));
} }
} else if (vin.getOpcode() == ASTORE && (vin.var == 6 || vin.var == 7)) { } else if (vin.getOpcode() == ASTORE && (vin.var == 6 || vin.var == 7)) {
// fix most but not all calls to `slots.get`
if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) { if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) {
if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) { if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) {
if(prevPrevInsn.owner.equals(DefaultedList)) { if(prevPrevInsn.owner.equals(DefaultedList)) {
var insertPoint = prevPrevInsn.getPrevious();
if (insertPoint.getOpcode() == ILOAD) {
var beforeInsert = insertPoint.getPrevious();
if (beforeInsert != null && beforeInsert.getPrevious() != null){
if (beforeInsert.getOpcode() == GETFIELD && beforeInsert.getPrevious().getOpcode() == ALOAD) {
insertPoint = beforeInsert.getPrevious();
} else {
continue;
}
}
LabelNode LnotBag = new LabelNode(); LabelNode LnotBag = new LabelNode();
LabelNode Lend = new LabelNode(); LabelNode Lend = (LabelNode) vin.getNext();
mn.instructions.insertBefore(prevPrevInsn.getPrevious().getPrevious().getPrevious(), insns(
mn.instructions.insertBefore(insertPoint, insns(
ILOAD(1), ILOAD(1),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFEQ(LnotBag), IFEQ(LnotBag),
@ -66,51 +99,126 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer {
GOTO(Lend), GOTO(Lend),
LnotBag LnotBag
)); ));
mn.instructions.insert(vin, insns(
Lend
));
} }
} }
} }
} }
} }
} }
}/* else if (mn.name.equals(insertItem)) { }
} else if (mn.name.endsWith("debugify$handleCtrlQCrafting")) { // ughghghhghghghghgh
for (var insn : mn.instructions) { for (var insn : mn.instructions) {
if (insn instanceof VarInsnNode vin && vin.getOpcode() == ASTORE && vin.var == 7) { if (insn instanceof VarInsnNode vin && vin.getOpcode() == ASTORE && vin.var == 6) {
if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) { if (vin.getPrevious() instanceof TypeInsnNode prevInsn && prevInsn.getOpcode() == CHECKCAST && prevInsn.desc.equals(Slot)) {
if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) { if (prevInsn.getPrevious() instanceof MethodInsnNode prevPrevInsn && prevPrevInsn.getOpcode() == INVOKEVIRTUAL) {
if(prevPrevInsn.owner.equals(DefaultedList)) { if(prevPrevInsn.owner.equals(DefaultedList)) {
var insertPoint = prevPrevInsn.getPrevious();
if (insertPoint.getOpcode() == ILOAD) {
var beforeInsert = insertPoint.getPrevious();
if (beforeInsert != null && beforeInsert.getPrevious() != null && beforeInsert.getOpcode() == GETFIELD && beforeInsert.getPrevious().getOpcode() == ALOAD) {
insertPoint = beforeInsert.getPrevious();
}
LabelNode LnotBag = new LabelNode(); LabelNode LnotBag = new LabelNode();
LabelNode LpastSlot = new LabelNode(); LabelNode Lend = (LabelNode) vin.getNext();
mn.instructions.insertBefore(prevPrevInsn.getPrevious().getPrevious().getPrevious(), insns(
INVOKESTATIC("pm/c7/scout/ScoutUtil", "getPlayerScreenHandler", "()" + LPlayerScreenHandler), mn.instructions.insertBefore(insertPoint, insns(
ASTORE(20), ILOAD(1),
ALOAD(20),
IFNULL(LpastSlot),
ILOAD(6),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"), INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFEQ(LnotBag), IFEQ(LnotBag),
ILOAD(6), ILOAD(1),
ALOAD(20), ALOAD(4),
GETFIELD(PlayerEntity, playerScreenHandler, LPlayerScreenHandler),
INVOKESTATIC("pm/c7/scout/ScoutUtil", "getBagSlot", "(I" + LPlayerScreenHandler + ")" + LSlot), INVOKESTATIC("pm/c7/scout/ScoutUtil", "getBagSlot", "(I" + LPlayerScreenHandler + ")" + LSlot),
CHECKCAST(Slot), CHECKCAST(Slot),
ASTORE(vin.var), ASTORE(vin.var),
LnotBag, GOTO(Lend),
ILOAD(6), LnotBag
INVOKESTATIC("pm/c7/scout/ScoutUtil", "isBagSlot", "(I)Z"),
IFNE(LpastSlot)
));
mn.instructions.insert(vin, insns(
LpastSlot
)); ));
} }
} }
} }
} }
} }
}
}
}
}
// debug, keeping here for future use
/*private static List<Field> allOpcodes = Arrays.asList(Opcodes.class.getFields());
private String getOpcodeName(int v) {
Optional<Field> opcode = allOpcodes.stream()
.filter(f -> f.getType() == int.class)
.filter(f -> {
if (f.getName().startsWith("F_")) {
return f.getName().equals("F_NEW");
} else {
return !f.getName().startsWith("V")
&& !f.getName().startsWith("ASM")
&& !f.getName().startsWith("SOURCE_")
&& !f.getName().startsWith("ACC_")
&& !f.getName().startsWith("H_")
&& !f.getName().startsWith("T_");
}
})
.filter(f -> {
try {
var field = f.get(Opcodes.class);
//LOGGER.info("{} {} | {}", f.getName(), f.getType(), field);
return field.equals(v);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
})
.findFirst();
if (opcode.isEmpty()) {
return "<unknown: " + String.valueOf(v) + ">";
}
return opcode.get().getName();
}
private void printNode(AbstractInsnNode node) {
var name = getOpcodeName(node.getOpcode());
String val = "";
if (node instanceof VarInsnNode vin) {
val = String.valueOf(vin.var);
} else if(node instanceof FieldInsnNode fin) {
val = fin.owner + "." + fin.name + ":" + fin.desc;
} else if(node instanceof MethodInsnNode min) {
val = min.owner + "." + min.name + ":" + min.desc;
} else if (node instanceof TypeInsnNode tin) {
val = tin.desc;
} else if (node instanceof JumpInsnNode jin) {
val = jin.label.toString();
} else if (node instanceof LabelNode label) {
name = "L";
val = label.toString();
}
LOGGER.info("{} {}", name, val);
}
private void dumpInstructions(InsnList insns, int start, int end) {
for (var i = start; i < end + 1; i++) {
printNode(insns.get(i));
}
}*/ }*/
}
private String slash(String clazz) {
return clazz.replaceAll("\\.", "/");
} }
private String L(String clazz) { private String L(String clazz) {
@ -122,34 +230,34 @@ public class ScreenHandlerTransformer implements ClassNodeTransformer {
for (var i : insns) li.add(i); for (var i : insns) li.add(i);
return li; return li;
} }
private static VarInsnNode ILOAD(int var) { private static VarInsnNode ILOAD(int v) {
return new VarInsnNode(ILOAD, var); return new VarInsnNode(ILOAD, v);
} }
private static MethodInsnNode INVOKESTATIC(String owner, String name, String desc) { private static MethodInsnNode INVOKESTATIC(String owner, String name, String desc) {
return new MethodInsnNode(INVOKESTATIC, owner, name, desc); return new MethodInsnNode(INVOKESTATIC, owner, name, desc);
} }
private static JumpInsnNode IFNE(LabelNode var) { private static JumpInsnNode IFNE(LabelNode v) {
return new JumpInsnNode(IFNE, var); return new JumpInsnNode(IFNE, v);
} }
private static JumpInsnNode IFEQ(LabelNode var) { private static JumpInsnNode IFEQ(LabelNode v) {
return new JumpInsnNode(IFEQ, var); return new JumpInsnNode(IFEQ, v);
} }
private static VarInsnNode ALOAD(int var) { private static VarInsnNode ALOAD(int v) {
return new VarInsnNode(ALOAD, var); return new VarInsnNode(ALOAD, v);
} }
private static VarInsnNode ASTORE(int var) { private static VarInsnNode ASTORE(int v) {
return new VarInsnNode(ASTORE, var); return new VarInsnNode(ASTORE, v);
} }
private static FieldInsnNode GETFIELD(String owner, String name, String desc) { private static FieldInsnNode GETFIELD(String owner, String name, String desc) {
return new FieldInsnNode(GETFIELD, owner, name, desc); return new FieldInsnNode(GETFIELD, owner, name, desc);
} }
private static JumpInsnNode IFNULL(LabelNode var) {
return new JumpInsnNode(IFNULL, var);
}
private static TypeInsnNode CHECKCAST(String desc) { private static TypeInsnNode CHECKCAST(String desc) {
return new TypeInsnNode(CHECKCAST, desc); return new TypeInsnNode(CHECKCAST, desc);
} }
private static JumpInsnNode GOTO(LabelNode var) { private static JumpInsnNode GOTO(LabelNode v) {
return new JumpInsnNode(GOTO, var); return new JumpInsnNode(GOTO, v);
}
private static InsnNode RETURN() {
return new InsnNode(RETURN);
} }
} }

View file

@ -5,10 +5,8 @@ import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screen.ingame.AbstractFurnaceScreen; import net.minecraft.client.gui.screen.ingame.AbstractFurnaceScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.recipe.book.RecipeBookProvider; import net.minecraft.client.gui.screen.recipe.book.RecipeBookProvider;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.AbstractFurnaceScreenHandler; import net.minecraft.screen.AbstractFurnaceScreenHandler;
import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;

View file

@ -3,14 +3,12 @@ package pm.c7.scout.mixin.client;
import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.systems.RenderSystem;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
import net.minecraft.client.gui.screen.ingame.HandledScreen; import net.minecraft.client.gui.screen.ingame.HandledScreen;
import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;
import net.minecraft.client.gui.screen.ingame.ShulkerBoxScreen; import net.minecraft.client.gui.screen.ingame.ShulkerBoxScreen;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
@ -46,7 +44,7 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
@Shadow @Shadow
protected int backgroundHeight; protected int backgroundHeight;
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/util/math/MatrixStack;FII)V")) @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawBackground(Lnet/minecraft/client/gui/GuiGraphics;FII)V"))
private void scout$drawSatchelRow(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) { private void scout$drawSatchelRow(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (this.client != null && this.client.player != null && !ScoutUtil.isScreenBlacklisted(this)) { if (this.client != null && this.client.player != null && !ScoutUtil.isScreenBlacklisted(this)) {
ItemStack backStack = ScoutUtil.findBagItem(this.client.player, BaseBagItem.BagType.SATCHEL, false); ItemStack backStack = ScoutUtil.findBagItem(this.client.player, BaseBagItem.BagType.SATCHEL, false);
@ -272,13 +270,12 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
} }
} }
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawForeground(Lnet/minecraft/client/util/math/MatrixStack;II)V")) @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/ingame/HandledScreen;drawForeground(Lnet/minecraft/client/gui/GuiGraphics;II)V"))
public void scout$drawOurSlots(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) { public void scout$drawOurSlots(GuiGraphics graphics, int mouseX, int mouseY, float delta, CallbackInfo ci) {
if (this.client != null && this.client.player != null && !ScoutUtil.isScreenBlacklisted(this)) { if (this.client != null && this.client.player != null && !ScoutUtil.isScreenBlacklisted(this)) {
for (int i = ScoutUtil.SATCHEL_SLOT_START; i > ScoutUtil.BAG_SLOTS_END; i--) { for (int i = ScoutUtil.SATCHEL_SLOT_START; i > ScoutUtil.BAG_SLOTS_END; i--) {
BagSlot slot = (BagSlot) ScoutUtil.getBagSlot(i, this.client.player.playerScreenHandler); BagSlot slot = (BagSlot) ScoutUtil.getBagSlot(i, this.client.player.playerScreenHandler);
if (slot != null && slot.isEnabled()) { if (slot != null && slot.isEnabled()) {
RenderSystem.setShader(GameRenderer::getPositionTexShader);
this.drawSlot(graphics, slot); this.drawSlot(graphics, slot);
} }

View file

@ -2,25 +2,21 @@ package pm.c7.scout.mixin.client;
import org.objectweb.asm.tree.*; import org.objectweb.asm.tree.*;
import org.quiltmc.loader.api.QuiltLoader; import org.quiltmc.loader.api.QuiltLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pm.c7.scout.mixinsupport.ClassNodeTransformer; import pm.c7.scout.mixinsupport.ClassNodeTransformer;
import static org.objectweb.asm.Opcodes.*; import static org.objectweb.asm.Opcodes.*;
public class HandledScreenTransformer implements ClassNodeTransformer { public class HandledScreenTransformer implements ClassNodeTransformer {
private static Logger LOGGER = LoggerFactory.getLogger("Scout:HandledScreenTransformer");
@Override @Override
public void transform(String name, ClassNode node) { public void transform(String name, ClassNode node) {
var drawSlot = "m_zioswvnu"; var resolver = QuiltLoader.getMappingResolver();
var Slot = "net/minecraft/unmapped/C_nhvqfffd"; var namespace = "intermediary";
var y = "f_tttqoodj";
if (QuiltLoader.isDevelopmentEnvironment()) { var drawSlot = resolver.mapMethodName(namespace, name, "method_2385", "(Lnet/minecraft/class_332;Lnet/minecraft/class_1735;)V");
drawSlot = "drawSlot"; var Slot = resolver.mapClassName(namespace, "net.minecraft.class_1735");
Slot = "net/minecraft/screen/slot/Slot"; var y = resolver.mapFieldName(namespace, Slot, "field_7872", "I");
y = "y"; Slot = slash(Slot);
}
for (var mn : node.methods) { for (var mn : node.methods) {
if (mn.name.equals(drawSlot)) { if (mn.name.equals(drawSlot)) {
@ -55,19 +51,23 @@ public class HandledScreenTransformer implements ClassNodeTransformer {
} }
} }
private String slash(String clazz) {
return clazz.replaceAll("\\.", "/");
}
private InsnList insns(AbstractInsnNode... insns) { private InsnList insns(AbstractInsnNode... insns) {
var li = new InsnList(); var li = new InsnList();
for (var i : insns) li.add(i); for (var i : insns) li.add(i);
return li; return li;
} }
private static JumpInsnNode IFEQ(LabelNode var) { private static JumpInsnNode IFEQ(LabelNode v) {
return new JumpInsnNode(IFEQ, var); return new JumpInsnNode(IFEQ, v);
} }
private static VarInsnNode ALOAD(int var) { private static VarInsnNode ALOAD(int v) {
return new VarInsnNode(ALOAD, var); return new VarInsnNode(ALOAD, v);
} }
private static VarInsnNode ASTORE(int var) { private static VarInsnNode ASTORE(int v) {
return new VarInsnNode(ASTORE, var); return new VarInsnNode(ASTORE, v);
} }
private static TypeInsnNode INSTANCEOF(String desc) { private static TypeInsnNode INSTANCEOF(String desc) {
return new TypeInsnNode(INSTANCEOF, desc); return new TypeInsnNode(INSTANCEOF, desc);
@ -78,7 +78,7 @@ public class HandledScreenTransformer implements ClassNodeTransformer {
private static MethodInsnNode INVOKEVIRTUAL(String owner, String name, String desc) { private static MethodInsnNode INVOKEVIRTUAL(String owner, String name, String desc) {
return new MethodInsnNode(INVOKEVIRTUAL, owner, name, desc); return new MethodInsnNode(INVOKEVIRTUAL, owner, name, desc);
} }
private static VarInsnNode ISTORE(int var) { private static VarInsnNode ISTORE(int v) {
return new VarInsnNode(ISTORE, var); return new VarInsnNode(ISTORE, v);
} }
} }

View file

@ -1,19 +1,15 @@
package pm.c7.scout.mixin.client; package pm.c7.scout.mixin.client;
import com.mojang.blaze3d.systems.RenderSystem;
import net.fabricmc.api.EnvType; import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment; import net.fabricmc.api.Environment;
import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen; import net.minecraft.client.gui.screen.ingame.AbstractInventoryScreen;
import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.client.gui.screen.ingame.InventoryScreen;
import net.minecraft.client.gui.screen.recipe.book.RecipeBookProvider; import net.minecraft.client.gui.screen.recipe.book.RecipeBookProvider;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import pm.c7.scout.ScoutUtil; import pm.c7.scout.ScoutUtil;
import pm.c7.scout.item.BaseBagItem; import pm.c7.scout.item.BaseBagItem;

View file

@ -6,7 +6,7 @@ import net.minecraft.inventory.Inventory;
import net.minecraft.item.BlockItem; import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
import pm.c7.scout.config.ScoutConfigHandler; import pm.c7.scout.ScoutConfig;
import pm.c7.scout.item.BaseBagItem; import pm.c7.scout.item.BaseBagItem;
public class BagSlot extends Slot { public class BagSlot extends Slot {
@ -38,7 +38,7 @@ public class BagSlot extends Slot {
if (stack.getItem() instanceof BlockItem blockItem) { if (stack.getItem() instanceof BlockItem blockItem) {
if (blockItem.getBlock() instanceof ShulkerBoxBlock) if (blockItem.getBlock() instanceof ShulkerBoxBlock)
return (boolean) ScoutConfigHandler.getConfigValue("allowShulkers").value(); return enabled && inventory != null && ScoutConfig.CONFIG.allowShulkers.value();
} }
return enabled && inventory != null; return enabled && inventory != null;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

After

Width:  |  Height:  |  Size: 225 B

View file

@ -55,16 +55,6 @@
} }
], ],
"breaks": [ "breaks": [
{
"id": "roughlyenoughitems",
"versions": "*",
"reason": "API constantly rewritten for no reason, arrogant mod author, features stolen from EMI."
},
{
"id": "inventoryprofilesnext",
"versions": "*",
"reason": "Crashes with no intent to fix on their end."
},
{ {
"id": "infinitory", "id": "infinitory",
"versions": "*", "versions": "*",
@ -84,6 +74,16 @@
"id": "inventory_backpack", "id": "inventory_backpack",
"versions": "*", "versions": "*",
"reason": "Infinite/Incompatible inventory mod." "reason": "Infinite/Incompatible inventory mod."
},
{
"id": "realistic-inventory",
"versions": "*",
"reason": "Infinite/Incompatible inventory mod."
},
{
"id": "inventorymod",
"versions": "*",
"reason": "Infinite/Incompatible inventory mod."
} }
] ]
}, },