properly log sign edits

This commit is contained in:
Brokkonaut
2023-06-12 03:41:12 +02:00
parent 31bef400a6
commit 419f3530ad
9 changed files with 302 additions and 109 deletions

View File

@ -36,6 +36,7 @@ import org.bukkit.World;
import org.bukkit.block.BlockState;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.block.sign.Side;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -340,14 +341,16 @@ public class Consumer extends Thread {
* Location of the placed sign
* @param type
* BlockData of the sign
* @param side
* @param lines
* The four lines on the sign.
*/
public void queueSignChange(Actor actor, Location loc, BlockData type, String[] lines) {
public void queueSignChange(Actor actor, Location loc, BlockState state, Side side, String[] lines) {
BlockData type = state.getBlockData();
if (!BukkitUtils.isSign(type.getMaterial())) {
return;
}
queueBlock(actor, loc, type, type, null, BlockStateCodecSign.serialize(lines), null);
queueBlock(actor, loc, type, type, null, BlockStateCodecSign.INSTANCE.serialize(state, side, lines), null);
}
public void queueChat(Actor player, String message) {

View File

@ -9,6 +9,7 @@ import de.diddiz.LogBlock.util.Utils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.sign.Side;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.inventory.ItemStack;
@ -674,7 +675,7 @@ class Updater {
if (!nullBlock && signText != null) {
String[] lines = signText.split("\0", 4);
byte[] bytes = Utils.serializeYamlConfiguration(BlockStateCodecSign.serialize(lines));
byte[] bytes = Utils.serializeYamlConfiguration(BlockStateCodecSign.INSTANCE.serialize(null, Side.FRONT, lines));
Material replacedMaterial = MaterialConverter.getBlockData(replaced, -1).getMaterial();
Material typeMaterial = MaterialConverter.getBlockData(type, -1).getMaterial();

View File

@ -1,6 +1,7 @@
package de.diddiz.LogBlock.blockstate;
import de.diddiz.LogBlock.util.BukkitUtils;
import de.diddiz.LogBlock.util.Reflections;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -8,9 +9,15 @@ import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.block.Sign;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
public class BlockStateCodecSign implements BlockStateCodec {
public static final BlockStateCodecSign INSTANCE = new BlockStateCodecSign();
@Override
public Material[] getApplicableMaterials() {
return BukkitUtils.getAllSignMaterials().toArray(new Material[BukkitUtils.getAllSignMaterials().size()]);
@ -18,44 +25,63 @@ public class BlockStateCodecSign implements BlockStateCodec {
@Override
public YamlConfiguration serialize(BlockState state) {
if (state instanceof Sign) {
Sign sign = (Sign) state;
String[] lines = sign.getLines();
boolean hasText = false;
for (int i = 0; i < lines.length; i++) {
if (lines[i] != null && lines[i].length() > 0) {
hasText = true;
break;
}
YamlConfiguration conf = null;
if (state instanceof Sign sign) {
boolean waxed = Reflections.isSignWaxed(sign);
if (waxed) {
conf = new YamlConfiguration();
conf.set("waxed", waxed);
}
DyeColor signColor = sign.getColor();
if (signColor == null) {
signColor = DyeColor.BLACK;
}
if (hasText || signColor != DyeColor.BLACK) {
YamlConfiguration conf = new YamlConfiguration();
if (hasText) {
conf.set("lines", Arrays.asList(lines));
for (Side side : Side.values()) {
SignSide signSide = sign.getSide(side);
String[] lines = signSide.getLines();
boolean hasText = false;
for (int i = 0; i < lines.length; i++) {
if (lines[i] != null && lines[i].length() > 0) {
hasText = true;
break;
}
}
if (signColor != DyeColor.BLACK) {
conf.set("color", signColor.name());
DyeColor signColor = signSide.getColor();
if (signColor == null) {
signColor = DyeColor.BLACK;
}
if (sign.isGlowingText()) {
conf.set("glowing", true);
boolean glowing = signSide.isGlowingText();
if (hasText || signColor != DyeColor.BLACK || glowing) {
if (conf == null) {
conf = new YamlConfiguration();
}
ConfigurationSection sideSection = side == Side.FRONT ? conf : conf.createSection(side.name().toLowerCase());
if (hasText) {
sideSection.set("lines", Arrays.asList(lines));
}
if (signColor != DyeColor.BLACK) {
sideSection.set("color", signColor.name());
}
if (glowing) {
sideSection.set("glowing", true);
}
}
return conf;
}
}
return null;
return conf;
}
/**
* This is required for the SignChangeEvent, because we have no BlockState there.
* This is required for the SignChangeEvent, because we have no updated BlockState there.
* @param state
*/
public static YamlConfiguration serialize(String[] lines) {
YamlConfiguration conf = new YamlConfiguration();
public YamlConfiguration serialize(BlockState state, Side side, String[] lines) {
YamlConfiguration conf = state == null ? null : serialize(state);
if (lines != null) {
conf.set("lines", Arrays.asList(lines));
if (conf == null) {
conf = new YamlConfiguration();
}
ConfigurationSection sideSection = side == Side.FRONT ? conf : conf.getConfigurationSection(side.name().toLowerCase());
if (sideSection == null) {
sideSection = conf.createSection(side.name().toLowerCase());
}
sideSection.set("lines", Arrays.asList(lines));
}
return conf;
}
@ -64,82 +90,105 @@ public class BlockStateCodecSign implements BlockStateCodec {
public void deserialize(BlockState state, YamlConfiguration conf) {
if (state instanceof Sign) {
Sign sign = (Sign) state;
DyeColor signColor = DyeColor.BLACK;
boolean glowing = false;
List<String> lines = Collections.emptyList();
if (conf != null) {
if (conf.contains("lines")) {
lines = conf.getStringList("lines");
}
if (conf.contains("color")) {
try {
signColor = DyeColor.valueOf(conf.getString("color"));
} catch (IllegalArgumentException | NullPointerException e) {
// ignored
sign.setEditable(!conf.getBoolean("waxed"));
for (Side side : Side.values()) {
ConfigurationSection sideSection = side == Side.FRONT ? conf : conf.getConfigurationSection(side.name().toLowerCase());
DyeColor signColor = DyeColor.BLACK;
boolean glowing = false;
List<String> lines = Collections.emptyList();
if (sideSection != null) {
if (sideSection.contains("lines")) {
lines = sideSection.getStringList("lines");
}
if (sideSection.contains("color")) {
try {
signColor = DyeColor.valueOf(sideSection.getString("color"));
} catch (IllegalArgumentException | NullPointerException e) {
// ignored
}
}
glowing = sideSection.getBoolean("glowing", false);
}
SignSide signSide = sign.getSide(side);
for (int i = 0; i < 4; i++) {
String line = lines.size() > i && lines.get(i) != null ? lines.get(i) : "";
signSide.setLine(i, line);
}
signSide.setColor(signColor);
signSide.setGlowingText(glowing);
}
glowing = conf.getBoolean("glowing", false);
}
for (int i = 0; i < 4; i++) {
String line = lines.size() > i && lines.get(i) != null ? lines.get(i) : "";
sign.setLine(i, line);
}
sign.setColor(signColor);
sign.setGlowingText(glowing);
}
}
@Override
public String toString(YamlConfiguration state, YamlConfiguration oldState) {
if (state != null) {
List<String> lines = state.getStringList("lines");
List<String> oldLines = Collections.emptyList();
DyeColor signColor = DyeColor.BLACK;
if (state.contains("color")) {
try {
signColor = DyeColor.valueOf(state.getString("color"));
} catch (IllegalArgumentException | NullPointerException e) {
// ignored
}
StringBuilder sb = new StringBuilder();
boolean isWaxed = state.getBoolean("waxed");
boolean oldWaxed = oldState != null && oldState.getBoolean("waxed");
if (isWaxed != oldWaxed) {
sb.append(isWaxed ? "(waxed)" : "(not waxed)");
}
DyeColor oldSignColor = DyeColor.BLACK;
boolean glowing = state.getBoolean("glowing", false);
boolean oldGlowing = false;
if (oldState != null) {
oldLines = oldState.getStringList("lines");
if (oldState.contains("color")) {
for (Side side : Side.values()) {
ConfigurationSection sideSection = side == Side.FRONT ? state : state.getConfigurationSection(side.name().toLowerCase());
if (!sb.isEmpty()) {
sb.append(" ");
}
sb.append(side.name()).append(":");
List<String> lines = sideSection == null ? Collections.emptyList() : sideSection.getStringList("lines");
List<String> oldLines = Collections.emptyList();
DyeColor signColor = DyeColor.BLACK;
if (sideSection != null && sideSection.contains("color")) {
try {
oldSignColor = DyeColor.valueOf(oldState.getString("color"));
signColor = DyeColor.valueOf(sideSection.getString("color"));
} catch (IllegalArgumentException | NullPointerException e) {
// ignored
}
}
oldGlowing = oldState.getBoolean("glowing", false);
}
DyeColor oldSignColor = DyeColor.BLACK;
boolean glowing = sideSection != null && sideSection.getBoolean("glowing", false);
boolean oldGlowing = false;
if (oldState != null) {
ConfigurationSection oldSideSection = side == Side.FRONT ? oldState : oldState.getConfigurationSection(side.name().toLowerCase());
if (oldSideSection != null) {
oldLines = oldSideSection.getStringList("lines");
if (oldSideSection.contains("color")) {
try {
oldSignColor = DyeColor.valueOf(oldSideSection.getString("color"));
} catch (IllegalArgumentException | NullPointerException e) {
// ignored
}
}
oldGlowing = oldSideSection.getBoolean("glowing", false);
}
}
StringBuilder sb = new StringBuilder();
if (!lines.equals(oldLines)) {
for (String line : lines) {
if (!lines.equals(oldLines)) {
for (String line : lines) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append("[").append(line).append("]");
}
}
if (signColor != oldSignColor) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append("[").append(line).append("]");
sb.append("(color: " + signColor.name().toLowerCase() + ")");
}
}
if (signColor != oldSignColor) {
if (sb.length() > 0) {
sb.append(" ");
}
sb.append("(color: " + signColor.name().toLowerCase() + ")");
}
if (glowing != oldGlowing) {
if (sb.length() > 0) {
sb.append(" ");
}
if (glowing) {
sb.append("(glowing)");
} else {
sb.append("(not glowing)");
if (glowing != oldGlowing) {
if (sb.length() > 0) {
sb.append(" ");
}
if (glowing) {
sb.append("(glowing)");
} else {
sb.append("(not glowing)");
}
}
}
return sb.toString();

View File

@ -21,7 +21,7 @@ public class BlockStateCodecs {
}
static {
registerCodec(new BlockStateCodecSign());
registerCodec(BlockStateCodecSign.INSTANCE);
registerCodec(new BlockStateCodecSkull());
registerCodec(new BlockStateCodecBanner());
registerCodec(new BlockStateCodecSpawner());

View File

@ -5,6 +5,7 @@ import de.diddiz.LogBlock.LogBlock;
import de.diddiz.LogBlock.Logging;
import de.diddiz.LogBlock.config.WorldConfig;
import de.diddiz.LogBlock.util.BukkitUtils;
import de.diddiz.LogBlock.util.Reflections;
import java.util.UUID;
import org.bukkit.DyeColor;
import org.bukkit.GameEvent;
@ -30,6 +31,8 @@ import org.bukkit.block.data.type.NoteBlock;
import org.bukkit.block.data.type.Repeater;
import org.bukkit.block.data.type.Switch;
import org.bukkit.block.data.type.TurtleEgg;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.entity.Player;
import org.bukkit.event.Event.Result;
import org.bukkit.event.EventHandler;
@ -93,30 +96,34 @@ public class InteractLogging extends LoggingListener {
} else if (BukkitUtils.isSign(type)) {
if (wcfg.isLogging(Logging.SIGNTEXT) && event.getAction() == Action.RIGHT_CLICK_BLOCK && event.getItem() != null) {
Material itemType = event.getItem().getType();
if (BukkitUtils.isDye(itemType) || itemType == Material.GLOW_INK_SAC || itemType == Material.INK_SAC) {
if (BukkitUtils.isDye(itemType) || itemType == Material.GLOW_INK_SAC || itemType == Material.INK_SAC || itemType == Material.HONEYCOMB) {
final BlockState before = event.getClickedBlock().getState();
if (before instanceof Sign) {
if (itemType == Material.GLOW_INK_SAC) {
Sign signBefore = (Sign) before;
if (!signBefore.isGlowingText()) {
final Sign signAfter = (Sign) event.getClickedBlock().getState();
signAfter.setGlowingText(true);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
}
} else if (itemType == Material.INK_SAC) {
Sign signBefore = (Sign) before;
if (signBefore.isGlowingText()) {
final Sign signAfter = (Sign) event.getClickedBlock().getState();
signAfter.setGlowingText(false);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
}
} else {
DyeColor newColor = BukkitUtils.dyeToDyeColor(itemType);
Sign signBefore = (Sign) before;
if (newColor != null && signBefore.getColor() != newColor) {
final Sign signAfter = (Sign) event.getClickedBlock().getState();
signAfter.setColor(newColor);
if (before instanceof Sign signBefore) {
boolean waxed = Reflections.isSignWaxed(signBefore);
if (!waxed) {
final Sign signAfter = (Sign) event.getClickedBlock().getState();
Side side = BukkitUtils.getFacingSignSide(player, clicked);
SignSide signSideBefore = signBefore.getSide(side);
SignSide signSideAfter = signAfter.getSide(side);
if (itemType == Material.GLOW_INK_SAC) {
if (!signSideBefore.isGlowingText()) {
signSideAfter.setGlowingText(true);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
}
} else if (itemType == Material.INK_SAC) {
if (signSideBefore.isGlowingText()) {
signSideAfter.setGlowingText(false);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
}
} else if (itemType == Material.HONEYCOMB) {
signAfter.setEditable(false);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
} else if (BukkitUtils.isDye(itemType)) {
DyeColor newColor = BukkitUtils.dyeToDyeColor(itemType);
if (newColor != null && signSideBefore.getColor() != newColor) {
signSideAfter.setColor(newColor);
consumer.queueBlockReplace(Actor.actorFromEntity(player), signBefore, signAfter);
}
}
}
}

View File

@ -17,7 +17,7 @@ public class SignChangeLogging extends LoggingListener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSignChange(SignChangeEvent event) {
if (isLogging(event.getBlock().getWorld(), Logging.SIGNTEXT)) {
consumer.queueSignChange(Actor.actorFromEntity(event.getPlayer()), event.getBlock().getLocation(), event.getBlock().getBlockData(), event.getLines());
consumer.queueSignChange(Actor.actorFromEntity(event.getPlayer()), event.getBlock().getLocation(), event.getBlock().getState(), event.getSide(), event.getLines());
}
}
}

View File

@ -36,9 +36,16 @@ import org.bukkit.block.DoubleChest;
import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.Bisected.Half;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
import org.bukkit.block.data.type.HangingSign;
import org.bukkit.block.data.type.Sign;
import org.bukkit.block.data.type.Slab;
import org.bukkit.block.data.type.Slab.Type;
import org.bukkit.block.data.type.Stairs;
import org.bukkit.block.data.type.WallHangingSign;
import org.bukkit.block.data.type.WallSign;
import org.bukkit.block.sign.Side;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
@ -1223,4 +1230,83 @@ public class BukkitUtils {
public static boolean isSign(Material m) {
return allSigns.contains(m);
}
public static Side getFacingSignSide(Entity entity, Block sign) {
BlockData data = sign.getBlockData();
Material type = data.getMaterial();
BlockFace signFace = null;
double centerx = 0.5;
double centerz = 0.5;
double yRotationDegree = 0;
if (type.data == Sign.class || type.data == HangingSign.class) {
Rotatable rotatableData = (Rotatable) data;
signFace = rotatableData.getRotation();
if (signFace == BlockFace.SOUTH) {
yRotationDegree = 360 * 0.0 / 16.0;
} else if (signFace == BlockFace.SOUTH_SOUTH_WEST) {
yRotationDegree = 360 * 1.0 / 16.0;
} else if (signFace == BlockFace.SOUTH_WEST) {
yRotationDegree = 360 * 2.0 / 16.0;
} else if (signFace == BlockFace.WEST_SOUTH_WEST) {
yRotationDegree = 360 * 3.0 / 16.0;
} else if (signFace == BlockFace.WEST) {
yRotationDegree = 360 * 4.0 / 16.0;
} else if (signFace == BlockFace.WEST_NORTH_WEST) {
yRotationDegree = 360 * 5.0 / 16.0;
} else if (signFace == BlockFace.NORTH_WEST) {
yRotationDegree = 360 * 6.0 / 16.0;
} else if (signFace == BlockFace.NORTH_NORTH_WEST) {
yRotationDegree = 360 * 7.0 / 16.0;
} else if (signFace == BlockFace.NORTH) {
yRotationDegree = 360 * 8.0 / 16.0;
} else if (signFace == BlockFace.NORTH_NORTH_EAST) {
yRotationDegree = 360 * 9.0 / 16.0;
} else if (signFace == BlockFace.NORTH_EAST) {
yRotationDegree = 360 * 10.0 / 16.0;
} else if (signFace == BlockFace.EAST_NORTH_EAST) {
yRotationDegree = 360 * 11.0 / 16.0;
} else if (signFace == BlockFace.EAST) {
yRotationDegree = 360 * 12.0 / 16.0;
} else if (signFace == BlockFace.EAST_SOUTH_EAST) {
yRotationDegree = 360 * 13.0 / 16.0;
} else if (signFace == BlockFace.SOUTH_EAST) {
yRotationDegree = 360 * 14.0 / 16.0;
} else if (signFace == BlockFace.SOUTH_SOUTH_EAST) {
yRotationDegree = 360 * 15.0 / 16.0;
}
} else if (type.data == WallSign.class || type.data == WallHangingSign.class) {
Directional directionalData = (Directional) data;
signFace = directionalData.getFacing();
if (signFace == BlockFace.SOUTH) {
yRotationDegree = 0;
} else if (signFace == BlockFace.WEST) {
yRotationDegree = 90;
} else if (signFace == BlockFace.NORTH) {
yRotationDegree = 180;
} else if (signFace == BlockFace.EAST) {
yRotationDegree = 270;
}
// wall signs are not centered on the block (but hanging wall signs are)
if (type.data == WallSign.class) {
if (signFace == BlockFace.NORTH) {
centerz = 15.0 / 16.0;
} else if (signFace == BlockFace.SOUTH) {
centerz = 1.0 / 16.0;
} else if (signFace == BlockFace.WEST) {
centerx = 15.0 / 16.0;
} else if (signFace == BlockFace.EAST) {
centerx = 1.0 / 16.0;
}
}
} else {
throw new IllegalArgumentException("block is not a sign");
}
Location entityLoc = entity.getLocation();
double relativeX = entityLoc.getX() - (sign.getX() + centerx);
double relativeZ = entityLoc.getZ() - (sign.getZ() + centerz);
double f = Math.atan2(relativeZ, relativeX) * 180.0 / Math.PI - 90.0;
return Math.abs(Utils.warpDegrees(f - yRotationDegree)) <= 90.0 ? Side.FRONT : Side.BACK;
}
}

View File

@ -0,0 +1,36 @@
package de.diddiz.LogBlock.util;
import de.diddiz.LogBlock.LogBlock;
import java.lang.reflect.Field;
import java.util.logging.Level;
import org.bukkit.block.Sign;
public class Reflections {
private static Field FIELD_CraftBlockEntityState_snapshot;
private static Field FIELD_SignBlockEntity_isWaxed;
public static boolean isSignWaxed(Sign sign) {
try {
if (FIELD_CraftBlockEntityState_snapshot == null) {
FIELD_CraftBlockEntityState_snapshot = sign.getClass().getSuperclass().getDeclaredField("snapshot");
FIELD_CraftBlockEntityState_snapshot.setAccessible(true);
}
Object snapshot = FIELD_CraftBlockEntityState_snapshot.get(sign);
if (snapshot == null) {
return false;
}
if (FIELD_SignBlockEntity_isWaxed == null) {
for (Field f : snapshot.getClass().getDeclaredFields()) {
if (f.getType() == boolean.class) {
FIELD_SignBlockEntity_isWaxed = f;
FIELD_SignBlockEntity_isWaxed.setAccessible(true);
}
}
}
return FIELD_SignBlockEntity_isWaxed != null && FIELD_SignBlockEntity_isWaxed.getBoolean(snapshot);
} catch (SecurityException | NoSuchFieldException | IllegalArgumentException | IllegalAccessException e) {
LogBlock.getInstance().getLogger().log(Level.SEVERE, "Sign.isWaxed reflection failed", e);
}
return false;
}
}

View File

@ -284,4 +284,15 @@ public class Utils {
public static String serializeForSQL(YamlConfiguration conf) {
return mysqlPrepareBytesForInsertAllowNull(serializeYamlConfiguration(conf));
}
public static double warpDegrees(double degrees) {
double d = degrees % 360.0;
if (d >= 180.0) {
d -= 360.0;
}
if (d < -180.0) {
d += 360.0;
}
return d;
}
}