282 lines
9.3 KiB
Java
282 lines
9.3 KiB
Java
package net.minecraft.world.level.block.entity;
|
|
|
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
|
import com.mojang.logging.LogUtils;
|
|
import com.mojang.serialization.DynamicOps;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.function.UnaryOperator;
|
|
import net.minecraft.commands.CommandSource;
|
|
import net.minecraft.commands.CommandSourceStack;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.network.chat.ClickEvent;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.network.chat.ComponentUtils;
|
|
import net.minecraft.network.chat.Style;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.network.FilteredText;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.SignBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.phys.Vec2;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class SignBlockEntity extends BlockEntity {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private static final int MAX_TEXT_LINE_WIDTH = 90;
|
|
private static final int TEXT_LINE_HEIGHT = 10;
|
|
@Nullable
|
|
private UUID playerWhoMayEdit;
|
|
private SignText frontText = this.createDefaultSignText();
|
|
private SignText backText = this.createDefaultSignText();
|
|
private boolean isWaxed;
|
|
|
|
public SignBlockEntity(BlockPos pos, BlockState blockState) {
|
|
this(BlockEntityType.SIGN, pos, blockState);
|
|
}
|
|
|
|
public SignBlockEntity(BlockEntityType type, BlockPos pos, BlockState blockState) {
|
|
super(type, pos, blockState);
|
|
}
|
|
|
|
protected SignText createDefaultSignText() {
|
|
return new SignText();
|
|
}
|
|
|
|
public boolean isFacingFrontText(Player player) {
|
|
if (this.getBlockState().getBlock() instanceof SignBlock signBlock) {
|
|
Vec3 vec3 = signBlock.getSignHitboxCenterPosition(this.getBlockState());
|
|
double d = player.getX() - (this.getBlockPos().getX() + vec3.x);
|
|
double e = player.getZ() - (this.getBlockPos().getZ() + vec3.z);
|
|
float f = signBlock.getYRotationDegrees(this.getBlockState());
|
|
float g = (float)(Mth.atan2(e, d) * 180.0F / (float)Math.PI) - 90.0F;
|
|
return Mth.degreesDifferenceAbs(f, g) <= 90.0F;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public SignText getText(boolean isFrontText) {
|
|
return isFrontText ? this.frontText : this.backText;
|
|
}
|
|
|
|
public SignText getFrontText() {
|
|
return this.frontText;
|
|
}
|
|
|
|
public SignText getBackText() {
|
|
return this.backText;
|
|
}
|
|
|
|
public int getTextLineHeight() {
|
|
return 10;
|
|
}
|
|
|
|
public int getMaxTextLineWidth() {
|
|
return 90;
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
|
super.saveAdditional(tag, registries);
|
|
DynamicOps<Tag> dynamicOps = registries.createSerializationContext(NbtOps.INSTANCE);
|
|
SignText.DIRECT_CODEC.encodeStart(dynamicOps, this.frontText).resultOrPartial(LOGGER::error).ifPresent(tagx -> tag.put("front_text", tagx));
|
|
SignText.DIRECT_CODEC.encodeStart(dynamicOps, this.backText).resultOrPartial(LOGGER::error).ifPresent(tagx -> tag.put("back_text", tagx));
|
|
tag.putBoolean("is_waxed", this.isWaxed);
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
|
super.loadAdditional(tag, registries);
|
|
DynamicOps<Tag> dynamicOps = registries.createSerializationContext(NbtOps.INSTANCE);
|
|
if (tag.contains("front_text")) {
|
|
SignText.DIRECT_CODEC
|
|
.parse(dynamicOps, tag.getCompound("front_text"))
|
|
.resultOrPartial(LOGGER::error)
|
|
.ifPresent(signText -> this.frontText = this.loadLines(signText));
|
|
}
|
|
|
|
if (tag.contains("back_text")) {
|
|
SignText.DIRECT_CODEC
|
|
.parse(dynamicOps, tag.getCompound("back_text"))
|
|
.resultOrPartial(LOGGER::error)
|
|
.ifPresent(signText -> this.backText = this.loadLines(signText));
|
|
}
|
|
|
|
this.isWaxed = tag.getBoolean("is_waxed");
|
|
}
|
|
|
|
private SignText loadLines(SignText text) {
|
|
for (int i = 0; i < 4; i++) {
|
|
Component component = this.loadLine(text.getMessage(i, false));
|
|
Component component2 = this.loadLine(text.getMessage(i, true));
|
|
text = text.setMessage(i, component, component2);
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
private Component loadLine(Component lineText) {
|
|
if (this.level instanceof ServerLevel serverLevel) {
|
|
try {
|
|
return ComponentUtils.updateForEntity(createCommandSourceStack(null, serverLevel, this.worldPosition), lineText, null, 0);
|
|
} catch (CommandSyntaxException var4) {
|
|
}
|
|
}
|
|
|
|
return lineText;
|
|
}
|
|
|
|
public void updateSignText(Player player, boolean isFrontText, List<FilteredText> filteredText) {
|
|
if (!this.isWaxed() && player.getUUID().equals(this.getPlayerWhoMayEdit()) && this.level != null) {
|
|
this.updateText(signText -> this.setMessages(player, filteredText, signText), isFrontText);
|
|
this.setAllowedPlayerEditor(null);
|
|
this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3);
|
|
} else {
|
|
LOGGER.warn("Player {} just tried to change non-editable sign", player.getName().getString());
|
|
}
|
|
}
|
|
|
|
public boolean updateText(UnaryOperator<SignText> updater, boolean isFrontText) {
|
|
SignText signText = this.getText(isFrontText);
|
|
return this.setText((SignText)updater.apply(signText), isFrontText);
|
|
}
|
|
|
|
private SignText setMessages(Player player, List<FilteredText> filteredText, SignText text) {
|
|
for (int i = 0; i < filteredText.size(); i++) {
|
|
FilteredText filteredText2 = (FilteredText)filteredText.get(i);
|
|
Style style = text.getMessage(i, player.isTextFilteringEnabled()).getStyle();
|
|
if (player.isTextFilteringEnabled()) {
|
|
text = text.setMessage(i, Component.literal(filteredText2.filteredOrEmpty()).setStyle(style));
|
|
} else {
|
|
text = text.setMessage(i, Component.literal(filteredText2.raw()).setStyle(style), Component.literal(filteredText2.filteredOrEmpty()).setStyle(style));
|
|
}
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
public boolean setText(SignText text, boolean isFrontText) {
|
|
return isFrontText ? this.setFrontText(text) : this.setBackText(text);
|
|
}
|
|
|
|
private boolean setBackText(SignText text) {
|
|
if (text != this.backText) {
|
|
this.backText = text;
|
|
this.markUpdated();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private boolean setFrontText(SignText text) {
|
|
if (text != this.frontText) {
|
|
this.frontText = text;
|
|
this.markUpdated();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean canExecuteClickCommands(boolean isFrontText, Player player) {
|
|
return this.isWaxed() && this.getText(isFrontText).hasAnyClickCommands(player);
|
|
}
|
|
|
|
public boolean executeClickCommandsIfPresent(Player player, Level level, BlockPos pos, boolean frontText) {
|
|
boolean bl = false;
|
|
|
|
for (Component component : this.getText(frontText).getMessages(player.isTextFilteringEnabled())) {
|
|
Style style = component.getStyle();
|
|
ClickEvent clickEvent = style.getClickEvent();
|
|
if (clickEvent != null && clickEvent.getAction() == ClickEvent.Action.RUN_COMMAND) {
|
|
player.getServer().getCommands().performPrefixedCommand(createCommandSourceStack(player, level, pos), clickEvent.getValue());
|
|
bl = true;
|
|
}
|
|
}
|
|
|
|
return bl;
|
|
}
|
|
|
|
private static CommandSourceStack createCommandSourceStack(@Nullable Player player, Level level, BlockPos pos) {
|
|
String string = player == null ? "Sign" : player.getName().getString();
|
|
Component component = (Component)(player == null ? Component.literal("Sign") : player.getDisplayName());
|
|
return new CommandSourceStack(CommandSource.NULL, Vec3.atCenterOf(pos), Vec2.ZERO, (ServerLevel)level, 2, string, component, level.getServer(), player);
|
|
}
|
|
|
|
public ClientboundBlockEntityDataPacket getUpdatePacket() {
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
}
|
|
|
|
@Override
|
|
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
|
|
return this.saveCustomOnly(registries);
|
|
}
|
|
|
|
@Override
|
|
public boolean onlyOpCanSetNbt() {
|
|
return true;
|
|
}
|
|
|
|
public void setAllowedPlayerEditor(@Nullable UUID playWhoMayEdit) {
|
|
this.playerWhoMayEdit = playWhoMayEdit;
|
|
}
|
|
|
|
@Nullable
|
|
public UUID getPlayerWhoMayEdit() {
|
|
return this.playerWhoMayEdit;
|
|
}
|
|
|
|
private void markUpdated() {
|
|
this.setChanged();
|
|
this.level.sendBlockUpdated(this.getBlockPos(), this.getBlockState(), this.getBlockState(), 3);
|
|
}
|
|
|
|
public boolean isWaxed() {
|
|
return this.isWaxed;
|
|
}
|
|
|
|
public boolean setWaxed(boolean isWaxed) {
|
|
if (this.isWaxed != isWaxed) {
|
|
this.isWaxed = isWaxed;
|
|
this.markUpdated();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public boolean playerIsTooFarAwayToEdit(UUID uuid) {
|
|
Player player = this.level.getPlayerByUUID(uuid);
|
|
return player == null || !player.canInteractWithBlock(this.getBlockPos(), 4.0);
|
|
}
|
|
|
|
public static void tick(Level level, BlockPos pos, BlockState state, SignBlockEntity sign) {
|
|
UUID uUID = sign.getPlayerWhoMayEdit();
|
|
if (uUID != null) {
|
|
sign.clearInvalidPlayerWhoMayEdit(sign, level, uUID);
|
|
}
|
|
}
|
|
|
|
private void clearInvalidPlayerWhoMayEdit(SignBlockEntity sign, Level level, UUID uuid) {
|
|
if (sign.playerIsTooFarAwayToEdit(uuid)) {
|
|
sign.setAllowedPlayerEditor(null);
|
|
}
|
|
}
|
|
|
|
public SoundEvent getSignInteractionFailedSoundEvent() {
|
|
return SoundEvents.WAXED_SIGN_INTERACT_FAIL;
|
|
}
|
|
}
|