diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/ScreenHandlerMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/ScreenHandlerMixin.java index 4f4ebaeb..31f2b8d9 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/ScreenHandlerMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/ScreenHandlerMixin.java @@ -1,13 +1,15 @@ package com.github.quiltservertools.ledger.mixin; import com.github.quiltservertools.ledger.utility.HandledSlot; -import com.github.quiltservertools.ledger.utility.HandlerWithPlayer; +import com.github.quiltservertools.ledger.utility.HandlerWithContext; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.SlotActionType; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -17,9 +19,12 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(ScreenHandler.class) -public abstract class ScreenHandlerMixin implements HandlerWithPlayer { +public abstract class ScreenHandlerMixin implements HandlerWithContext { @Unique private ServerPlayerEntity player = null; + + @Unique + private BlockPos pos = null; @Inject(method = "addSlot", at = @At(value = "HEAD")) private void ledgerGiveSlotHandlerReference(Slot slot, CallbackInfoReturnable cir) { @@ -51,4 +56,15 @@ private void ledgerDropInventoryGetPlayer(PlayerEntity player, Inventory invento public ServerPlayerEntity getPlayer() { return player; } + + @Nullable + @Override + public BlockPos getPos() { + return pos; + } + + @Override + public void setPos(@NotNull BlockPos pos) { + this.pos = pos; + } } diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/SlotMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/SlotMixin.java index fdbaae10..83a1409f 100644 --- a/src/main/java/com/github/quiltservertools/ledger/mixin/SlotMixin.java +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/SlotMixin.java @@ -5,7 +5,7 @@ import com.github.quiltservertools.ledger.callbacks.ItemInsertCallback; import com.github.quiltservertools.ledger.callbacks.ItemRemoveCallback; import com.github.quiltservertools.ledger.utility.HandledSlot; -import com.github.quiltservertools.ledger.utility.HandlerWithPlayer; +import com.github.quiltservertools.ledger.utility.HandlerWithContext; import com.github.quiltservertools.ledger.utility.Sources; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; @@ -55,10 +55,10 @@ public void setHandler(@NotNull ScreenHandler handler) { @Inject(method = "markDirty", at = @At(value = "HEAD")) private void ledgerLogChanges(CallbackInfo ci) { BlockPos pos = getInventoryLocation(); - HandlerWithPlayer handlerWithPlayer = (HandlerWithPlayer) handler; + HandlerWithContext handlerWithContext = (HandlerWithContext) handler; - if (pos != null && handlerWithPlayer.getPlayer() != null) { - logChange(handlerWithPlayer.getPlayer(), oldStack, this.getStack().copy(), pos); + if (pos != null && handlerWithContext.getPlayer() != null) { + logChange(handlerWithContext.getPlayer(), oldStack, this.getStack().copy(), pos); } oldStack = this.getStack().copy(); diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ChestBlockMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ChestBlockMixin.java new file mode 100644 index 00000000..e883703e --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ChestBlockMixin.java @@ -0,0 +1,32 @@ +package com.github.quiltservertools.ledger.mixin.preview; + +import com.github.quiltservertools.ledger.utility.HandlerWithContext; +import net.minecraft.block.entity.ChestBlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ScreenHandler; +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; + +// An anonymous inner class inside an anonymous inner class +@Mixin(targets = "net/minecraft/block/ChestBlock$2$1") +public abstract class ChestBlockMixin { + + @Shadow + ChestBlockEntity field_17358; + + @Inject( + method = "createMenu(ILnet/minecraft/entity/player/PlayerInventory;Lnet/minecraft/entity/player/PlayerEntity;)Lnet/minecraft/screen/ScreenHandler;", + at = @At("RETURN") + ) + private void addPositionContext(int i, PlayerInventory playerInventory, PlayerEntity playerEntity, CallbackInfoReturnable cir) { + ScreenHandler screenHandler = cir.getReturnValue(); + if (screenHandler != null) { + ((HandlerWithContext) screenHandler).setPos(this.field_17358.getPos()); + } + } + +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LockableContainerBlockEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LockableContainerBlockEntityMixin.java new file mode 100644 index 00000000..8d717c34 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LockableContainerBlockEntityMixin.java @@ -0,0 +1,32 @@ +package com.github.quiltservertools.ledger.mixin.preview; + +import com.github.quiltservertools.ledger.utility.HandlerWithContext; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ScreenHandler; +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.CallbackInfoReturnable; + +@Mixin(LockableContainerBlockEntity.class) +public abstract class LockableContainerBlockEntityMixin extends BlockEntity { + + public LockableContainerBlockEntityMixin(BlockEntityType type, BlockPos pos, BlockState state) { + super(type, pos, state); + } + + @Inject(method = "createMenu", at = @At("RETURN")) + private void addPositionContext(int i, PlayerInventory playerInventory, PlayerEntity playerEntity, CallbackInfoReturnable cir) { + ScreenHandler screenHandler = cir.getReturnValue(); + if (screenHandler != null) { + ((HandlerWithContext) screenHandler).setPos(this.getPos()); + } + } + +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LootableContainerBlockEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LootableContainerBlockEntityMixin.java new file mode 100644 index 00000000..d152e660 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/LootableContainerBlockEntityMixin.java @@ -0,0 +1,32 @@ +package com.github.quiltservertools.ledger.mixin.preview; + +import com.github.quiltservertools.ledger.utility.HandlerWithContext; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.block.entity.LockableContainerBlockEntity; +import net.minecraft.block.entity.LootableContainerBlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ScreenHandler; +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.CallbackInfoReturnable; + +@Mixin(LootableContainerBlockEntity.class) +public abstract class LootableContainerBlockEntityMixin extends LockableContainerBlockEntity { + + protected LootableContainerBlockEntityMixin(BlockEntityType blockEntityType, BlockPos blockPos, BlockState blockState) { + super(blockEntityType, blockPos, blockState); + } + + @Inject(method = "createMenu", at = @At("RETURN")) + private void addPositionContext(int i, PlayerInventory playerInventory, PlayerEntity playerEntity, CallbackInfoReturnable cir) { + ScreenHandler screenHandler = cir.getReturnValue(); + if (screenHandler != null) { + ((HandlerWithContext) screenHandler).setPos(this.getPos()); + } + } + +} diff --git a/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ServerPlayerEntityMixin.java b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ServerPlayerEntityMixin.java new file mode 100644 index 00000000..1119d587 --- /dev/null +++ b/src/main/java/com/github/quiltservertools/ledger/mixin/preview/ServerPlayerEntityMixin.java @@ -0,0 +1,67 @@ +package com.github.quiltservertools.ledger.mixin.preview; + +import com.github.quiltservertools.ledger.Ledger; +import com.github.quiltservertools.ledger.actionutils.Preview; +import com.github.quiltservertools.ledger.utility.HandlerWithContext; +import com.llamalad7.mixinextras.sugar.Local; +import kotlin.Pair; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +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.ModifyArg; + +import java.util.List; + +@Mixin(targets = "net.minecraft.server.network.ServerPlayerEntity$1") +public abstract class ServerPlayerEntityMixin { + + // synthetic field ServerPlayerEntity from the outer class + @Final + @Shadow + ServerPlayerEntity field_29182; + + @ModifyArg( + method = "updateState", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/network/packet/s2c/play/InventoryS2CPacket;(IILnet/minecraft/util/collection/DefaultedList;Lnet/minecraft/item/ItemStack;)V" + ), index = 2 + ) + private DefaultedList modifyStacks(DefaultedList stacks, @Local(argsOnly = true) ScreenHandler handler) { + BlockPos pos = ((HandlerWithContext) handler).getPos(); + if (pos == null) return stacks; + Preview preview = Ledger.previewCache.get(field_29182.getUuid()); + if (preview == null) return stacks; + List> modifiedItems = preview.getModifiedItems().get(pos); + if (modifiedItems == null) return stacks; + // Copy original list + DefaultedList previewStacks = DefaultedList.of(); + previewStacks.addAll(stacks); + for (Pair modifiedItem : modifiedItems) { + if (modifiedItem.component2()) { + // Add item + for (int i = 0; i < previewStacks.size(); i++) { + if (previewStacks.get(i).isEmpty()) { + previewStacks.set(i, modifiedItem.component1()); + break; + } + } + } else { + // Remove item + for (int i = 0; i < previewStacks.size(); i++) { + if (ItemStack.areItemsEqual(previewStacks.get(i), modifiedItem.component1())) { + previewStacks.set(i, ItemStack.EMPTY); + break; + } + } + } + } + return previewStacks; + } +} diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt b/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt index e06afa3d..acd8ab2a 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt @@ -1,5 +1,6 @@ package com.github.quiltservertools.ledger +import com.github.quiltservertools.ledger.config.config as realConfig import com.github.quiltservertools.ledger.actionutils.ActionSearchParams import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.api.ExtensionManager @@ -53,6 +54,7 @@ object Ledger : DedicatedServerModInitializer, CoroutineScope { lateinit var config: Config lateinit var server: MinecraftServer val searchCache = ConcurrentHashMap() + @JvmField // Required for mixin access val previewCache = ConcurrentHashMap() override val coroutineContext: CoroutineContext = Dispatchers.IO 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 02ca1d95..7112a204 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/AbstractActionType.kt @@ -1,10 +1,12 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.utility.MessageUtils 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 java.time.Instant import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.ClickEvent @@ -15,7 +17,6 @@ import net.minecraft.util.Identifier import net.minecraft.util.Util import net.minecraft.util.math.BlockPos import net.minecraft.world.World -import java.time.Instant import kotlin.time.ExperimentalTime abstract class AbstractActionType : ActionType { @@ -32,8 +33,8 @@ abstract class AbstractActionType : ActionType { override var rolledBack: Boolean = false override fun rollback(server: MinecraftServer): Boolean = false - override fun previewRollback(player: ServerPlayerEntity) = Unit - override fun previewRestore(player: ServerPlayerEntity) = Unit + override fun previewRollback(preview: Preview, player: ServerPlayerEntity) = Unit + override fun previewRestore(preview: Preview, player: ServerPlayerEntity) = Unit override fun restore(server: MinecraftServer): Boolean = false @ExperimentalTime 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 d8e4239c..2445645c 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ActionType.kt @@ -1,14 +1,15 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.config.ActionsSpec import com.github.quiltservertools.ledger.config.config import com.mojang.authlib.GameProfile +import java.time.Instant import net.minecraft.server.MinecraftServer import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text import net.minecraft.util.Identifier import net.minecraft.util.math.BlockPos -import java.time.Instant import kotlin.time.ExperimentalTime interface ActionType { @@ -27,8 +28,8 @@ interface ActionType { fun rollback(server: MinecraftServer): Boolean fun restore(server: MinecraftServer): Boolean - fun previewRollback(player: ServerPlayerEntity) - fun previewRestore(player: ServerPlayerEntity) + fun previewRollback(preview: Preview, player: ServerPlayerEntity) + fun previewRestore(preview: Preview, player: ServerPlayerEntity) fun getTranslationType(): String @ExperimentalTime 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 57e9b28d..c50a6459 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/BlockChangeActionType.kt @@ -1,5 +1,6 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.logWarn import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.TextColorPallet @@ -30,9 +31,10 @@ open class BlockChangeActionType : AbstractActionType() { return true } - override fun previewRollback(player: ServerPlayerEntity) { - if (player.world.registryKey == player.world.registryKey) { + override fun previewRollback(preview: Preview, player: ServerPlayerEntity) { + if (player.world.registryKey.value == world) { player.networkHandler.sendPacket(BlockUpdateS2CPacket(pos, oldBlockState())) + preview.positions.add(pos) } } @@ -44,9 +46,10 @@ open class BlockChangeActionType : AbstractActionType() { return true } - override fun previewRestore(player: ServerPlayerEntity) { - if (player.world.registryKey == player.world.registryKey) { + override fun previewRestore(preview: Preview, player: ServerPlayerEntity) { + if (player.world.registryKey.value == world) { player.networkHandler.sendPacket(BlockUpdateS2CPacket(pos, newBlockState())) + preview.positions.add(pos) } } 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 910b5d75..b2d9aa63 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/EntityKillActionType.kt @@ -1,12 +1,16 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.utility.UUID import com.github.quiltservertools.ledger.utility.getWorld import net.minecraft.entity.Entity import net.minecraft.entity.LivingEntity import net.minecraft.nbt.StringNbtReader +import net.minecraft.network.packet.s2c.play.EntitiesDestroyS2CPacket +import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket import net.minecraft.registry.Registries import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.util.math.Vec3d class EntityKillActionType : AbstractActionType() { @@ -14,6 +18,35 @@ class EntityKillActionType : AbstractActionType() { override fun getTranslationType() = "entity" + override fun previewRollback(preview: Preview, player: ServerPlayerEntity) { + val world = player.server.getWorld(world) + + val entityType = Registries.ENTITY_TYPE.getOrEmpty(objectIdentifier) + if (entityType.isEmpty) return + + val entity: LivingEntity = (entityType.get().create(world) as LivingEntity?)!! + entity.readNbt(StringNbtReader.parse(extraData)) + entity.health = entity.defaultMaxHealth.toFloat() + entity.velocity = Vec3d.ZERO + entity.fireTicks = 0 + player.networkHandler.sendPacket(EntitySpawnS2CPacket(entity)) + preview.spawnedEntityIds.add(entity.id) + } + + override fun previewRestore(preview: Preview, player: ServerPlayerEntity) { + val world = player.server.getWorld(world) + + val tag = StringNbtReader.parse(extraData) + if (tag.containsUuid("UUID")) { + val uuid = tag.getUuid("UUID") + val entity = world?.getEntity(uuid) + entity?.let { + player.networkHandler.sendPacket(EntitiesDestroyS2CPacket(it.id)) + preview.removedEntityUuids.add(it.uuid) + } + } + } + override fun rollback(server: MinecraftServer): Boolean { val world = server.getWorld(world) 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 65978e51..66a6789d 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemChangeActionType.kt @@ -1,9 +1,12 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import com.github.quiltservertools.ledger.utility.NbtUtils import com.github.quiltservertools.ledger.utility.TextColorPallet +import com.github.quiltservertools.ledger.utility.getOtherChestSide import com.github.quiltservertools.ledger.utility.getWorld import com.github.quiltservertools.ledger.utility.literal +import net.minecraft.block.Blocks import net.minecraft.block.ChestBlock import net.minecraft.block.InventoryProvider import net.minecraft.block.LecternBlock @@ -16,10 +19,12 @@ import net.minecraft.item.ItemStack import net.minecraft.item.Items import net.minecraft.registry.Registries import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.server.world.ServerWorld import net.minecraft.text.HoverEvent import net.minecraft.text.Text import net.minecraft.util.Util +import net.minecraft.util.math.BlockPos abstract class ItemChangeActionType : AbstractActionType() { override fun getTranslationType(): String { @@ -51,6 +56,26 @@ abstract class ItemChangeActionType : AbstractActionType() { } } + protected fun previewItemChange(preview: Preview, player: ServerPlayerEntity, insert: Boolean) { + val world = player.server.getWorld(world) + val state = world?.getBlockState(pos) + state?.isOf(Blocks.CHEST)?.let { + if (it) { + val otherPos = getOtherChestSide(state, pos) + if (otherPos != null) { + addPreview(preview, otherPos, insert) + } + } + } + addPreview(preview, pos, insert) + } + + private fun addPreview(preview: Preview, pos: BlockPos, insert: Boolean) { + preview.modifiedItems.compute(pos) { _, list -> + list ?: mutableListOf() + }?.add(Pair(NbtUtils.itemFromProperties(extraData, objectIdentifier), insert)) + } + private fun getInventory(world: ServerWorld): Inventory? { var inventory: Inventory? = null val blockState = world.getBlockState(pos) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemInsertActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemInsertActionType.kt index b3a00330..fe7504d2 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemInsertActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemInsertActionType.kt @@ -1,10 +1,20 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayerEntity class ItemInsertActionType : ItemChangeActionType() { override val identifier: String = "item-insert" + override fun previewRollback(preview: Preview, player: ServerPlayerEntity) { + previewItemChange(preview, player, false) + } + + override fun previewRestore(preview: Preview, player: ServerPlayerEntity) { + previewItemChange(preview, player, true) + } + override fun rollback(server: MinecraftServer) = removeMatchingItem(server) override fun restore(server: MinecraftServer) = addItem(server) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemRemoveActionType.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemRemoveActionType.kt index d203fa03..69e4fdaa 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemRemoveActionType.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actions/ItemRemoveActionType.kt @@ -1,10 +1,20 @@ package com.github.quiltservertools.ledger.actions +import com.github.quiltservertools.ledger.actionutils.Preview import net.minecraft.server.MinecraftServer +import net.minecraft.server.network.ServerPlayerEntity class ItemRemoveActionType : ItemChangeActionType() { override val identifier: String = "item-remove" + override fun previewRollback(preview: Preview, player: ServerPlayerEntity) { + previewItemChange(preview, player, true) + } + + override fun previewRestore(preview: Preview, player: ServerPlayerEntity) { + previewItemChange(preview, player, false) + } + override fun rollback(server: MinecraftServer): Boolean = addItem(server) override fun restore(server: MinecraftServer): Boolean = removeMatchingItem(server) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/Preview.kt b/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/Preview.kt index b37d3bcb..846227dc 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/Preview.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/actionutils/Preview.kt @@ -5,7 +5,12 @@ import com.github.quiltservertools.ledger.commands.subcommands.RestoreCommand import com.github.quiltservertools.ledger.commands.subcommands.RollbackCommand import com.github.quiltservertools.ledger.utility.Context import com.github.quiltservertools.ledger.utility.TextColorPallet +import java.util.* +import net.minecraft.item.ItemStack import net.minecraft.network.packet.s2c.play.BlockUpdateS2CPacket +import net.minecraft.network.packet.s2c.play.BundleS2CPacket +import net.minecraft.network.packet.s2c.play.EntitiesDestroyS2CPacket +import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket import net.minecraft.server.network.ServerPlayerEntity import net.minecraft.text.Text import net.minecraft.util.math.BlockPos @@ -16,7 +21,14 @@ class Preview( player: ServerPlayerEntity, private val type: Type ) { - private val positions = mutableSetOf() + val positions = mutableSetOf() + // Preview entities that got spawned. Need to removed + val spawnedEntityIds = mutableSetOf() + // Preview entities that got removed. Need to be spawned + val removedEntityUuids = mutableSetOf() + // Preview items that should be modified in screen handlers (true = added, false = removed) + val modifiedItems = mutableMapOf>>() + init { player.sendMessage( @@ -29,10 +41,9 @@ class Preview( for (action in actions) { when (type) { - Type.ROLLBACK -> action.previewRollback(player) - Type.RESTORE -> action.previewRestore(player) + Type.ROLLBACK -> action.previewRollback(this, player) + Type.RESTORE -> action.previewRestore(this, player) } - positions.add(action.pos) } } @@ -40,9 +51,31 @@ class Preview( for (pos in positions) { player.networkHandler.sendPacket(BlockUpdateS2CPacket(player.world, pos)) } + cleanup(player) + } + + private fun cleanup(player: ServerPlayerEntity) { + // Cleanup preview entities, to keep client and server in sync + val destroyPackets = spawnedEntityIds.map { + EntitiesDestroyS2CPacket(it) + } + player.networkHandler.sendPacket(BundleS2CPacket(destroyPackets)) + + spawnedEntityIds.forEach { + player.networkHandler.sendPacket(EntitiesDestroyS2CPacket(it)) + } + val spawnPackets = removedEntityUuids.mapNotNull { + val world = player.serverWorld + val entity = world?.getEntity(it) + entity?.let { + EntitySpawnS2CPacket(entity) + } + } + player.networkHandler.sendPacket(BundleS2CPacket(spawnPackets)) } fun apply(context: Context) { + cleanup(context.source.playerOrThrow) when (type) { Type.ROLLBACK -> RollbackCommand.rollback(context, params) Type.RESTORE -> RestoreCommand.restore(context, params) diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PreviewCommand.kt b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PreviewCommand.kt index 4032f4ae..180700c5 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PreviewCommand.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/commands/subcommands/PreviewCommand.kt @@ -75,9 +75,9 @@ object PreviewCommand : BuildableCommand { if (Ledger.previewCache.containsKey(uuid)) { Ledger.previewCache[uuid]?.apply(context) + Ledger.previewCache.remove(uuid) } else { context.source.sendError(Text.translatable("error.ledger.no_preview")) - Ledger.previewCache.remove(uuid) return -1 } diff --git a/src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithPlayer.kt b/src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithContext.kt similarity index 53% rename from src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithPlayer.kt rename to src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithContext.kt index f317c4a8..7fa0d550 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithPlayer.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/HandlerWithContext.kt @@ -1,7 +1,10 @@ package com.github.quiltservertools.ledger.utility import net.minecraft.server.network.ServerPlayerEntity +import net.minecraft.util.math.BlockPos -interface HandlerWithPlayer { +interface HandlerWithContext { fun getPlayer(): ServerPlayerEntity? + fun getPos(): BlockPos? + fun setPos(pos: BlockPos) } 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 4c0e1802..63c89c8f 100644 --- a/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt +++ b/src/main/kotlin/com/github/quiltservertools/ledger/utility/InspectionManager.kt @@ -4,6 +4,7 @@ import com.github.quiltservertools.ledger.Ledger import com.github.quiltservertools.ledger.actionutils.ActionSearchParams import com.github.quiltservertools.ledger.actionutils.SearchResults import com.github.quiltservertools.ledger.database.DatabaseManager +import java.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import net.minecraft.block.BedBlock @@ -21,7 +22,6 @@ 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.* private val inspectingUsers = HashSet() @@ -100,7 +100,7 @@ fun ServerCommandSource.inspectBlock(pos: BlockPos) { } } -private fun getOtherChestSide(state: BlockState, pos: BlockPos): BlockPos? { +fun getOtherChestSide(state: BlockState, pos: BlockPos): BlockPos? { val type = state.get(ChestBlock.CHEST_TYPE) return if (type != ChestType.SINGLE) { // We now need to query other container results in the same chest diff --git a/src/main/resources/ledger.mixins.json b/src/main/resources/ledger.mixins.json index e0360928..0957f148 100644 --- a/src/main/resources/ledger.mixins.json +++ b/src/main/resources/ledger.mixins.json @@ -89,7 +89,11 @@ "entities.SheepEntityMixin", "entities.SnowGolemEntityMixin", "entities.VillagerEntityMixin", - "entities.WolfEntityMixin" + "entities.WolfEntityMixin", + "preview.ChestBlockMixin", + "preview.LockableContainerBlockEntityMixin", + "preview.LootableContainerBlockEntityMixin", + "preview.ServerPlayerEntityMixin" ], "injectors": { "defaultRequire": 1