diff --git a/docs/commands/search.md b/docs/commands/search.md index 52157b79..5709db10 100644 --- a/docs/commands/search.md +++ b/docs/commands/search.md @@ -13,7 +13,7 @@ After executing your search, you will see all the results and can paginate betwe ![Search example](../assets/search-example.png) | `Display:` | Time ago | Source of the action | Action that occurred | Object that was changed | Location | -|------------|------------|----------------------|---------------------|-------------------------|-------------------| -| `Hover:` | Exact time | | Action identifier | Object identifier | Teleport on click | +|------------|------------|----------------------|----------------------|-------------------------|-------------------| +| `Hover:` | Exact time | | Action identifier | Object identifier | Teleport on click | *Order can be customized, see [configuration](../config.md)* diff --git a/docs/commands/status.md b/docs/commands/status.md index 7af2c5b6..4822868e 100644 --- a/docs/commands/status.md +++ b/docs/commands/status.md @@ -8,11 +8,11 @@ Permission: `ledger.commands.status` ### `/ledger status` This command will give you the following information. -| Info | Explanation | -|--------------|-----------------------------------------------------------------| -| Version | The current version of Ledger on your server | -| Queue Status | Whether or not the [queue](../queue.md) is **free** or **busy**.| -| Discord | A link to the Fabric Server Tools discord for support | -| Wiki | A link to the wiki | +| Info | Explanation | +|--------------|------------------------------------------------------------------| +| Version | The current version of Ledger on your server | +| Queue Status | Whether or not the [queue](../queue.md) is **free** or **busy**. | +| Discord | A link to the Fabric Server Tools discord for support | +| Wiki | A link to the wiki | ![status example](../assets/status-example.png) \ No newline at end of file diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/AnvilScreenHandlerMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/AnvilScreenHandlerMixin.java index d4978910..d4c9b0fc 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/AnvilScreenHandlerMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/AnvilScreenHandlerMixin.java @@ -13,7 +13,6 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.ModifyArgs; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.invoke.arg.Args; @Mixin(AnvilScreenHandler.class) diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/DyeItemMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/DyeItemMixin.java new file mode 100644 index 00000000..2e287499 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/DyeItemMixin.java @@ -0,0 +1,32 @@ +package com.github.quiltservertools.ledger.mixin; + +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.DyeItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(DyeItem.class) +public abstract class DyeItemMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "useOnEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;setColor(Lnet/minecraft/util/DyeColor;)V")) + private void ledgerOldEntity(ItemStack stack, PlayerEntity player, LivingEntity entity, Hand hand, CallbackInfoReturnable cir) { + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "useOnEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;setColor(Lnet/minecraft/util/DyeColor;)V", shift = At.Shift.AFTER)) + private void ledgerPlayerDyeSheep(ItemStack stack, PlayerEntity player, LivingEntity entity, Hand hand, CallbackInfoReturnable cir) { + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, stack, player, Sources.DYE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/ItemScattererMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/ItemScattererMixin.java index eadaaadb..ff4b9d14 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/ItemScattererMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/ItemScattererMixin.java @@ -10,11 +10,7 @@ import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.ModifyArg; import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; import org.spongepowered.asm.mixin.injection.invoke.arg.Args; @Mixin(ItemScatterer.class) diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/SnowGolemEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/SnowGolemEntityMixin.java deleted file mode 100644 index 3b7debee..00000000 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/SnowGolemEntityMixin.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.quiltservertools.ledger.mixin; - -import com.github.quiltservertools.ledger.callbacks.BlockPlaceCallback; -import com.github.quiltservertools.ledger.utility.Sources; -import net.minecraft.block.BlockState; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.passive.SnowGolemEntity; -import net.minecraft.util.math.BlockPos; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArgs; -import org.spongepowered.asm.mixin.injection.invoke.arg.Args; - -@Mixin(SnowGolemEntity.class) -public abstract class SnowGolemEntityMixin { - @ModifyArgs(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z")) - public void logSnowGolemSnow(Args args) { - BlockPos pos = args.get(0); - BlockState state = args.get(1); - BlockPlaceCallback.EVENT.invoker().place(((LivingEntity) (Object) this).getWorld(), pos, state, null, Sources.SNOW_GOLEM); - } -} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/BedBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/BedBlockMixin.java new file mode 100644 index 00000000..bc8ef93f --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/BedBlockMixin.java @@ -0,0 +1,46 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BedBlock; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.enums.BedPart; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(BedBlock.class) +public abstract class BedBlockMixin { + @Unique + private BlockEntity oldBlockEntity = null; + + @Inject(method = "onBreak", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void storeBlockEntity(World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfoReturnable cir, BedPart bedPart, BlockPos blockPos, BlockState blockState) { + oldBlockEntity = world.getBlockEntity(blockPos); + } + + @Inject(method = "onBreak", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void logBedBreak(World world, BlockPos pos, BlockState state, PlayerEntity player, CallbackInfoReturnable cir, BedPart bedPart, BlockPos blockPos, BlockState blockState) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, blockPos, blockState, oldBlockEntity, player); + } + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;removeBlock(Lnet/minecraft/util/math/BlockPos;Z)Z")) + public void storeBlockEntity(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + oldBlockEntity = world.getBlockEntity(pos); + } + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;removeBlock(Lnet/minecraft/util/math/BlockPos;Z)Z", shift = At.Shift.AFTER)) + public void logBedExplosion(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, pos, state, oldBlockEntity, Sources.INTERACT, player); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CactusBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CactusBlockMixin.java new file mode 100644 index 00000000..bfad40cf --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CactusBlockMixin.java @@ -0,0 +1,21 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.CactusBlock; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(CactusBlock.class) +public abstract class CactusBlockMixin { + @Inject(method = "scheduledTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;breakBlock(Lnet/minecraft/util/math/BlockPos;Z)Z")) + public void logCactusBreak(BlockState state, ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, pos, state, null, Sources.GRAVITY); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CampfireBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CampfireBlockMixin.java index eadcf5ea..030114dd 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CampfireBlockMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CampfireBlockMixin.java @@ -5,7 +5,6 @@ import net.minecraft.block.BlockState; import net.minecraft.block.CampfireBlock; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.CampfireBlockEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.projectile.ProjectileEntity; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CarvedPumpkinBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CarvedPumpkinBlockMixin.java new file mode 100644 index 00000000..467e0026 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/CarvedPumpkinBlockMixin.java @@ -0,0 +1,24 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.CarvedPumpkinBlock; +import net.minecraft.block.pattern.BlockPattern; +import net.minecraft.block.pattern.CachedBlockPosition; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(CarvedPumpkinBlock.class) +public abstract class CarvedPumpkinBlockMixin { + @Inject(method = "breakPatternBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private static void logStatueBreak(World world, BlockPattern.Result patternResult, CallbackInfo ci, int i, int j, CachedBlockPosition cachedBlockPosition) { + if (cachedBlockPosition.getBlockState().isAir()) { + return; + } + BlockBreakCallback.EVENT.invoker().breakBlock(world, cachedBlockPosition.getBlockPos(), cachedBlockPosition.getBlockState(), cachedBlockPosition.getBlockEntity(), Sources.STATUE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusFlowerBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusFlowerBlockMixin.java new file mode 100644 index 00000000..6aada4ab --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusFlowerBlockMixin.java @@ -0,0 +1,28 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.ChorusFlowerBlock; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.ProjectileEntity; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChorusFlowerBlock.class) +public abstract class ChorusFlowerBlockMixin { + @Inject(method = "onProjectileHit", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;)Z")) + public void logChorusFlowerBreak(World world, BlockState state, BlockHitResult hit, ProjectileEntity projectile, CallbackInfo ci) { + Entity entity = projectile.getOwner(); + if (entity instanceof PlayerEntity player) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, hit.getBlockPos(), state, null, Sources.PROJECTILE, player); + } else { + BlockBreakCallback.EVENT.invoker().breakBlock(world, hit.getBlockPos(), state, null, Sources.PROJECTILE); + } + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusPlantBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusPlantBlockMixin.java new file mode 100644 index 00000000..4bd1568f --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ChorusPlantBlockMixin.java @@ -0,0 +1,21 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.ChorusPlantBlock; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChorusPlantBlock.class) +public abstract class ChorusPlantBlockMixin { + @Inject(method = "scheduledTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;breakBlock(Lnet/minecraft/util/math/BlockPos;Z)Z")) + public void logChorusPlantBreak(BlockState state, ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, pos, state, null, Sources.GRAVITY); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ComparatorBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ComparatorBlockMixin.java new file mode 100644 index 00000000..1cf958da --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/ComparatorBlockMixin.java @@ -0,0 +1,32 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.ComparatorBlock; +import net.minecraft.block.enums.ComparatorMode; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.EnumProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.callback.CallbackInfoReturnable; + +@Mixin(ComparatorBlock.class) +public abstract class ComparatorBlockMixin { + @Shadow + @Final + public static EnumProperty MODE; + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logComparatorInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state.cycle(MODE), state, world.getBlockEntity(pos), world.getBlockEntity(pos), Sources.INTERACT, player); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/DoorBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/DoorBlockMixin.java new file mode 100644 index 00000000..46e5476e --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/DoorBlockMixin.java @@ -0,0 +1,38 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.DoorBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(DoorBlock.class) +public abstract class DoorBlockMixin { + @Shadow + @Final + public static BooleanProperty OPEN; + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logDoorInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state.cycle(OPEN), state, null, null, Sources.INTERACT, player); + } + + @Inject(method = "neighborUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logRedstoneDoorInteraction(BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify, CallbackInfo ci) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state, state.cycle(OPEN), null, null, Sources.REDSTONE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/FenceGateBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/FenceGateBlockMixin.java new file mode 100644 index 00000000..8d995c7c --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/FenceGateBlockMixin.java @@ -0,0 +1,38 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.FenceGateBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(FenceGateBlock.class) +public abstract class FenceGateBlockMixin { + @Shadow + @Final + public static BooleanProperty OPEN; + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logFenceGateInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state.cycle(OPEN), state, null, null, Sources.INTERACT, player); + } + + @Inject(method = "neighborUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logRedstoneFenceGateInteraction(BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify, CallbackInfo ci) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state, state.cycle(OPEN), null, null, Sources.REDSTONE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/LilyPadBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/LilyPadBlockMixin.java new file mode 100644 index 00000000..b1f101fb --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/LilyPadBlockMixin.java @@ -0,0 +1,28 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.LilyPadBlock; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.vehicle.BoatEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LilyPadBlock.class) +public abstract class LilyPadBlockMixin { + @Inject(method = "onEntityCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;breakBlock(Lnet/minecraft/util/math/BlockPos;ZLnet/minecraft/entity/Entity;)Z")) + private void ledgerLogLilyPadBreak(BlockState state, World world, BlockPos pos, Entity entity, CallbackInfo ci) { + BoatEntity boat = (BoatEntity) entity; + if (boat.getFirstPassenger() instanceof PlayerEntity player) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, new BlockPos(pos), state, null, Sources.VEHICLE, player); + } else { + BlockBreakCallback.EVENT.invoker().breakBlock(world, new BlockPos(pos), state, null, Sources.VEHICLE); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/RepeaterBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/RepeaterBlockMixin.java new file mode 100644 index 00000000..04a02363 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/RepeaterBlockMixin.java @@ -0,0 +1,31 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.RepeaterBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.IntProperty; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.callback.CallbackInfoReturnable; + +@Mixin(RepeaterBlock.class) +public abstract class RepeaterBlockMixin { + @Shadow + @Final + public static IntProperty DELAY; + + @Inject(method = "onUse", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logRepeaterInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, CallbackInfoReturnable cir) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state, state.cycle(DELAY), null, null, Sources.INTERACT, player); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/SugarCaneBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/SugarCaneBlockMixin.java new file mode 100644 index 00000000..efd6994e --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/SugarCaneBlockMixin.java @@ -0,0 +1,21 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.SugarCaneBlock; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.random.Random; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(SugarCaneBlock.class) +public abstract class SugarCaneBlockMixin { + @Inject(method = "scheduledTick", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;breakBlock(Lnet/minecraft/util/math/BlockPos;Z)Z")) + public void logSugarCaneBreak(BlockState state, ServerWorld world, BlockPos pos, Random random, CallbackInfo ci) { + BlockBreakCallback.EVENT.invoker().breakBlock(world, pos, state, null, Sources.GRAVITY); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/TrapdoorBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/TrapdoorBlockMixin.java new file mode 100644 index 00000000..f14af511 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/TrapdoorBlockMixin.java @@ -0,0 +1,34 @@ +package com.github.quiltservertools.ledger.mixin.blocks; + +import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.TrapdoorBlock; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +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.callback.CallbackInfo; + +@Mixin(TrapdoorBlock.class) +public abstract class TrapdoorBlockMixin { + @Shadow + @Final + public static BooleanProperty OPEN; + + @Inject(method = "flip", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logTrapdoorInteraction(BlockState state, World world, BlockPos pos, PlayerEntity player, CallbackInfo ci) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state.cycle(OPEN), state, null, null, Sources.INTERACT, player); + } + + @Inject(method = "neighborUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;I)Z")) + public void logTrapdoorRedstoneInteraction(BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify, CallbackInfo ci) { + BlockChangeCallback.EVENT.invoker().changeBlock(world, pos, state, state.cycle(OPEN), null, null, Sources.REDSTONE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/cauldron/LeveledCauldronBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/cauldron/LeveledCauldronBlockMixin.java index 6c0f585b..a615d709 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/cauldron/LeveledCauldronBlockMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/cauldron/LeveledCauldronBlockMixin.java @@ -1,6 +1,5 @@ package com.github.quiltservertools.ledger.mixin.blocks.cauldron; -import com.github.quiltservertools.ledger.LedgerKt; import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback; import com.github.quiltservertools.ledger.utility.Sources; import net.minecraft.block.BlockState; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternBlockMixin.java new file mode 100644 index 00000000..4d6633cf --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternBlockMixin.java @@ -0,0 +1,34 @@ +package com.github.quiltservertools.ledger.mixin.blocks.lectern; + +import com.github.quiltservertools.ledger.callbacks.ItemInsertCallback; +import com.github.quiltservertools.ledger.utility.PlayerLecternHook; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.block.LecternBlock; +import net.minecraft.block.entity.LecternBlockEntity; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(LecternBlock.class) +public class LecternBlockMixin { + @Inject(method = "putBook", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;playSound(Lnet/minecraft/entity/player/PlayerEntity;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FF)V")) + private static void logPutBook(Entity user, World world, BlockPos pos, BlockState state, ItemStack stack, CallbackInfo ci) { + LecternBlockEntity blockEntity = (LecternBlockEntity) world.getBlockEntity(pos); + if (blockEntity == null) return; + ItemInsertCallback.EVENT.invoker().insert(blockEntity.getBook(), pos, (ServerWorld) world, Sources.PLAYER, (ServerPlayerEntity) user); + } + + @Inject(method = "openScreen", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;openHandledScreen(Lnet/minecraft/screen/NamedScreenHandlerFactory;)Ljava/util/OptionalInt;")) + public void storeLectern(World world, BlockPos pos, PlayerEntity player, CallbackInfo ci) { + PlayerLecternHook.getActiveHandlers().put(player, world.getBlockEntity(pos)); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternScreenHandlerMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternScreenHandlerMixin.java new file mode 100644 index 00000000..f15f07e0 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/LecternScreenHandlerMixin.java @@ -0,0 +1,25 @@ +package com.github.quiltservertools.ledger.mixin.blocks.lectern; + +import com.github.quiltservertools.ledger.callbacks.ItemRemoveCallback; +import com.github.quiltservertools.ledger.utility.PlayerLecternHook; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.LecternScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(LecternScreenHandler.class) +public class LecternScreenHandlerMixin { + @Inject(method = "onButtonClick", at = @At(value = "INVOKE", target = "Lnet/minecraft/inventory/Inventory;markDirty()V"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void logPickBook(PlayerEntity player, int id, CallbackInfoReturnable cir, ItemStack itemStack) { + ServerPlayerEntity serverPlayer = (ServerPlayerEntity) player; + BlockEntity blockEntity = PlayerLecternHook.getActiveHandlers().get(player); + ItemRemoveCallback.EVENT.invoker().remove(itemStack, blockEntity.getPos(), serverPlayer.getServerWorld(), Sources.PLAYER, serverPlayer); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/ScreenHandlerMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/ScreenHandlerMixin.java new file mode 100644 index 00000000..e778eee9 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/blocks/lectern/ScreenHandlerMixin.java @@ -0,0 +1,20 @@ +package com.github.quiltservertools.ledger.mixin.blocks.lectern; + +import com.github.quiltservertools.ledger.utility.PlayerLecternHook; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.screen.LecternScreenHandler; +import net.minecraft.screen.ScreenHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ScreenHandler.class) +public class ScreenHandlerMixin { + @Inject(method = "onClosed", at = @At(value = "INVOKE")) + public void onClosed(PlayerEntity player, CallbackInfo ci) { + if (player.currentScreenHandler instanceof LecternScreenHandler) { + PlayerLecternHook.getActiveHandlers().remove(player); + } + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ArmorStandEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ArmorStandEntityMixin.java new file mode 100644 index 00000000..1b94f492 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ArmorStandEntityMixin.java @@ -0,0 +1,57 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityKillCallback; +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ArmorStandEntity.class) +public abstract class ArmorStandEntityMixin { + @Unique + private NbtCompound oldEntityTags; + @Unique + private ItemStack oldEntityStack; + + @Inject(method = "equip", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ArmorStandEntity;equipStack(Lnet/minecraft/entity/EquipmentSlot;Lnet/minecraft/item/ItemStack;)V")) + private void legerLogOldEntity(PlayerEntity player, EquipmentSlot slot, ItemStack playerStack, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + this.oldEntityTags = entity.writeNbt(new NbtCompound()); + this.oldEntityStack = entity.getEquippedStack(slot); + } + + @Inject(method = "equip", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ArmorStandEntity;equipStack(Lnet/minecraft/entity/EquipmentSlot;Lnet/minecraft/item/ItemStack;)V", shift = At.Shift.AFTER)) + private void ledgerArmorStandInteract(PlayerEntity player, EquipmentSlot slot, ItemStack playerStack, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + if (!oldEntityStack.isEmpty()) { + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, oldEntityStack, player, Sources.REMOVE); + } + if (!playerStack.isEmpty()) { + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, playerStack, player, Sources.EQUIP); + } + } + + @Inject(method = "updateHealth", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ArmorStandEntity;kill()V")) + private void ledgerArmorStandKill(DamageSource damageSource, float amount, CallbackInfo ci) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityKillCallback.EVENT.invoker().kill(entity.getWorld(), entity.getBlockPos(), entity, damageSource); + } + + @Inject(method = "damage", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ArmorStandEntity;kill()V")) + private void ledgerArmorStandKill(DamageSource source, float amount, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityKillCallback.EVENT.invoker().kill(entity.getWorld(), entity.getBlockPos(), entity, source); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/CatEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/CatEntityMixin.java new file mode 100644 index 00000000..939b8032 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/CatEntityMixin.java @@ -0,0 +1,33 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.passive.CatEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(CatEntity.class) +public abstract class CatEntityMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/CatEntity;setCollarColor(Lnet/minecraft/util/DyeColor;)V")) + private void ledgerLogOldEntity(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + this.oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/CatEntity;setCollarColor(Lnet/minecraft/util/DyeColor;)V", shift = At.Shift.AFTER)) + private void ledgerCatCollarColour(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, player.getStackInHand(hand), player, Sources.DYE); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/CreeperEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/CreeperEntityMixin.java similarity index 95% rename from src/main/java/com/github/quiltservertools/ledger/mixin/CreeperEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/CreeperEntityMixin.java index cbc45ff6..3dad3a3f 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/CreeperEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/CreeperEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.utility.PlayerCausable; import net.minecraft.entity.mob.CreeperEntity; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/EndCrystalEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EndCrystalEntityMixin.java similarity index 96% rename from src/main/java/com/github/quiltservertools/ledger/mixin/EndCrystalEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/EndCrystalEntityMixin.java index df8f125d..340a0ea1 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/EndCrystalEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EndCrystalEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.utility.PlayerCausable; import net.minecraft.entity.damage.DamageSource; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EnderDragonEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EnderDragonEntityMixin.java new file mode 100644 index 00000000..28c4169c --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EnderDragonEntityMixin.java @@ -0,0 +1,23 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; +import net.minecraft.entity.boss.dragon.EnderDragonEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.Box; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(EnderDragonEntity.class) +public abstract class EnderDragonEntityMixin { + @Inject(method = "destroyBlocks", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;removeBlock(Lnet/minecraft/util/math/BlockPos;Z)Z"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private void logEnderDragonBreakingBlocks(Box box, CallbackInfoReturnable cir, int i, int j, int k, int l, int m, int n, boolean bl, boolean bl2, int o, int p, int q, BlockPos blockPos) { + EnderDragonEntity entity = (EnderDragonEntity) (Object) this; + World world = entity.getEntityWorld(); + BlockBreakCallback.EVENT.invoker().breakBlock(world, blockPos, world.getBlockState(blockPos), world.getBlockEntity(blockPos), Registries.ENTITY_TYPE.getId(entity.getType()).getPath()); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EvokerEntityWololoGoalMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EvokerEntityWololoGoalMixin.java new file mode 100644 index 00000000..97355af6 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/EvokerEntityWololoGoalMixin.java @@ -0,0 +1,35 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.mob.EvokerEntity; +import net.minecraft.entity.passive.SheepEntity; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.DyeColor; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(EvokerEntity.WololoGoal.class) +public abstract class EvokerEntityWololoGoalMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "castSpell", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;setColor(Lnet/minecraft/util/DyeColor;)V"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void legerLogOldEntity(CallbackInfo ci, SheepEntity sheepEntity) { + if (sheepEntity.getColor() != DyeColor.RED) { + this.oldEntityTags = sheepEntity.writeNbt(new NbtCompound()); + } + } + + @Inject(method = "castSpell", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;setColor(Lnet/minecraft/util/DyeColor;)V", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void ledgerEvokerDyeSheep(CallbackInfo ci, SheepEntity sheepEntity) { + if (oldEntityTags != null) { + EntityModifyCallback.EVENT.invoker().modify(sheepEntity.getWorld(), sheepEntity.getBlockPos(), oldEntityTags, sheepEntity, Items.RED_DYE.getDefaultStack(), null, Sources.DYE); + } + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/ExplosiveProjectileEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ExplosiveProjectileEntityMixin.java similarity index 92% rename from src/main/java/com/github/quiltservertools/ledger/mixin/ExplosiveProjectileEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/ExplosiveProjectileEntityMixin.java index ff800edd..b911ec29 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/ExplosiveProjectileEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ExplosiveProjectileEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.utility.PlayerCausable; import net.minecraft.entity.mob.MobEntity; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/FallingBlockEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/FallingBlockEntityMixin.java similarity index 97% rename from src/main/java/com/github/quiltservertools/ledger/mixin/FallingBlockEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/FallingBlockEntityMixin.java index 113ae23d..55802056 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/FallingBlockEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/FallingBlockEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; import com.github.quiltservertools.ledger.callbacks.BlockPlaceCallback; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemEntityMixin.java new file mode 100644 index 00000000..596cb7e9 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemEntityMixin.java @@ -0,0 +1,26 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.ItemPickUpCallback; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ItemEntity.class) +public abstract class ItemEntityMixin { + @Unique + private ItemEntity itemEntity; + + @Inject(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;insertStack(Lnet/minecraft/item/ItemStack;)Z")) + private void storeEntity(PlayerEntity player, CallbackInfo ci) { + itemEntity = ((ItemEntity) (Object) this).copy(); + } + + @Inject(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;sendPickup(Lnet/minecraft/entity/Entity;I)V")) + private void logPlayerItemPickUp(PlayerEntity player, CallbackInfo ci) { + ItemPickUpCallback.EVENT.invoker().pickUp(itemEntity, player); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemFrameEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemFrameEntityMixin.java new file mode 100644 index 00000000..b7b7c5cc --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/ItemFrameEntityMixin.java @@ -0,0 +1,82 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityKillCallback; +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.Entity; +import net.minecraft.entity.damage.DamageSource; +import net.minecraft.entity.decoration.ItemFrameEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(ItemFrameEntity.class) +public abstract class ItemFrameEntityMixin { + @Shadow + public abstract ItemStack getHeldItemStack(); + + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "interact", at = @At(value = "HEAD")) + private void ledgerLogOldEntity(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + Entity entity = (Entity) (Object) this; + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "dropHeldStack", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ItemFrameEntity;setHeldItemStack(Lnet/minecraft/item/ItemStack;)V")) + private void ledgerLogOldEntity2(@Nullable Entity entityActor, boolean alwaysDrop, CallbackInfo ci) { + if (entityActor == null) { + return; + } + Entity entity = (Entity) (Object) this; + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "interact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ItemFrameEntity;setHeldItemStack(Lnet/minecraft/item/ItemStack;)V", shift = At.Shift.AFTER)) + private void ledgerItemFrameEquip(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + ItemStack playerStack = player.getStackInHand(hand); + Entity entity = (Entity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, playerStack, player, Sources.EQUIP); + } + + @Inject(method = "dropHeldStack", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ItemFrameEntity;setHeldItemStack(Lnet/minecraft/item/ItemStack;)V")) + private void ledgerItemFrameRemove(@Nullable Entity entityActor, boolean alwaysDrop, CallbackInfo ci) { + ItemStack entityStack = this.getHeldItemStack(); + if (entityStack.isEmpty() || entityActor == null) { + return; + } + Entity entity = (Entity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(entityActor.getWorld(), entity.getBlockPos(), oldEntityTags, entity, entityStack, entityActor, Sources.REMOVE); + } + + @Inject(method = "interact", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/ItemFrameEntity;setRotation(I)V", shift = At.Shift.AFTER)) + private void ledgerItemFrameRotate(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + Entity entity = (Entity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, null, player, Sources.ROTATE); + } + + @Inject(method = "damage", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/decoration/AbstractDecorationEntity;damage(Lnet/minecraft/entity/damage/DamageSource;F)Z")) + private void ledgerItemFrameKill(DamageSource source, float amount, CallbackInfoReturnable cir) { + Entity entity = (Entity) (Object) this; + EntityKillCallback.EVENT.invoker().kill(entity.getWorld(), entity.getBlockPos(), entity, source); + } + + @Inject(method = "canStayAttached", at = @At(value = "RETURN")) + private void ledgerItemFrameKill(CallbackInfoReturnable cir) { + if (cir.getReturnValue() == Boolean.FALSE) { + Entity entity = (Entity) (Object) this; + EntityKillCallback.EVENT.invoker().kill(entity.getWorld(), entity.getBlockPos(), entity, entity.getDamageSources().magic()); + } + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/LightningEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/LightningEntityMixin.java new file mode 100644 index 00000000..268c10b2 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/LightningEntityMixin.java @@ -0,0 +1,27 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.BlockPlaceCallback; +import net.minecraft.block.BlockState; +import net.minecraft.entity.LightningEntity; +import net.minecraft.registry.Registries; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(LightningEntity.class) +public abstract class LightningEntityMixin { + @Inject(method = "spawnFire", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z", shift = At.Shift.AFTER, ordinal = 0), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private void logFirePlacedByLightningBolt(int spreadAttempts, CallbackInfo ci, BlockPos blockPos, BlockState blockState) { + LightningEntity entity = (LightningEntity) (Object) this; + BlockPlaceCallback.EVENT.invoker().place(entity.getEntityWorld(), blockPos, blockState, null, Registries.ENTITY_TYPE.getId(entity.getType()).getPath()); + } + + @Inject(method = "spawnFire", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z", shift = At.Shift.AFTER, ordinal = 1), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private void logFirePlacedByLightningBolt(int spreadAttempts, CallbackInfo ci, BlockPos blockPos, BlockState blockState, int i, BlockPos blockPos2) { + LightningEntity entity = (LightningEntity) (Object) this; + BlockPlaceCallback.EVENT.invoker().place(entity.getEntityWorld(), blockPos2, blockState, null, Registries.ENTITY_TYPE.getId(entity.getType()).getPath()); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/LivingEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/LivingEntityMixin.java similarity index 93% rename from src/main/java/com/github/quiltservertools/ledger/mixin/LivingEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/LivingEntityMixin.java index 79f7fbe3..4752401b 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/LivingEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/LivingEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.callbacks.EntityKillCallback; import net.minecraft.entity.LivingEntity; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/PlayerEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/PlayerEntityMixin.java new file mode 100644 index 00000000..f121a672 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/PlayerEntityMixin.java @@ -0,0 +1,20 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.ItemDropCallback; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(PlayerEntity.class) +public abstract class PlayerEntityMixin { + @Inject(method = "dropItem(Lnet/minecraft/item/ItemStack;ZZ)Lnet/minecraft/entity/ItemEntity;", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/ItemEntity;setPickupDelay(I)V"), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + private void logPlayerItemDrop(ItemStack stack, boolean throwRandomly, boolean retainOwnership, CallbackInfoReturnable cir, double d, ItemEntity itemEntity) { + PlayerEntity player = (PlayerEntity) (Object) this; + ItemDropCallback.EVENT.invoker().drop(itemEntity, player); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/RavagerEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/RavagerEntityMixin.java similarity index 94% rename from src/main/java/com/github/quiltservertools/ledger/mixin/RavagerEntityMixin.java rename to src/main/java/com/github/quiltservertools/ledger/mixin/entities/RavagerEntityMixin.java index 28d94ad5..e8339af2 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/RavagerEntityMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/RavagerEntityMixin.java @@ -1,4 +1,4 @@ -package com.github.quiltservertools.ledger.mixin; +package com.github.quiltservertools.ledger.mixin.entities; import com.github.quiltservertools.ledger.callbacks.BlockBreakCallback; import net.minecraft.entity.mob.RavagerEntity; diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SheepEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SheepEntityMixin.java new file mode 100644 index 00000000..9fffbdad --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SheepEntityMixin.java @@ -0,0 +1,33 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.passive.SheepEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(SheepEntity.class) +public abstract class SheepEntityMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V")) + private void ledgerLogOldEntity(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SheepEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V", shift = At.Shift.AFTER)) + private void ledgerSheepWoolShear(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, null, player, Sources.SHEAR); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SnowGolemEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SnowGolemEntityMixin.java new file mode 100644 index 00000000..64e4d844 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/SnowGolemEntityMixin.java @@ -0,0 +1,43 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.BlockPlaceCallback; +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.block.BlockState; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.passive.SnowGolemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +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.LocalCapture; + +@Mixin(SnowGolemEntity.class) +public abstract class SnowGolemEntityMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "tickMovement", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/World;setBlockState(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION) + public void logSnowGolemSnow(CallbackInfo ci, BlockState blockState, int i, int j, int k, int l, BlockPos blockPos) { + BlockPlaceCallback.EVENT.invoker().place(((LivingEntity) (Object) this).getWorld(), blockPos, blockState, null, Sources.SNOW_GOLEM); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SnowGolemEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V")) + private void ledgerOldEntity(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/SnowGolemEntity;sheared(Lnet/minecraft/sound/SoundCategory;)V", shift = At.Shift.AFTER)) + private void ledgerSnowGolemPumpkinShear(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, null, player, Sources.SHEAR); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/VillagerEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/VillagerEntityMixin.java new file mode 100644 index 00000000..cc5fbf8a --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/VillagerEntityMixin.java @@ -0,0 +1,20 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityKillCallback; +import net.minecraft.entity.LightningEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.passive.VillagerEntity; +import net.minecraft.server.world.ServerWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(VillagerEntity.class) +public abstract class VillagerEntityMixin { + @Inject(method = "onStruckByLightning", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;spawnEntityAndPassengers(Lnet/minecraft/entity/Entity;)V")) + private void ledgerVillagerToWitch(ServerWorld world, LightningEntity lightning, CallbackInfo ci) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityKillCallback.EVENT.invoker().kill(entity.getWorld(), entity.getBlockPos(), entity, world.getDamageSources().lightningBolt()); + } +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/entities/WolfEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/WolfEntityMixin.java new file mode 100644 index 00000000..9e8e4daf --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/entities/WolfEntityMixin.java @@ -0,0 +1,33 @@ +package com.github.quiltservertools.ledger.mixin.entities; + +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback; +import com.github.quiltservertools.ledger.utility.Sources; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.passive.WolfEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(WolfEntity.class) +public abstract class WolfEntityMixin { + @Unique + private NbtCompound oldEntityTags; + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/WolfEntity;setCollarColor(Lnet/minecraft/util/DyeColor;)V")) + private void ledgerOldEntity(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + oldEntityTags = entity.writeNbt(new NbtCompound()); + } + + @Inject(method = "interactMob", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/passive/WolfEntity;setCollarColor(Lnet/minecraft/util/DyeColor;)V", shift = At.Shift.AFTER)) + private void ledgerDogCollarColour(PlayerEntity player, Hand hand, CallbackInfoReturnable cir) { + LivingEntity entity = (LivingEntity) (Object) this; + EntityModifyCallback.EVENT.invoker().modify(player.getWorld(), entity.getBlockPos(), oldEntityTags, entity, player.getStackInHand(hand), player, Sources.DYE); + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt b/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt index c34c5471..a8871d48 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt @@ -30,7 +30,8 @@ import java.nio.file.Files import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.coroutines.CoroutineContext -import kotlin.time.Duration +import kotlin.time.Duration.Companion.minutes +import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime import com.github.quiltservertools.ledger.config.config as realConfig @@ -92,10 +93,10 @@ object Ledger : DedicatedServerModInitializer, CoroutineScope { @OptIn(ExperimentalTime::class) private fun serverStopped(server: MinecraftServer) { runBlocking { - withTimeout(Duration.minutes(config[DatabaseSpec.queueTimeoutMin])) { + withTimeout(config[DatabaseSpec.queueTimeoutMin].minutes) { while (DatabaseManager.dbMutex.isLocked) { logInfo("Database queue is still draining. If you exit now actions WILL be lost") - delay(Duration.seconds(config[DatabaseSpec.queueCheckDelaySec])) + delay(config[DatabaseSpec.queueCheckDelaySec].seconds) } } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt index a940da55..02ca1d95 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt @@ -5,7 +5,6 @@ import com.github.quiltservertools.ledger.utility.Sources import com.github.quiltservertools.ledger.utility.TextColorPallet import com.github.quiltservertools.ledger.utility.literal import com.mojang.authlib.GameProfile -import net.minecraft.block.BlockState import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.ClickEvent @@ -25,8 +24,8 @@ abstract class AbstractActionType : ActionType { override var world: Identifier? = null override var objectIdentifier: Identifier = Identifier("air") override var oldObjectIdentifier: Identifier = Identifier("air") - override var blockState: BlockState? = null - override var oldBlockState: BlockState? = null + override var objectState: String? = null + override var oldObjectState: String? = null override var sourceName: String = Sources.UNKNOWN override var sourceProfile: GameProfile? = null override var extraData: String? = null diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt index 97654822..d8e4239c 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt @@ -3,7 +3,6 @@ package com.github.quiltservertools.ledger.actions import com.github.quiltservertools.ledger.config.ActionsSpec import com.github.quiltservertools.ledger.config.config import com.mojang.authlib.GameProfile -import net.minecraft.block.BlockState import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text @@ -19,8 +18,8 @@ interface ActionType { var world: Identifier? var objectIdentifier: Identifier var oldObjectIdentifier: Identifier - var blockState: BlockState? - var oldBlockState: BlockState? + var objectState: String? + var oldObjectState: String? var sourceName: String var sourceProfile: GameProfile? var extraData: String? diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt index 4009d8e9..67896873 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt @@ -1,6 +1,7 @@ package com.github.quiltservertools.ledger.actions import com.github.quiltservertools.ledger.logWarn +import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.TextColorPallet import com.github.quiltservertools.ledger.utility.getWorld import com.github.quiltservertools.ledger.utility.literal @@ -85,9 +86,19 @@ open class BlockChangeActionType : AbstractActionType() { return text } - fun oldBlockState() = checkForBlockState(oldObjectIdentifier, oldBlockState) - - fun newBlockState() = checkForBlockState(objectIdentifier, blockState) + fun oldBlockState() = checkForBlockState(oldObjectIdentifier, oldObjectState?.let { + NbtUtils.blockStateFromProperties( + StringNbtReader.parse(it), + oldObjectIdentifier + ) + }) + + fun newBlockState() = checkForBlockState(objectIdentifier, objectState?.let { + NbtUtils.blockStateFromProperties( + StringNbtReader.parse(it), + objectIdentifier + ) + }) private fun checkForBlockState(identifier: Identifier, checkState: BlockState?): BlockState { val block = Registries.BLOCK.getOrEmpty(identifier) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityChangeActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityChangeActionType.kt new file mode 100644 index 00000000..b404fcf6 --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityChangeActionType.kt @@ -0,0 +1,111 @@ +package com.github.quiltservertools.ledger.actions + +import com.github.quiltservertools.ledger.utility.NbtUtils +import com.github.quiltservertools.ledger.utility.TextColorPallet +import com.github.quiltservertools.ledger.utility.UUID +import com.github.quiltservertools.ledger.utility.getWorld +import com.github.quiltservertools.ledger.utility.literal +import net.minecraft.entity.LivingEntity +import net.minecraft.entity.decoration.AbstractDecorationEntity +import net.minecraft.entity.decoration.ItemFrameEntity +import net.minecraft.item.AliasedBlockItem +import net.minecraft.item.BlockItem +import net.minecraft.item.ItemStack +import net.minecraft.nbt.StringNbtReader +import net.minecraft.registry.Registries +import net.minecraft.server.MinecraftServer +import net.minecraft.text.HoverEvent +import net.minecraft.text.Text +import net.minecraft.util.Identifier +import net.minecraft.util.Util + +class EntityChangeActionType : AbstractActionType() { + override val identifier = "entity-change" + + override fun getTranslationType(): String { + val item = Registries.ITEM.get(Identifier(extraData)) + return if (item is BlockItem && item !is AliasedBlockItem) { + "block" + } else { + "item" + } + } + + override fun getObjectMessage(): Text { + val text = Text.literal("") + text.append( + Text.translatable( + Util.createTranslationKey( + "entity", + objectIdentifier + ) + ).setStyle(TextColorPallet.secondaryVariant).styled { + it.withHoverEvent( + HoverEvent( + HoverEvent.Action.SHOW_TEXT, + objectIdentifier.toString().literal() + ) + ) + }) + + if (extraData != null && Identifier(extraData) != Identifier.tryParse("minecraft:air")) { + val stack = NbtUtils.itemFromProperties(null, Identifier(extraData)) + text.append(Text.literal(" ").append(Text.translatable("text.ledger.action_message.with")).append(" ")) + text.append( + Text.translatable( + Util.createTranslationKey( + this.getTranslationType(), + Identifier(extraData) + ) + ).setStyle(TextColorPallet.secondaryVariant).styled { + it.withHoverEvent( + HoverEvent( + HoverEvent.Action.SHOW_ITEM, + HoverEvent.ItemStackContent(stack) + ) + ) + }) + } + return text + } + + override fun rollback(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + + val oldEntity = StringNbtReader.parse(oldObjectState) + val uuid = oldEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity != null) { + if (entity is ItemFrameEntity) { + entity.heldItemStack = ItemStack.EMPTY + } + when (entity) { + is LivingEntity -> entity.readCustomDataFromNbt(oldEntity) + is AbstractDecorationEntity -> entity.readCustomDataFromNbt(oldEntity) + } + return true + } + return false + } + + + override fun restore(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + val newEntity = StringNbtReader.parse(objectState) + val uuid = newEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity != null) { + if (entity is ItemFrameEntity) { + entity.heldItemStack = ItemStack.EMPTY + } + when (entity) { + is LivingEntity -> entity.readCustomDataFromNbt(newEntity) + is AbstractDecorationEntity -> entity.readCustomDataFromNbt(newEntity) + } + return true + } + return false + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt index 88e346a0..910b5d75 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt @@ -1,5 +1,6 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.utility.UUID import com.github.quiltservertools.ledger.utility.getWorld import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity @@ -18,11 +19,11 @@ class EntityKillActionType : AbstractActionType() { val entityType = Registries.ENTITY_TYPE.getOrEmpty(objectIdentifier) if (entityType.isPresent) { - val entity: LivingEntity = (entityType.get().create(world) as LivingEntity?)!! + val entity = entityType.get().create(world)!! entity.readNbt(StringNbtReader.parse(extraData)) - entity.health = entity.defaultMaxHealth.toFloat() entity.velocity = Vec3d.ZERO entity.fireTicks = 0 + if (entity is LivingEntity) { entity.health = entity.defaultMaxHealth.toFloat() } world?.spawnEntity(entity) @@ -35,15 +36,12 @@ class EntityKillActionType : AbstractActionType() { override fun restore(server: MinecraftServer): Boolean { val world = server.getWorld(world) - val tag = StringNbtReader.parse(extraData) - if (tag.containsUuid("UUID")) { - val uuid = tag.getUuid("UUID") - val entity = world?.getEntity(uuid) + val uuid = StringNbtReader.parse(extraData)!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) - if (entity != null) { - entity.remove(Entity.RemovalReason.DISCARDED) - return true - } + if (entity != null) { + entity.remove(Entity.RemovalReason.DISCARDED) + return true } return false diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt index 552ecf35..65978e51 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt @@ -1,15 +1,19 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.TextColorPallet import com.github.quiltservertools.ledger.utility.getWorld import com.github.quiltservertools.ledger.utility.literal import net.minecraft.block.ChestBlock import net.minecraft.block.InventoryProvider +import net.minecraft.block.LecternBlock import net.minecraft.block.entity.ChestBlockEntity +import net.minecraft.block.entity.LecternBlockEntity import net.minecraft.inventory.Inventory +import net.minecraft.item.AliasedBlockItem import net.minecraft.item.BlockItem import net.minecraft.item.ItemStack -import net.minecraft.nbt.StringNbtReader +import net.minecraft.item.Items import net.minecraft.registry.Registries import net.minecraft.server.MinecraftServer import net.minecraft.server.world.ServerWorld @@ -20,7 +24,7 @@ import net.minecraft.util.Util abstract class ItemChangeActionType : AbstractActionType() { override fun getTranslationType(): String { val item = Registries.ITEM.get(objectIdentifier) - return if (item is BlockItem) { + return if (item is BlockItem && item !is AliasedBlockItem) { "block" } else { "item" @@ -28,7 +32,7 @@ abstract class ItemChangeActionType : AbstractActionType() { } override fun getObjectMessage(): Text { - val stack = ItemStack.fromNbt(StringNbtReader.parse(extraData)) + val stack = NbtUtils.itemFromProperties(extraData, objectIdentifier) return "${stack.count} ".literal().append( Text.translatable( @@ -47,7 +51,7 @@ abstract class ItemChangeActionType : AbstractActionType() { } } - protected fun getInventory(world: ServerWorld): Inventory? { + private fun getInventory(world: ServerWorld): Inventory? { var inventory: Inventory? = null val blockState = world.getBlockState(pos) val block = blockState.block @@ -70,13 +74,22 @@ abstract class ItemChangeActionType : AbstractActionType() { val world = server.getWorld(world) val inventory = world?.let { getInventory(it) } - if (world != null && inventory != null) { - val rollbackStack = ItemStack.fromNbt(StringNbtReader.parse(extraData)) + if (world != null) { + val rollbackStack = NbtUtils.itemFromProperties(extraData, objectIdentifier) - for (i in 0 until inventory.size()) { - val stack = inventory.getStack(i) - if (ItemStack.areItemsEqual(stack, rollbackStack)) { - inventory.setStack(i, ItemStack.EMPTY) + if (inventory != null) { + for (i in 0 until inventory.size()) { + val stack = inventory.getStack(i) + if (ItemStack.areItemsEqual(stack, rollbackStack)) { + inventory.setStack(i, ItemStack.EMPTY) + return true + } + } + } else if (rollbackStack.isOf(Items.WRITABLE_BOOK) || rollbackStack.isOf(Items.WRITTEN_BOOK)) { + val blockEntity = world.getBlockEntity(pos) + if (blockEntity is LecternBlockEntity) { + blockEntity.book = ItemStack.EMPTY + LecternBlock.setHasBook(null, world, pos, blockEntity.cachedState, false) return true } } @@ -89,13 +102,22 @@ abstract class ItemChangeActionType : AbstractActionType() { val world = server.getWorld(world) val inventory = world?.let { getInventory(it) } - if (world != null && inventory != null) { - val rollbackStack = ItemStack.fromNbt(StringNbtReader.parse(extraData)) + if (world != null) { + val rollbackStack = NbtUtils.itemFromProperties(extraData, objectIdentifier) - for (i in 0 until inventory.size()) { - val stack = inventory.getStack(i) - if (stack.isEmpty) { - inventory.setStack(i, rollbackStack) + if (inventory != null) { + for (i in 0 until inventory.size()) { + val stack = inventory.getStack(i) + if (stack.isEmpty) { + inventory.setStack(i, rollbackStack) + return true + } + } + } else if (rollbackStack.isOf(Items.WRITABLE_BOOK) || rollbackStack.isOf(Items.WRITTEN_BOOK)) { + val blockEntity = world.getBlockEntity(pos) + if (blockEntity is LecternBlockEntity && !blockEntity.hasBook()) { + blockEntity.book = rollbackStack + LecternBlock.setHasBook(null, world, pos, blockEntity.cachedState, true) return true } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemDropActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemDropActionType.kt new file mode 100644 index 00000000..6af0b5e9 --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemDropActionType.kt @@ -0,0 +1,78 @@ +package com.github.quiltservertools.ledger.actions + +import com.github.quiltservertools.ledger.utility.NbtUtils +import com.github.quiltservertools.ledger.utility.TextColorPallet +import com.github.quiltservertools.ledger.utility.UUID +import com.github.quiltservertools.ledger.utility.getWorld +import com.github.quiltservertools.ledger.utility.literal +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityType +import net.minecraft.entity.ItemEntity +import net.minecraft.item.AliasedBlockItem +import net.minecraft.item.BlockItem +import net.minecraft.nbt.StringNbtReader +import net.minecraft.registry.Registries +import net.minecraft.server.MinecraftServer +import net.minecraft.text.HoverEvent +import net.minecraft.text.Text +import net.minecraft.util.Util + +open class ItemDropActionType : AbstractActionType() { + override val identifier = "item-drop" + + override fun getTranslationType(): String { + val item = Registries.ITEM.get(objectIdentifier) + return if (item is BlockItem && item !is AliasedBlockItem) { + "block" + } else { + "item" + } + } + + override fun getObjectMessage(): Text { + val stack = NbtUtils.itemFromProperties(extraData, objectIdentifier) + + return "${stack.count} ".literal().append( + Text.translatable( + Util.createTranslationKey( + getTranslationType(), objectIdentifier + ) + ) + ).setStyle(TextColorPallet.secondaryVariant).styled { + it.withHoverEvent( + HoverEvent( + HoverEvent.Action.SHOW_ITEM, HoverEvent.ItemStackContent(stack) + ) + ) + } + } + + override fun rollback(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + + val newEntity = StringNbtReader.parse(objectState) + val uuid = newEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity != null) { + entity.remove(Entity.RemovalReason.DISCARDED) + return true + } + return false + } + + override fun restore(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + + val newEntity = StringNbtReader.parse(objectState) + val uuid = newEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity == null) { + val entity = ItemEntity(EntityType.ITEM, world) + entity.readNbt(newEntity) + world?.spawnEntity(entity) + } + return true + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemPickUpActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemPickUpActionType.kt new file mode 100644 index 00000000..29d6c811 --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemPickUpActionType.kt @@ -0,0 +1,78 @@ +package com.github.quiltservertools.ledger.actions + +import com.github.quiltservertools.ledger.utility.NbtUtils +import com.github.quiltservertools.ledger.utility.TextColorPallet +import com.github.quiltservertools.ledger.utility.UUID +import com.github.quiltservertools.ledger.utility.getWorld +import com.github.quiltservertools.ledger.utility.literal +import net.minecraft.entity.Entity +import net.minecraft.entity.EntityType +import net.minecraft.entity.ItemEntity +import net.minecraft.item.AliasedBlockItem +import net.minecraft.item.BlockItem +import net.minecraft.nbt.StringNbtReader +import net.minecraft.registry.Registries +import net.minecraft.server.MinecraftServer +import net.minecraft.text.HoverEvent +import net.minecraft.text.Text +import net.minecraft.util.Util + +open class ItemPickUpActionType : AbstractActionType() { + override val identifier = "item-pick-up" + + override fun getTranslationType(): String { + val item = Registries.ITEM.get(objectIdentifier) + return if (item is BlockItem && item !is AliasedBlockItem) { + "block" + } else { + "item" + } + } + + override fun getObjectMessage(): Text { + val stack = NbtUtils.itemFromProperties(extraData, objectIdentifier) + + return "${stack.count} ".literal().append( + Text.translatable( + Util.createTranslationKey( + getTranslationType(), objectIdentifier + ) + ) + ).setStyle(TextColorPallet.secondaryVariant).styled { + it.withHoverEvent( + HoverEvent( + HoverEvent.Action.SHOW_ITEM, HoverEvent.ItemStackContent(stack) + ) + ) + } + } + + override fun rollback(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + + val oldEntity = StringNbtReader.parse(oldObjectState) + val uuid = oldEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity == null) { + val entity = ItemEntity(EntityType.ITEM, world) + entity.readNbt(oldEntity) + world?.spawnEntity(entity) + } + return true + } + + override fun restore(server: MinecraftServer): Boolean { + val world = server.getWorld(world) + + val oldEntity = StringNbtReader.parse(oldObjectState) + val uuid = oldEntity!!.getUuid(UUID) ?: return false + val entity = world?.getEntity(uuid) + + if (entity != null) { + entity.remove(Entity.RemovalReason.DISCARDED) + return true + } + return false + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/ActionFactory.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/ActionFactory.kt index 1eb0ec76..9b8f160d 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/ActionFactory.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/ActionFactory.kt @@ -4,14 +4,19 @@ import com.github.quiltservertools.ledger.actions.ActionType import com.github.quiltservertools.ledger.actions.BlockBreakActionType import com.github.quiltservertools.ledger.actions.BlockChangeActionType import com.github.quiltservertools.ledger.actions.BlockPlaceActionType +import com.github.quiltservertools.ledger.actions.EntityChangeActionType import com.github.quiltservertools.ledger.actions.EntityKillActionType +import com.github.quiltservertools.ledger.actions.ItemDropActionType import com.github.quiltservertools.ledger.actions.ItemInsertActionType +import com.github.quiltservertools.ledger.actions.ItemPickUpActionType import com.github.quiltservertools.ledger.actions.ItemRemoveActionType +import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.Sources import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.entity.BlockEntity -import net.minecraft.entity.LivingEntity +import net.minecraft.entity.Entity +import net.minecraft.entity.ItemEntity import net.minecraft.entity.damage.DamageSource import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemStack @@ -88,8 +93,8 @@ object ActionFactory { action.world = world.registryKey.value action.objectIdentifier = Registries.BLOCK.getId(state.block) action.oldObjectIdentifier = Registries.BLOCK.getId(oldState.block) - action.blockState = state - action.oldBlockState = oldState + action.objectState = NbtUtils.blockStateToProperties(state)?.asString() + action.oldObjectState = NbtUtils.blockStateToProperties(oldState)?.asString() action.sourceName = source action.extraData = entity?.createNbt()?.asString() } @@ -134,6 +139,34 @@ object ActionFactory { return action } + fun itemPickUpAction( + entity: ItemEntity, + source: PlayerEntity + ): ItemPickUpActionType { + val action = ItemPickUpActionType() + + setItemData(action, entity.blockPos, entity.world, entity.stack, Sources.PLAYER) + + action.oldObjectState = entity.writeNbt(NbtCompound())?.asString() + action.sourceProfile = source.gameProfile + + return action + } + + fun itemDropAction( + entity: ItemEntity, + source: PlayerEntity + ): ItemDropActionType { + val action = ItemDropActionType() + + setItemData(action, entity.blockPos, entity.world, entity.stack, Sources.PLAYER) + + action.objectState = entity.writeNbt(NbtCompound())?.asString() + action.sourceProfile = source.gameProfile + + return action + } + fun blockChangeAction( world: World, pos: BlockPos, @@ -160,10 +193,10 @@ object ActionFactory { action.world = world.registryKey.value action.objectIdentifier = Registries.ITEM.getId(stack.item) action.sourceName = source - action.extraData = stack.writeNbt(NbtCompound())?.asString() + action.extraData = NbtUtils.itemToProperties(stack)?.asString() } - fun entityKillAction(world: World, pos: BlockPos, entity: LivingEntity, cause: DamageSource): EntityKillActionType { + fun entityKillAction(world: World, pos: BlockPos, entity: Entity, cause: DamageSource): EntityKillActionType { val killer = cause.attacker val action = EntityKillActionType() @@ -186,7 +219,7 @@ object ActionFactory { action: ActionType, pos: BlockPos, world: World, - entity: LivingEntity, + entity: Entity, source: String ) { action.pos = pos @@ -195,4 +228,34 @@ object ActionFactory { action.sourceName = source action.extraData = entity.writeNbt(NbtCompound())?.asString() } + + fun entityChangeAction( + world: World, + pos: BlockPos, + oldEntityTags: NbtCompound, + entity: Entity, + itemStack: ItemStack?, + entityActor: Entity?, + sourceType: String + ): EntityChangeActionType { + val action = EntityChangeActionType() + + action.pos = pos + action.world = world.registryKey.value + action.objectIdentifier = Registries.ENTITY_TYPE.getId(entity.type) + action.oldObjectIdentifier = Registries.ENTITY_TYPE.getId(entity.type) + + if (itemStack != null) { + action.extraData = Registries.ITEM.getId(itemStack.item).toString() + } + action.oldObjectState = oldEntityTags.asString() + action.objectState = entity.writeNbt(NbtCompound())?.asString() + action.sourceName = sourceType + + if (entityActor is PlayerEntity) { + action.sourceProfile = entityActor.gameProfile + } + + return action + } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityKillCallback.kt b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityKillCallback.kt index 920a7165..0b8aa4b0 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityKillCallback.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityKillCallback.kt @@ -2,13 +2,13 @@ package com.github.quiltservertools.ledger.callbacks import net.fabricmc.fabric.api.event.Event import net.fabricmc.fabric.api.event.EventFactory -import net.minecraft.entity.LivingEntity +import net.minecraft.entity.Entity import net.minecraft.entity.damage.DamageSource import net.minecraft.util.math.BlockPos import net.minecraft.world.World fun interface EntityKillCallback { - fun kill(world: World, pos: BlockPos, entity: LivingEntity, source: DamageSource) + fun kill(world: World, pos: BlockPos, entity: Entity, source: DamageSource) companion object { @JvmField diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityModifyCallback.kt b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityModifyCallback.kt new file mode 100644 index 00000000..c305a365 --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/EntityModifyCallback.kt @@ -0,0 +1,33 @@ +package com.github.quiltservertools.ledger.callbacks + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory +import net.minecraft.entity.Entity +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtCompound +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World + +fun interface EntityModifyCallback { + fun modify( + world: World, + pos: BlockPos, + oldEntityTags: NbtCompound, + newEntity: Entity, + itemStack: ItemStack?, + entityActor: Entity?, + sourceType: String + ) + + companion object { + @JvmField + val EVENT: Event = + EventFactory.createArrayBacked(EntityModifyCallback::class.java) { listeners -> + EntityModifyCallback { world, pos, oldEntityTags, newEntity, itemStack, entityActor, sourceType -> + for (listener in listeners) { + listener.modify(world, pos, oldEntityTags, newEntity, itemStack, entityActor, sourceType) + } + } + } + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemDropCallback.kt b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemDropCallback.kt new file mode 100644 index 00000000..30941fac --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemDropCallback.kt @@ -0,0 +1,21 @@ +package com.github.quiltservertools.ledger.callbacks + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory +import net.minecraft.entity.ItemEntity +import net.minecraft.entity.player.PlayerEntity + +fun interface ItemDropCallback { + fun drop(entity: ItemEntity, player: PlayerEntity) + + companion object { + @JvmField + val EVENT: Event = EventFactory.createArrayBacked(ItemDropCallback::class.java) { listeners -> + ItemDropCallback { entity, player -> + for (listener in listeners) { + listener.drop(entity, player) + } + } + } + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemPickUpCallback.kt b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemPickUpCallback.kt new file mode 100644 index 00000000..9b63967c --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/callbacks/ItemPickUpCallback.kt @@ -0,0 +1,22 @@ +package com.github.quiltservertools.ledger.callbacks + +import net.fabricmc.fabric.api.event.Event +import net.fabricmc.fabric.api.event.EventFactory +import net.minecraft.entity.ItemEntity +import net.minecraft.entity.player.PlayerEntity + +fun interface ItemPickUpCallback { + fun pickUp(entity: ItemEntity, player: PlayerEntity) + + companion object { + @JvmField + val EVENT: Event = + EventFactory.createArrayBacked(ItemPickUpCallback::class.java) { listeners -> + ItemPickUpCallback { entity, player -> + for (listener in listeners) { + listener.pickUp(entity, player) + } + } + } + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt b/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt index 62f5c104..97b65316 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/database/DatabaseManager.kt @@ -12,7 +12,6 @@ import com.github.quiltservertools.ledger.config.config import com.github.quiltservertools.ledger.logInfo import com.github.quiltservertools.ledger.logWarn import com.github.quiltservertools.ledger.registry.ActionRegistry -import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.Negatable import com.github.quiltservertools.ledger.utility.PlayerResult import com.mojang.authlib.GameProfile @@ -23,7 +22,6 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import net.minecraft.nbt.StringNbtReader import net.minecraft.server.MinecraftServer import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos @@ -50,7 +48,7 @@ import org.jetbrains.exposed.sql.transactions.transaction import java.io.File import java.time.Instant import java.time.temporal.ChronoUnit -import java.util.UUID +import java.util.* import kotlin.math.ceil object DatabaseManager { @@ -157,18 +155,8 @@ object DatabaseManager { type.world = action.world.identifier type.objectIdentifier = action.objectId.identifier type.oldObjectIdentifier = action.oldObjectId.identifier - type.blockState = action.blockState?.let { - NbtUtils.blockStateFromProperties( - StringNbtReader.parse(it), - action.objectId.identifier - ) - } - type.oldBlockState = action.oldBlockState?.let { - NbtUtils.blockStateFromProperties( - StringNbtReader.parse(it), - action.oldObjectId.identifier - ) - } + type.objectState = action.blockState + type.oldObjectState = action.oldBlockState type.sourceName = action.sourceName.name type.sourceProfile = action.sourcePlayer?.let { GameProfile(it.playerId, it.playerName) } type.extraData = action.extraData @@ -388,8 +376,8 @@ object DatabaseManager { objectId = selectRegistryKey(action.objectIdentifier) oldObjectId = selectRegistryKey(action.oldObjectIdentifier) world = selectWorld(action.world ?: Ledger.server.overworld.registryKey.value) - blockState = action.blockState?.let { NbtUtils.blockStateToProperties(it)?.asString() } - oldBlockState = action.oldBlockState?.let { NbtUtils.blockStateToProperties(it)?.asString() } + blockState = action.objectState + oldBlockState = action.oldObjectState sourceName = insertAndSelectSource(action.sourceName) sourcePlayer = action.sourceProfile?.let { selectPlayer(it.id) } extraData = action.extraData diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/listeners/EntityCallbackListener.kt b/src/main/kotlin/com/github/quiltservertools/ledger/listeners/EntityCallbackListener.kt index c0267af1..2677ab16 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/listeners/EntityCallbackListener.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/listeners/EntityCallbackListener.kt @@ -1,21 +1,38 @@ package com.github.quiltservertools.ledger.listeners -import net.minecraft.entity.LivingEntity -import net.minecraft.entity.damage.DamageSource -import net.minecraft.util.math.BlockPos -import net.minecraft.world.World import com.github.quiltservertools.ledger.actionutils.ActionFactory import com.github.quiltservertools.ledger.callbacks.EntityKillCallback +import com.github.quiltservertools.ledger.callbacks.EntityModifyCallback import com.github.quiltservertools.ledger.database.DatabaseManager +import net.minecraft.entity.Entity +import net.minecraft.entity.damage.DamageSource +import net.minecraft.item.ItemStack +import net.minecraft.nbt.NbtCompound +import net.minecraft.util.math.BlockPos +import net.minecraft.world.World fun registerEntityListeners() { EntityKillCallback.EVENT.register(::onKill) + EntityModifyCallback.EVENT.register(::onModify) } -private fun onKill(world: World, pos: BlockPos, entity: LivingEntity, source: DamageSource) { +private fun onKill(world: World, pos: BlockPos, entity: Entity, source: DamageSource) { DatabaseManager.logAction( ActionFactory.entityKillAction(world, pos, entity, source) ) } +private fun onModify( + world: World, + pos: BlockPos, + oldEntityTags: NbtCompound, + entity: Entity, + itemStack: ItemStack?, + entityActor: Entity?, + sourceType: String +) { + DatabaseManager.logAction( + ActionFactory.entityChangeAction(world, pos, oldEntityTags, entity, itemStack, entityActor, sourceType) + ) +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/listeners/PlayerEventListener.kt b/src/main/kotlin/com/github/quiltservertools/ledger/listeners/PlayerEventListener.kt index 3369639b..e1159658 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/listeners/PlayerEventListener.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/listeners/PlayerEventListener.kt @@ -2,6 +2,8 @@ package com.github.quiltservertools.ledger.listeners import com.github.quiltservertools.ledger.Ledger import com.github.quiltservertools.ledger.actionutils.ActionFactory +import com.github.quiltservertools.ledger.callbacks.ItemDropCallback +import com.github.quiltservertools.ledger.callbacks.ItemPickUpCallback import com.github.quiltservertools.ledger.database.DatabaseManager import com.github.quiltservertools.ledger.network.Networking.disableNetworking import com.github.quiltservertools.ledger.utility.inspectBlock @@ -14,6 +16,7 @@ import net.fabricmc.fabric.api.networking.v1.PacketSender import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents import net.minecraft.block.BlockState import net.minecraft.block.entity.BlockEntity +import net.minecraft.entity.ItemEntity import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemPlacementContext import net.minecraft.server.MinecraftServer @@ -31,6 +34,8 @@ fun registerPlayerListeners() { ServerPlayConnectionEvents.DISCONNECT.register(::onLeave) AttackBlockCallback.EVENT.register(::onBlockAttack) UseBlockCallback.EVENT.register(::onUseBlock) + ItemPickUpCallback.EVENT.register(::onItemPickUp) + ItemDropCallback.EVENT.register(::onItemDrop) } fun onLeave(handler: ServerPlayNetworkHandler, server: MinecraftServer) { @@ -111,3 +116,17 @@ private fun onBlockBreak( ) ) } + +private fun onItemPickUp( + entity: ItemEntity, + player: PlayerEntity +) { + DatabaseManager.logAction(ActionFactory.itemPickUpAction(entity, player)) +} + +private fun onItemDrop( + entity: ItemEntity, + player: PlayerEntity +) { + DatabaseManager.logAction(ActionFactory.itemDropAction(entity, player)) +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/registry/ActionRegistry.kt b/src/main/kotlin/com/github/quiltservertools/ledger/registry/ActionRegistry.kt index 37353a46..b2f9b03a 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/registry/ActionRegistry.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/registry/ActionRegistry.kt @@ -1,13 +1,7 @@ package com.github.quiltservertools.ledger.registry import com.github.quiltservertools.ledger.Ledger -import com.github.quiltservertools.ledger.actions.ActionType -import com.github.quiltservertools.ledger.actions.BlockBreakActionType -import com.github.quiltservertools.ledger.actions.BlockChangeActionType -import com.github.quiltservertools.ledger.actions.BlockPlaceActionType -import com.github.quiltservertools.ledger.actions.EntityKillActionType -import com.github.quiltservertools.ledger.actions.ItemInsertActionType -import com.github.quiltservertools.ledger.actions.ItemRemoveActionType +import com.github.quiltservertools.ledger.actions.* import com.github.quiltservertools.ledger.database.DatabaseManager import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap import it.unimi.dsi.fastutil.objects.ObjectSet @@ -37,8 +31,10 @@ object ActionRegistry { registerActionType { BlockChangeActionType() } registerActionType { ItemInsertActionType() } registerActionType { ItemRemoveActionType() } + registerActionType { ItemPickUpActionType() } + registerActionType { ItemDropActionType() } registerActionType { EntityKillActionType() } - registerActionType { BlockPlaceActionType() } + registerActionType { EntityChangeActionType() } } fun getType(id: String) = actionTypes[id] diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt index f19e7dd9..4c0e1802 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt @@ -6,10 +6,14 @@ import com.github.quiltservertools.ledger.actionutils.SearchResults import com.github.quiltservertools.ledger.database.DatabaseManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import net.minecraft.block.BedBlock import net.minecraft.block.BlockState import net.minecraft.block.Blocks import net.minecraft.block.ChestBlock +import net.minecraft.block.DoorBlock +import net.minecraft.block.enums.BedPart import net.minecraft.block.enums.ChestType +import net.minecraft.block.enums.DoubleBlockHalf import net.minecraft.entity.player.PlayerEntity import net.minecraft.server.command.ServerCommandSource import net.minecraft.text.Text @@ -17,7 +21,7 @@ import net.minecraft.util.Formatting import net.minecraft.util.math.BlockBox import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction -import java.util.UUID +import java.util.* private val inspectingUsers = HashSet() @@ -60,6 +64,14 @@ fun ServerCommandSource.inspectBlock(pos: BlockPos) { getOtherChestSide(state, pos)?.let { area = BlockBox.create(pos, it) } + } else if (state.block is DoorBlock) { + getOtherDoorHalf(state, pos).let { + area = BlockBox.create(pos, it) + } + } else if (state.block is BedBlock) { + getOtherBedPart(state, pos).let { + area = BlockBox.create(pos, it) + } } val params = ActionSearchParams.build { @@ -102,6 +114,25 @@ private fun getOtherChestSide(state: BlockState, pos: BlockPos): BlockPos? { } else null } +private fun getOtherDoorHalf(state: BlockState, pos: BlockPos): BlockPos { + val half = state.get(DoorBlock.HALF) + return if (half == DoubleBlockHalf.LOWER) { + pos.offset(Direction.UP) + } else { + pos.offset(Direction.DOWN) + } +} + +private fun getOtherBedPart(state: BlockState, pos: BlockPos): BlockPos { + val part = state.get(BedBlock.PART) + val direction = state.get(BedBlock.FACING) + return if (part == BedPart.FOOT) { + pos.offset(direction) + } else { + pos.offset(direction.opposite) + } +} + suspend fun PlayerEntity.getInspectResults(pos: BlockPos): SearchResults { val source = this.commandSource val params = ActionSearchParams.build { diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt index 85d6e951..1ff4efef 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/MessageUtils.kt @@ -112,7 +112,6 @@ object MessageUtils { } } - @OptIn(ExperimentalTime::class) fun instantToText(time: Instant): MutableText { val duration = Duration.between(time, Instant.now()).toKotlinDuration() val text: MutableText = "".literal() diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/NbtUtils.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/NbtUtils.kt index e7a821ed..56743d53 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/NbtUtils.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/NbtUtils.kt @@ -2,12 +2,17 @@ package com.github.quiltservertools.ledger.utility import net.fabricmc.fabric.api.util.NbtType import net.minecraft.block.BlockState +import net.minecraft.item.ItemStack import net.minecraft.nbt.NbtCompound import net.minecraft.nbt.NbtHelper +import net.minecraft.nbt.StringNbtReader import net.minecraft.registry.Registries import net.minecraft.util.Identifier -private const val PROPERTIES = "Properties" +const val PROPERTIES = "Properties" // BlockState +const val COUNT = "Count" // ItemStack +const val TAG = "tag" // ItemStack +const val UUID = "UUID" // Entity object NbtUtils { fun blockStateToProperties(state: BlockState): NbtCompound? { @@ -22,4 +27,42 @@ object NbtUtils { stateTag.put(PROPERTIES, tag) return NbtHelper.toBlockState(Registries.BLOCK.readOnlyWrapper, stateTag) } + + fun itemToProperties(item: ItemStack): NbtCompound? { + val itemTag = NbtCompound() + + // Don't log the item count if there is only 1 item. The log itself indicates there must be at least 1 + if (item.count > 1) { + itemTag.putByte(COUNT, item.count.toByte()) + } + + if (item.nbt != null) { + itemTag.put(TAG, item.nbt) + } + return if (itemTag.isEmpty) null else itemTag + } + + fun itemFromProperties(tag: String?, name: Identifier): ItemStack { + val itemTag = NbtCompound() + + itemTag.putString("id", name.toString()) + if (tag == null) { + itemTag.putByte(COUNT, 1) + return ItemStack.fromNbt(itemTag) + } + + val tagNbt = StringNbtReader.parse(tag) + // Item was missing count tag. If it's been logged, it must have a single item + if (tagNbt.contains(COUNT)) { + itemTag.putByte(COUNT, tagNbt.getByte(COUNT)) + } else { + itemTag.putByte(COUNT, 1) + } + + if (tagNbt.contains(TAG)) { + itemTag.put(TAG, tagNbt.getCompound(TAG)) + } + + return ItemStack.fromNbt(itemTag) + } } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerLecternHook.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerLecternHook.kt new file mode 100644 index 00000000..888eccf7 --- /dev/null +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerLecternHook.kt @@ -0,0 +1,9 @@ +package com.github.quiltservertools.ledger.utility + +import net.minecraft.block.entity.BlockEntity +import net.minecraft.entity.player.PlayerEntity + +object PlayerLecternHook { + @JvmStatic + val activeHandlers = HashMap() +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerResult.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerResult.kt index cb2b34b3..882faa54 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerResult.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/PlayerResult.kt @@ -4,11 +4,9 @@ import com.github.quiltservertools.ledger.database.Tables import net.minecraft.text.Text import java.time.Instant import java.util.* -import kotlin.time.ExperimentalTime data class PlayerResult(val uuid: UUID, val name: String, val firstJoin: Instant, val lastJoin: Instant) { - @OptIn(ExperimentalTime::class) fun toText(): Text { return Text.translatable( "text.ledger.player.result", diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/Sources.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/Sources.kt index e81e4e60..1e464082 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/Sources.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/Sources.kt @@ -28,5 +28,12 @@ object Sources { const val SPONGE = "sponge" const val PORTAL = "portal" const val COMMAND = "command" + const val PROJECTILE = "projectile" + const val VEHICLE = "vehicle" + const val EQUIP = "equip" + const val ROTATE = "rotate" + const val SHEAR = "shear" + const val DYE = "dye" + const val STATUE = "statue" const val UNKNOWN = "unknown" } diff --git a/src/main/resources/data/ledger/lang/en_gb.json b/src/main/resources/data/ledger/lang/en_gb.json index fdef53f7..d94e73b2 100644 --- a/src/main/resources/data/ledger/lang/en_gb.json +++ b/src/main/resources/data/ledger/lang/en_gb.json @@ -24,6 +24,7 @@ "text.ledger.action_message": "%1$s %2$s %3$s %4$s %5$s", "text.ledger.action_message.time_diff": "%s ago", "text.ledger.action_message.location.hover": "Click to teleport", + "text.ledger.action_message.with": "with", "text.ledger.rollback.start": "Rolling back %s actions", "text.ledger.rollback.finish": "Rollback complete", @@ -49,7 +50,10 @@ "text.ledger.action.block-break": "broke", "text.ledger.action.item-insert": "added", "text.ledger.action.item-remove": "removed", + "text.ledger.action.item-pick-up": "picked up", + "text.ledger.action.item-drop": "dropped", "text.ledger.action.entity-kill": "killed", + "text.ledger.action.entity-change": "changed", "text.ledger.network.protocols_mismatched": "Protocol versions mismatched: client mod is Ledger protocol version %d and the client version is %d", "text.ledger.network.no_mod_info": "Unable to determine mod information for your client mod", diff --git a/src/main/resources/data/ledger/lang/en_us.json b/src/main/resources/data/ledger/lang/en_us.json index 10dbe03a..faa16d1a 100644 --- a/src/main/resources/data/ledger/lang/en_us.json +++ b/src/main/resources/data/ledger/lang/en_us.json @@ -24,6 +24,7 @@ "text.ledger.action_message": "%1$s %2$s %3$s %4$s %5$s", "text.ledger.action_message.time_diff": "%s ago", "text.ledger.action_message.location.hover": "Click to teleport", + "text.ledger.action_message.with": "with", "text.ledger.rollback.start": "Rolling back %s actions", "text.ledger.rollback.finish": "Rollback complete", @@ -50,7 +51,10 @@ "text.ledger.action.block-change": "changed", "text.ledger.action.item-insert": "added", "text.ledger.action.item-remove": "removed", + "text.ledger.action.item-pick-up": "picked up", + "text.ledger.action.item-drop": "dropped", "text.ledger.action.entity-kill": "killed", + "text.ledger.action.entity-change": "changed", "text.ledger.network.protocols_mismatched": "Protocol versions mismatched: client mod is Ledger protocol version %d and the client version is %d", "text.ledger.network.no_mod_info": "Unable to determine mod information for your client mod", diff --git a/src/main/resources/data/ledger/lang/ru_ru.json b/src/main/resources/data/ledger/lang/ru_ru.json index bacf5971..81e0b80d 100644 --- a/src/main/resources/data/ledger/lang/ru_ru.json +++ b/src/main/resources/data/ledger/lang/ru_ru.json @@ -23,6 +23,7 @@ "text.ledger.action_message": "%1$s %2$s %3$s %4$s %5$s", "text.ledger.action_message.time_diff": "%s назад", "text.ledger.action_message.location.hover": "Нажмите, чтобы телепортироваться", + "text.ledger.action_message.with": "с помощью", "text.ledger.rollback.start": "Откат %s действий(я)", "text.ledger.rollback.finish": "Откат завершен", @@ -48,7 +49,10 @@ "text.ledger.action.block-break": "сломал", "text.ledger.action.item-insert": "добавил", "text.ledger.action.item-remove": "удалил", + "text.ledger.action.item-pick-up": "подобрал", + "text.ledger.action.item-drop": "выкинул", "text.ledger.action.entity-kill": "убил", + "text.ledger.action.entity-change": "изменил", "text.ledger.network.protocols_mismatched": "Несоответствие версий протокола: клиентский мод - это версия протокола Ledger %d, а версия клиента - %d" } \ No newline at end of file diff --git a/src/main/resources/ledger.mixins.json b/src/main/resources/ledger.mixins.json index f7e0763d..fa543ec6 100644 --- a/src/main/resources/ledger.mixins.json +++ b/src/main/resources/ledger.mixins.json @@ -10,36 +10,38 @@ "BucketDispenserBehaviorMixin", "BucketItemMixin", "CampfireBlockEntityMixin", - "CreeperEntityMixin", "DoubleInventoryMixin", - "EndCrystalEntityMixin", + "DyeItemMixin", "ExplosionMixin", - "ExplosiveProjectileEntityMixin", - "FallingBlockEntityMixin", "FillCommandMixin", "FlintAndSteelItemMixin", "FrostWalkerEnchantmentMixin", "HoeItemMixin", "HoneycombItemMixin", "ItemScattererMixin", - "LivingEntityMixin", "LockableContainerBlockEntityMixin", "MusicDiscItemMixin", "NetherPortalMixin", - "RavagerEntityMixin", "ScreenHandlerMixin", "SetBlockCommandMixin", "ShovelItemMixin", "SlotMixin", - "SnowGolemEntityMixin", "blocks.AbstractPlantPartBlockMixin", "blocks.AbstractPlantStemBlockMixin", "blocks.BambooBlockMixin", + "blocks.BedBlockMixin", + "blocks.CactusBlockMixin", "blocks.CakeBlockMixin", "blocks.CampfireBlockMixin", "blocks.CandleBlockMixin", + "blocks.CarvedPumpkinBlockMixin", + "blocks.ChorusFlowerBlockMixin", + "blocks.ChorusPlantBlockMixin", + "blocks.ComparatorBlockMixin", "blocks.DaylightDetectorBlockMixin", + "blocks.DoorBlockMixin", "blocks.FarmlandBlockMixin", + "blocks.FenceGateBlockMixin", "blocks.FireBlockMixin", "blocks.FlowerPotBlockMixin", "blocks.FluidBlockMixin", @@ -47,12 +49,16 @@ "blocks.JukeBoxBlockMixin", "blocks.LeavesBlockMixin", "blocks.LeverBlockMixin", + "blocks.LilyPadBlockMixin", "blocks.NoteBlockMixin", "blocks.PointedDripstoneBlockMixin", + "blocks.RepeaterBlockMixin", "blocks.RootedDirtBlockMixin", "blocks.ScaffoldingBlockMixin", "blocks.SpongeBlockMixin", "blocks.SpreadableBlockMixin", + "blocks.SugarCaneBlockMixin", + "blocks.TrapdoorBlockMixin", "blocks.WetSpongeBlockMixin", "blocks.cauldron.CauldronBehaviorMixin", "blocks.cauldron.CauldronBlockMixin", @@ -60,7 +66,28 @@ "blocks.coral.CoralBlockBlockMixin", "blocks.coral.CoralBlockMixin", "blocks.coral.CoralFanBlockMixin", - "blocks.coral.CoralWallFanBlockMixin" + "blocks.coral.CoralWallFanBlockMixin", + "blocks.lectern.LecternBlockMixin", + "blocks.lectern.LecternScreenHandlerMixin", + "blocks.lectern.ScreenHandlerMixin", + "entities.ArmorStandEntityMixin", + "entities.CatEntityMixin", + "entities.CreeperEntityMixin", + "entities.EndCrystalEntityMixin", + "entities.EnderDragonEntityMixin", + "entities.EvokerEntityWololoGoalMixin", + "entities.ExplosiveProjectileEntityMixin", + "entities.FallingBlockEntityMixin", + "entities.ItemEntityMixin", + "entities.ItemFrameEntityMixin", + "entities.LightningEntityMixin", + "entities.LivingEntityMixin", + "entities.PlayerEntityMixin", + "entities.RavagerEntityMixin", + "entities.SheepEntityMixin", + "entities.SnowGolemEntityMixin", + "entities.VillagerEntityMixin", + "entities.WolfEntityMixin" ], "injectors": { "defaultRequire": 1 diff --git a/src/testmod/java/com/github/quiltservertools/ledger/testmod/mixin/ClientPlayerInteractionManagerMixin.java b/src/testmod/java/com/github/quiltservertools/ledger/testmod/mixin/ClientPlayerInteractionManagerMixin.java index f348ec5c..914045de 100644 --- a/src/testmod/java/com/github/quiltservertools/ledger/testmod/mixin/ClientPlayerInteractionManagerMixin.java +++ b/src/testmod/java/com/github/quiltservertools/ledger/testmod/mixin/ClientPlayerInteractionManagerMixin.java @@ -11,7 +11,6 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import com.github.quiltservertools.ledger.testmod.LedgerTest; import com.github.quiltservertools.ledger.testmod.commands.InspectCommand; @Mixin(ClientPlayerInteractionManager.class)