Skip to content

Commit

Permalink
Entity kill and Item change preview (#241)
Browse files Browse the repository at this point in the history
  • Loading branch information
DrexHD authored Apr 10, 2024
1 parent 2a5ce29 commit 3fd878c
Show file tree
Hide file tree
Showing 19 changed files with 329 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Slot> cir) {
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<ScreenHandler> cir) {
ScreenHandler screenHandler = cir.getReturnValue();
if (screenHandler != null) {
((HandlerWithContext) screenHandler).setPos(this.field_17358.getPos());
}
}

}
Original file line number Diff line number Diff line change
@@ -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<ScreenHandler> cir) {
ScreenHandler screenHandler = cir.getReturnValue();
if (screenHandler != null) {
((HandlerWithContext) screenHandler).setPos(this.getPos());
}
}

}
Original file line number Diff line number Diff line change
@@ -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<ScreenHandler> cir) {
ScreenHandler screenHandler = cir.getReturnValue();
if (screenHandler != null) {
((HandlerWithContext) screenHandler).setPos(this.getPos());
}
}

}
Original file line number Diff line number Diff line change
@@ -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;<init>(IILnet/minecraft/util/collection/DefaultedList;Lnet/minecraft/item/ItemStack;)V"
), index = 2
)
private DefaultedList<ItemStack> modifyStacks(DefaultedList<ItemStack> 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<Pair<ItemStack, Boolean>> modifiedItems = preview.getModifiedItems().get(pos);
if (modifiedItems == null) return stacks;
// Copy original list
DefaultedList<ItemStack> previewStacks = DefaultedList.of();
previewStacks.addAll(stacks);
for (Pair<ItemStack, Boolean> 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;
}
}
2 changes: 2 additions & 0 deletions src/main/kotlin/com/github/quiltservertools/ledger/Ledger.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -53,6 +54,7 @@ object Ledger : DedicatedServerModInitializer, CoroutineScope {
lateinit var config: Config
lateinit var server: MinecraftServer
val searchCache = ConcurrentHashMap<String, ActionSearchParams>()
@JvmField // Required for mixin access
val previewCache = ConcurrentHashMap<UUID, Preview>()

override val coroutineContext: CoroutineContext = Dispatchers.IO
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
}
}

Expand All @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
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() {
override val identifier = "entity-kill"

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)

Expand Down
Loading

0 comments on commit 3fd878c

Please sign in to comment.