Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Log Sign Editing and Waxing #243

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.github.quiltservertools.ledger.mixin.blocks.sign;

import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import net.minecraft.block.AbstractSignBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.SignChangingItem;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(AbstractSignBlock.class)
public class AbstractSignBlockMixin {

/**
* Wraps the operation of item-based interactions with signs like waxing, dyeing, and using glow ink, with a
* block-change log action.
* <p>
* Uses a weird and probably bad hack that copies the NBT data of the sign into a new block entity instance so that
* the old data can be preserved for rollbacks.
*
* @param instance The {@linkplain Item item} that is being used on the sign
* @param world The world of the interaction
* @param signBlockEntity The sign block entity being interacted with
* @param front Whether the interaction is happening on the front of the sign
* @param player The player interacting with the sign
* @param original The original {@link SignChangingItem#useOnSign(World, SignBlockEntity, boolean, PlayerEntity)}
* operation that this mixin wraps.
* @return Returns the result of calling {@code original} with this method's parameters.
*/
@WrapOperation(
method = "onUse",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/item/SignChangingItem;useOnSign(Lnet/minecraft/world/World;Lnet/minecraft/block/entity/SignBlockEntity;ZLnet/minecraft/entity/player/PlayerEntity;)Z"
)
)
private boolean logSignItemInteraction(
SignChangingItem instance,
World world,
SignBlockEntity signBlockEntity,
boolean front,
PlayerEntity player,
Operation<Boolean> original
) {

BlockState state = signBlockEntity.getCachedState();
BlockPos pos = signBlockEntity.getPos();

// a bad hack to copy the old sign block entity for rollbacks
@Nullable BlockEntity oldSignEntity = BlockEntity.createFromNbt(pos, state, signBlockEntity.createNbtWithId());

boolean result = original.call(instance, world, signBlockEntity, front, player);
if (result && oldSignEntity != null) {
BlockChangeCallback.EVENT.invoker()
.changeBlock(
world,
pos,
state,
state, // the state doesn't update, the block entity does
oldSignEntity,
signBlockEntity,
player
);
}

return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.github.quiltservertools.ledger.mixin.blocks.sign;

import com.github.quiltservertools.ledger.callbacks.BlockChangeCallback;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import java.util.UUID;
import java.util.function.UnaryOperator;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.block.entity.SignText;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;

@Mixin(SignBlockEntity.class)
public abstract class SignBlockEntityMixin {

/**
* Wraps the operation of sign text editing with signs with a block-change log action.
* <p>
* Uses a weird and probably bad hack that copies the NBT data of the sign into a new block entity instance so that
* the old data can be preserved for rollbacks.
*
* @param instance The sign block entity being edited
* @param textChanger A parameter for the original operation
* @param front Whether the interaction is happening on the front of the sign
* @param original The original {@link SignBlockEntity#changeText(UnaryOperator, boolean)} operation that this
* mixin wraps.
* @return Returns the result of calling {@code original} with this method's parameters.
*/
@WrapOperation(
method = "tryChangeText",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/entity/SignBlockEntity;changeText(Ljava/util/function/UnaryOperator;Z)Z"
)
)
private boolean logSignTextChange(
SignBlockEntity instance,
UnaryOperator<SignText> textChanger,
boolean front,
Operation<Boolean> original
) {

BlockPos pos = instance.getPos();
BlockState state = instance.getCachedState();

// a bad hack to copy the old sign block entity for rollbacks
@Nullable BlockEntity oldSignEntity = BlockEntity.createFromNbt(pos, state, instance.createNbtWithId());

boolean result = original.call(instance, textChanger, front);
if (result && oldSignEntity != null) {

World world = instance.getWorld();
assert world != null : "World cannot be null, this is already in the target method";

UUID editorID = instance.getEditor();
PlayerEntity player = world.getPlayerByUuid(editorID);
assert player != null : "The editor must exist, this is already checked in target method";

BlockChangeCallback.EVENT.invoker()
.changeBlock(
world,
pos,
state,
state, // the state doesn't update, the block entity does
oldSignEntity,
instance,
player
);
}

return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ open class BlockChangeActionType : AbstractActionType() {
override fun rollback(server: MinecraftServer): Boolean {
val world = server.getWorld(world)
world?.setBlockState(pos, oldBlockState())

world?.getBlockEntity(pos)?.readNbt(StringNbtReader.parse(extraData))
world?.chunkManager?.markForUpdate(pos)

return true
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/ledger.mixins.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
"blocks.lectern.LecternBlockMixin",
"blocks.lectern.LecternScreenHandlerMixin",
"blocks.lectern.ScreenHandlerMixin",
"blocks.sign.AbstractSignBlockMixin",
"blocks.sign.SignBlockEntityMixin",
"entities.ArmorStandEntityMixin",
"entities.CatEntityMixin",
"entities.CreeperEntityMixin",
Expand Down
Loading