From a8a44db1b0e7d389b6e0d1b4eac1d8d83c582b4e Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:08:38 +0200 Subject: [PATCH 01/55] sign rework - add API classes --- .../sign/AbstractCraftPilotSign.java | 17 ++ .../movecraft/sign/AbstractCraftSign.java | 123 ++++++++++ .../movecraft/sign/AbstractCruiseSign.java | 169 ++++++++++++++ .../sign/AbstractInformationSign.java | 165 ++++++++++++++ .../movecraft/sign/AbstractMovecraftSign.java | 160 +++++++++++++ .../movecraft/sign/AbstractSignListener.java | 211 ++++++++++++++++++ .../movecraft/sign/AbstractSubcraftSign.java | 178 +++++++++++++++ 7 files changed, 1023 insertions(+) create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java new file mode 100644 index 000000000..c2d80313a --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java @@ -0,0 +1,17 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.craft.type.CraftType; + +/* + * Base implementation for all craft pilot signs, does nothing but has the relevant CraftType instance backed + */ +public abstract class AbstractCraftPilotSign extends AbstractMovecraftSign { + + protected final CraftType craftType; + + public AbstractCraftPilotSign(final CraftType craftType) { + super(); + this.craftType = craftType; + } + +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java new file mode 100644 index 000000000..232601e82 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java @@ -0,0 +1,123 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.PilotedCraft; +import net.countercraft.movecraft.craft.PlayerCraft; +import net.countercraft.movecraft.events.CraftDetectEvent; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; + +import javax.annotation.Nullable; +import java.util.Optional; + +/* + * Extension of @AbstractMovecraftSign + * The difference is, that for this sign to work, it must exist on a craft instance + * + * Also this will react to the SignTranslate event + */ +public abstract class AbstractCraftSign extends AbstractMovecraftSign { + + // Helper method for the listener + public static Optional tryGetCraftSign(final Component ident) { + if (ident == null) { + return Optional.empty(); + } + final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); + return tryGetCraftSign(identStr); + } + + public static Optional tryGetCraftSign(final String ident) { + Optional tmp = AbstractCraftSign.tryGet(ident); + if (tmp.isPresent() && tmp.get() instanceof AbstractCraftSign acs) { + return Optional.of(acs); + } + return Optional.empty(); + } + + protected final boolean ignoreCraftIsBusy; + + public AbstractCraftSign(boolean ignoreCraftIsBusy) { + this(null, ignoreCraftIsBusy); + } + + public AbstractCraftSign(final String permission, boolean ignoreCraftIsBusy) { + super(permission); + this.ignoreCraftIsBusy = ignoreCraftIsBusy; + } + + // Similar to the super class variant + // In addition a check for a existing craft is being made + // If the craft is a player craft that is currently processing and ignoreCraftIsBusy is set to false, this will quit early and call onCraftIsBusy() + // If no craft is found, onCraftNotFound() is called + // Return true to cancel the event + @Override + public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (!this.isSignValid(clickType, sign, player)) { + return false; + } + if (!this.canPlayerUseSign(clickType, sign, player)) { + return false; + } + Craft craft = this.getCraft(sign); + if (craft == null) { + this.onCraftNotFound(player, sign); + return false; + } + + if (craft instanceof PlayerCraft pc) { + if (!pc.isNotProcessing() && !this.ignoreCraftIsBusy) { + this.onCraftIsBusy(player, craft); + return false; + } + } + + return internalProcessSign(clickType, sign, player, craft); + } + + // Implementation of the standard method. + // The craft instance is required here and it's existance is being confirmed in processSignClick() in beforehand + // After that, canPlayerUseSignOn() is being called. If that is successful, the result of internalProcessSignWithCraft() is returned + @Override + protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft) { + if (craft == null) { + throw new IllegalStateException("Somehow craft is not set here. It should always be present here!"); + } + if (this.canPlayerUseSignOn(player, craft)) { + return this.internalProcessSignWithCraft(clickType, sign, craft, player); + } + return false; + } + + // Called when the craft is a player craft and is processing and ignoreCraftIsBusy is set to false + protected abstract void onCraftIsBusy(Player player, Craft craft); + + // Validation method, intended to indicate if a player is allowed to execute a sign action on a mounted craft + // By default, this returns wether or not the player is the pilot of the craft + protected boolean canPlayerUseSignOn(Player player, @Nullable Craft craft) { + if (craft instanceof PilotedCraft pc) { + return pc.getPilot() == player; + } + return true; + } + + // Called when there is no craft instance for this sign + protected abstract void onCraftNotFound(Player player, AbstractSignListener.SignWrapper sign); + + // By default we don't react to CraftDetectEvent here + public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + // Do nothing by default + } + + public void onSignMovedByCraft(SignTranslateEvent event) { + // Do nothing by default + } + + // Gets called by internalProcessSign if a craft is found + // Always override this as the validation has been made already when this is being called + protected abstract boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player); + +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java new file mode 100644 index 000000000..48b25588a --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -0,0 +1,169 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.CruiseDirection; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.PilotedCraft; +import net.countercraft.movecraft.events.CraftDetectEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.Nullable; + +/* + * Base class for all cruise signs + * + * Has the relevant logic for the "state" suffix (on / off) as well as calling the relevant methods and setting the craft to cruising + * + */ +public abstract class AbstractCruiseSign extends AbstractCraftSign { + + private final String suffixOn; + private final String suffixOff; + private final String ident = AbstractMovecraftSign.findIdent(this); + + public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String suffixOn, final String suffixOff) { + this(null, ignoreCraftIsBusy, suffixOn, suffixOff); + } + + public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, final String suffixOn, final String suffixOff) { + super(permission, ignoreCraftIsBusy); + this.suffixOn = suffixOn; + this.suffixOff = suffixOff; + } + + // Checks if the header is empty, if yes, it quits early (unnecessary actually as if it was empty this would never be called) + // Afterwards the header is validated, if it's splitted variant doesn't have exactly 2 entries it is invalid + // Finally, the "state" (second part of the header) isn't matching suffixOn or suffixOff, it is invalid + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (PlainTextComponentSerializer.plainText().serialize(sign.line(0)).isBlank()) { + return false; + } + String[] headerSplit = getSplitHeader(sign); + if (headerSplit.length != 2) { + return false; + } + String suffix = headerSplit[1]; + return suffix.equalsIgnoreCase(this.suffixOff) || suffix.equalsIgnoreCase(this.suffixOn); + } + + // Returns the raw header, which should consist of the ident and either the suffixOn or suffixOff value + // Returns null if the header is blank + @Nullable + protected static String[] getSplitHeader(final AbstractSignListener.SignWrapper sign) { + String header = PlainTextComponentSerializer.plainText().serialize(sign.line(0)); + if (header.isBlank()) { + return null; + } + return header.split(":"); + } + + // If the suffix matches the suffixOn field it will returnt true + // calls getSplitHeader() to retrieve the raw header string + protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { + String[] headerSplit = getSplitHeader(sign); + if (headerSplit == null || headerSplit.length != 2) { + return false; + } + String suffix = headerSplit[1]; + return suffix.equalsIgnoreCase(this.suffixOn); + } + + // By default, cancel the event if the processing was successful, or the invoker was not sneaking => Allows breaking signs while sneaking + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + return processingSuccessful || !sneaking; + } + + // Hook to do stuff that run after stopping to cruise + protected void onAfterStoppingCruise(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player) { + + } + + // Hook to do stuff that run after starting to cruise + protected void onAfterStartingCruise(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player) { + + } + + // Actual processing, determines wether the sign will switch to on or off + // If it will be on, the CruiseDirection is retrieved and then setCraftCruising() is called + // Otherwise, the craft will stop cruising + // Then the sign is updated and the block resetted + // Finally, the relevant hooks are called + // This always returns true + @Override + protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { + boolean isOn = this.isOnOrOff(sign); + boolean willBeOn = !isOn; + if (willBeOn) { + CruiseDirection direction = this.getCruiseDirection(sign); + this.setCraftCruising(player, direction, craft); + } else { + craft.setCruising(false); + } + + // Update sign + sign.line(0, buildHeader(willBeOn)); + sign.block().update(true); + // TODO: What to replace this with? + craft.resetSigns(sign.block()); + + if (willBeOn) { + this.onAfterStartingCruise(craft, sign, player); + } else { + this.onAfterStoppingCruise(craft, sign, player); + } + + return true; + } + + // On sign placement, if the entered header is the same as our ident, it will append the off-suffix automatically + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + String header = sign.getRaw(0).trim(); + if (header.equalsIgnoreCase(this.ident)) { + sign.line(0, buildHeaderOff()); + } + return true; + } + + // On craft detection, we set all the headers to the "off" header + @Override + public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + Player p = null; + if (event.getCraft() instanceof PilotedCraft pc) { + p = pc.getPilot(); + } + + if (this.isSignValid(Action.PHYSICAL, sign, p)) { + sign.line(0, buildHeader(false)); + } else { + // TODO: Error? React in any way? + sign.line(0, buildHeader(false)); + } + } + + // Helper method to build the headline for on or off state + protected Component buildHeader(boolean on) { + return on ? buildHeaderOn() : buildHeaderOff(); + } + + protected Component buildHeaderOn() { + return Component.text(this.ident).append(Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); + } + + protected Component buildHeaderOff() { + return Component.text(this.ident).append(Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); + } + + // Should call the craft's relevant methods to start cruising + protected abstract void setCraftCruising(Player player, CruiseDirection direction, Craft craft); + + // TODO: Rework cruise direction to vectors => Vector defines the skip distance and the direction + // Returns the direction in which the craft should cruise + protected abstract CruiseDirection getCruiseDirection(AbstractSignListener.SignWrapper sign); +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java new file mode 100644 index 000000000..fed132cb3 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -0,0 +1,165 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.CraftDetectEvent; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractInformationSign extends AbstractCraftSign { + + public static final Component EMPTY = Component.text(""); + + protected static final Style STYLE_COLOR_GREEN = Style.style(TextColor.color(0, 255, 0)); + protected static final Style STYLE_COLOR_YELLOW = Style.style(TextColor.color(255, 255, 0)); + protected static final Style STYLE_COLOR_RED = Style.style(TextColor.color(255, 0, 0)); + protected static final Style STYLE_COLOR_WHITE = Style.style(TextColor.color(255, 255, 255)); + + public enum REFRESH_CAUSE { + SIGN_CREATION, + CRAFT_DETECT, + SIGN_MOVED_BY_CRAFT, + SIGN_CLICK + } + + public AbstractInformationSign() { + // Info signs only display things, that should not require permissions, also it doesn't matter if the craft is busy or not + super(null, true); + } + + @Override + protected boolean canPlayerUseSignOn(Player player, Craft craft) { + // Permcheck related, no perms required, return true + return true; + } + + @Override + public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + // TODO: Check if the craft supports this sign? If no, cancel + super.onCraftDetect(event, sign); + this.refreshSign(event.getCraft(), sign, true, REFRESH_CAUSE.CRAFT_DETECT); + } + + @Override + public void onSignMovedByCraft(SignTranslateEvent event) { + super.onSignMovedByCraft(event); + final Craft craft = event.getCraft(); + AbstractSignListener.SignWrapper wrapperTmp = new AbstractSignListener.SignWrapper(null, event::line, event.lines(), event::line, BlockFace.SELF); + if (this.refreshSign(craft, wrapperTmp, false, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT)) { + for (MovecraftLocation movecraftLocation : event.getLocations()) { + Block block = movecraftLocation.toBukkit(craft.getWorld()).getBlock(); + if (block instanceof Sign sign) { + AbstractSignListener.SignWrapper wrapperTmpTmp = new AbstractSignListener.SignWrapper(sign, event::line, event.lines(), event::line, event.facing()); + this.sendUpdatePacket(craft, wrapperTmpTmp, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT); + } + } + } + } + + @Override + protected void onCraftNotFound(Player player, AbstractSignListener.SignWrapper sign) { + // Nothing to do + } + + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + return true; + } + + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + if (processingSuccessful) { + return true; + } + return !sneaking; + } + + @Override + protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { + if (this.refreshSign(craft, sign, false, REFRESH_CAUSE.SIGN_CLICK)) { + this.sendUpdatePacket(craft, sign, REFRESH_CAUSE.SIGN_CLICK); + } + return true; + } + + // Called whenever the info needs to be refreshed + // That happens on CraftDetect, sign right click (new), Sign Translate + // The new and old values are gathered here and compared + // If nothing has changed, no update happens + // If something has changed, performUpdate() and sendUpdatePacket() are called + // Returns wether or not something has changed + protected boolean refreshSign(@Nullable Craft craft, AbstractSignListener.SignWrapper sign, boolean fillDefault, REFRESH_CAUSE refreshCause) { + boolean changedSome = false; + Component[] updatePayload = new Component[sign.lines().size()]; + for(int i = 1; i < sign.lines().size(); i++) { + Component oldComponent = sign.line(i); + Component potentiallyNew; + if (craft == null || fillDefault) { + potentiallyNew = this.getDefaultString(i, oldComponent); + } else { + potentiallyNew = this.getUpdateString(i, oldComponent, craft); + } + if (potentiallyNew != null && !potentiallyNew.equals(oldComponent)) { + String oldValue = PlainTextComponentSerializer.plainText().serialize(oldComponent); + String newValue = PlainTextComponentSerializer.plainText().serialize(potentiallyNew); + if (!oldValue.equals(newValue)) { + changedSome = true; + updatePayload[i] = potentiallyNew; + } + } + } + if (changedSome) { + this.performUpdate(updatePayload, sign, refreshCause); + } + return changedSome; + } + + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + if (this.refreshSign(null, sign, true, REFRESH_CAUSE.SIGN_CREATION)) { + this.sendUpdatePacket(null, sign, REFRESH_CAUSE.SIGN_CREATION); + } + return true; + } + + /* + Data to set on the sign. Return null if no update should happen! + Attention: A update will only be performed, if the new and old component are different! + */ + @Nullable + protected abstract Component getUpdateString(int lineIndex, Component oldData, Craft craft); + + // Returns the default value for this info sign per line + // Used on CraftDetect and on sign change + @Nullable + protected abstract Component getDefaultString(int lineIndex, Component oldComponent); + + /* + * @param newComponents: Array of nullable values. The index represents the index on the sign. Only contains the updated components + * + * Only gets called if at least one line has changed + */ + protected abstract void performUpdate(Component[] newComponents, AbstractSignListener.SignWrapper sign, REFRESH_CAUSE refreshCause); + + /* + Gets called after performUpdate has been called + */ + protected void sendUpdatePacket(Craft craft, AbstractSignListener.SignWrapper sign, REFRESH_CAUSE refreshCause) { + if (sign.block() == null) { + return; + } + for (Player player : sign.block().getLocation().getNearbyPlayers(16)) { + player.sendSignChange(sign.block().getLocation(), sign.lines()); + } + } +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java new file mode 100644 index 000000000..516ee6afa --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -0,0 +1,160 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.type.CraftType; +import net.countercraft.movecraft.util.MathUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Function; + +// DONE: In 1.21 signs can have multiple sides! This requires us to pass the clicked side through or well the relevant lines and the set method for the clicked side => Resolved using the SignWrapper +/* + * Base class for all signs + * + * A instance of a sign needs to be registered using the register function. + * Signs react to the following events: + * - SignChangeEvent + * - PlayerInteractEvent, if the clicked block is a sign + * - CraftDetectEvent + * - SignTranslateEvent (if the sign is a subclass of AbstractCraftSign) + * + * Whenenver one of those events are cought by the AbstractSignListener instance, it is attempted to retrieve the relevant AbstractMovecraftSign instance. + * For that, the first line of the sign's clicked side is extracted and formatting removed. If it matches the format "foo: bar", only "foo:" will be used. + * With that ident, the sign is attempted to be retrieved vy tryGet(). If that returns something, the object's relevant method is called. + */ +public abstract class AbstractMovecraftSign { + + private static final Map SIGNS = Collections.synchronizedMap(new HashMap<>()); + + public static boolean hasBeenRegistered(final String ident) { + return SIGNS.containsKey(ident); + } + + // Special case for pilot signs, they are registered via the crafttypes name + public static void registerCraftPilotSigns(Set loadedTypes, Function signFactory) { + SIGNS.entrySet().removeIf(entry -> entry.getValue() instanceof AbstractCraftPilotSign); + // Now, add all types... + for (CraftType type : loadedTypes) { + AbstractCraftPilotSign sign = signFactory.apply(type); + register(type.getStringProperty(CraftType.NAME), sign, true); + } + } + + public static Optional tryGet(final Component ident) { + if (ident == null) { + return Optional.empty(); + } + final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); + return tryGet(identStr); + } + + // Attempts to find a AbstractMovecraftSign instance, if something has been registered + // If the ident follows the format "foo: bar", only "foo:" is used as ident to search for + public static Optional tryGet(final String ident) { + String identToUse = ident.toUpperCase(); + if (identToUse.contains(":")) { + identToUse = identToUse.split(":")[0]; + // Re-add the : cause things should be registered with : at the end + identToUse = identToUse + ":"; + } + return Optional.ofNullable(SIGNS.getOrDefault(identToUse, null)); + } + + // Registers a sign in all cases + public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance) { + register(ident, instance, true); + } + + // Registers a sign + // If @param overrideIfAlreadyRegistered is set to false, it won't be registered if something has elready been registered using that name + public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance, boolean overrideIfAlreadyRegistered) { + if (overrideIfAlreadyRegistered) { + SIGNS.put(ident.toUpperCase(), instance); + } else { + SIGNS.putIfAbsent(ident.toUpperCase(), instance); + } + } + + // Optional permission for this sign + // Note that this is only checked against in normal processSignClick by default + // When using the default constructor, the permission will not be set + @Nullable + protected final String permissionString; + + public AbstractMovecraftSign() { + this(null); + } + + public AbstractMovecraftSign(String permissionNode) { + this.permissionString = permissionNode; + } + + // Utility function to retrieve the ident of a a given sign instance + // DO NOT call this for unregistered instances! + // It is a good idea to cache the return value of this function cause otherwise a loop over all registered sign instances will be necessary + public static String findIdent(AbstractMovecraftSign instance) { + if (!SIGNS.containsValue(instance)) { + throw new IllegalArgumentException("MovecraftSign instance must be registered!"); + } + for (Map.Entry entry : SIGNS.entrySet()) { + if (entry.getValue() == instance) { + return entry.getKey(); + } + } + throw new IllegalStateException("Somehow didn't find a key for a value that is in the map!"); + } + + // Called whenever a player clicks the sign + // SignWrapper wraps the relevant clicked side of the sign and the sign block itself + // If true is returned, the event will be cancelled + public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (!this.isSignValid(clickType, sign, player)) { + return false; + } + if (!this.canPlayerUseSign(clickType, sign, player)) { + return false; + } + + return internalProcessSign(clickType, sign, player, getCraft(sign)); + } + + // Validation method + // By default this checks if the player has the set permission + protected boolean canPlayerUseSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (this.permissionString == null || this.permissionString.isBlank()) { + return true; + } + return player.hasPermission(this.permissionString); + } + + // Helper method, simply calls the existing methods + @Nullable + protected Craft getCraft(AbstractSignListener.SignWrapper sign) { + return MathUtils.getCraftByPersistentBlockData(sign.block().getLocation()); + } + + // Used by the event handler to determine if the event should be cancelled + // processingSuccessful is the output of processSignClick() or processSignChange() + // This is only called for the PlayerInteractEvent and the SignChangeEvent + public abstract boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking); + + // Validation method, called by default in processSignClick + // If false is returned, nothing will be processed + protected abstract boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player); + + // Called by processSignClick after validation. At this point, isSignValid() and canPlayerUseSign() have been called already + // If the sign belongs to a craft, that craft is given in the @param craft argument + // Return true, if everything was ok + protected abstract boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft); + + // Called by the event handler when SignChangeEvent is being cought + // Return true, if everything was ok + public abstract boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign); +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java new file mode 100644 index 000000000..db3008138 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -0,0 +1,211 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.CraftDetectEvent; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/* + * As soon as 1.18 support is dropped, the adapter system will be dropped too + */ +@Deprecated(forRemoval = true) +public abstract class AbstractSignListener implements Listener { + + public static AbstractSignListener INSTANCE; + + public AbstractSignListener() { + INSTANCE = this; + } + + /* + * As soon as 1.18 support is dropped, the adapter system will be dropped too + */ + @Deprecated(forRemoval = true) + public record SignWrapper( + @Nullable Sign block, + Function getLine, + List lines, + BiConsumer setLine, + BlockFace facing + ) { + public Component line(int index) { + if (index >= lines.size() || index < 0) { + throw new IndexOutOfBoundsException(); + } + return getLine().apply(index); + } + + public void line(int index, Component component) { + setLine.accept(index, component); + } + + public String getRaw(int index) { + return PlainTextComponentSerializer.plainText().serialize(line(index)); + } + + public String[] rawLines() { + String[] result = new String[this.lines.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = this.getRaw(i); + } + return result; + } + + public boolean isEmpty() { + for(String s : this.rawLines()) { + if (s.trim().isEmpty() || s.trim().isBlank()) { + continue; + } + else { + return false; + } + } + return true; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj instanceof SignWrapper other) { + return areSignsEqual(other); + } + return false; + } + + public boolean areSignsEqual(SignWrapper other) { + return areSignsEqual(this, other); + } + + public static boolean areSignsEqual(SignWrapper a, SignWrapper b) { + String[] aLines = a.rawLines(); + String[] bLines = b.rawLines(); + + if (aLines.length != bLines.length) { + return false; + } + + for (int i = 0; i < aLines.length; i++) { + String aLine = aLines[i].trim(); + String bLine = bLines[i].trim(); + + if (!aLine.equalsIgnoreCase(bLine)) { + return false; + } + } + + // Now check the facing too! + return a.facing().equals(b.facing()); + } + + public static boolean areSignsEqual(SignWrapper[] a, SignWrapper[] b) { + if (a == null || b == null) { + return false; + } + if (a.length != b.length) { + return false; + } + + for (int i = 0; i < a.length; i++) { + SignWrapper aWrap = a[i]; + SignWrapper bWrap = b[i]; + if (!areSignsEqual(aWrap, bWrap)) { + return false; + } + } + return true; + } + + public void copyContent(SignWrapper other) { + for (int i = 0; i < this.lines().size() && i < other.lines().size(); i++) { + this.line(i, other.line(i)); + } + } + + } + + public SignWrapper[] getSignWrappers(Sign sign) { + return getSignWrappers(sign, false); + }; + public abstract SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty); + protected abstract SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent); + protected abstract SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent); + + public abstract void processSignTranslation(final Craft craft, boolean checkEventIsUpdated); + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onCraftDetect(CraftDetectEvent event) { + final World world = event.getCraft().getWorld(); + event.getCraft().getHitBox().forEach( + (mloc) -> { + Block block = mloc.toBukkit(world).getBlock(); + BlockState state = block.getState(); + if (state instanceof Sign sign) { + for (SignWrapper wrapper : this.getSignWrappers(sign)) { + AbstractCraftSign.tryGetCraftSign(wrapper.line(0)).ifPresent(acs -> acs.onCraftDetect(event, wrapper)); + } + } + } + ); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onSignTranslate(SignTranslateEvent event) { + AbstractCraftSign.tryGetCraftSign(event.line(0)).ifPresent(acs -> acs.onSignMovedByCraft(event)); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onSignChange(SignChangeEvent event) { + Block block = event.getBlock(); + BlockState state = block.getState(); + if (state instanceof Sign sign) { + SignWrapper wrapper = this.getSignWrapper(sign, event); + AbstractMovecraftSign.tryGet(wrapper.line(0)).ifPresent(ams -> { + + boolean success = ams.processSignChange(event, wrapper); + if (ams.shouldCancelEvent(success, null, event.getPlayer().isSneaking())) { + event.setCancelled(true); + } + }); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) + public void onSignClick(PlayerInteractEvent event) { + Block block = event.getClickedBlock(); + if (block == null) { + return; + } + BlockState state = block.getState(); + if (state instanceof Sign sign) { + SignWrapper wrapper = this.getSignWrapper(sign, event); + AbstractMovecraftSign.tryGet(wrapper.line(0)).ifPresent(ams -> { + boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); + if (ams.shouldCancelEvent(success, event.getAction(), event.getPlayer().isSneaking())) { + event.setCancelled(true); + } + }); + } + } + +} diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java new file mode 100644 index 000000000..cf9d40cb7 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -0,0 +1,178 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.PlayerCraft; +import net.countercraft.movecraft.craft.type.CraftType; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.plugin.Plugin; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; + +public abstract class AbstractSubcraftSign extends AbstractCraftSign { + + // TODO: Replace by writing to the signs nbt data + protected static final Set IN_USE = Collections.synchronizedSet(new HashSet<>()); + + protected final Function craftTypeRetrievalFunction; + + protected final Supplier pluginInstance; + + public AbstractSubcraftSign(Function craftTypeRetrievalFunction, final Supplier plugin) { + this(null, craftTypeRetrievalFunction, plugin); + } + + public AbstractSubcraftSign(final String permission, Function craftTypeRetrievalFunction, final Supplier plugin) { + super(permission, false); + this.craftTypeRetrievalFunction = craftTypeRetrievalFunction; + this.pluginInstance = plugin; + } + + @Override + public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (!this.isSignValid(clickType, sign, player)) { + return false; + } + if (!this.canPlayerUseSign(clickType, sign, player)) { + return false; + } + Craft craft = this.getCraft(sign); + + if (craft instanceof PlayerCraft pc) { + if (!pc.isNotProcessing() && !this.ignoreCraftIsBusy) { + this.onCraftIsBusy(player, craft); + return false; + } + } + + return internalProcessSign(clickType, sign, player, craft); + } + + @Override + protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, Craft craft) { + if (craft != null) { + // TODO: Add property to crafts that they can use subcrafts? + if (!this.canPlayerUseSignOn(player, craft)) { + return false; + } + } + return this.internalProcessSignWithCraft(clickType, sign, craft, player); + } + + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + return processingSuccessful || !sneaking; + } + + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + // TODO: Implement + return false; + } + + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + String[] headerSplit = sign.getRaw(0).split(" "); + if (headerSplit.length != 2) { + return false; + } + // TODO: Change to enums? + String action = headerSplit[headerSplit.length - 1].toUpperCase(); + if (!this.isActionAllowed(action)) { + return false; + } + return this.getCraftType(sign) != null; + } + + @Override + protected boolean canPlayerUseSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (!super.canPlayerUseSign(clickType, sign, player)) { + return false; + } + CraftType craftType = this.getCraftType(sign); + if (craftType != null) { + return player.hasPermission("movecraft." + craftType.getStringProperty(CraftType.NAME) + ".pilot") && this.canPlayerUseSignForCraftType(clickType, sign, player, craftType); + } + return false; + } + + @Override + protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, @Nullable Craft craft, Player player) { + CraftType subcraftType = this.getCraftType(sign); + + final Location signLoc = sign.block().getLocation(); + final MovecraftLocation startPoint = new MovecraftLocation(signLoc.getBlockX(), signLoc.getBlockY(), signLoc.getBlockZ()); + + if (craft != null) { + craft.setProcessing(true); + // TODO: SOlve this more elegantly... + new BukkitRunnable() { + @Override + public void run() { + craft.setProcessing(false); + } + }.runTaskLater(this.pluginInstance.get(), (10)); + } + + if (!IN_USE.add(startPoint)) { + this.onActionAlreadyInProgress(player); + return true; + } + + this.applyDefaultText(sign); + + final World world = sign.block().getWorld(); + + this.runDetectTask(clickType, subcraftType, craft, world, player, startPoint); + + // TODO: Change this, it is ugly, should be done by the detect task itself + new BukkitRunnable() { + @Override + public void run() { + IN_USE.remove(startPoint); + } + }.runTaskLater(this.pluginInstance.get(), 4); + + return false; + } + + protected void applyDefaultText(AbstractSignListener.SignWrapper sign) { + if (sign.getRaw(2).isBlank() && sign.getRaw(3).isBlank()) { + Component l3 = this.getDefaultTextFor(2); + Component l4 = this.getDefaultTextFor(3); + if (l3 != null) { + sign.line(2, l3); + } + if (l4 != null) { + sign.line(2, l4); + } + } + } + + @Nullable + protected CraftType getCraftType(AbstractSignListener.SignWrapper wrapper) { + String ident = wrapper.getRaw(2); + if (ident.trim().isBlank()) { + return null; + } + return this.craftTypeRetrievalFunction.apply(ident); + } + + protected abstract void runDetectTask(Action clickType, CraftType subcraftType, Craft parentCraft, World world, Player player, MovecraftLocation startPoint); + protected abstract boolean isActionAllowed(final String action); + protected abstract void onActionAlreadyInProgress(Player player); + protected abstract Component getDefaultTextFor(int line); + protected abstract boolean canPlayerUseSignForCraftType(Action clickType, AbstractSignListener.SignWrapper sign, Player player, CraftType subCraftType); + +} From 766cb8c757073c482040bcf7da5ed98c1cd669ee Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:09:20 +0200 Subject: [PATCH 02/55] utility method to get Rotation by action --- .../countercraft/movecraft/MovecraftRotation.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java b/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java index f9b580ee7..17f737c00 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java @@ -18,5 +18,18 @@ package net.countercraft.movecraft; public enum MovecraftRotation { - CLOCKWISE, NONE, ANTICLOCKWISE + CLOCKWISE, NONE, ANTICLOCKWISE; + + public static MovecraftRotation fromAction(Action clickType) { + switch (clickType) { + case LEFT_CLICK_AIR: + case LEFT_CLICK_BLOCK: + return ANTICLOCKWISE; + case RIGHT_CLICK_AIR: + case RIGHT_CLICK_BLOCK: + return CLOCKWISE; + default: + return NONE; + } + } } From 2074d9d522e2cde2aad5d8a27de489490a5ee4a8 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:13:47 +0200 Subject: [PATCH 03/55] necessary changes to SignTranslateEvent => use components (basically...) --- .../movecraft/events/SignTranslateEvent.java | 59 ++++++++++++++++--- 1 file changed, 51 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java index 5439cfdad..e615f8c5f 100644 --- a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java +++ b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java @@ -2,46 +2,89 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.sign.AbstractSignListener; +import net.kyori.adventure.text.Component; +import org.bukkit.block.BlockFace; import org.bukkit.event.HandlerList; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Collections; import java.util.List; +// TODO: Rewrite to use the adventure API public class SignTranslateEvent extends CraftEvent{ private static final HandlerList HANDLERS = new HandlerList(); @NotNull private final List locations; - @NotNull private final String[] lines; + @NotNull private final AbstractSignListener.SignWrapper backing; private boolean updated = false; + @Deprecated(forRemoval = true) public SignTranslateEvent(@NotNull Craft craft, @NotNull String[] lines, @NotNull List locations) throws IndexOutOfBoundsException{ super(craft); this.locations = locations; - if(lines.length!=4) - throw new IndexOutOfBoundsException(); - this.lines=lines; + List components = new ArrayList<>(); + for (String s : lines) { + components.add(Component.text(s)); + } + this.backing = new AbstractSignListener.SignWrapper(null, components::get, components, components::set, BlockFace.SELF); + } + + public SignTranslateEvent(@NotNull Craft craft, @NotNull AbstractSignListener.SignWrapper backing, @NotNull List locations) throws IndexOutOfBoundsException{ + super(craft); + this.locations = locations; + this.backing = backing; } @NotNull - @Deprecated + @Deprecated(forRemoval = true) public String[] getLines() { + // TODO: Why does this set it to updated? This is just reading... this.updated = true; - return lines; + return backing.rawLines(); } + @Deprecated(forRemoval = true) public String getLine(int index) throws IndexOutOfBoundsException{ if(index > 3 || index < 0) throw new IndexOutOfBoundsException(); - return lines[index]; + return backing.getRaw(index); } + @Deprecated(forRemoval = true) public void setLine(int index, String line){ if(index > 3 || index < 0) throw new IndexOutOfBoundsException(); this.updated = true; - lines[index]=line; + backing.line(index, Component.text(line)); + } + + public Component line(int index) { + return backing.line(index); + } + + public void line(int index, Component component) { + this.updated = true; + backing.line(index, component); + } + + public String getRaw(int index) { + return backing.getRaw(index); + } + + public String[] rawLines() { + return backing.rawLines(); + } + + public BlockFace facing() { + return backing.facing(); + } + + public List lines() { + return backing.lines(); } + // Bukkit crap @Override public HandlerList getHandlers() { return HANDLERS; From e4292f140f1291cf6d8cf53a1dd1cbd0032523f1 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:13:58 +0200 Subject: [PATCH 04/55] signListener implementations --- .../movecraft/compat/v1_18/SignListener.java | 136 +++++++++++++++ .../movecraft/compat/v1_20/SignListener.java | 160 ++++++++++++++++++ .../movecraft/compat/v1_21/SignListener.java | 160 ++++++++++++++++++ 3 files changed, 456 insertions(+) create mode 100644 v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java create mode 100644 v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java create mode 100644 v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java new file mode 100644 index 000000000..7f1e122a6 --- /dev/null +++ b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java @@ -0,0 +1,136 @@ +package net.countercraft.movecraft.compat.v1_18; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.sign.AbstractSignListener; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Tag; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.data.Directional; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.*; + +/* + * As soon as 1.18 support is dropped, the adapter system will be dropped too + */ +@Deprecated(forRemoval = true) +public class SignListener extends AbstractSignListener { + + protected final SignWrapper createFromSign(final Sign sign) { + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + SignWrapper wrapper = new SignWrapper( + sign, + sign::line, + sign.lines(), + sign::line, + face + ); + return wrapper; + } + + @Override + public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { + SignWrapper wrapper = this.createFromSign(sign); + if (ignoreEmpty && wrapper.isEmpty()) { + return new SignWrapper[] {}; + } + return new SignWrapper[] {wrapper}; + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + SignWrapper wrapper = new SignWrapper( + sign, + signChangeEvent::line, + signChangeEvent.lines(), + signChangeEvent::line, + face + ); + return wrapper; + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { + return this.createFromSign(sign); + } + + @Override + public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + @Override + public int hashCode(SignWrapper strings) { + return Arrays.hashCode(strings.rawLines()); + } + + @Override + public boolean equals(SignWrapper a, SignWrapper b) { + return SignWrapper.areSignsEqual(a, b); + } + }); + Map signStates = new HashMap<>(); + + for (MovecraftLocation location : craft.getHitBox()) { + Block block = location.toBukkit(craft.getWorld()).getBlock(); + if(!Tag.SIGNS.isTagged(block.getType())){ + continue; + } + BlockState state = block.getState(); + if (state instanceof Sign) { + Sign sign = (Sign) state; + SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); + if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { + continue; + } + for (SignWrapper wrapper : wrappersAtLoc) { + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(location); + } + signStates.put(location, wrappersAtLoc); + } + } + // TODO: This is not good yet, this doesn't really care about a signs sides... + for(Map.Entry> entry : signs.entrySet()){ + final List components = new ArrayList<>(entry.getKey().lines()); + SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); + SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); + Bukkit.getServer().getPluginManager().callEvent(event); + // if(!event.isUpdated()){ + // continue; + // } + // TODO: This is implemented only to fix client caching + // ideally we wouldn't do the update and would instead fake it out to the player + for(MovecraftLocation location : entry.getValue()){ + Block block = location.toBukkit(craft.getWorld()).getBlock(); + BlockState state = block.getState(); + if (!(state instanceof Sign)) { + continue; + } + SignWrapper[] signsAtLoc = signStates.get(location); + if (signsAtLoc != null && signsAtLoc.length > 0) { + for (SignWrapper sw : signsAtLoc) { + if (!checkEventIsUpdated || event.isUpdated()) { + sw.copyContent(entry.getKey()); + } + } + try { + ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + } catch(ClassCastException ex) { + // Ignore + } + } + } + } + } + +} diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java new file mode 100644 index 000000000..6a5d9add0 --- /dev/null +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -0,0 +1,160 @@ +package net.countercraft.movecraft.compat.v1_20; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.sign.AbstractSignListener; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Tag; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.data.Directional; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/* + * As soon as 1.18 support is dropped, the adapter system will be dropped too + */ +@Deprecated(forRemoval = true) +public class SignListener extends AbstractSignListener { + + protected final SignWrapper createFromSide(final Sign sign, final Side side) { + SignSide signSide = sign.getSide(side); + return createFromSide(sign, signSide, side); + } + + protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signSide::line, + signSide.lines(), + signSide::line, + face + ); + return wrapper; + } + + @Override + public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { + Side[] sides = new Side[Side.values().length]; + List wrappers = new ArrayList<>(); + for (int i = 0; i < sides.length; i++) { + Side side = sides[i]; + SignSide signSide = sign.getSide(side); + SignWrapper wrapper = this.createFromSide(sign, signSide, side); + wrappers.add(wrapper); + } + if (ignoreEmpty) + wrappers.removeIf(SignWrapper::isEmpty); + return wrappers.toArray(new SignWrapper[wrappers.size()]); + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { + @NotNull Side side = signChangeEvent.getSide(); + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signChangeEvent::line, + signChangeEvent.lines(), + signChangeEvent::line, + face + ); + return wrapper; + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { + @NotNull SignSide side = sign.getTargetSide(interactEvent.getPlayer()); + return this.createFromSide(sign, side, sign.getInteractableSideFor(interactEvent.getPlayer())); + } + + @Override + public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + @Override + public int hashCode(SignWrapper strings) { + return Arrays.hashCode(strings.rawLines()) * strings.facing().hashCode(); + } + + @Override + public boolean equals(SignWrapper a, SignWrapper b) { + return SignWrapper.areSignsEqual(a, b); + } + }); + Map signStates = new HashMap<>(); + + for (MovecraftLocation location : craft.getHitBox()) { + Block block = location.toBukkit(craft.getWorld()).getBlock(); + if(!Tag.SIGNS.isTagged(block.getType())){ + continue; + } + BlockState state = block.getState(); + if (state instanceof Sign) { + Sign sign = (Sign) state; + SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); + if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { + continue; + } + for (SignWrapper wrapper : wrappersAtLoc) { + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(location); + } + signStates.put(location, wrappersAtLoc); + } + } + for(Map.Entry> entry : signs.entrySet()){ + final List components = new ArrayList<>(entry.getKey().lines()); + SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); + SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); + Bukkit.getServer().getPluginManager().callEvent(event); + // if(!event.isUpdated()){ + // continue; + // } + // TODO: This is implemented only to fix client caching + // ideally we wouldn't do the update and would instead fake it out to the player + for(MovecraftLocation location : entry.getValue()){ + Block block = location.toBukkit(craft.getWorld()).getBlock(); + BlockState state = block.getState(); + if (!(state instanceof Sign)) { + continue; + } + SignWrapper[] signsAtLoc = signStates.get(location); + if (signsAtLoc != null && signsAtLoc.length > 0) { + for (SignWrapper sw : signsAtLoc) { + // Important: Check if the wrapper faces the right way! + if (!sw.facing().equals(entry.getKey().facing())) { + continue; + } + if (!checkEventIsUpdated || event.isUpdated()) { + sw.copyContent(entry.getKey()); + } + } + try { + ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + } catch(ClassCastException ex) { + // Ignore + } + } + } + } + } +} diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java new file mode 100644 index 000000000..f49ed798c --- /dev/null +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -0,0 +1,160 @@ +package net.countercraft.movecraft.compat.v1_21; + +import it.unimi.dsi.fastutil.Hash; +import it.unimi.dsi.fastutil.objects.Object2ObjectMap; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.sign.AbstractSignListener; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.Tag; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; +import org.bukkit.block.data.Directional; +import org.bukkit.block.sign.Side; +import org.bukkit.block.sign.SignSide; +import org.bukkit.event.block.SignChangeEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/* + * As soon as 1.18 support is dropped, the adapter system will be dropped too + */ +@Deprecated(forRemoval = true) +public class SignListener extends AbstractSignListener { + + protected final SignWrapper createFromSide(final Sign sign, final Side side) { + SignSide signSide = sign.getSide(side); + return createFromSide(sign, signSide, side); + } + + protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signSide::line, + signSide.lines(), + signSide::line, + face + ); + return wrapper; + } + + @Override + public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { + Side[] sides = new Side[Side.values().length]; + List wrappers = new ArrayList<>(); + for (int i = 0; i < sides.length; i++) { + Side side = sides[i]; + SignSide signSide = sign.getSide(side); + SignWrapper wrapper = this.createFromSide(sign, signSide, side); + wrappers.add(wrapper); + } + if (ignoreEmpty) + wrappers.removeIf(SignWrapper::isEmpty); + return wrappers.toArray(new SignWrapper[wrappers.size()]); + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { + @NotNull Side side = signChangeEvent.getSide(); + BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signChangeEvent::line, + signChangeEvent.lines(), + signChangeEvent::line, + face + ); + return wrapper; + } + + @Override + protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { + @NotNull SignSide side = sign.getTargetSide(interactEvent.getPlayer()); + return this.createFromSide(sign, side, sign.getInteractableSideFor(interactEvent.getPlayer())); + } + + @Override + public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + @Override + public int hashCode(SignWrapper strings) { + return Arrays.hashCode(strings.rawLines()) * strings.facing().hashCode(); + } + + @Override + public boolean equals(SignWrapper a, SignWrapper b) { + return SignWrapper.areSignsEqual(a, b); + } + }); + Map signStates = new HashMap<>(); + + for (MovecraftLocation location : craft.getHitBox()) { + Block block = location.toBukkit(craft.getWorld()).getBlock(); + if(!Tag.SIGNS.isTagged(block.getType())){ + continue; + } + BlockState state = block.getState(); + if (state instanceof Sign) { + Sign sign = (Sign) state; + SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); + if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { + continue; + } + for (SignWrapper wrapper : wrappersAtLoc) { + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(location); + } + signStates.put(location, wrappersAtLoc); + } + } + for(Map.Entry> entry : signs.entrySet()){ + final List components = new ArrayList<>(entry.getKey().lines()); + SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); + SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); + Bukkit.getServer().getPluginManager().callEvent(event); + // if(!event.isUpdated()){ + // continue; + // } + // TODO: This is implemented only to fix client caching + // ideally we wouldn't do the update and would instead fake it out to the player + for(MovecraftLocation location : entry.getValue()){ + Block block = location.toBukkit(craft.getWorld()).getBlock(); + BlockState state = block.getState(); + if (!(state instanceof Sign)) { + continue; + } + SignWrapper[] signsAtLoc = signStates.get(location); + if (signsAtLoc != null && signsAtLoc.length > 0) { + for (SignWrapper sw : signsAtLoc) { + // Important: Check if the wrapper faces the right way! + if (!sw.facing().equals(entry.getKey().facing())) { + continue; + } + if (!checkEventIsUpdated || event.isUpdated()) { + sw.copyContent(entry.getKey()); + } + } + try { + ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + } catch(ClassCastException ex) { + // Ignore + } + } + } + } + } +} From 301b0bf167dd8709b9e808974e6737941a9b8a52 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 17:16:06 +0200 Subject: [PATCH 05/55] add instance for sign listener --- .../java/net/countercraft/movecraft/Movecraft.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index fe69859c1..c9ab11f02 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -59,6 +59,7 @@ public class Movecraft extends JavaPlugin { private SmoothTeleport smoothTeleport; private AsyncManager asyncManager; private WreckManager wreckManager; + private AbstractSignListener abstractSignListener; public static synchronized Movecraft getInstance() { return instance; @@ -132,6 +133,13 @@ public void onEnable() { getLogger().warning("Falling back to bukkit teleportation provider."); } } + + // Create instance of sign listener + final Class signListenerClass = Class.forName("net.countercraft.movecraft.compat." + WorldHandler.getPackageName(minecraftVersion) + ".SignListener"); + if (AbstractSignListener.class.isAssignableFrom(signListenerClass)) { + abstractSignListener = (AbstractSignListener) signListenerClass.getConstructor().newInstance(); + getServer().getPluginManager().registerEvents(abstractSignListener, this); + } } catch (final Exception e) { e.printStackTrace(); @@ -340,4 +348,8 @@ public AsyncManager getAsyncManager() { public @NotNull WreckManager getWreckManager(){ return wreckManager; } + + public AbstractSignListener getAbstractSignListener() { + return abstractSignListener; + } } From ff84ee82922468c2a92f1b7b15ebf01a17a5f020 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:03:15 +0200 Subject: [PATCH 06/55] add base signs (most basic signs that exist) --- .../net/countercraft/movecraft/Movecraft.java | 6 +- .../movecraft/craft/CraftManager.java | 5 + .../movecraft/listener/CraftTypeListener.java | 17 +++ .../{CraftSign.java => CraftPilotSign.java} | 129 ++++++++++-------- .../movecraft/sign/ReleaseSign.java | 59 ++++---- 5 files changed, 134 insertions(+), 82 deletions(-) create mode 100644 Movecraft/src/main/java/net/countercraft/movecraft/listener/CraftTypeListener.java rename Movecraft/src/main/java/net/countercraft/movecraft/sign/{CraftSign.java => CraftPilotSign.java} (60%) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index c9ab11f02..40f179deb 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -221,7 +221,6 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new PlayerListener(), this); getServer().getPluginManager().registerEvents(new ChunkManager(), this); getServer().getPluginManager().registerEvents(new AscendSign(), this); - getServer().getPluginManager().registerEvents(new CraftSign(), this); getServer().getPluginManager().registerEvents(new CruiseSign(), this); getServer().getPluginManager().registerEvents(new DescendSign(), this); getServer().getPluginManager().registerEvents(new HelmSign(), this); @@ -229,7 +228,6 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new NameSign(), this); getServer().getPluginManager().registerEvents(new PilotSign(), this); getServer().getPluginManager().registerEvents(new RelativeMoveSign(), this); - getServer().getPluginManager().registerEvents(new ReleaseSign(), this); getServer().getPluginManager().registerEvents(new RemoteSign(), this); getServer().getPluginManager().registerEvents(new SpeedSign(), this); getServer().getPluginManager().registerEvents(new SubcraftRotateSign(), this); @@ -237,6 +235,10 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new ScuttleSign(), this); getServer().getPluginManager().registerEvents(new CraftPilotListener(), this); getServer().getPluginManager().registerEvents(new CraftReleaseListener(), this); + getServer().getPluginManager().registerEvents(new CraftTypeListener(), this); + + // Signs + AbstractMovecraftSign.register("Release", new ReleaseSign()); var contactsManager = new ContactsManager(); contactsManager.runTaskTimerAsynchronously(this, 0, 20); diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java index 1db3db008..dd8001831 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/craft/CraftManager.java @@ -31,6 +31,8 @@ import net.countercraft.movecraft.processing.effects.Effect; import net.countercraft.movecraft.processing.functions.CraftSupplier; import net.countercraft.movecraft.processing.tasks.detection.DetectionTask; +import net.countercraft.movecraft.sign.AbstractMovecraftSign; +import net.countercraft.movecraft.sign.CraftPilotSign; import net.kyori.adventure.audience.Audience; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -96,6 +98,9 @@ private CraftManager(boolean loadCraftTypes) { craftTypes = loadCraftTypes(); else craftTypes = new HashSet<>(); + + // Since the event is only created in reload cases... + AbstractMovecraftSign.registerCraftPilotSigns(CraftManager.getInstance().getCraftTypes(), CraftPilotSign::new); } @NotNull diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/listener/CraftTypeListener.java b/Movecraft/src/main/java/net/countercraft/movecraft/listener/CraftTypeListener.java new file mode 100644 index 000000000..6b3748b89 --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/listener/CraftTypeListener.java @@ -0,0 +1,17 @@ +package net.countercraft.movecraft.listener; + +import net.countercraft.movecraft.craft.CraftManager; +import net.countercraft.movecraft.events.TypesReloadedEvent; +import net.countercraft.movecraft.sign.AbstractMovecraftSign; +import net.countercraft.movecraft.sign.CraftPilotSign; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class CraftTypeListener implements Listener { + + @EventHandler + public void onReload(TypesReloadedEvent event) { + AbstractMovecraftSign.registerCraftPilotSigns(CraftManager.getInstance().getCraftTypes(), CraftPilotSign::new); + } + +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java similarity index 60% rename from Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftSign.java rename to Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java index ec5a977c5..a3827e8b0 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java @@ -4,12 +4,7 @@ import net.countercraft.movecraft.Movecraft; import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.config.Settings; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.craft.CraftManager; -import net.countercraft.movecraft.craft.CruiseOnPilotCraft; -import net.countercraft.movecraft.craft.CruiseOnPilotSubCraft; -import net.countercraft.movecraft.craft.PlayerCraftImpl; -import net.countercraft.movecraft.craft.SubCraft; +import net.countercraft.movecraft.craft.*; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftPilotEvent; import net.countercraft.movecraft.events.CraftReleaseEvent; @@ -17,76 +12,77 @@ import net.countercraft.movecraft.processing.functions.Result; import net.countercraft.movecraft.util.Pair; import org.bukkit.Bukkit; -import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.data.Directional; +import org.bukkit.block.BlockFace; import org.bukkit.entity.Player; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.scheduler.BukkitRunnable; -import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.HashSet; +import java.util.Optional; import java.util.Set; -public final class CraftSign implements Listener { - private final Set piloting = new HashSet<>(); +//TODO: This is not very pretty... +public class CraftPilotSign extends AbstractCraftPilotSign { - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onSignChange(@NotNull SignChangeEvent event) { - if (CraftManager.getInstance().getCraftTypeFromString(event.getLine(0)) == null) - return; + static final Set PILOTING = Collections.synchronizedSet(new HashSet<>()); - if (!Settings.RequireCreatePerm) - return; - - if (!event.getPlayer().hasPermission("movecraft." + ChatColor.stripColor(event.getLine(0)) + ".create")) { - event.getPlayer().sendMessage(I18nSupport.getInternationalisedString("Insufficient Permissions")); - event.setCancelled(true); - } + public CraftPilotSign(CraftType craftType) { + super(craftType); } - @EventHandler(priority = EventPriority.LOW, ignoreCancelled = true) - public void onSignClick(@NotNull PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK || event.getClickedBlock() == null) - return; - - BlockState state = event.getClickedBlock().getState(); - if (!(state instanceof Sign)) - return; - - Sign sign = (Sign) state; - CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(ChatColor.stripColor(sign.getLine(0))); - if (craftType == null) - return; + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + return processingSuccessful || !sneaking; + } - // Valid sign prompt for ship command. - event.setCancelled(true); - Player player = event.getPlayer(); - if (!player.hasPermission("movecraft." + ChatColor.stripColor(sign.getLine(0)) + ".pilot")) { + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + String header = sign.getRaw(0).trim(); + CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(header); + if (craftType != this.craftType) { + return false; + } + if (!player.hasPermission("movecraft." + header + ".pilot")) { player.sendMessage(I18nSupport.getInternationalisedString("Insufficient Permissions")); - return; + return false; + } else { + return true; } + } - Location loc = event.getClickedBlock().getLocation(); + @Override + protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @javax.annotation.Nullable Craft craft) { + if (this.craftType.getBoolProperty(CraftType.MUST_BE_SUBCRAFT) && craft == null) { + return false; + } + World world = sign.block().getWorld(); + if (craft != null) { + world = craft.getWorld(); + } + Location loc = sign.block().getLocation(); MovecraftLocation startPoint = new MovecraftLocation(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - if (piloting.contains(startPoint)) { - return; + + if (PILOTING.contains(startPoint)) { + // Always return true + return true; } - // Attempt to run detection - World world = event.getClickedBlock().getWorld(); + runDetectTask(startPoint, player, sign, craft, world); + + return true; + } + protected void runDetectTask(MovecraftLocation startPoint, Player player, AbstractSignListener.SignWrapper signWrapper, Craft parentCraft, World world) { + PILOTING.add(startPoint); CraftManager.getInstance().detect( startPoint, craftType, (type, w, p, parents) -> { + // Assert instructions are not available normally, also this is checked in beforehand sort of assert p != null; // Note: This only passes in a non-null player. if (type.getBoolProperty(CraftType.CRUISE_ON_PILOT)) { if (parents.size() > 1) @@ -122,10 +118,12 @@ public void onSignClick(@NotNull PlayerInteractEvent event) { if (craft.getType().getBoolProperty(CraftType.CRUISE_ON_PILOT)) { // Setup cruise direction - if (sign.getBlockData() instanceof Directional) + BlockFace facing = signWrapper.facing(); + craft.setCruiseDirection(CruiseDirection.fromBlockFace(facing)); + /*if (signWrapper.block().getBlockData() instanceof Directional) craft.setCruiseDirection(CruiseDirection.fromBlockFace(((Directional) sign.getBlockData()).getFacing())); else - craft.setCruiseDirection(CruiseDirection.NONE); + craft.setCruiseDirection(CruiseDirection.NONE);*/ // Start craft cruising craft.setLastCruiseUpdate(System.currentTimeMillis()); @@ -148,11 +146,34 @@ public void run() { } } ); + // TODO: Move this to be directly called by the craftmanager post detection... + // Or use the event handler or something new BukkitRunnable() { @Override public void run() { - piloting.remove(startPoint); + PILOTING.remove(startPoint); } }.runTaskLater(Movecraft.getInstance(), 4); + + } + + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + String header = sign.getRaw(0).trim(); + CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(header); + if (craftType != this.craftType) { + return false; + } + if (Settings.RequireCreatePerm) { + Player player = event.getPlayer(); + if (!player.hasPermission("movecraft." + header + ".create")) { + player.sendMessage(I18nSupport.getInternationalisedString("Insufficient Permissions")); + return false; + } else { + return true; + } + } else { + return true; + } } } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java index 91e9aa544..44c43e754 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java @@ -3,37 +3,44 @@ import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.CraftManager; import net.countercraft.movecraft.events.CraftReleaseEvent; -import org.bukkit.ChatColor; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; +import org.bukkit.entity.Player; import org.bukkit.event.block.Action; -import org.bukkit.event.player.PlayerInteractEvent; -import org.jetbrains.annotations.NotNull; +import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.Nullable; -public final class ReleaseSign implements Listener{ - private static final String HEADER = "Release"; +public class ReleaseSign extends AbstractMovecraftSign { - @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) - public void onSignClick(@NotNull PlayerInteractEvent event) { - if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { - return; - } - BlockState state = event.getClickedBlock().getState(); - if (!(state instanceof Sign)) { - return; - } - Sign sign = (Sign) state; - if (!ChatColor.stripColor(sign.getLine(0)).equalsIgnoreCase(HEADER)) { - return; + public ReleaseSign() { + super(null); + } + + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + if (processingSuccessful) { + return true; } - event.setCancelled(true); - Craft craft = CraftManager.getInstance().getCraftByPlayer(event.getPlayer()); + return !sneaking; + } + + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + return true; + } + + @Override + protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft) { if (craft == null) { - return; + craft = CraftManager.getInstance().getCraftByPlayer(player); + } + if (craft != null) { + CraftManager.getInstance().release(craft, CraftReleaseEvent.Reason.PLAYER, false); } - CraftManager.getInstance().release(craft, CraftReleaseEvent.Reason.PLAYER, false); + return true; } + + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + return false; + } + } From 71076338c7bd5afcf0438c570ba323402930bfe8 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 31 Aug 2024 18:20:00 +0200 Subject: [PATCH 07/55] forgot action import --- .../main/java/net/countercraft/movecraft/MovecraftRotation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java b/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java index 17f737c00..1e9362bbe 100644 --- a/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java +++ b/api/src/main/java/net/countercraft/movecraft/MovecraftRotation.java @@ -17,6 +17,8 @@ package net.countercraft.movecraft; +import org.bukkit.event.block.Action; + public enum MovecraftRotation { CLOCKWISE, NONE, ANTICLOCKWISE; From df4d4efd42673a9b3949515600ae8b34164e3642 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:05:01 +0200 Subject: [PATCH 08/55] fix getSignWrappers() method for sign => NPE (cherry picked from commit 5248bc244accc651f97e4a8a17f9e5cbf01bdf7a) (cherry picked from commit 960b30a7ed8c4ecc6d9a4f89dbb7af48ca4b5b4c) --- .../net/countercraft/movecraft/compat/v1_20/SignListener.java | 4 +--- .../net/countercraft/movecraft/compat/v1_21/SignListener.java | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index 6a5d9add0..c0dc6685c 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -51,10 +51,8 @@ protected final SignWrapper createFromSide(final Sign sign, final SignSide signS @Override public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { - Side[] sides = new Side[Side.values().length]; List wrappers = new ArrayList<>(); - for (int i = 0; i < sides.length; i++) { - Side side = sides[i]; + for (Side side : Side.values()) { SignSide signSide = sign.getSide(side); SignWrapper wrapper = this.createFromSide(sign, signSide, side); wrappers.add(wrapper); diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index f49ed798c..922caaf95 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -51,10 +51,8 @@ protected final SignWrapper createFromSide(final Sign sign, final SignSide signS @Override public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { - Side[] sides = new Side[Side.values().length]; List wrappers = new ArrayList<>(); - for (int i = 0; i < sides.length; i++) { - Side side = sides[i]; + for (Side side : Side.values()) { SignSide signSide = sign.getSide(side); SignWrapper wrapper = this.createFromSide(sign, signSide, side); wrappers.add(wrapper); From f9e1ff6b06b6db2661f66b363e72cfa17439ac4e Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:28:11 +0200 Subject: [PATCH 09/55] corrections to BlockFace access (cherry picked from commit 7f468956f5bf4a0207f08d23b514927caec8a1a6) (cherry picked from commit 39e08a5c26b00deecbcbf4337667ba5a86d7d588) --- .../countercraft/movecraft/compat/v1_20/SignListener.java | 4 ++-- .../countercraft/movecraft/compat/v1_21/SignListener.java | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index c0dc6685c..59ea73196 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -35,7 +35,7 @@ protected final SignWrapper createFromSide(final Sign sign, final Side side) { } protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); if (side == Side.BACK) { face = face.getOppositeFace(); } @@ -65,7 +65,7 @@ public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { @Override protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { @NotNull Side side = signChangeEvent.getSide(); - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); if (side == Side.BACK) { face = face.getOppositeFace(); } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 922caaf95..23707abc7 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -6,6 +6,8 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.processing.CachedMovecraftWorld; +import net.countercraft.movecraft.processing.MovecraftWorld; import net.countercraft.movecraft.sign.AbstractSignListener; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -14,6 +16,7 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; @@ -35,7 +38,7 @@ protected final SignWrapper createFromSide(final Sign sign, final Side side) { } protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); if (side == Side.BACK) { face = face.getOppositeFace(); } @@ -65,7 +68,7 @@ public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { @Override protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { @NotNull Side side = signChangeEvent.getSide(); - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); if (side == Side.BACK) { face = face.getOppositeFace(); } From b6bbdab62623fba557f249ac061915bfbd3f8ee4 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:39:33 +0200 Subject: [PATCH 10/55] more fixes regarding getting the blockface of a sign... (cherry picked from commit d5ded2eddf9406148ff7452eb422c705cf06c96c) (cherry picked from commit 9b7ea60e82c573f749156f5ad910bfcc38380f24) --- .../movecraft/compat/v1_18/SignListener.java | 24 ++++++++++++++-- .../movecraft/compat/v1_20/SignListener.java | 27 ++++++++++++++++-- .../movecraft/compat/v1_21/SignListener.java | 28 ++++++++++++++++--- 3 files changed, 71 insertions(+), 8 deletions(-) diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java index 7f1e122a6..f6a4244ab 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java @@ -14,7 +14,9 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; @@ -27,7 +29,16 @@ public class SignListener extends AbstractSignListener { protected final SignWrapper createFromSign(final Sign sign) { - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } SignWrapper wrapper = new SignWrapper( sign, sign::line, @@ -49,7 +60,16 @@ public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { @Override protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { - BlockFace face = ((Directional) sign.getBlockData()).getFacing(); + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } SignWrapper wrapper = new SignWrapper( sign, signChangeEvent::line, diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index 59ea73196..497a6ffa8 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -14,7 +14,9 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; import org.bukkit.event.block.SignChangeEvent; @@ -35,7 +37,17 @@ protected final SignWrapper createFromSide(final Sign sign, final Side side) { } protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + if (side == Side.BACK) { face = face.getOppositeFace(); } @@ -65,7 +77,18 @@ public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { @Override protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { @NotNull Side side = signChangeEvent.getSide(); - BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); + + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + if (side == Side.BACK) { face = face.getOppositeFace(); } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 23707abc7..1c8621fe9 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -6,8 +6,6 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.events.SignTranslateEvent; -import net.countercraft.movecraft.processing.CachedMovecraftWorld; -import net.countercraft.movecraft.processing.MovecraftWorld; import net.countercraft.movecraft.sign.AbstractSignListener; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -18,6 +16,7 @@ import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; import org.bukkit.block.data.Directional; +import org.bukkit.block.data.Rotatable; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; import org.bukkit.event.block.SignChangeEvent; @@ -38,7 +37,17 @@ protected final SignWrapper createFromSide(final Sign sign, final Side side) { } protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + if (side == Side.BACK) { face = face.getOppositeFace(); } @@ -68,7 +77,18 @@ public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { @Override protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { @NotNull Side side = signChangeEvent.getSide(); - BlockFace face = ((Directional) sign.getBlock().getBlockData()).getFacing(); + + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + if (side == Side.BACK) { face = face.getOppositeFace(); } From 674c132271073e5fd1bc406e3bd49c8b161433f7 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:43:13 +0200 Subject: [PATCH 11/55] NPE prevention (cherry picked from commit aff2a1da968dde6f5d9d1a6c168405aa4688faf6) (cherry picked from commit 48a9bc14bd475fba803ce97d0ff123819e121ac1) --- .../countercraft/movecraft/sign/AbstractSignListener.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index db3008138..519e0148b 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -98,6 +98,12 @@ public boolean areSignsEqual(SignWrapper other) { } public static boolean areSignsEqual(SignWrapper a, SignWrapper b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } String[] aLines = a.rawLines(); String[] bLines = b.rawLines(); From cb19e75fb8e0e11b909356c0415aa5a19d328468 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:53:01 +0200 Subject: [PATCH 12/55] add TODO note (cherry picked from commit dc85f9df038c0b72a959c085fda6dbbff854b2d3) (cherry picked from commit ca35cd37e0eee5657da3652a9a35dffa7d270db7) --- .../net/countercraft/movecraft/sign/AbstractInformationSign.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java index fed132cb3..4eef24d1e 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -76,6 +76,7 @@ protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper return true; } + // TODO: Add "reason" what to cancel, we might not want to cancel all edit events @Override public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { if (processingSuccessful) { From bf4c7c99d98f22e999740ec1d2ef2ea5def959d2 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:28:49 +0200 Subject: [PATCH 13/55] remove spaces from suffix (cherry picked from commit 464fcd672919c715de5492c26f195a7e31a1c857) --- .../net/countercraft/movecraft/sign/AbstractCruiseSign.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index 48b25588a..0cd52d94a 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -47,7 +47,7 @@ protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper if (headerSplit.length != 2) { return false; } - String suffix = headerSplit[1]; + String suffix = headerSplit[1].trim(); return suffix.equalsIgnoreCase(this.suffixOff) || suffix.equalsIgnoreCase(this.suffixOn); } @@ -69,7 +69,7 @@ protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { if (headerSplit == null || headerSplit.length != 2) { return false; } - String suffix = headerSplit[1]; + String suffix = headerSplit[1].trim(); return suffix.equalsIgnoreCase(this.suffixOn); } From 8422b222970bf273f4c349f10beb534e2d24e84f Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 13:57:15 +0200 Subject: [PATCH 14/55] Startup correction cherry picked --- .../countercraft/movecraft/sign/AbstractCruiseSign.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index 0cd52d94a..f249d0585 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -23,16 +23,17 @@ public abstract class AbstractCruiseSign extends AbstractCraftSign { private final String suffixOn; private final String suffixOff; - private final String ident = AbstractMovecraftSign.findIdent(this); + private final String ident; - public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String suffixOn, final String suffixOff) { - this(null, ignoreCraftIsBusy, suffixOn, suffixOff); + public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { + this(null, ignoreCraftIsBusy, ident, suffixOn, suffixOff); } - public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, final String suffixOn, final String suffixOff) { + public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { super(permission, ignoreCraftIsBusy); this.suffixOn = suffixOn; this.suffixOff = suffixOff; + this.ident = ident; } // Checks if the header is empty, if yes, it quits early (unnecessary actually as if it was empty this would never be called) From 221d0f22bc32017734b4e4b4e8615101503c40cd Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:35:56 +0200 Subject: [PATCH 15/55] correction => if the ident has trailing : => don't add them yourself again (cherry picked from commit a30780380579e77cf4b2cff5caf583cd24541477) (cherry picked from commit b65562e1f1a8e002c82588d02aa42284f7e09e7c) --- .../countercraft/movecraft/sign/AbstractCruiseSign.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index f249d0585..fe4b38dcc 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -24,6 +24,8 @@ public abstract class AbstractCruiseSign extends AbstractCraftSign { private final String suffixOn; private final String suffixOff; private final String ident; + private final Component headerOn = this.buildHeaderOn(); + private final Component headerOff = this.buildHeaderOff(); public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { this(null, ignoreCraftIsBusy, ident, suffixOn, suffixOff); @@ -150,15 +152,15 @@ public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapp // Helper method to build the headline for on or off state protected Component buildHeader(boolean on) { - return on ? buildHeaderOn() : buildHeaderOff(); + return on ? this.headerOn : this.headerOff; } protected Component buildHeaderOn() { - return Component.text(this.ident).append(Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.empty() : Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); } protected Component buildHeaderOff() { - return Component.text(this.ident).append(Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.empty() : Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); } // Should call the craft's relevant methods to start cruising From 1e60c8364cddd9b4b28209d65cf12c5f9c53f53b Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:46:27 +0200 Subject: [PATCH 16/55] startup correction (cherry picked from commit 132f1796e7c4c23ae3622a8ca2ed3879d7a4958b) (cherry picked from commit 453b0a581e077a8c6af3bfac099ee445c0b873e6) --- .../countercraft/movecraft/sign/AbstractCruiseSign.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index fe4b38dcc..cfa89f122 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -24,8 +24,8 @@ public abstract class AbstractCruiseSign extends AbstractCraftSign { private final String suffixOn; private final String suffixOff; private final String ident; - private final Component headerOn = this.buildHeaderOn(); - private final Component headerOff = this.buildHeaderOff(); + private final Component headerOn; + private final Component headerOff; public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { this(null, ignoreCraftIsBusy, ident, suffixOn, suffixOff); @@ -36,6 +36,9 @@ public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, fi this.suffixOn = suffixOn; this.suffixOff = suffixOff; this.ident = ident; + + this.headerOn = this.buildHeaderOn(); + this.headerOff = this.buildHeaderOff(); } // Checks if the header is empty, if yes, it quits early (unnecessary actually as if it was empty this would never be called) From 719a1ec2842eddd3050301e7919aecd5523bba2b Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:52:37 +0200 Subject: [PATCH 17/55] forgot the space (cherry picked from commit ce7f379980b79ae21d7ae0220150d6cbc8f0cc63) --- .../net/countercraft/movecraft/sign/AbstractCruiseSign.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index cfa89f122..04a74669a 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -159,11 +159,11 @@ protected Component buildHeader(boolean on) { } protected Component buildHeaderOn() { - return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.empty() : Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); } protected Component buildHeaderOff() { - return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.empty() : Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); } // Should call the craft's relevant methods to start cruising From 956c8c5cad11007a2f93ed2ef8147179b82073b6 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:13:54 +0200 Subject: [PATCH 18/55] crafttype is specified in second, not the third line (cherry picked from commit 1aaf852c2eb6f25b3ffad1be8194a98651bedf96) --- .../net/countercraft/movecraft/sign/AbstractSubcraftSign.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index cf9d40cb7..300bfc358 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -41,7 +41,7 @@ public AbstractSubcraftSign(final String permission, Function Date: Thu, 5 Sep 2024 17:15:18 +0200 Subject: [PATCH 19/55] typo (cherry picked from commit d70b65b760b0aeb66f227e64c21628d034a043e3) --- .../net/countercraft/movecraft/sign/AbstractSubcraftSign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 300bfc358..81bdee52a 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -41,7 +41,7 @@ public AbstractSubcraftSign(final String permission, Function Date: Fri, 6 Sep 2024 13:40:37 +0200 Subject: [PATCH 20/55] fix information signs not updating (cherry picked from commit e2bae7d68b6218fbfa317c9225eeb5dcb151b5f6) --- .../movecraft/sign/AbstractSignListener.java | 18 ++++- .../movecraft/compat/v1_20/SignListener.java | 62 +++++++++-------- .../movecraft/compat/v1_21/SignListener.java | 66 ++++++++++++++----- 3 files changed, 98 insertions(+), 48 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index 519e0148b..3810dd048 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -97,7 +97,15 @@ public boolean areSignsEqual(SignWrapper other) { return areSignsEqual(this, other); } + public static boolean areSignsEqualIgnoreFace(SignWrapper a, SignWrapper b) { + return areSignsEqual(a, b, true); + } + public static boolean areSignsEqual(SignWrapper a, SignWrapper b) { + return areSignsEqual(a, b, false); + } + + public static boolean areSignsEqual(SignWrapper a, SignWrapper b, boolean ignoreFace) { if (a == b) { return true; } @@ -121,7 +129,7 @@ public static boolean areSignsEqual(SignWrapper a, SignWrapper b) { } // Now check the facing too! - return a.facing().equals(b.facing()); + return ignoreFace || a.facing().equals(b.facing()); } public static boolean areSignsEqual(SignWrapper[] a, SignWrapper[] b) { @@ -143,8 +151,12 @@ public static boolean areSignsEqual(SignWrapper[] a, SignWrapper[] b) { } public void copyContent(SignWrapper other) { - for (int i = 0; i < this.lines().size() && i < other.lines().size(); i++) { - this.line(i, other.line(i)); + this.copyContent(other::line, (i) -> i < other.lines().size()); + } + + public void copyContent(Function retrievalFunction, Function indexValidator) { + for (int i = 0; i < this.lines().size() && indexValidator.apply(i); i++) { + this.line(i, retrievalFunction.apply(i)); } } diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index 497a6ffa8..c2042cb4b 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -110,18 +110,20 @@ protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEven @Override public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + // Ignore facing value here and directly store the associated wrappers in the list + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { @Override public int hashCode(SignWrapper strings) { - return Arrays.hashCode(strings.rawLines()) * strings.facing().hashCode(); + return Arrays.hashCode(strings.rawLines()); } @Override public boolean equals(SignWrapper a, SignWrapper b) { - return SignWrapper.areSignsEqual(a, b); + return SignWrapper.areSignsEqualIgnoreFace(a, b); } }); - Map signStates = new HashMap<>(); + // Remember the locations for the event! + Map> wrapperToLocs = new HashMap<>(); for (MovecraftLocation location : craft.getHitBox()) { Block block = location.toBukkit(craft.getWorld()).getBlock(); @@ -136,45 +138,49 @@ public boolean equals(SignWrapper a, SignWrapper b) { continue; } for (SignWrapper wrapper : wrappersAtLoc) { - List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); - values.add(location); + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(wrapper); + wrapperToLocs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()).add(location); } - signStates.put(location, wrappersAtLoc); } } - for(Map.Entry> entry : signs.entrySet()){ + Set keySet = new HashSet<>(); + for(Map.Entry> entry : signs.entrySet()){ final List components = new ArrayList<>(entry.getKey().lines()); SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); - SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); + SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, wrapperToLocs.getOrDefault(entry.getKey(), new ArrayList<>())); Bukkit.getServer().getPluginManager().callEvent(event); // if(!event.isUpdated()){ // continue; // } // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - for(MovecraftLocation location : entry.getValue()){ - Block block = location.toBukkit(craft.getWorld()).getBlock(); + + System.out.println("New lines: "); + for (String s : event.rawLines()) { + System.out.println(" - " + s); + } + System.out.println("Old lines: "); + for (String s : entry.getKey().rawLines()) { + System.out.println(" - " + s); + } + // Values get changed definitely, but perhaps it does not get applied to the sign after all? + for(SignWrapper wrapperTmp : entry.getValue()){ + if (!checkEventIsUpdated || event.isUpdated()) { + wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); + keySet.add(entry.getKey()); + } + } + } + + for (SignWrapper wrapperTmp : keySet) { + for (MovecraftLocation mLoc : wrapperToLocs.getOrDefault(wrapperTmp, new ArrayList<>())) { + Block block = mLoc.toBukkit(craft.getWorld()).getBlock(); BlockState state = block.getState(); if (!(state instanceof Sign)) { continue; } - SignWrapper[] signsAtLoc = signStates.get(location); - if (signsAtLoc != null && signsAtLoc.length > 0) { - for (SignWrapper sw : signsAtLoc) { - // Important: Check if the wrapper faces the right way! - if (!sw.facing().equals(entry.getKey().facing())) { - continue; - } - if (!checkEventIsUpdated || event.isUpdated()) { - sw.copyContent(entry.getKey()); - } - } - try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); - } catch(ClassCastException ex) { - // Ignore - } - } + ((Sign)block).update(false, false); } } } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 1c8621fe9..356d77eda 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -110,18 +110,20 @@ protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEven @Override public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + // Ignore facing value here and directly store the associated wrappers in the list + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { @Override public int hashCode(SignWrapper strings) { - return Arrays.hashCode(strings.rawLines()) * strings.facing().hashCode(); + return Arrays.hashCode(strings.rawLines()); } @Override public boolean equals(SignWrapper a, SignWrapper b) { - return SignWrapper.areSignsEqual(a, b); + return SignWrapper.areSignsEqualIgnoreFace(a, b); } }); - Map signStates = new HashMap<>(); + // Remember the locations for the event! + Map> wrapperToLocs = new HashMap<>(); for (MovecraftLocation location : craft.getHitBox()) { Block block = location.toBukkit(craft.getWorld()).getBlock(); @@ -136,45 +138,75 @@ public boolean equals(SignWrapper a, SignWrapper b) { continue; } for (SignWrapper wrapper : wrappersAtLoc) { - List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); - values.add(location); + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(wrapper); + wrapperToLocs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()).add(location); } - signStates.put(location, wrappersAtLoc); } } - for(Map.Entry> entry : signs.entrySet()){ + Set keySet = new HashSet<>(); + for(Map.Entry> entry : signs.entrySet()){ final List components = new ArrayList<>(entry.getKey().lines()); SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); - SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); + SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, wrapperToLocs.getOrDefault(entry.getKey(), new ArrayList<>())); Bukkit.getServer().getPluginManager().callEvent(event); // if(!event.isUpdated()){ // continue; // } // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - for(MovecraftLocation location : entry.getValue()){ - Block block = location.toBukkit(craft.getWorld()).getBlock(); + + System.out.println("New lines: "); + for (String s : event.rawLines()) { + System.out.println(" - " + s); + } + System.out.println("Old lines: "); + for (String s : entry.getKey().rawLines()) { + System.out.println(" - " + s); + } + // Values get changed definitely, but perhaps it does not get applied to the sign after all? + for(SignWrapper wrapperTmp : entry.getValue()){ + /*Block block = location.toBukkit(craft.getWorld()).getBlock(); BlockState state = block.getState(); if (!(state instanceof Sign)) { continue; } SignWrapper[] signsAtLoc = signStates.get(location); if (signsAtLoc != null && signsAtLoc.length > 0) { + boolean hadCorrectSide = false; for (SignWrapper sw : signsAtLoc) { // Important: Check if the wrapper faces the right way! - if (!sw.facing().equals(entry.getKey().facing())) { + if (!sw.facing().equals(event.facing())) { continue; } + hadCorrectSide = true; if (!checkEventIsUpdated || event.isUpdated()) { - sw.copyContent(entry.getKey()); + sw.copyContent(event::line, (i) -> i < event.lines().size()); } } - try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); - } catch(ClassCastException ex) { - // Ignore + if (hadCorrectSide) { + try { + ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + } catch(ClassCastException ex) { + // Ignore + } } + }*/ + if (!checkEventIsUpdated || event.isUpdated()) { + wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); + keySet.add(entry.getKey()); + } + } + } + + for (SignWrapper wrapperTmp : keySet) { + for (MovecraftLocation mLoc : wrapperToLocs.getOrDefault(wrapperTmp, new ArrayList<>())) { + Block block = mLoc.toBukkit(craft.getWorld()).getBlock(); + BlockState state = block.getState(); + if (!(state instanceof Sign)) { + continue; } + ((Sign)block).update(false, false); } } } From d9afa5fd25ed3b8632410ccb0d640be18ea967f2 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:42:49 +0200 Subject: [PATCH 21/55] comment out debug logs (cherry picked from commit d86b510a94f444107b3945a4e6233e5d86c9f976) --- .../countercraft/movecraft/compat/v1_20/SignListener.java | 8 -------- .../countercraft/movecraft/compat/v1_21/SignListener.java | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index c2042cb4b..afe255d25 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -156,14 +156,6 @@ public boolean equals(SignWrapper a, SignWrapper b) { // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - System.out.println("New lines: "); - for (String s : event.rawLines()) { - System.out.println(" - " + s); - } - System.out.println("Old lines: "); - for (String s : entry.getKey().rawLines()) { - System.out.println(" - " + s); - } // Values get changed definitely, but perhaps it does not get applied to the sign after all? for(SignWrapper wrapperTmp : entry.getValue()){ if (!checkEventIsUpdated || event.isUpdated()) { diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 356d77eda..219ac408d 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -156,14 +156,14 @@ public boolean equals(SignWrapper a, SignWrapper b) { // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - System.out.println("New lines: "); + /*System.out.println("New lines: "); for (String s : event.rawLines()) { System.out.println(" - " + s); } System.out.println("Old lines: "); for (String s : entry.getKey().rawLines()) { System.out.println(" - " + s); - } + }*/ // Values get changed definitely, but perhaps it does not get applied to the sign after all? for(SignWrapper wrapperTmp : entry.getValue()){ /*Block block = location.toBukkit(craft.getWorld()).getBlock(); From 19e2171c313e9904b301781bf07e45c876841136 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:58:23 +0200 Subject: [PATCH 22/55] call update on the state, not on the block... (cherry picked from commit 3194ed75a540aa79f7b7c531dcd4e6abfe099202) --- .../countercraft/movecraft/compat/v1_18/SignListener.java | 2 +- .../countercraft/movecraft/compat/v1_20/SignListener.java | 2 +- .../countercraft/movecraft/compat/v1_21/SignListener.java | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java index f6a4244ab..c20f5bd4d 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java @@ -144,7 +144,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { } } try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + ((Sign)location.toBukkit(craft.getWorld()).getBlock().getState()).update(false, false); } catch(ClassCastException ex) { // Ignore } diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index afe255d25..b8d945fac 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -172,7 +172,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { if (!(state instanceof Sign)) { continue; } - ((Sign)block).update(false, false); + ((Sign)state).update(false, false); } } } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 219ac408d..3bc1b77c7 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -156,14 +156,14 @@ public boolean equals(SignWrapper a, SignWrapper b) { // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - /*System.out.println("New lines: "); + System.out.println("New lines: "); for (String s : event.rawLines()) { System.out.println(" - " + s); } System.out.println("Old lines: "); for (String s : entry.getKey().rawLines()) { System.out.println(" - " + s); - }*/ + } // Values get changed definitely, but perhaps it does not get applied to the sign after all? for(SignWrapper wrapperTmp : entry.getValue()){ /*Block block = location.toBukkit(craft.getWorld()).getBlock(); @@ -206,7 +206,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { if (!(state instanceof Sign)) { continue; } - ((Sign)block).update(false, false); + ((Sign)state).update(false, false); } } } From 6d8b47a235c5df04a2c7461e571f6c09ae7005e4 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:16:41 +0200 Subject: [PATCH 23/55] more elegant solution (cherry picked from commit 2df03851c8d4b4e4f07651064306ff511d1b9513) --- .../movecraft/compat/v1_18/SignListener.java | 5 +- .../movecraft/compat/v1_20/SignListener.java | 51 +++++++++++++++---- .../movecraft/compat/v1_21/SignListener.java | 21 +++----- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java index c20f5bd4d..170619320 100644 --- a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java +++ b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java @@ -141,13 +141,14 @@ public boolean equals(SignWrapper a, SignWrapper b) { for (SignWrapper sw : signsAtLoc) { if (!checkEventIsUpdated || event.isUpdated()) { sw.copyContent(entry.getKey()); + sw.block().update(false, false); } } - try { + /*try { ((Sign)location.toBukkit(craft.getWorld()).getBlock().getState()).update(false, false); } catch(ClassCastException ex) { // Ignore - } + }*/ } } } diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index b8d945fac..483b44baf 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -144,7 +144,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { } } } - Set keySet = new HashSet<>(); + Set signsToUpdate = new HashSet<>(); for(Map.Entry> entry : signs.entrySet()){ final List components = new ArrayList<>(entry.getKey().lines()); SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); @@ -156,24 +156,53 @@ public boolean equals(SignWrapper a, SignWrapper b) { // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player + /*System.out.println("New lines: "); + for (String s : event.rawLines()) { + System.out.println(" - " + s); + } + System.out.println("Old lines: "); + for (String s : entry.getKey().rawLines()) { + System.out.println(" - " + s); + }*/ // Values get changed definitely, but perhaps it does not get applied to the sign after all? for(SignWrapper wrapperTmp : entry.getValue()){ + /*Block block = location.toBukkit(craft.getWorld()).getBlock(); + BlockState state = block.getState(); + if (!(state instanceof Sign)) { + continue; + } + SignWrapper[] signsAtLoc = signStates.get(location); + if (signsAtLoc != null && signsAtLoc.length > 0) { + boolean hadCorrectSide = false; + for (SignWrapper sw : signsAtLoc) { + // Important: Check if the wrapper faces the right way! + if (!sw.facing().equals(event.facing())) { + continue; + } + hadCorrectSide = true; + if (!checkEventIsUpdated || event.isUpdated()) { + sw.copyContent(event::line, (i) -> i < event.lines().size()); + } + } + if (hadCorrectSide) { + try { + ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); + } catch(ClassCastException ex) { + // Ignore + } + } + }*/ if (!checkEventIsUpdated || event.isUpdated()) { wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); - keySet.add(entry.getKey()); + if (wrapperTmp.block() != null) { + signsToUpdate.add(wrapperTmp.block()); + } } } } - for (SignWrapper wrapperTmp : keySet) { - for (MovecraftLocation mLoc : wrapperToLocs.getOrDefault(wrapperTmp, new ArrayList<>())) { - Block block = mLoc.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - ((Sign)state).update(false, false); - } + for (Sign sign : signsToUpdate) { + sign.update(false, false); } } } diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 3bc1b77c7..0b6eb9d0d 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -144,7 +144,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { } } } - Set keySet = new HashSet<>(); + Set signsToUpdate = new HashSet<>(); for(Map.Entry> entry : signs.entrySet()){ final List components = new ArrayList<>(entry.getKey().lines()); SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); @@ -156,14 +156,14 @@ public boolean equals(SignWrapper a, SignWrapper b) { // TODO: This is implemented only to fix client caching // ideally we wouldn't do the update and would instead fake it out to the player - System.out.println("New lines: "); + /*System.out.println("New lines: "); for (String s : event.rawLines()) { System.out.println(" - " + s); } System.out.println("Old lines: "); for (String s : entry.getKey().rawLines()) { System.out.println(" - " + s); - } + }*/ // Values get changed definitely, but perhaps it does not get applied to the sign after all? for(SignWrapper wrapperTmp : entry.getValue()){ /*Block block = location.toBukkit(craft.getWorld()).getBlock(); @@ -194,20 +194,15 @@ public boolean equals(SignWrapper a, SignWrapper b) { }*/ if (!checkEventIsUpdated || event.isUpdated()) { wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); - keySet.add(entry.getKey()); + if (wrapperTmp.block() != null) { + signsToUpdate.add(wrapperTmp.block()); + } } } } - for (SignWrapper wrapperTmp : keySet) { - for (MovecraftLocation mLoc : wrapperToLocs.getOrDefault(wrapperTmp, new ArrayList<>())) { - Block block = mLoc.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - ((Sign)state).update(false, false); - } + for (Sign sign : signsToUpdate) { + sign.update(false, false); } } } From 94ccf3c6595db4727aa5036a06356b6bcd4291bf Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:55:16 +0200 Subject: [PATCH 24/55] add eventtype enum (to api classes) (cherry picked from commit 91ff87275ef33faaba3d00f7c3ac04ef493d1331) --- .../movecraft/sign/AbstractCruiseSign.java | 2 +- .../sign/AbstractInformationSign.java | 2 +- .../movecraft/sign/AbstractMovecraftSign.java | 20 ++++++++++++++++++- .../movecraft/sign/AbstractSubcraftSign.java | 2 +- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index 04a74669a..e474e419c 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -81,7 +81,7 @@ protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { // By default, cancel the event if the processing was successful, or the invoker was not sneaking => Allows breaking signs while sneaking @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { return processingSuccessful || !sneaking; } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java index 4eef24d1e..2136377e1 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -78,7 +78,7 @@ protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper // TODO: Add "reason" what to cancel, we might not want to cancel all edit events @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { if (processingSuccessful) { return true; } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index 516ee6afa..c30697c6e 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -140,10 +140,28 @@ protected Craft getCraft(AbstractSignListener.SignWrapper sign) { return MathUtils.getCraftByPersistentBlockData(sign.block().getLocation()); } + public enum EventType { + SIGN_CREATION, + SIGN_EDIT, + SIGN_EDIT_ON_CRAFT(true), + SIGN_CLICK, + SIGN_CLICK_ON_CRAFT(true); + + private boolean onCraft; + + EventType() { + this(false); + } + + EventType(boolean onCraft) { + this.onCraft = onCraft; + } + } + // Used by the event handler to determine if the event should be cancelled // processingSuccessful is the output of processSignClick() or processSignChange() // This is only called for the PlayerInteractEvent and the SignChangeEvent - public abstract boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking); + public abstract boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType); // Validation method, called by default in processSignClick // If false is returned, nothing will be processed diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 81bdee52a..46f255944 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -71,7 +71,7 @@ protected boolean internalProcessSign(Action clickType, AbstractSignListener.Sig } @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { return processingSuccessful || !sneaking; } From 0a7fd09e74dc7e0dbfa2baad440fce1ef38b6706 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 7 Sep 2024 00:56:05 +0200 Subject: [PATCH 25/55] refactor for eventtype enum (implementations (simple)) (cherry picked from commit 633f33c8c46bf89e70bc90229c2840e2b632e209) --- .../java/net/countercraft/movecraft/sign/CraftPilotSign.java | 3 +-- .../main/java/net/countercraft/movecraft/sign/ReleaseSign.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java index a3827e8b0..db9fcbe25 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java @@ -23,7 +23,6 @@ import java.util.Collections; import java.util.HashSet; -import java.util.Optional; import java.util.Set; //TODO: This is not very pretty... @@ -36,7 +35,7 @@ public CraftPilotSign(CraftType craftType) { } @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { return processingSuccessful || !sneaking; } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java index 44c43e754..1488e9c26 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java @@ -15,7 +15,7 @@ public ReleaseSign() { } @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking) { + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { if (processingSuccessful) { return true; } From a611a4f423d4214884bc07a8df383a2475865feb Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:44:29 +0200 Subject: [PATCH 26/55] supply eventType and use nullable for sign get access --- .../movecraft/sign/AbstractCraftSign.java | 16 +++---- .../movecraft/sign/AbstractMovecraftSign.java | 10 ++-- .../movecraft/sign/AbstractSignListener.java | 46 ++++++++++++++++--- 3 files changed, 52 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java index 232601e82..775d73cb9 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java @@ -22,20 +22,20 @@ public abstract class AbstractCraftSign extends AbstractMovecraftSign { // Helper method for the listener - public static Optional tryGetCraftSign(final Component ident) { + public static @Nullable AbstractCraftSign getCraftSign(final Component ident) { if (ident == null) { - return Optional.empty(); + return null; } final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); - return tryGetCraftSign(identStr); + return getCraftSign(identStr); } - public static Optional tryGetCraftSign(final String ident) { - Optional tmp = AbstractCraftSign.tryGet(ident); - if (tmp.isPresent() && tmp.get() instanceof AbstractCraftSign acs) { - return Optional.of(acs); + public static @Nullable AbstractCraftSign getCraftSign(final String ident) { + AbstractMovecraftSign tmp = AbstractCraftSign.get(ident); + if (tmp != null && tmp instanceof AbstractCraftSign acs) { + return acs; } - return Optional.empty(); + return null; } protected final boolean ignoreCraftIsBusy; diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index c30697c6e..49201c2c6 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -47,24 +47,24 @@ public static void registerCraftPilotSigns(Set loadedTypes, Function< } } - public static Optional tryGet(final Component ident) { + public static @Nullable AbstractMovecraftSign get(final Component ident) { if (ident == null) { - return Optional.empty(); + return null; } final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); - return tryGet(identStr); + return get(identStr); } // Attempts to find a AbstractMovecraftSign instance, if something has been registered // If the ident follows the format "foo: bar", only "foo:" is used as ident to search for - public static Optional tryGet(final String ident) { + public static @Nullable AbstractMovecraftSign get(final String ident) { String identToUse = ident.toUpperCase(); if (identToUse.contains(":")) { identToUse = identToUse.split(":")[0]; // Re-add the : cause things should be registered with : at the end identToUse = identToUse + ":"; } - return Optional.ofNullable(SIGNS.getOrDefault(identToUse, null)); + return SIGNS.getOrDefault(identToUse, null); } // Registers a sign in all cases diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index 3810dd048..d08d87a71 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -3,6 +3,7 @@ import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.events.CraftDetectEvent; import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.util.MathUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.World; @@ -10,11 +11,13 @@ import org.bukkit.block.BlockFace; import org.bukkit.block.BlockState; import org.bukkit.block.Sign; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -167,7 +170,10 @@ public SignWrapper[] getSignWrappers(Sign sign) { }; public abstract SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty); protected abstract SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent); - protected abstract SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent); + protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { + return this.getSignWrapper(sign, interactEvent.getPlayer()); + } + protected abstract SignWrapper getSignWrapper(Sign sign, Player player); public abstract void processSignTranslation(final Craft craft, boolean checkEventIsUpdated); @@ -180,7 +186,11 @@ public void onCraftDetect(CraftDetectEvent event) { BlockState state = block.getState(); if (state instanceof Sign sign) { for (SignWrapper wrapper : this.getSignWrappers(sign)) { - AbstractCraftSign.tryGetCraftSign(wrapper.line(0)).ifPresent(acs -> acs.onCraftDetect(event, wrapper)); + // Would be one more readable line if using Optionals but Nullables were wanted + AbstractCraftSign acs = AbstractCraftSign.getCraftSign(wrapper.line(0)); + if (acs != null) { + acs.onCraftDetect(event, wrapper); + } } } } @@ -189,7 +199,11 @@ public void onCraftDetect(CraftDetectEvent event) { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void onSignTranslate(SignTranslateEvent event) { - AbstractCraftSign.tryGetCraftSign(event.line(0)).ifPresent(acs -> acs.onSignMovedByCraft(event)); + // Would be one more readable line if using Optionals but Nullables were wanted + AbstractCraftSign acs = AbstractCraftSign.getCraftSign(event.line(0)); + if (acs != null) { + acs.onSignMovedByCraft(event); + } } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) @@ -197,11 +211,19 @@ public void onSignChange(SignChangeEvent event) { Block block = event.getBlock(); BlockState state = block.getState(); if (state instanceof Sign sign) { + SignWrapper signWrapper = this.getSignWrapper(sign, event.getPlayer()); SignWrapper wrapper = this.getSignWrapper(sign, event); - AbstractMovecraftSign.tryGet(wrapper.line(0)).ifPresent(ams -> { + AbstractMovecraftSign.EventType eventType = signWrapper.isEmpty() ? AbstractMovecraftSign.EventType.SIGN_CREATION : AbstractMovecraftSign.EventType.SIGN_EDIT; + if (eventType == AbstractMovecraftSign.EventType.SIGN_EDIT && MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null) { + eventType = AbstractMovecraftSign.EventType.SIGN_EDIT_ON_CRAFT; + } + final AbstractMovecraftSign.EventType eventTypeTmp = eventType; + AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); + // Would be one more readable line if using Optionals but Nullables were wanted + if (ams != null) { boolean success = ams.processSignChange(event, wrapper); - if (ams.shouldCancelEvent(success, null, event.getPlayer().isSneaking())) { + if (ams.shouldCancelEvent(success, null, event.getPlayer().isSneaking(), eventTypeTmp)) { event.setCancelled(true); } }); @@ -217,9 +239,19 @@ public void onSignClick(PlayerInteractEvent event) { BlockState state = block.getState(); if (state instanceof Sign sign) { SignWrapper wrapper = this.getSignWrapper(sign, event); - AbstractMovecraftSign.tryGet(wrapper.line(0)).ifPresent(ams -> { + boolean shift = event.getPlayer().isSneaking(); + ItemStack heldItem = event.getItem(); + + // TODO: If the item is a feather (right click) or a axe (left click) and shift is pressed, then we don't do movecraft stuff + + // Would be one more readable line if using Optionals but Nullables were wanted + AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); + if (ams != null) { boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); - if (ams.shouldCancelEvent(success, event.getAction(), event.getPlayer().isSneaking())) { + if (ams.shouldCancelEvent(success, event.getAction(), event.getPlayer().isSneaking(), AbstractMovecraftSign.EventType.SIGN_CLICK)) { + event.setCancelled(true); + } else if (MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null) { + // Cancel interact in all cases when on a craft event.setCancelled(true); } }); From be724f92f4bd383ccb6d7c1ed9ae06b29d8dadd1 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 19:46:30 +0200 Subject: [PATCH 27/55] just call it override (cherry picked from commit 88baa63888a310fb190ab7945eef888e348da2d9) --- .../countercraft/movecraft/sign/AbstractMovecraftSign.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index 49201c2c6..303cb4f2c 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -74,8 +74,8 @@ public static void register(final String ident, final @Nonnull AbstractMovecraft // Registers a sign // If @param overrideIfAlreadyRegistered is set to false, it won't be registered if something has elready been registered using that name - public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance, boolean overrideIfAlreadyRegistered) { - if (overrideIfAlreadyRegistered) { + public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance, boolean override) { + if (override) { SIGNS.put(ident.toUpperCase(), instance); } else { SIGNS.putIfAbsent(ident.toUpperCase(), instance); From 8c0e50e4e7d96ab6a463266570638f27c8716717 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:05:19 +0200 Subject: [PATCH 28/55] add registry base class, implement it, and use it (cherry picked from commit 1853ba716b49f568d695fb570a87b2f337bb3724) --- .../craft/datatag/CraftDataTagRegistry.java | 3 + .../movecraft/sign/AbstractCraftSign.java | 16 ++-- .../movecraft/sign/AbstractMovecraftSign.java | 39 ++++----- .../movecraft/sign/MovecraftSignRegistry.java | 80 +++++++++++++++++++ .../movecraft/util/SimpleRegistry.java | 46 +++++++++++ 5 files changed, 150 insertions(+), 34 deletions(-) create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/MovecraftSignRegistry.java create mode 100644 api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java diff --git a/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java index d542f8405..96207624e 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/datatag/CraftDataTagRegistry.java @@ -10,6 +10,9 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Function; +/** + * TODO: Change to extend @link SimpleRegistry in the future + */ public class CraftDataTagRegistry { public static final @NotNull CraftDataTagRegistry INSTANCE = new CraftDataTagRegistry(); diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java index 775d73cb9..2c73c8962 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java @@ -22,20 +22,16 @@ public abstract class AbstractCraftSign extends AbstractMovecraftSign { // Helper method for the listener + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static @Nullable AbstractCraftSign getCraftSign(final Component ident) { - if (ident == null) { - return null; - } - final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); - return getCraftSign(identStr); + return MovecraftSignRegistry.INSTANCE.getCraftSign(ident); } + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static @Nullable AbstractCraftSign getCraftSign(final String ident) { - AbstractMovecraftSign tmp = AbstractCraftSign.get(ident); - if (tmp != null && tmp instanceof AbstractCraftSign acs) { - return acs; - } - return null; + return MovecraftSignRegistry.INSTANCE.getCraftSign(ident); } protected final boolean ignoreCraftIsBusy; diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index 303cb4f2c..f5608e0a7 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -38,48 +38,39 @@ public static boolean hasBeenRegistered(final String ident) { } // Special case for pilot signs, they are registered via the crafttypes name + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static void registerCraftPilotSigns(Set loadedTypes, Function signFactory) { - SIGNS.entrySet().removeIf(entry -> entry.getValue() instanceof AbstractCraftPilotSign); - // Now, add all types... - for (CraftType type : loadedTypes) { - AbstractCraftPilotSign sign = signFactory.apply(type); - register(type.getStringProperty(CraftType.NAME), sign, true); - } + MovecraftSignRegistry.INSTANCE.registerCraftPilotSigns(loadedTypes, signFactory); } + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static @Nullable AbstractMovecraftSign get(final Component ident) { - if (ident == null) { - return null; - } - final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); - return get(identStr); + return MovecraftSignRegistry.INSTANCE.get(ident); } // Attempts to find a AbstractMovecraftSign instance, if something has been registered // If the ident follows the format "foo: bar", only "foo:" is used as ident to search for + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static @Nullable AbstractMovecraftSign get(final String ident) { - String identToUse = ident.toUpperCase(); - if (identToUse.contains(":")) { - identToUse = identToUse.split(":")[0]; - // Re-add the : cause things should be registered with : at the end - identToUse = identToUse + ":"; - } - return SIGNS.getOrDefault(identToUse, null); + return MovecraftSignRegistry.INSTANCE.get(ident); } // Registers a sign in all cases + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance) { - register(ident, instance, true); + MovecraftSignRegistry.INSTANCE.register(ident, instance); } // Registers a sign // If @param overrideIfAlreadyRegistered is set to false, it won't be registered if something has elready been registered using that name + // Use the methods in MovecraftSignRegistry instead + @Deprecated(forRemoval = true) public static void register(final String ident, final @Nonnull AbstractMovecraftSign instance, boolean override) { - if (override) { - SIGNS.put(ident.toUpperCase(), instance); - } else { - SIGNS.putIfAbsent(ident.toUpperCase(), instance); - } + MovecraftSignRegistry.INSTANCE.register(ident, instance, override); } // Optional permission for this sign diff --git a/api/src/main/java/net/countercraft/movecraft/sign/MovecraftSignRegistry.java b/api/src/main/java/net/countercraft/movecraft/sign/MovecraftSignRegistry.java new file mode 100644 index 000000000..a068f94ba --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/MovecraftSignRegistry.java @@ -0,0 +1,80 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; +import net.countercraft.movecraft.craft.type.CraftType; +import net.countercraft.movecraft.util.SimpleRegistry; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; +import java.util.function.Function; + +public class MovecraftSignRegistry extends SimpleRegistry { + + public static final @NotNull MovecraftSignRegistry INSTANCE = new MovecraftSignRegistry(); + + public void registerCraftPilotSigns(Set loadedTypes, Function signFactory) { + _register.entrySet().removeIf(entry -> entry.getValue() instanceof AbstractCraftPilotSign); + // Now, add all types... + for (CraftType type : loadedTypes) { + AbstractCraftPilotSign sign = signFactory.apply(type); + register(type.getStringProperty(CraftType.NAME), sign, true); + } + } + + public @NotNull AbstractMovecraftSign register(@NotNull String key, @NotNull AbstractMovecraftSign value, String... aliases) throws IllegalArgumentException { + return register(key, value, false, aliases); + } + + public @NotNull AbstractMovecraftSign register(@NotNull String key, @NotNull AbstractMovecraftSign value, boolean override, String... aliases) throws IllegalArgumentException { + AbstractMovecraftSign result = this.register(key, value, override); + for (String alias : aliases) { + this.register(alias, result, override); + } + return result; + } + + public @Nullable AbstractMovecraftSign get(Component key) { + if (key == null) { + return null; + } + final String identStr = PlainTextComponentSerializer.plainText().serialize(key); + return get(identStr); + } + + @Override + public @Nullable AbstractMovecraftSign get(@NotNull String key) { + String identToUse = key.toUpperCase(); + if (identToUse.contains(":")) { + identToUse = identToUse.split(":")[0]; + // Re-add the : cause things should be registered with : at the end + identToUse = identToUse + ":"; + } + return super.get(identToUse); + } + + // Helper method for the listener + public @Nullable AbstractCraftSign getCraftSign(final Component ident) { + if (ident == null) { + return null; + } + final String identStr = PlainTextComponentSerializer.plainText().serialize(ident); + return this.getCraftSign(identStr); + } + + public @Nullable AbstractCraftSign getCraftSign(final String ident) { + AbstractMovecraftSign tmp = this.get(ident); + if (tmp != null && tmp instanceof AbstractCraftSign acs) { + return acs; + } + return null; + } + + + @Override + public @NotNull AbstractMovecraftSign register(@NotNull String key, @NotNull AbstractMovecraftSign value, boolean override) throws IllegalArgumentException { + return super.register(key.toUpperCase(), value, override); + } +} diff --git a/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java b/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java new file mode 100644 index 000000000..bddee0e9d --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java @@ -0,0 +1,46 @@ +package net.countercraft.movecraft.util; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class SimpleRegistry { + + protected final @NotNull ConcurrentMap<@NotNull K, @NotNull T> _register; + + public SimpleRegistry(){ + _register = new ConcurrentHashMap<>(); + } + + public @NotNull T register(final @NotNull K key, final @NotNull T value) throws IllegalArgumentException { + return this.register(key, value, false); + } + + public @NotNull T register(final @NotNull K key, final @NotNull T value, boolean override) throws IllegalArgumentException { + T previous = _register.putIfAbsent(key, value); + if(previous != null && !override){ + throw new IllegalArgumentException(String.format("Key %s is already registered.", key)); + } + + return value; + } + + public @Nullable T get(final @NotNull K key) { + return _register.getOrDefault(key, null); + } + + public boolean isRegistered(final @NotNull K key){ + return _register.containsKey(key); + } + + /** + * Get an iterable over all keys currently registered. + * @return An immutable iterable over the registry keys + */ + public @NotNull Iterable<@NotNull K> getAllKeys(){ + return _register.keySet().stream().toList(); + } + +} From 8a2af8bbddb857a47223ca33fc9bdfec476c7235 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:06:47 +0200 Subject: [PATCH 29/55] adjust comment (cherry picked from commit 23168768bef04534665e5e09acb8d8a1f86087de) --- .../net/countercraft/movecraft/events/SignTranslateEvent.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java index e615f8c5f..34fa72835 100644 --- a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java +++ b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java @@ -39,7 +39,8 @@ public SignTranslateEvent(@NotNull Craft craft, @NotNull AbstractSignListener.Si @NotNull @Deprecated(forRemoval = true) public String[] getLines() { - // TODO: Why does this set it to updated? This is just reading... + // Why does this set it to updated? This is just reading... + // => Lines can be updated externally. We need to mark all signs as updated so it displays properly on clients this.updated = true; return backing.rawLines(); } From 0bfce9047a828b8c2f18b6f5099f76bf94127f6a Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:07:08 +0200 Subject: [PATCH 30/55] adjust comment (cherry picked from commit 238e989fa852a21e2d99ca4fb4efc3eff3b2101d) --- .../net/countercraft/movecraft/events/SignTranslateEvent.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java index 34fa72835..38ab5c040 100644 --- a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java +++ b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java @@ -85,7 +85,7 @@ public List lines() { return backing.lines(); } - // Bukkit crap + // Bukkit @Override public HandlerList getHandlers() { return HANDLERS; From 17b458e5b9b87799a070df8c238a353ccb9c2d7c Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 10 Sep 2024 20:13:57 +0200 Subject: [PATCH 31/55] remove old code (cherry picked from commit fd70b5053987a9a7ede55a938a38b0926b38920c) --- .../java/net/countercraft/movecraft/sign/CraftPilotSign.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java index db9fcbe25..13d69239b 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java @@ -119,10 +119,6 @@ protected void runDetectTask(MovecraftLocation startPoint, Player player, Abstra // Setup cruise direction BlockFace facing = signWrapper.facing(); craft.setCruiseDirection(CruiseDirection.fromBlockFace(facing)); - /*if (signWrapper.block().getBlockData() instanceof Directional) - craft.setCruiseDirection(CruiseDirection.fromBlockFace(((Directional) sign.getBlockData()).getFacing())); - else - craft.setCruiseDirection(CruiseDirection.NONE);*/ // Start craft cruising craft.setLastCruiseUpdate(System.currentTimeMillis()); From 4a6941a964cdbaae7936b804a76bbe517fc077e7 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:27:39 +0200 Subject: [PATCH 32/55] remove 1.18 signListener (cherry picked from commit 54336e5adeda0b8586d80f1c859e7f6908f01308) --- .../movecraft/compat/v1_18/SignListener.java | 157 ------------------ 1 file changed, 157 deletions(-) delete mode 100644 v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java diff --git a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java b/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java deleted file mode 100644 index 170619320..000000000 --- a/v1_18/src/main/java/net/countercraft/movecraft/compat/v1_18/SignListener.java +++ /dev/null @@ -1,157 +0,0 @@ -package net.countercraft.movecraft.compat.v1_18; - -import it.unimi.dsi.fastutil.Hash; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import net.countercraft.movecraft.MovecraftLocation; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.events.SignTranslateEvent; -import net.countercraft.movecraft.sign.AbstractSignListener; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.Tag; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Rotatable; -import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.event.player.PlayerInteractEvent; - -import java.util.*; - -/* - * As soon as 1.18 support is dropped, the adapter system will be dropped too - */ -@Deprecated(forRemoval = true) -public class SignListener extends AbstractSignListener { - - protected final SignWrapper createFromSign(final Sign sign) { - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - SignWrapper wrapper = new SignWrapper( - sign, - sign::line, - sign.lines(), - sign::line, - face - ); - return wrapper; - } - - @Override - public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { - SignWrapper wrapper = this.createFromSign(sign); - if (ignoreEmpty && wrapper.isEmpty()) { - return new SignWrapper[] {}; - } - return new SignWrapper[] {wrapper}; - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - SignWrapper wrapper = new SignWrapper( - sign, - signChangeEvent::line, - signChangeEvent.lines(), - signChangeEvent::line, - face - ); - return wrapper; - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { - return this.createFromSign(sign); - } - - @Override - public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(SignWrapper strings) { - return Arrays.hashCode(strings.rawLines()); - } - - @Override - public boolean equals(SignWrapper a, SignWrapper b) { - return SignWrapper.areSignsEqual(a, b); - } - }); - Map signStates = new HashMap<>(); - - for (MovecraftLocation location : craft.getHitBox()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - if(!Tag.SIGNS.isTagged(block.getType())){ - continue; - } - BlockState state = block.getState(); - if (state instanceof Sign) { - Sign sign = (Sign) state; - SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); - if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { - continue; - } - for (SignWrapper wrapper : wrappersAtLoc) { - List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); - values.add(location); - } - signStates.put(location, wrappersAtLoc); - } - } - // TODO: This is not good yet, this doesn't really care about a signs sides... - for(Map.Entry> entry : signs.entrySet()){ - final List components = new ArrayList<>(entry.getKey().lines()); - SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); - SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, entry.getValue()); - Bukkit.getServer().getPluginManager().callEvent(event); - // if(!event.isUpdated()){ - // continue; - // } - // TODO: This is implemented only to fix client caching - // ideally we wouldn't do the update and would instead fake it out to the player - for(MovecraftLocation location : entry.getValue()){ - Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - SignWrapper[] signsAtLoc = signStates.get(location); - if (signsAtLoc != null && signsAtLoc.length > 0) { - for (SignWrapper sw : signsAtLoc) { - if (!checkEventIsUpdated || event.isUpdated()) { - sw.copyContent(entry.getKey()); - sw.block().update(false, false); - } - } - /*try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock().getState()).update(false, false); - } catch(ClassCastException ex) { - // Ignore - }*/ - } - } - } - } - -} From 000d9875e82e1e77d5caeeb92549ecbe86b0d9bc Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:35:31 +0200 Subject: [PATCH 33/55] fix build errors --- .../countercraft/movecraft/sign/AbstractSignListener.java | 4 ++-- .../countercraft/movecraft/compat/v1_20/SignListener.java | 7 ++++--- .../countercraft/movecraft/compat/v1_21/SignListener.java | 7 ++++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index d08d87a71..a4217715f 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -226,7 +226,7 @@ public void onSignChange(SignChangeEvent event) { if (ams.shouldCancelEvent(success, null, event.getPlayer().isSneaking(), eventTypeTmp)) { event.setCancelled(true); } - }); + } } } @@ -254,7 +254,7 @@ public void onSignClick(PlayerInteractEvent event) { // Cancel interact in all cases when on a craft event.setCancelled(true); } - }); + } } } diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java index 483b44baf..c10007800 100644 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java @@ -19,6 +19,7 @@ import org.bukkit.block.data.Rotatable; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; +import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.jetbrains.annotations.NotNull; @@ -103,9 +104,9 @@ protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) } @Override - protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { - @NotNull SignSide side = sign.getTargetSide(interactEvent.getPlayer()); - return this.createFromSide(sign, side, sign.getInteractableSideFor(interactEvent.getPlayer())); + protected SignWrapper getSignWrapper(Sign sign, Player player) { + @NotNull SignSide side = sign.getTargetSide(player); + return this.createFromSide(sign, side, sign.getInteractableSideFor(player)); } @Override diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java index 0b6eb9d0d..1c626b547 100644 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java @@ -19,6 +19,7 @@ import org.bukkit.block.data.Rotatable; import org.bukkit.block.sign.Side; import org.bukkit.block.sign.SignSide; +import org.bukkit.entity.Player; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.jetbrains.annotations.NotNull; @@ -103,9 +104,9 @@ protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) } @Override - protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { - @NotNull SignSide side = sign.getTargetSide(interactEvent.getPlayer()); - return this.createFromSide(sign, side, sign.getInteractableSideFor(interactEvent.getPlayer())); + protected SignWrapper getSignWrapper(Sign sign, Player player) { + @NotNull SignSide side = sign.getTargetSide(player); + return this.createFromSide(sign, side, sign.getInteractableSideFor(player)); } @Override From 8349dd83fdbcbe50f0966bb2de566a129c3c465f Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:45:13 +0200 Subject: [PATCH 34/55] properly implement shouldCancel method (API + examples) (cherry picked from commit 27b94915644c0010c0a4d44f957d4b23b8dea0ae) --- .../movecraft/sign/CraftPilotSign.java | 6 ------ .../countercraft/movecraft/sign/ReleaseSign.java | 8 -------- .../movecraft/sign/AbstractCruiseSign.java | 6 ------ .../movecraft/sign/AbstractInformationSign.java | 9 --------- .../movecraft/sign/AbstractMovecraftSign.java | 14 +++++++++++++- .../movecraft/sign/AbstractSubcraftSign.java | 5 ----- 6 files changed, 13 insertions(+), 35 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java index 13d69239b..35dbea79c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java @@ -19,7 +19,6 @@ import org.bukkit.event.block.Action; import org.bukkit.event.block.SignChangeEvent; import org.bukkit.scheduler.BukkitRunnable; -import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.HashSet; @@ -34,11 +33,6 @@ public CraftPilotSign(CraftType craftType) { super(craftType); } - @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - return processingSuccessful || !sneaking; - } - @Override protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { String header = sign.getRaw(0).trim(); diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java index 1488e9c26..ae62fa419 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java @@ -14,14 +14,6 @@ public ReleaseSign() { super(null); } - @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - if (processingSuccessful) { - return true; - } - return !sneaking; - } - @Override protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { return true; diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index e474e419c..bf038f968 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -79,12 +79,6 @@ protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { return suffix.equalsIgnoreCase(this.suffixOn); } - // By default, cancel the event if the processing was successful, or the invoker was not sneaking => Allows breaking signs while sneaking - @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - return processingSuccessful || !sneaking; - } - // Hook to do stuff that run after stopping to cruise protected void onAfterStoppingCruise(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player) { diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java index 2136377e1..801f59082 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -76,15 +76,6 @@ protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper return true; } - // TODO: Add "reason" what to cancel, we might not want to cancel all edit events - @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - if (processingSuccessful) { - return true; - } - return !sneaking; - } - @Override protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { if (this.refreshSign(craft, sign, false, REFRESH_CAUSE.SIGN_CLICK)) { diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index f5608e0a7..c26648716 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -147,12 +147,24 @@ public enum EventType { EventType(boolean onCraft) { this.onCraft = onCraft; } + + public boolean isOnCraft() { + return this.onCraft; + } } // Used by the event handler to determine if the event should be cancelled // processingSuccessful is the output of processSignClick() or processSignChange() // This is only called for the PlayerInteractEvent and the SignChangeEvent - public abstract boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType); + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { + if (eventType.isOnCraft()) { + return true; + } + if (processingSuccessful && !sneaking) { + return eventType == EventType.SIGN_CLICK; + } + return false; + } // Validation method, called by default in processSignClick // If false is returned, nothing will be processed diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 46f255944..3203242c8 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -70,11 +70,6 @@ protected boolean internalProcessSign(Action clickType, AbstractSignListener.Sig return this.internalProcessSignWithCraft(clickType, sign, craft, player); } - @Override - public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - return processingSuccessful || !sneaking; - } - @Override public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { // TODO: Implement From e0f09d12160ab5bc2e3c35b412584f43c542ce2f Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 17:51:38 +0200 Subject: [PATCH 35/55] add override items and tools (cherry picked from commit f7fd65e52ab3f880e2645c03de3d91194c01e879) --- .../movecraft/sign/AbstractSignListener.java | 16 +++++++++++++--- .../net/countercraft/movecraft/util/Tags.java | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index a4217715f..2ad4d3d97 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -4,6 +4,7 @@ import net.countercraft.movecraft.events.CraftDetectEvent; import net.countercraft.movecraft.events.SignTranslateEvent; import net.countercraft.movecraft.util.MathUtils; +import net.countercraft.movecraft.util.Tags; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.World; @@ -239,16 +240,25 @@ public void onSignClick(PlayerInteractEvent event) { BlockState state = block.getState(); if (state instanceof Sign sign) { SignWrapper wrapper = this.getSignWrapper(sign, event); - boolean shift = event.getPlayer().isSneaking(); ItemStack heldItem = event.getItem(); + boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; + boolean sneaking = event.getPlayer().isSneaking(); + // Allow editing and breaking signs with tools + if (!onCraft && sneaking) { + if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType()) && (event.getAction().isRightClick())) { + return; + } - // TODO: If the item is a feather (right click) or a axe (left click) and shift is pressed, then we don't do movecraft stuff + if (Tags.SIGN_BYPASS_LEFT_CLICK.contains(heldItem.getType()) && (event.getAction().isLeftClick())) { + return; + } + } // Would be one more readable line if using Optionals but Nullables were wanted AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); if (ams != null) { boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); - if (ams.shouldCancelEvent(success, event.getAction(), event.getPlayer().isSneaking(), AbstractMovecraftSign.EventType.SIGN_CLICK)) { + if (ams.shouldCancelEvent(success, event.getAction(), sneaking, onCraft ? AbstractMovecraftSign.EventType.SIGN_CLICK_ON_CRAFT : AbstractMovecraftSign.EventType.SIGN_CLICK)) { event.setCancelled(true); } else if (MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null) { // Cancel interact in all cases when on a craft diff --git a/api/src/main/java/net/countercraft/movecraft/util/Tags.java b/api/src/main/java/net/countercraft/movecraft/util/Tags.java index 399ebb92a..e385afca7 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/Tags.java +++ b/api/src/main/java/net/countercraft/movecraft/util/Tags.java @@ -21,6 +21,9 @@ public class Tags { public static final EnumSet BUCKETS = EnumSet.of(Material.LAVA_BUCKET, Material.WATER_BUCKET, Material.MILK_BUCKET, Material.COD_BUCKET, Material.PUFFERFISH_BUCKET, Material.SALMON_BUCKET, Material.TROPICAL_FISH_BUCKET); public static final EnumSet WALL_TORCHES = EnumSet.of(Material.WALL_TORCH, Material.SOUL_WALL_TORCH, Material.REDSTONE_WALL_TORCH); public static final EnumSet LANTERNS = EnumSet.of(Material.LANTERN, Material.SOUL_LANTERN); + // TODO: Move to tags + public static final EnumSet SIGN_BYPASS_RIGHT_CLICK = EnumSet.of(Material.FEATHER); + public static final EnumSet SIGN_BYPASS_LEFT_CLICK = EnumSet.copyOf(Tag.ITEMS_AXES.getValues()); static { FRAGILE_MATERIALS.add(Material.PISTON_HEAD); From c68ff556b63fcf254dd7ee015dadfe6e37bad080 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:16:09 +0200 Subject: [PATCH 36/55] small tweaks regarding interactions (cherry picked from commit 6a09a703ac2003d70c725b96f8a5bf4ffab7cc77) --- .../movecraft/sign/AbstractSignListener.java | 7 +++++++ .../movecraft/sign/AbstractSubcraftSign.java | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index 2ad4d3d97..7ec87e50b 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -223,6 +223,13 @@ public void onSignChange(SignChangeEvent event) { AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); // Would be one more readable line if using Optionals but Nullables were wanted if (ams != null) { + if (!eventType.isOnCraft()) { + ItemStack heldItem = event.getPlayer().getActiveItem(); + if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType())) { + return; + } + } + boolean success = ams.processSignChange(event, wrapper); if (ams.shouldCancelEvent(success, null, event.getPlayer().isSneaking(), eventTypeTmp)) { event.setCancelled(true); diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 3203242c8..dbe3fa80e 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -164,6 +164,15 @@ protected CraftType getCraftType(AbstractSignListener.SignWrapper wrapper) { return this.craftTypeRetrievalFunction.apply(ident); } + @Override + public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { + boolean resultSuper = !super.shouldCancelEvent(processingSuccessful, type, sneaking, eventType); + if (!resultSuper) { + return eventType == EventType.SIGN_CLICK_ON_CRAFT || eventType == EventType.SIGN_CLICK; + } + return resultSuper; + } + protected abstract void runDetectTask(Action clickType, CraftType subcraftType, Craft parentCraft, World world, Player player, MovecraftLocation startPoint); protected abstract boolean isActionAllowed(final String action); protected abstract void onActionAlreadyInProgress(Player player); From e4ecbf96c7deb2374d769b45ab0e8a0c06735973 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:28:13 +0200 Subject: [PATCH 37/55] NPE prevention (cherry picked from commit 8e7f1bff11db72e136ac5a16e66690aa55892ec1) --- .../net/countercraft/movecraft/sign/AbstractSignListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index 7ec87e50b..b67c48966 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -251,7 +251,7 @@ public void onSignClick(PlayerInteractEvent event) { boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; boolean sneaking = event.getPlayer().isSneaking(); // Allow editing and breaking signs with tools - if (!onCraft && sneaking) { + if (heldItem != null && !onCraft && sneaking) { if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType()) && (event.getAction().isRightClick())) { return; } From 6ab1baa7ed7834527fda47a3627b1b17398c5137 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:14:52 +0200 Subject: [PATCH 38/55] respect edits with dyecolors, wax, etc (cherry picked from commit 08b4e6dcfb943e558632a1a2ce8631473370022e) --- .../movecraft/sign/AbstractSignListener.java | 25 +++++++++++++------ .../net/countercraft/movecraft/util/Tags.java | 22 ++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index b67c48966..d09d60708 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -7,6 +7,7 @@ import net.countercraft.movecraft.util.Tags; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; @@ -225,6 +226,11 @@ public void onSignChange(SignChangeEvent event) { if (ams != null) { if (!eventType.isOnCraft()) { ItemStack heldItem = event.getPlayer().getActiveItem(); + // Allow the usage of the colors and wax for things => Cleaner solution: Use NMS to check if the item is an instanceof of SignApplicator... + if (Tags.SIGN_EDIT_MATERIALS.contains(heldItem.getType())) { + return; + } + if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType())) { return; } @@ -251,13 +257,19 @@ public void onSignClick(PlayerInteractEvent event) { boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; boolean sneaking = event.getPlayer().isSneaking(); // Allow editing and breaking signs with tools - if (heldItem != null && !onCraft && sneaking) { - if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType()) && (event.getAction().isRightClick())) { + if (heldItem != null && !onCraft) { + // Allow the usage of the colors and wax for things => Cleaner solution: Use NMS to check if the item is an instanceof of SignApplicator... + if (Tags.SIGN_EDIT_MATERIALS.contains(heldItem.getType()) && event.getAction().isRightClick()) { return; } + if (sneaking) { + if (Tags.SIGN_BYPASS_RIGHT_CLICK.contains(heldItem.getType()) && (event.getAction().isRightClick())) { + return; + } - if (Tags.SIGN_BYPASS_LEFT_CLICK.contains(heldItem.getType()) && (event.getAction().isLeftClick())) { - return; + if (Tags.SIGN_BYPASS_LEFT_CLICK.contains(heldItem.getType()) && (event.getAction().isLeftClick())) { + return; + } } } @@ -265,11 +277,10 @@ public void onSignClick(PlayerInteractEvent event) { AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); if (ams != null) { boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); + // Always cancel, regardless of the success + event.setCancelled(true); if (ams.shouldCancelEvent(success, event.getAction(), sneaking, onCraft ? AbstractMovecraftSign.EventType.SIGN_CLICK_ON_CRAFT : AbstractMovecraftSign.EventType.SIGN_CLICK)) { event.setCancelled(true); - } else if (MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null) { - // Cancel interact in all cases when on a craft - event.setCancelled(true); } } } diff --git a/api/src/main/java/net/countercraft/movecraft/util/Tags.java b/api/src/main/java/net/countercraft/movecraft/util/Tags.java index e385afca7..15bd9a204 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/Tags.java +++ b/api/src/main/java/net/countercraft/movecraft/util/Tags.java @@ -24,6 +24,7 @@ public class Tags { // TODO: Move to tags public static final EnumSet SIGN_BYPASS_RIGHT_CLICK = EnumSet.of(Material.FEATHER); public static final EnumSet SIGN_BYPASS_LEFT_CLICK = EnumSet.copyOf(Tag.ITEMS_AXES.getValues()); + public static final EnumSet SIGN_EDIT_MATERIALS = EnumSet.copyOf(Tag.ITEMS_AXES.getValues()); static { FRAGILE_MATERIALS.add(Material.PISTON_HEAD); @@ -57,6 +58,27 @@ public class Tags { FALL_THROUGH_BLOCKS.add(Material.POTATO); FALL_THROUGH_BLOCKS.addAll(Tag.FENCES.getValues()); FALL_THROUGH_BLOCKS.addAll(FLUID); + + SIGN_EDIT_MATERIALS.add(Material.HONEYCOMB); + SIGN_EDIT_MATERIALS.add(Material.INK_SAC); + SIGN_EDIT_MATERIALS.add(Material.GLOW_INK_SAC); + + SIGN_EDIT_MATERIALS.add(Material.WHITE_DYE); + SIGN_EDIT_MATERIALS.add(Material.LIGHT_GRAY_DYE); + SIGN_EDIT_MATERIALS.add(Material.GRAY_DYE); + SIGN_EDIT_MATERIALS.add(Material.BLACK_DYE); + SIGN_EDIT_MATERIALS.add(Material.BROWN_DYE); + SIGN_EDIT_MATERIALS.add(Material.RED_DYE); + SIGN_EDIT_MATERIALS.add(Material.ORANGE_DYE); + SIGN_EDIT_MATERIALS.add(Material.YELLOW_DYE); + SIGN_EDIT_MATERIALS.add(Material.LIME_DYE); + SIGN_EDIT_MATERIALS.add(Material.GREEN_DYE); + SIGN_EDIT_MATERIALS.add(Material.CYAN_DYE); + SIGN_EDIT_MATERIALS.add(Material.LIGHT_BLUE_DYE); + SIGN_EDIT_MATERIALS.add(Material.BLUE_DYE); + SIGN_EDIT_MATERIALS.add(Material.PURPLE_DYE); + SIGN_EDIT_MATERIALS.add(Material.MAGENTA_DYE); + SIGN_EDIT_MATERIALS.add(Material.PINK_DYE); } @Nullable From 4a1f9857b52c83709a4643b371c033f44c95845b Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:23:00 +0200 Subject: [PATCH 39/55] extract base elements from Cruise sign into a toggle sign (cherry picked from commit e3a730c8ba65d1734b83b0764a201b6400b69d4e) --- .../movecraft/sign/AbstractCruiseSign.java | 130 ++-------------- .../movecraft/sign/AbstractToggleSign.java | 141 ++++++++++++++++++ 2 files changed, 155 insertions(+), 116 deletions(-) create mode 100644 api/src/main/java/net/countercraft/movecraft/sign/AbstractToggleSign.java diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index bf038f968..258048b3b 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -19,64 +19,14 @@ * Has the relevant logic for the "state" suffix (on / off) as well as calling the relevant methods and setting the craft to cruising * */ -public abstract class AbstractCruiseSign extends AbstractCraftSign { +public abstract class AbstractCruiseSign extends AbstractToggleSign { - private final String suffixOn; - private final String suffixOff; - private final String ident; - private final Component headerOn; - private final Component headerOff; - - public AbstractCruiseSign(boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { - this(null, ignoreCraftIsBusy, ident, suffixOn, suffixOff); + public AbstractCruiseSign(boolean ignoreCraftIsBusy, String ident, String suffixOn, String suffixOff) { + super(ignoreCraftIsBusy, ident, suffixOn, suffixOff); } - public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { - super(permission, ignoreCraftIsBusy); - this.suffixOn = suffixOn; - this.suffixOff = suffixOff; - this.ident = ident; - - this.headerOn = this.buildHeaderOn(); - this.headerOff = this.buildHeaderOff(); - } - - // Checks if the header is empty, if yes, it quits early (unnecessary actually as if it was empty this would never be called) - // Afterwards the header is validated, if it's splitted variant doesn't have exactly 2 entries it is invalid - // Finally, the "state" (second part of the header) isn't matching suffixOn or suffixOff, it is invalid - @Override - protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { - if (PlainTextComponentSerializer.plainText().serialize(sign.line(0)).isBlank()) { - return false; - } - String[] headerSplit = getSplitHeader(sign); - if (headerSplit.length != 2) { - return false; - } - String suffix = headerSplit[1].trim(); - return suffix.equalsIgnoreCase(this.suffixOff) || suffix.equalsIgnoreCase(this.suffixOn); - } - - // Returns the raw header, which should consist of the ident and either the suffixOn or suffixOff value - // Returns null if the header is blank - @Nullable - protected static String[] getSplitHeader(final AbstractSignListener.SignWrapper sign) { - String header = PlainTextComponentSerializer.plainText().serialize(sign.line(0)); - if (header.isBlank()) { - return null; - } - return header.split(":"); - } - - // If the suffix matches the suffixOn field it will returnt true - // calls getSplitHeader() to retrieve the raw header string - protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { - String[] headerSplit = getSplitHeader(sign); - if (headerSplit == null || headerSplit.length != 2) { - return false; - } - String suffix = headerSplit[1].trim(); - return suffix.equalsIgnoreCase(this.suffixOn); + public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, String ident, String suffixOn, String suffixOff) { + super(permission, ignoreCraftIsBusy, ident, suffixOn, suffixOff); } // Hook to do stuff that run after stopping to cruise @@ -89,77 +39,25 @@ protected void onAfterStartingCruise(Craft craft, AbstractSignListener.SignWrapp } - // Actual processing, determines wether the sign will switch to on or off - // If it will be on, the CruiseDirection is retrieved and then setCraftCruising() is called - // Otherwise, the craft will stop cruising - // Then the sign is updated and the block resetted - // Finally, the relevant hooks are called - // This always returns true @Override - protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { - boolean isOn = this.isOnOrOff(sign); - boolean willBeOn = !isOn; - if (willBeOn) { - CruiseDirection direction = this.getCruiseDirection(sign); - this.setCraftCruising(player, direction, craft); + protected void onAfterToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean toggledToOn) { + if (toggledToOn) { + this.onAfterStartingCruise(craft, signWrapper, player); } else { - craft.setCruising(false); + this.onAfterStoppingCruise(craft, signWrapper, player); } - - // Update sign - sign.line(0, buildHeader(willBeOn)); - sign.block().update(true); - // TODO: What to replace this with? - craft.resetSigns(sign.block()); - - if (willBeOn) { - this.onAfterStartingCruise(craft, sign, player); - } else { - this.onAfterStoppingCruise(craft, sign, player); - } - - return true; - } - - // On sign placement, if the entered header is the same as our ident, it will append the off-suffix automatically - @Override - public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { - String header = sign.getRaw(0).trim(); - if (header.equalsIgnoreCase(this.ident)) { - sign.line(0, buildHeaderOff()); - } - return true; } - // On craft detection, we set all the headers to the "off" header @Override - public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { - Player p = null; - if (event.getCraft() instanceof PilotedCraft pc) { - p = pc.getPilot(); - } - - if (this.isSignValid(Action.PHYSICAL, sign, p)) { - sign.line(0, buildHeader(false)); + protected void onBeforeToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean willBeOn) { + if (willBeOn) { + CruiseDirection cruiseDirection = this.getCruiseDirection(signWrapper); + this.setCraftCruising(player, cruiseDirection, craft); } else { - // TODO: Error? React in any way? - sign.line(0, buildHeader(false)); + craft.setCruising(false); } } - // Helper method to build the headline for on or off state - protected Component buildHeader(boolean on) { - return on ? this.headerOn : this.headerOff; - } - - protected Component buildHeaderOn() { - return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); - } - - protected Component buildHeaderOff() { - return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); - } - // Should call the craft's relevant methods to start cruising protected abstract void setCraftCruising(Player player, CruiseDirection direction, Craft craft); diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractToggleSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractToggleSign.java new file mode 100644 index 000000000..6ea302063 --- /dev/null +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractToggleSign.java @@ -0,0 +1,141 @@ +package net.countercraft.movecraft.sign; + +import net.countercraft.movecraft.CruiseDirection; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.PilotedCraft; +import net.countercraft.movecraft.events.CraftDetectEvent; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.Style; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import org.bukkit.entity.Player; +import org.bukkit.event.block.Action; +import org.bukkit.event.block.SignChangeEvent; +import org.jetbrains.annotations.Nullable; + +public abstract class AbstractToggleSign extends AbstractCraftSign { + + private final String suffixOn; + private final String suffixOff; + private final String ident; + private final Component headerOn; + private final Component headerOff; + + public AbstractToggleSign(boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { + this(null, ignoreCraftIsBusy, ident, suffixOn, suffixOff); + } + + public AbstractToggleSign(final String permission, boolean ignoreCraftIsBusy, final String ident, final String suffixOn, final String suffixOff) { + super(permission, ignoreCraftIsBusy); + this.suffixOn = suffixOn; + this.suffixOff = suffixOff; + this.ident = ident; + + this.headerOn = this.buildHeaderOn(); + this.headerOff = this.buildHeaderOff(); + } + + // Checks if the header is empty, if yes, it quits early (unnecessary actually as if it was empty this would never be called) + // Afterwards the header is validated, if it's splitted variant doesn't have exactly 2 entries it is invalid + // Finally, the "state" (second part of the header) isn't matching suffixOn or suffixOff, it is invalid + @Override + protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + if (PlainTextComponentSerializer.plainText().serialize(sign.line(0)).isBlank()) { + return false; + } + String[] headerSplit = getSplitHeader(sign); + if (headerSplit.length != 2) { + return false; + } + String suffix = headerSplit[1].trim(); + return suffix.equalsIgnoreCase(this.suffixOff) || suffix.equalsIgnoreCase(this.suffixOn); + } + + // Returns the raw header, which should consist of the ident and either the suffixOn or suffixOff value + // Returns null if the header is blank + @Nullable + protected static String[] getSplitHeader(final AbstractSignListener.SignWrapper sign) { + String header = PlainTextComponentSerializer.plainText().serialize(sign.line(0)); + if (header.isBlank()) { + return null; + } + return header.split(":"); + } + + // If the suffix matches the suffixOn field it will returnt true + // calls getSplitHeader() to retrieve the raw header string + protected boolean isOnOrOff(AbstractSignListener.SignWrapper sign) { + String[] headerSplit = getSplitHeader(sign); + if (headerSplit == null || headerSplit.length != 2) { + return false; + } + String suffix = headerSplit[1].trim(); + return suffix.equalsIgnoreCase(this.suffixOn); + } + + protected abstract void onAfterToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean toggledToOn); + protected abstract void onBeforeToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean willBeOn); + + // Actual processing, determines wether the sign will switch to on or off + // If it will be on, the CruiseDirection is retrieved and then setCraftCruising() is called + // Otherwise, the craft will stop cruising + // Then the sign is updated and the block resetted + // Finally, the relevant hooks are called + // This always returns true + @Override + protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { + boolean isOn = this.isOnOrOff(sign); + boolean willBeOn = !isOn; + + this.onBeforeToggle(craft, sign, player, willBeOn); + + // Update sign + sign.line(0, buildHeader(willBeOn)); + sign.block().update(true); + craft.resetSigns(sign.block()); + + this.onAfterToggle(craft, sign, player, willBeOn); + + return true; + } + + // On sign placement, if the entered header is the same as our ident, it will append the off-suffix automatically + @Override + public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + String header = sign.getRaw(0).trim(); + if (header.equalsIgnoreCase(this.ident)) { + sign.line(0, buildHeaderOff()); + } + return true; + } + + // On craft detection, we set all the headers to the "off" header + @Override + public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + Player p = null; + if (event.getCraft() instanceof PilotedCraft pc) { + p = pc.getPilot(); + } + + if (this.isSignValid(Action.PHYSICAL, sign, p)) { + sign.line(0, buildHeader(false)); + } else { + // TODO: Error? React in any way? + sign.line(0, buildHeader(false)); + } + } + + // Helper method to build the headline for on or off state + protected Component buildHeader(boolean on) { + return on ? this.headerOn : this.headerOff; + } + + protected Component buildHeaderOn() { + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOn, Style.style(TextColor.color(0, 255, 0)))); + } + + protected Component buildHeaderOff() { + return Component.text(this.ident).append(this.ident.endsWith(":") ? Component.text(" ") : Component.text(": ")).append(Component.text(this.suffixOff, Style.style(TextColor.color(255, 0, 0)))); + } + +} From b697375794d440ac140276e03bac2b7f908f43f6 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:47:44 +0200 Subject: [PATCH 40/55] update signs after detect (cherry picked from commit a426bcfe41179780c0901f7d29b500d3b1255c9e) --- .../net/countercraft/movecraft/sign/AbstractInformationSign.java | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java index 801f59082..896e341d4 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -48,6 +48,7 @@ public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapp // TODO: Check if the craft supports this sign? If no, cancel super.onCraftDetect(event, sign); this.refreshSign(event.getCraft(), sign, true, REFRESH_CAUSE.CRAFT_DETECT); + sign.block().update(); } @Override From b4f90329e35430e0955538a122153129957b8f28 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:07:35 +0200 Subject: [PATCH 41/55] cancel sign clicks on crafts in all cases (cherry picked from commit 869cb5a35517d005862169e03914d8a75d27faf3) --- .../countercraft/movecraft/sign/AbstractSignListener.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index d09d60708..09a84d947 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -255,6 +255,12 @@ public void onSignClick(PlayerInteractEvent event) { SignWrapper wrapper = this.getSignWrapper(sign, event); ItemStack heldItem = event.getItem(); boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; + + // Always cancel if on craft => Avoid clicking empty sides and entering edit mode + if (onCraft) { + event.setCancelled(true); + } + boolean sneaking = event.getPlayer().isSneaking(); // Allow editing and breaking signs with tools if (heldItem != null && !onCraft) { From 35ea71e8f518c1a96ec9bda1d6c4d2555bf29b1a Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:19:47 +0200 Subject: [PATCH 42/55] only cancel when sign is empty (cherry picked from commit c345a2541822c905bcbd856e0564a075940d9620) --- .../net/countercraft/movecraft/sign/AbstractSignListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java index 09a84d947..4852dac60 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSignListener.java @@ -257,7 +257,7 @@ public void onSignClick(PlayerInteractEvent event) { boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; // Always cancel if on craft => Avoid clicking empty sides and entering edit mode - if (onCraft) { + if (onCraft && wrapper.isEmpty()) { event.setCancelled(true); } From 9653b434a7e3945e1d6866f692f77a47de94ffec Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sat, 14 Sep 2024 00:02:26 +0200 Subject: [PATCH 43/55] fix override param (cherry picked from commit 834794f9659e264c6433d86250ee05e637780350) --- .../net/countercraft/movecraft/util/SimpleRegistry.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java b/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java index bddee0e9d..04f2548c9 100644 --- a/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java +++ b/api/src/main/java/net/countercraft/movecraft/util/SimpleRegistry.java @@ -19,11 +19,11 @@ public SimpleRegistry(){ } public @NotNull T register(final @NotNull K key, final @NotNull T value, boolean override) throws IllegalArgumentException { - T previous = _register.putIfAbsent(key, value); - if(previous != null && !override){ + T previous = _register.put(key, value); + if (previous != null && !override) { + _register.put(key, value); throw new IllegalArgumentException(String.format("Key %s is already registered.", key)); } - return value; } From 78d19f33109c7c8a5a604e86e50038806c35d51d Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sun, 15 Sep 2024 14:51:04 +0200 Subject: [PATCH 44/55] fix subcraft sign creation (cherry picked from commit 2454f78c087a5dd58869ca1e0fbea65e70d1a5ba) --- .../net/countercraft/movecraft/sign/AbstractSubcraftSign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index dbe3fa80e..15a1d5bb1 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -166,7 +166,7 @@ protected CraftType getCraftType(AbstractSignListener.SignWrapper wrapper) { @Override public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action type, boolean sneaking, EventType eventType) { - boolean resultSuper = !super.shouldCancelEvent(processingSuccessful, type, sneaking, eventType); + boolean resultSuper = super.shouldCancelEvent(processingSuccessful, type, sneaking, eventType); if (!resultSuper) { return eventType == EventType.SIGN_CLICK_ON_CRAFT || eventType == EventType.SIGN_CLICK; } From 889a5cf4f862233a366b181692a20024ab59dc06 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:34:13 +0200 Subject: [PATCH 45/55] Remote version specific SignListener implementations --- .../net/countercraft/movecraft/Movecraft.java | 11 +- .../movecraft/events/SignTranslateEvent.java | 17 +- .../movecraft/sign/AbstractCraftSign.java | 22 +- .../movecraft/sign/AbstractCruiseSign.java | 19 +- .../sign/AbstractInformationSign.java | 36 +-- .../movecraft/sign/AbstractMovecraftSign.java | 13 +- .../movecraft/sign/AbstractSubcraftSign.java | 18 +- .../movecraft/sign/AbstractToggleSign.java | 16 +- ...actSignListener.java => SignListener.java} | 182 +++++++++++++-- .../movecraft/compat/v1_20/SignListener.java | 209 ------------------ .../movecraft/compat/v1_21/SignListener.java | 209 ------------------ 11 files changed, 240 insertions(+), 512 deletions(-) rename api/src/main/java/net/countercraft/movecraft/sign/{AbstractSignListener.java => SignListener.java} (61%) delete mode 100644 v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java delete mode 100644 v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index 40f179deb..179e6b6ee 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -23,7 +23,6 @@ import net.countercraft.movecraft.config.Settings; import net.countercraft.movecraft.craft.ChunkManager; import net.countercraft.movecraft.craft.CraftManager; -import net.countercraft.movecraft.craft.datatag.CraftDataTagRegistry; import net.countercraft.movecraft.features.contacts.ContactsCommand; import net.countercraft.movecraft.features.contacts.ContactsManager; import net.countercraft.movecraft.features.contacts.ContactsSign; @@ -133,13 +132,6 @@ public void onEnable() { getLogger().warning("Falling back to bukkit teleportation provider."); } } - - // Create instance of sign listener - final Class signListenerClass = Class.forName("net.countercraft.movecraft.compat." + WorldHandler.getPackageName(minecraftVersion) + ".SignListener"); - if (AbstractSignListener.class.isAssignableFrom(signListenerClass)) { - abstractSignListener = (AbstractSignListener) signListenerClass.getConstructor().newInstance(); - getServer().getPluginManager().registerEvents(abstractSignListener, this); - } } catch (final Exception e) { e.printStackTrace(); @@ -236,6 +228,7 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new CraftPilotListener(), this); getServer().getPluginManager().registerEvents(new CraftReleaseListener(), this); getServer().getPluginManager().registerEvents(new CraftTypeListener(), this); + getServer().getPluginManager().registerEvents(new SignListener(), this); // Signs AbstractMovecraftSign.register("Release", new ReleaseSign()); @@ -351,7 +344,7 @@ public AsyncManager getAsyncManager() { return wreckManager; } - public AbstractSignListener getAbstractSignListener() { + public SignListener getAbstractSignListener() { return abstractSignListener; } } diff --git a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java index 38ab5c040..75a10abff 100644 --- a/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java +++ b/api/src/main/java/net/countercraft/movecraft/events/SignTranslateEvent.java @@ -2,7 +2,7 @@ import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.sign.AbstractSignListener; +import net.countercraft.movecraft.sign.SignListener; import net.kyori.adventure.text.Component; import org.bukkit.block.BlockFace; import org.bukkit.event.HandlerList; @@ -12,11 +12,14 @@ import java.util.Collections; import java.util.List; -// TODO: Rewrite to use the adventure API +/** + * Obsolete, functionality is covered by the new sign system + */ +@Deprecated(forRemoval = true) public class SignTranslateEvent extends CraftEvent{ private static final HandlerList HANDLERS = new HandlerList(); @NotNull private final List locations; - @NotNull private final AbstractSignListener.SignWrapper backing; + @NotNull private final SignListener.SignWrapper backing; private boolean updated = false; @Deprecated(forRemoval = true) @@ -27,15 +30,19 @@ public SignTranslateEvent(@NotNull Craft craft, @NotNull String[] lines, @NotNul for (String s : lines) { components.add(Component.text(s)); } - this.backing = new AbstractSignListener.SignWrapper(null, components::get, components, components::set, BlockFace.SELF); + this.backing = new SignListener.SignWrapper(null, components::get, components, components::set, BlockFace.SELF); } - public SignTranslateEvent(@NotNull Craft craft, @NotNull AbstractSignListener.SignWrapper backing, @NotNull List locations) throws IndexOutOfBoundsException{ + public SignTranslateEvent(@NotNull Craft craft, @NotNull SignListener.SignWrapper backing, @NotNull List locations) throws IndexOutOfBoundsException{ super(craft); this.locations = locations; this.backing = backing; } + public @NotNull SignListener.SignWrapper getBacking() { + return this.backing; + } + @NotNull @Deprecated(forRemoval = true) public String[] getLines() { diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java index 2c73c8962..2dbfde88d 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftSign.java @@ -1,17 +1,17 @@ package net.countercraft.movecraft.sign; +import net.countercraft.movecraft.MovecraftLocation; import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.PilotedCraft; import net.countercraft.movecraft.craft.PlayerCraft; import net.countercraft.movecraft.events.CraftDetectEvent; import net.countercraft.movecraft.events.SignTranslateEvent; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; import javax.annotation.Nullable; -import java.util.Optional; +import java.util.List; /* * Extension of @AbstractMovecraftSign @@ -51,7 +51,7 @@ public AbstractCraftSign(final String permission, boolean ignoreCraftIsBusy) { // If no craft is found, onCraftNotFound() is called // Return true to cancel the event @Override - public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + public boolean processSignClick(Action clickType, SignListener.SignWrapper sign, Player player) { if (!this.isSignValid(clickType, sign, player)) { return false; } @@ -78,7 +78,7 @@ public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapp // The craft instance is required here and it's existance is being confirmed in processSignClick() in beforehand // After that, canPlayerUseSignOn() is being called. If that is successful, the result of internalProcessSignWithCraft() is returned @Override - protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft) { + protected boolean internalProcessSign(Action clickType, SignListener.SignWrapper sign, Player player, @Nullable Craft craft) { if (craft == null) { throw new IllegalStateException("Somehow craft is not set here. It should always be present here!"); } @@ -101,19 +101,25 @@ protected boolean canPlayerUseSignOn(Player player, @Nullable Craft craft) { } // Called when there is no craft instance for this sign - protected abstract void onCraftNotFound(Player player, AbstractSignListener.SignWrapper sign); + protected abstract void onCraftNotFound(Player player, SignListener.SignWrapper sign); // By default we don't react to CraftDetectEvent here - public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + public void onCraftDetect(CraftDetectEvent event, SignListener.SignWrapper sign) { // Do nothing by default } + // Return true if you modified anything + public boolean processSignTranslation(final Craft translatingCraft, SignListener.SignWrapper movingData, @Nullable List signLocations) { + // DO nothing by default + return false; + } + public void onSignMovedByCraft(SignTranslateEvent event) { - // Do nothing by default + this.processSignTranslation(event.getCraft(), event.getBacking(), event.getLocations()); } // Gets called by internalProcessSign if a craft is found // Always override this as the validation has been made already when this is being called - protected abstract boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player); + protected abstract boolean internalProcessSignWithCraft(Action clickType, SignListener.SignWrapper sign, Craft craft, Player player); } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java index 258048b3b..24df56341 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCruiseSign.java @@ -2,16 +2,7 @@ import net.countercraft.movecraft.CruiseDirection; import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.craft.PilotedCraft; -import net.countercraft.movecraft.events.CraftDetectEvent; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.Style; -import net.kyori.adventure.text.format.TextColor; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.entity.Player; -import org.bukkit.event.block.Action; -import org.bukkit.event.block.SignChangeEvent; -import org.jetbrains.annotations.Nullable; /* * Base class for all cruise signs @@ -30,17 +21,17 @@ public AbstractCruiseSign(final String permission, boolean ignoreCraftIsBusy, St } // Hook to do stuff that run after stopping to cruise - protected void onAfterStoppingCruise(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player) { + protected void onAfterStoppingCruise(Craft craft, SignListener.SignWrapper signWrapper, Player player) { } // Hook to do stuff that run after starting to cruise - protected void onAfterStartingCruise(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player) { + protected void onAfterStartingCruise(Craft craft, SignListener.SignWrapper signWrapper, Player player) { } @Override - protected void onAfterToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean toggledToOn) { + protected void onAfterToggle(Craft craft, SignListener.SignWrapper signWrapper, Player player, boolean toggledToOn) { if (toggledToOn) { this.onAfterStartingCruise(craft, signWrapper, player); } else { @@ -49,7 +40,7 @@ protected void onAfterToggle(Craft craft, AbstractSignListener.SignWrapper signW } @Override - protected void onBeforeToggle(Craft craft, AbstractSignListener.SignWrapper signWrapper, Player player, boolean willBeOn) { + protected void onBeforeToggle(Craft craft, SignListener.SignWrapper signWrapper, Player player, boolean willBeOn) { if (willBeOn) { CruiseDirection cruiseDirection = this.getCruiseDirection(signWrapper); this.setCraftCruising(player, cruiseDirection, craft); @@ -63,5 +54,5 @@ protected void onBeforeToggle(Craft craft, AbstractSignListener.SignWrapper sign // TODO: Rework cruise direction to vectors => Vector defines the skip distance and the direction // Returns the direction in which the craft should cruise - protected abstract CruiseDirection getCruiseDirection(AbstractSignListener.SignWrapper sign); + protected abstract CruiseDirection getCruiseDirection(SignListener.SignWrapper sign); } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java index 896e341d4..f3c97ac31 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractInformationSign.java @@ -16,6 +16,8 @@ import org.bukkit.event.block.SignChangeEvent; import org.jetbrains.annotations.Nullable; +import java.util.List; + public abstract class AbstractInformationSign extends AbstractCraftSign { public static final Component EMPTY = Component.text(""); @@ -44,7 +46,7 @@ protected boolean canPlayerUseSignOn(Player player, Craft craft) { } @Override - public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapper sign) { + public void onCraftDetect(CraftDetectEvent event, SignListener.SignWrapper sign) { // TODO: Check if the craft supports this sign? If no, cancel super.onCraftDetect(event, sign); this.refreshSign(event.getCraft(), sign, true, REFRESH_CAUSE.CRAFT_DETECT); @@ -52,33 +54,33 @@ public void onCraftDetect(CraftDetectEvent event, AbstractSignListener.SignWrapp } @Override - public void onSignMovedByCraft(SignTranslateEvent event) { - super.onSignMovedByCraft(event); - final Craft craft = event.getCraft(); - AbstractSignListener.SignWrapper wrapperTmp = new AbstractSignListener.SignWrapper(null, event::line, event.lines(), event::line, BlockFace.SELF); - if (this.refreshSign(craft, wrapperTmp, false, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT)) { - for (MovecraftLocation movecraftLocation : event.getLocations()) { - Block block = movecraftLocation.toBukkit(craft.getWorld()).getBlock(); + public boolean processSignTranslation(Craft translatingCraft, SignListener.SignWrapper movingData, @Nullable List signLocations) { + //SignListener.SignWrapper wrapperTmp = new SignListener.SignWrapper(null, movingData::line, movingData.lines(), movingData::line, BlockFace.SELF); + if (this.refreshSign(translatingCraft, movingData, false, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT)) { + for (MovecraftLocation movecraftLocation : signLocations) { + Block block = movecraftLocation.toBukkit(translatingCraft.getWorld()).getBlock(); if (block instanceof Sign sign) { - AbstractSignListener.SignWrapper wrapperTmpTmp = new AbstractSignListener.SignWrapper(sign, event::line, event.lines(), event::line, event.facing()); - this.sendUpdatePacket(craft, wrapperTmpTmp, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT); + SignListener.SignWrapper wrapperTmpTmp = new SignListener.SignWrapper(sign, movingData::line, movingData.lines(), movingData::line, movingData.facing()); + this.sendUpdatePacket(translatingCraft, wrapperTmpTmp, REFRESH_CAUSE.SIGN_MOVED_BY_CRAFT); } } } + // We looped over the lines, so we HAVE to return true here + return true; } @Override - protected void onCraftNotFound(Player player, AbstractSignListener.SignWrapper sign) { + protected void onCraftNotFound(Player player, SignListener.SignWrapper sign) { // Nothing to do } @Override - protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + protected boolean isSignValid(Action clickType, SignListener.SignWrapper sign, Player player) { return true; } @Override - protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignListener.SignWrapper sign, Craft craft, Player player) { + protected boolean internalProcessSignWithCraft(Action clickType, SignListener.SignWrapper sign, Craft craft, Player player) { if (this.refreshSign(craft, sign, false, REFRESH_CAUSE.SIGN_CLICK)) { this.sendUpdatePacket(craft, sign, REFRESH_CAUSE.SIGN_CLICK); } @@ -91,7 +93,7 @@ protected boolean internalProcessSignWithCraft(Action clickType, AbstractSignLis // If nothing has changed, no update happens // If something has changed, performUpdate() and sendUpdatePacket() are called // Returns wether or not something has changed - protected boolean refreshSign(@Nullable Craft craft, AbstractSignListener.SignWrapper sign, boolean fillDefault, REFRESH_CAUSE refreshCause) { + protected boolean refreshSign(@Nullable Craft craft, SignListener.SignWrapper sign, boolean fillDefault, REFRESH_CAUSE refreshCause) { boolean changedSome = false; Component[] updatePayload = new Component[sign.lines().size()]; for(int i = 1; i < sign.lines().size(); i++) { @@ -118,7 +120,7 @@ protected boolean refreshSign(@Nullable Craft craft, AbstractSignListener.SignWr } @Override - public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + public boolean processSignChange(SignChangeEvent event, SignListener.SignWrapper sign) { if (this.refreshSign(null, sign, true, REFRESH_CAUSE.SIGN_CREATION)) { this.sendUpdatePacket(null, sign, REFRESH_CAUSE.SIGN_CREATION); } @@ -142,12 +144,12 @@ public boolean processSignChange(SignChangeEvent event, AbstractSignListener.Sig * * Only gets called if at least one line has changed */ - protected abstract void performUpdate(Component[] newComponents, AbstractSignListener.SignWrapper sign, REFRESH_CAUSE refreshCause); + protected abstract void performUpdate(Component[] newComponents, SignListener.SignWrapper sign, REFRESH_CAUSE refreshCause); /* Gets called after performUpdate has been called */ - protected void sendUpdatePacket(Craft craft, AbstractSignListener.SignWrapper sign, REFRESH_CAUSE refreshCause) { + protected void sendUpdatePacket(Craft craft, SignListener.SignWrapper sign, REFRESH_CAUSE refreshCause) { if (sign.block() == null) { return; } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java index c26648716..d643fa7e8 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractMovecraftSign.java @@ -4,7 +4,6 @@ import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.util.MathUtils; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; import org.bukkit.event.block.SignChangeEvent; @@ -105,7 +104,7 @@ public static String findIdent(AbstractMovecraftSign instance) { // Called whenever a player clicks the sign // SignWrapper wraps the relevant clicked side of the sign and the sign block itself // If true is returned, the event will be cancelled - public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + public boolean processSignClick(Action clickType, SignListener.SignWrapper sign, Player player) { if (!this.isSignValid(clickType, sign, player)) { return false; } @@ -118,7 +117,7 @@ public boolean processSignClick(Action clickType, AbstractSignListener.SignWrapp // Validation method // By default this checks if the player has the set permission - protected boolean canPlayerUseSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + protected boolean canPlayerUseSign(Action clickType, SignListener.SignWrapper sign, Player player) { if (this.permissionString == null || this.permissionString.isBlank()) { return true; } @@ -127,7 +126,7 @@ protected boolean canPlayerUseSign(Action clickType, AbstractSignListener.SignWr // Helper method, simply calls the existing methods @Nullable - protected Craft getCraft(AbstractSignListener.SignWrapper sign) { + protected Craft getCraft(SignListener.SignWrapper sign) { return MathUtils.getCraftByPersistentBlockData(sign.block().getLocation()); } @@ -168,14 +167,14 @@ public boolean shouldCancelEvent(boolean processingSuccessful, @Nullable Action // Validation method, called by default in processSignClick // If false is returned, nothing will be processed - protected abstract boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player); + protected abstract boolean isSignValid(Action clickType, SignListener.SignWrapper sign, Player player); // Called by processSignClick after validation. At this point, isSignValid() and canPlayerUseSign() have been called already // If the sign belongs to a craft, that craft is given in the @param craft argument // Return true, if everything was ok - protected abstract boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft); + protected abstract boolean internalProcessSign(Action clickType, SignListener.SignWrapper sign, Player player, @Nullable Craft craft); // Called by the event handler when SignChangeEvent is being cought // Return true, if everything was ok - public abstract boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign); + public abstract boolean processSignChange(SignChangeEvent event, SignListener.SignWrapper sign); } diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 15a1d5bb1..827defb37 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -40,7 +40,7 @@ public AbstractSubcraftSign(final String permission, Function getLine, @@ -170,14 +173,159 @@ public void copyContent(Function retrievalFunction, Function public SignWrapper[] getSignWrappers(Sign sign) { return getSignWrappers(sign, false); }; - public abstract SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty); - protected abstract SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent); + protected SignWrapper getSignWrapper(Sign sign, PlayerInteractEvent interactEvent) { return this.getSignWrapper(sign, interactEvent.getPlayer()); } - protected abstract SignWrapper getSignWrapper(Sign sign, Player player); - public abstract void processSignTranslation(final Craft craft, boolean checkEventIsUpdated); + protected final SignWrapper createFromSide(final Sign sign, final Side side) { + SignSide signSide = sign.getSide(side); + return createFromSide(sign, signSide, side); + } + + protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signSide::line, + signSide.lines(), + signSide::line, + face + ); + return wrapper; + } + + public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { + List wrappers = new ArrayList<>(); + for (Side side : Side.values()) { + SignSide signSide = sign.getSide(side); + SignWrapper wrapper = this.createFromSide(sign, signSide, side); + wrappers.add(wrapper); + } + if (ignoreEmpty) + wrappers.removeIf(SignWrapper::isEmpty); + return wrappers.toArray(new SignWrapper[wrappers.size()]); + } + + protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { + @NotNull Side side = signChangeEvent.getSide(); + + BlockData blockData = sign.getBlock().getBlockData(); + BlockFace face; + if (blockData instanceof Directional directional) { + face = directional.getFacing(); + } else if (blockData instanceof Rotatable rotatable) { + face = rotatable.getRotation(); + } + else { + face = BlockFace.SELF; + } + + if (side == Side.BACK) { + face = face.getOppositeFace(); + } + SignWrapper wrapper = new SignWrapper( + sign, + signChangeEvent::line, + signChangeEvent.lines(), + signChangeEvent::line, + face + ); + return wrapper; + } + + protected SignWrapper getSignWrapper(Sign sign, Player player) { + @NotNull SignSide side = sign.getTargetSide(player); + return this.createFromSide(sign, side, sign.getInteractableSideFor(player)); + } + + public void processSignTranslation(final Craft craft, boolean checkEventIsUpdated) { + // Ignore facing value here and directly store the associated wrappers in the list + Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { + @Override + public int hashCode(SignWrapper strings) { + return Arrays.hashCode(strings.rawLines()); + } + + @Override + public boolean equals(SignWrapper a, SignWrapper b) { + return SignWrapper.areSignsEqualIgnoreFace(a, b); + } + }); + // Remember the locations for the event! + Map> wrapperToLocs = new HashMap<>(); + + for (MovecraftLocation location : craft.getHitBox()) { + Block block = location.toBukkit(craft.getWorld()).getBlock(); + if(!Tag.SIGNS.isTagged(block.getType())){ + continue; + } + BlockState state = block.getState(); + if (state instanceof Sign) { + Sign sign = (Sign) state; + SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); + if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { + continue; + } + for (SignWrapper wrapper : wrappersAtLoc) { + List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); + values.add(wrapper); + wrapperToLocs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()).add(location); + } + } + } + Set signsToUpdate = new HashSet<>(); + for(Map.Entry> entry : signs.entrySet()){ + final List components = new ArrayList<>(entry.getKey().lines()); + SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); + // if(!event.isUpdated()){ + // continue; + // } + // TODO: This is implemented only to fix client caching + // ideally we wouldn't do the update and would instead fake it out to the player + + /*System.out.println("New lines: "); + for (String s : event.rawLines()) { + System.out.println(" - " + s); + } + System.out.println("Old lines: "); + for (String s : entry.getKey().rawLines()) { + System.out.println(" - " + s); + }*/ + AbstractCraftSign acs = AbstractCraftSign.getCraftSign(backingForEvent.line(0)); + if (acs != null) { + if (acs.processSignTranslation(craft, backingForEvent, wrapperToLocs.get(entry.getKey()))) { + // Values get changed definitely, but perhaps it does not get applied to the sign after all? + for(SignWrapper wrapperTmp : entry.getValue()){ + if (!checkEventIsUpdated) { + wrapperTmp.copyContent(backingForEvent::line, (i) -> i < backingForEvent.lines().size()); + if (wrapperTmp.block() != null) { + signsToUpdate.add(wrapperTmp.block()); + } + } + } + } + } + + } + + for (Sign sign : signsToUpdate) { + sign.update(false, false); + } + } @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void onCraftDetect(CraftDetectEvent event) { diff --git a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java b/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java deleted file mode 100644 index c10007800..000000000 --- a/v1_20/src/main/java/net/countercraft/movecraft/compat/v1_20/SignListener.java +++ /dev/null @@ -1,209 +0,0 @@ -package net.countercraft.movecraft.compat.v1_20; - -import it.unimi.dsi.fastutil.Hash; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import net.countercraft.movecraft.MovecraftLocation; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.events.SignTranslateEvent; -import net.countercraft.movecraft.sign.AbstractSignListener; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.Tag; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Rotatable; -import org.bukkit.block.sign.Side; -import org.bukkit.block.sign.SignSide; -import org.bukkit.entity.Player; -import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.jetbrains.annotations.NotNull; - -import java.util.*; - -/* - * As soon as 1.18 support is dropped, the adapter system will be dropped too - */ -@Deprecated(forRemoval = true) -public class SignListener extends AbstractSignListener { - - protected final SignWrapper createFromSide(final Sign sign, final Side side) { - SignSide signSide = sign.getSide(side); - return createFromSide(sign, signSide, side); - } - - protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - - if (side == Side.BACK) { - face = face.getOppositeFace(); - } - SignWrapper wrapper = new SignWrapper( - sign, - signSide::line, - signSide.lines(), - signSide::line, - face - ); - return wrapper; - } - - @Override - public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { - List wrappers = new ArrayList<>(); - for (Side side : Side.values()) { - SignSide signSide = sign.getSide(side); - SignWrapper wrapper = this.createFromSide(sign, signSide, side); - wrappers.add(wrapper); - } - if (ignoreEmpty) - wrappers.removeIf(SignWrapper::isEmpty); - return wrappers.toArray(new SignWrapper[wrappers.size()]); - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { - @NotNull Side side = signChangeEvent.getSide(); - - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - - if (side == Side.BACK) { - face = face.getOppositeFace(); - } - SignWrapper wrapper = new SignWrapper( - sign, - signChangeEvent::line, - signChangeEvent.lines(), - signChangeEvent::line, - face - ); - return wrapper; - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, Player player) { - @NotNull SignSide side = sign.getTargetSide(player); - return this.createFromSide(sign, side, sign.getInteractableSideFor(player)); - } - - @Override - public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { - // Ignore facing value here and directly store the associated wrappers in the list - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(SignWrapper strings) { - return Arrays.hashCode(strings.rawLines()); - } - - @Override - public boolean equals(SignWrapper a, SignWrapper b) { - return SignWrapper.areSignsEqualIgnoreFace(a, b); - } - }); - // Remember the locations for the event! - Map> wrapperToLocs = new HashMap<>(); - - for (MovecraftLocation location : craft.getHitBox()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - if(!Tag.SIGNS.isTagged(block.getType())){ - continue; - } - BlockState state = block.getState(); - if (state instanceof Sign) { - Sign sign = (Sign) state; - SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); - if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { - continue; - } - for (SignWrapper wrapper : wrappersAtLoc) { - List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); - values.add(wrapper); - wrapperToLocs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()).add(location); - } - } - } - Set signsToUpdate = new HashSet<>(); - for(Map.Entry> entry : signs.entrySet()){ - final List components = new ArrayList<>(entry.getKey().lines()); - SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); - SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, wrapperToLocs.getOrDefault(entry.getKey(), new ArrayList<>())); - Bukkit.getServer().getPluginManager().callEvent(event); - // if(!event.isUpdated()){ - // continue; - // } - // TODO: This is implemented only to fix client caching - // ideally we wouldn't do the update and would instead fake it out to the player - - /*System.out.println("New lines: "); - for (String s : event.rawLines()) { - System.out.println(" - " + s); - } - System.out.println("Old lines: "); - for (String s : entry.getKey().rawLines()) { - System.out.println(" - " + s); - }*/ - // Values get changed definitely, but perhaps it does not get applied to the sign after all? - for(SignWrapper wrapperTmp : entry.getValue()){ - /*Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - SignWrapper[] signsAtLoc = signStates.get(location); - if (signsAtLoc != null && signsAtLoc.length > 0) { - boolean hadCorrectSide = false; - for (SignWrapper sw : signsAtLoc) { - // Important: Check if the wrapper faces the right way! - if (!sw.facing().equals(event.facing())) { - continue; - } - hadCorrectSide = true; - if (!checkEventIsUpdated || event.isUpdated()) { - sw.copyContent(event::line, (i) -> i < event.lines().size()); - } - } - if (hadCorrectSide) { - try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); - } catch(ClassCastException ex) { - // Ignore - } - } - }*/ - if (!checkEventIsUpdated || event.isUpdated()) { - wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); - if (wrapperTmp.block() != null) { - signsToUpdate.add(wrapperTmp.block()); - } - } - } - } - - for (Sign sign : signsToUpdate) { - sign.update(false, false); - } - } -} diff --git a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java b/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java deleted file mode 100644 index 1c626b547..000000000 --- a/v1_21/src/main/java/net/countercraft/movecraft/compat/v1_21/SignListener.java +++ /dev/null @@ -1,209 +0,0 @@ -package net.countercraft.movecraft.compat.v1_21; - -import it.unimi.dsi.fastutil.Hash; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap; -import net.countercraft.movecraft.MovecraftLocation; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.events.SignTranslateEvent; -import net.countercraft.movecraft.sign.AbstractSignListener; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.Tag; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.block.BlockState; -import org.bukkit.block.Sign; -import org.bukkit.block.data.BlockData; -import org.bukkit.block.data.Directional; -import org.bukkit.block.data.Rotatable; -import org.bukkit.block.sign.Side; -import org.bukkit.block.sign.SignSide; -import org.bukkit.entity.Player; -import org.bukkit.event.block.SignChangeEvent; -import org.bukkit.event.player.PlayerInteractEvent; -import org.jetbrains.annotations.NotNull; - -import java.util.*; - -/* - * As soon as 1.18 support is dropped, the adapter system will be dropped too - */ -@Deprecated(forRemoval = true) -public class SignListener extends AbstractSignListener { - - protected final SignWrapper createFromSide(final Sign sign, final Side side) { - SignSide signSide = sign.getSide(side); - return createFromSide(sign, signSide, side); - } - - protected final SignWrapper createFromSide(final Sign sign, final SignSide signSide, Side side) { - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - - if (side == Side.BACK) { - face = face.getOppositeFace(); - } - SignWrapper wrapper = new SignWrapper( - sign, - signSide::line, - signSide.lines(), - signSide::line, - face - ); - return wrapper; - } - - @Override - public SignWrapper[] getSignWrappers(Sign sign, boolean ignoreEmpty) { - List wrappers = new ArrayList<>(); - for (Side side : Side.values()) { - SignSide signSide = sign.getSide(side); - SignWrapper wrapper = this.createFromSide(sign, signSide, side); - wrappers.add(wrapper); - } - if (ignoreEmpty) - wrappers.removeIf(SignWrapper::isEmpty); - return wrappers.toArray(new SignWrapper[wrappers.size()]); - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, SignChangeEvent signChangeEvent) { - @NotNull Side side = signChangeEvent.getSide(); - - BlockData blockData = sign.getBlock().getBlockData(); - BlockFace face; - if (blockData instanceof Directional directional) { - face = directional.getFacing(); - } else if (blockData instanceof Rotatable rotatable) { - face = rotatable.getRotation(); - } - else { - face = BlockFace.SELF; - } - - if (side == Side.BACK) { - face = face.getOppositeFace(); - } - SignWrapper wrapper = new SignWrapper( - sign, - signChangeEvent::line, - signChangeEvent.lines(), - signChangeEvent::line, - face - ); - return wrapper; - } - - @Override - protected SignWrapper getSignWrapper(Sign sign, Player player) { - @NotNull SignSide side = sign.getTargetSide(player); - return this.createFromSide(sign, side, sign.getInteractableSideFor(player)); - } - - @Override - public void processSignTranslation(Craft craft, boolean checkEventIsUpdated) { - // Ignore facing value here and directly store the associated wrappers in the list - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(SignWrapper strings) { - return Arrays.hashCode(strings.rawLines()); - } - - @Override - public boolean equals(SignWrapper a, SignWrapper b) { - return SignWrapper.areSignsEqualIgnoreFace(a, b); - } - }); - // Remember the locations for the event! - Map> wrapperToLocs = new HashMap<>(); - - for (MovecraftLocation location : craft.getHitBox()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - if(!Tag.SIGNS.isTagged(block.getType())){ - continue; - } - BlockState state = block.getState(); - if (state instanceof Sign) { - Sign sign = (Sign) state; - SignWrapper[] wrappersAtLoc = this.getSignWrappers(sign, true); - if (wrappersAtLoc == null || wrappersAtLoc.length == 0) { - continue; - } - for (SignWrapper wrapper : wrappersAtLoc) { - List values = signs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()); - values.add(wrapper); - wrapperToLocs.computeIfAbsent(wrapper, (w) -> new ArrayList<>()).add(location); - } - } - } - Set signsToUpdate = new HashSet<>(); - for(Map.Entry> entry : signs.entrySet()){ - final List components = new ArrayList<>(entry.getKey().lines()); - SignWrapper backingForEvent = new SignWrapper(null, components::get, components, components::set, entry.getKey().facing()); - SignTranslateEvent event = new SignTranslateEvent(craft, backingForEvent, wrapperToLocs.getOrDefault(entry.getKey(), new ArrayList<>())); - Bukkit.getServer().getPluginManager().callEvent(event); - // if(!event.isUpdated()){ - // continue; - // } - // TODO: This is implemented only to fix client caching - // ideally we wouldn't do the update and would instead fake it out to the player - - /*System.out.println("New lines: "); - for (String s : event.rawLines()) { - System.out.println(" - " + s); - } - System.out.println("Old lines: "); - for (String s : entry.getKey().rawLines()) { - System.out.println(" - " + s); - }*/ - // Values get changed definitely, but perhaps it does not get applied to the sign after all? - for(SignWrapper wrapperTmp : entry.getValue()){ - /*Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - SignWrapper[] signsAtLoc = signStates.get(location); - if (signsAtLoc != null && signsAtLoc.length > 0) { - boolean hadCorrectSide = false; - for (SignWrapper sw : signsAtLoc) { - // Important: Check if the wrapper faces the right way! - if (!sw.facing().equals(event.facing())) { - continue; - } - hadCorrectSide = true; - if (!checkEventIsUpdated || event.isUpdated()) { - sw.copyContent(event::line, (i) -> i < event.lines().size()); - } - } - if (hadCorrectSide) { - try { - ((Sign)location.toBukkit(craft.getWorld()).getBlock()).update(false, false); - } catch(ClassCastException ex) { - // Ignore - } - } - }*/ - if (!checkEventIsUpdated || event.isUpdated()) { - wrapperTmp.copyContent(event::line, (i) -> i < event.lines().size()); - if (wrapperTmp.block() != null) { - signsToUpdate.add(wrapperTmp.block()); - } - } - } - } - - for (Sign sign : signsToUpdate) { - sign.update(false, false); - } - } -} From 754f22efc245067d81c9124338cfb31565bfa2b5 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:34:39 +0200 Subject: [PATCH 46/55] adapt to refactoring (cherry picked from commit dc459d25cef5780a9cea13c90139ecd1636366c7) --- .../net/countercraft/movecraft/sign/CraftPilotSign.java | 8 ++++---- .../java/net/countercraft/movecraft/sign/ReleaseSign.java | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java index 35dbea79c..3e5800b59 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/CraftPilotSign.java @@ -34,7 +34,7 @@ public CraftPilotSign(CraftType craftType) { } @Override - protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + protected boolean isSignValid(Action clickType, SignListener.SignWrapper sign, Player player) { String header = sign.getRaw(0).trim(); CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(header); if (craftType != this.craftType) { @@ -49,7 +49,7 @@ protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper } @Override - protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @javax.annotation.Nullable Craft craft) { + protected boolean internalProcessSign(Action clickType, SignListener.SignWrapper sign, Player player, @javax.annotation.Nullable Craft craft) { if (this.craftType.getBoolProperty(CraftType.MUST_BE_SUBCRAFT) && craft == null) { return false; } @@ -70,7 +70,7 @@ protected boolean internalProcessSign(Action clickType, AbstractSignListener.Sig return true; } - protected void runDetectTask(MovecraftLocation startPoint, Player player, AbstractSignListener.SignWrapper signWrapper, Craft parentCraft, World world) { + protected void runDetectTask(MovecraftLocation startPoint, Player player, SignListener.SignWrapper signWrapper, Craft parentCraft, World world) { PILOTING.add(startPoint); CraftManager.getInstance().detect( startPoint, @@ -147,7 +147,7 @@ public void run() { } @Override - public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + public boolean processSignChange(SignChangeEvent event, SignListener.SignWrapper sign) { String header = sign.getRaw(0).trim(); CraftType craftType = CraftManager.getInstance().getCraftTypeFromString(header); if (craftType != this.craftType) { diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java index ae62fa419..4982825b7 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java @@ -15,12 +15,12 @@ public ReleaseSign() { } @Override - protected boolean isSignValid(Action clickType, AbstractSignListener.SignWrapper sign, Player player) { + protected boolean isSignValid(Action clickType, SignListener.SignWrapper sign, Player player) { return true; } @Override - protected boolean internalProcessSign(Action clickType, AbstractSignListener.SignWrapper sign, Player player, @Nullable Craft craft) { + protected boolean internalProcessSign(Action clickType, SignListener.SignWrapper sign, Player player, @Nullable Craft craft) { if (craft == null) { craft = CraftManager.getInstance().getCraftByPlayer(player); } @@ -31,7 +31,7 @@ protected boolean internalProcessSign(Action clickType, AbstractSignListener.Sig } @Override - public boolean processSignChange(SignChangeEvent event, AbstractSignListener.SignWrapper sign) { + public boolean processSignChange(SignChangeEvent event, SignListener.SignWrapper sign) { return false; } From ff1fd2fc871816a4d01886bf62c64409beab98de Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:46:02 +0200 Subject: [PATCH 47/55] use newer method (cherry picked from commit 77bb394064a8be1daeccf6ff69f4cc3fcf64f479) --- .../net/countercraft/movecraft/sign/SignListener.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java index 3b7d59258..9d5c8476e 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java @@ -305,7 +305,7 @@ public boolean equals(SignWrapper a, SignWrapper b) { for (String s : entry.getKey().rawLines()) { System.out.println(" - " + s); }*/ - AbstractCraftSign acs = AbstractCraftSign.getCraftSign(backingForEvent.line(0)); + AbstractCraftSign acs = MovecraftSignRegistry.INSTANCE.getCraftSign(backingForEvent.line(0)); if (acs != null) { if (acs.processSignTranslation(craft, backingForEvent, wrapperToLocs.get(entry.getKey()))) { // Values get changed definitely, but perhaps it does not get applied to the sign after all? @@ -337,7 +337,7 @@ public void onCraftDetect(CraftDetectEvent event) { if (state instanceof Sign sign) { for (SignWrapper wrapper : this.getSignWrappers(sign)) { // Would be one more readable line if using Optionals but Nullables were wanted - AbstractCraftSign acs = AbstractCraftSign.getCraftSign(wrapper.line(0)); + AbstractCraftSign acs = MovecraftSignRegistry.INSTANCE.getCraftSign(wrapper.line(0)); if (acs != null) { acs.onCraftDetect(event, wrapper); } @@ -350,7 +350,7 @@ public void onCraftDetect(CraftDetectEvent event) { @EventHandler(ignoreCancelled = true, priority = EventPriority.LOW) public void onSignTranslate(SignTranslateEvent event) { // Would be one more readable line if using Optionals but Nullables were wanted - AbstractCraftSign acs = AbstractCraftSign.getCraftSign(event.line(0)); + AbstractCraftSign acs = MovecraftSignRegistry.INSTANCE.getCraftSign(event.line(0)); if (acs != null) { acs.onSignMovedByCraft(event); } @@ -369,7 +369,7 @@ public void onSignChange(SignChangeEvent event) { eventType = AbstractMovecraftSign.EventType.SIGN_EDIT_ON_CRAFT; } final AbstractMovecraftSign.EventType eventTypeTmp = eventType; - AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); + AbstractMovecraftSign ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); // Would be one more readable line if using Optionals but Nullables were wanted if (ams != null) { if (!eventType.isOnCraft()) { @@ -428,7 +428,7 @@ public void onSignClick(PlayerInteractEvent event) { } // Would be one more readable line if using Optionals but Nullables were wanted - AbstractMovecraftSign ams = AbstractMovecraftSign.get(wrapper.line(0)); + AbstractMovecraftSign ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); if (ams != null) { boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); // Always cancel, regardless of the success From ab2ebb755fc67933c973a9bc460e9ca3741d19a1 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:53:38 +0200 Subject: [PATCH 48/55] fix compile errors and remove old code --- .../mapUpdater/update/CraftRotateCommand.java | 52 +------------------ .../update/CraftTranslateCommand.java | 51 +----------------- 2 files changed, 4 insertions(+), 99 deletions(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftRotateCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftRotateCommand.java index 01fbef041..a42e68287 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftRotateCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftRotateCommand.java @@ -14,7 +14,7 @@ import net.countercraft.movecraft.craft.SinkingCraft; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftReleaseEvent; -import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.sign.SignListener; import net.countercraft.movecraft.util.CollectionUtils; import net.countercraft.movecraft.util.MathUtils; import net.countercraft.movecraft.util.Tags; @@ -201,55 +201,7 @@ public void doUpdate() { } private void sendSignEvents() { - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(String[] strings) { - return Arrays.hashCode(strings); - } - - @Override - public boolean equals(String[] a, String[] b) { - return Arrays.equals(a, b); - } - }); - Map signStates = new HashMap<>(); - - for (MovecraftLocation location : craft.getHitBox()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (state instanceof Sign) { - Sign sign = (Sign) block.getState(); - if (!signs.containsKey(sign.getLines())) - signs.put(sign.getLines(), new ArrayList<>()); - signs.get(sign.getLines()).add(location); - signStates.put(location, sign); - } - } - for (Map.Entry> entry : signs.entrySet()) { - SignTranslateEvent event = new SignTranslateEvent(craft, entry.getKey(), entry.getValue()); - Bukkit.getServer().getPluginManager().callEvent(event); - // if(!event.isUpdated()){ - // continue; - // } - // TODO: This is implemented only to fix client caching - // ideally we wouldn't do the update and would instead fake it out to the player - for (MovecraftLocation location : entry.getValue()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - BlockData data = block.getBlockData(); - if (!(state instanceof Sign)) { - continue; - } - Sign sign = signStates.get(location); - if (event.isUpdated()) { - for (int i = 0; i < 4; i++) { - sign.setLine(i, entry.getKey()[i]); - } - } - sign.update(false, false); - block.setBlockData(data); - } - } + SignListener.INSTANCE.processSignTranslation(craft, true); } @NotNull diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftTranslateCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftTranslateCommand.java index 7fa64e43a..6351a968f 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftTranslateCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/mapUpdater/update/CraftTranslateCommand.java @@ -14,7 +14,7 @@ import net.countercraft.movecraft.craft.SinkingCraft; import net.countercraft.movecraft.craft.type.CraftType; import net.countercraft.movecraft.events.CraftReleaseEvent; -import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.sign.SignListener; import net.countercraft.movecraft.util.MathUtils; import net.countercraft.movecraft.util.Tags; import net.countercraft.movecraft.util.hitboxes.HitBox; @@ -303,54 +303,7 @@ private LinkedList hullSearch(SetHitBox validExterior) { } private void sendSignEvents(){ - Object2ObjectMap> signs = new Object2ObjectOpenCustomHashMap<>(new Hash.Strategy() { - @Override - public int hashCode(String[] strings) { - return Arrays.hashCode(strings); - } - - @Override - public boolean equals(String[] a, String[] b) { - return Arrays.equals(a, b); - } - }); - Map signStates = new HashMap<>(); - - for (MovecraftLocation location : craft.getHitBox()) { - Block block = location.toBukkit(craft.getWorld()).getBlock(); - if(!Tag.SIGNS.isTagged(block.getType())){ - continue; - } - BlockState state = block.getState(); - if (state instanceof Sign) { - Sign sign = (Sign) state; - if(!signs.containsKey(sign.getLines())) - signs.put(sign.getLines(), new ArrayList<>()); - signs.get(sign.getLines()).add(location); - signStates.put(location, sign); - } - } - for(Map.Entry> entry : signs.entrySet()){ - SignTranslateEvent event = new SignTranslateEvent(craft, entry.getKey(), entry.getValue()); - Bukkit.getServer().getPluginManager().callEvent(event); - // if(!event.isUpdated()){ - // continue; - // } - // TODO: This is implemented only to fix client caching - // ideally we wouldn't do the update and would instead fake it out to the player - for(MovecraftLocation location : entry.getValue()){ - Block block = location.toBukkit(craft.getWorld()).getBlock(); - BlockState state = block.getState(); - if (!(state instanceof Sign)) { - continue; - } - Sign sign = signStates.get(location); - for(int i = 0; i<4; i++){ - sign.setLine(i, entry.getKey()[i]); - } - sign.update(false, false); - } - } + SignListener.INSTANCE.processSignTranslation(craft, false); } @NotNull From 8c14ddae2486286ecb80dde1427758b0ffd9ed6a Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:00:53 +0200 Subject: [PATCH 49/55] apply default text and implement processSignChange (cherry picked from commit 397e7fea24cc9b7145906981203a82ab016d3bbb) --- .../movecraft/sign/AbstractSubcraftSign.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 827defb37..10994ec33 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -72,8 +72,14 @@ protected boolean internalProcessSign(Action clickType, SignListener.SignWrapper @Override public boolean processSignChange(SignChangeEvent event, SignListener.SignWrapper sign) { - // TODO: Implement - return false; + if (!this.isSignValid(Action.PHYSICAL, sign, event.getPlayer())) { + for (int i = 0; i < sign.lines().size(); i++) { + sign.line(i, Component.empty()); + } + return false; + } + this.applyDefaultText(sign); + return true; } @Override From e86415017fb01e1c8e868848450840732fe98875 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sun, 15 Sep 2024 15:06:33 +0200 Subject: [PATCH 50/55] correct typo (cherry picked from commit 441452c67c565674dd57efeaeb56732fce00e952) --- .../net/countercraft/movecraft/sign/AbstractSubcraftSign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 10994ec33..89f6da2be 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -156,7 +156,7 @@ protected void applyDefaultText(SignListener.SignWrapper sign) { sign.line(2, l3); } if (l4 != null) { - sign.line(2, l4); + sign.line(3, l4); } } } From 8f9ee61327ce660500cbebfbd363cbba42fbacea Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:55:35 +0200 Subject: [PATCH 51/55] return true (cherry picked from commit 0433e31b63210607bf4f38fd93965587069ea0aa) --- .../net/countercraft/movecraft/sign/AbstractSubcraftSign.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java index 89f6da2be..a2b846366 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractSubcraftSign.java @@ -145,7 +145,7 @@ public void run() { } }.runTaskLater(this.pluginInstance.get(), 4); - return false; + return true; } protected void applyDefaultText(SignListener.SignWrapper sign) { From c722fbbecba57959b770db5e560fbe76437265a3 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:12:28 +0200 Subject: [PATCH 52/55] call stuff for the other side, if that side is a movecraft sign (cherry picked from commit 0ce7710e3ba35e359f62c7ebaca64e23c6bba62e) --- .../movecraft/sign/SignListener.java | 60 ++++++++++++++++++- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java b/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java index 9d5c8476e..76936a426 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/SignListener.java @@ -364,12 +364,40 @@ public void onSignChange(SignChangeEvent event) { SignWrapper signWrapper = this.getSignWrapper(sign, event.getPlayer()); SignWrapper wrapper = this.getSignWrapper(sign, event); + boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; + AbstractMovecraftSign.EventType eventType = signWrapper.isEmpty() ? AbstractMovecraftSign.EventType.SIGN_CREATION : AbstractMovecraftSign.EventType.SIGN_EDIT; - if (eventType == AbstractMovecraftSign.EventType.SIGN_EDIT && MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null) { + if (eventType == AbstractMovecraftSign.EventType.SIGN_EDIT && onCraft) { eventType = AbstractMovecraftSign.EventType.SIGN_EDIT_ON_CRAFT; } final AbstractMovecraftSign.EventType eventTypeTmp = eventType; - AbstractMovecraftSign ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); + AbstractMovecraftSign ams = null; + + // If the side is empty, we should try a different side, like, the next side that is not empty and which has a signHandler + if (wrapper.isEmpty()) { + SignWrapper[] wrapps = this.getSignWrappers(sign, true); + if (wrapps == null || wrapps.length == 0) { + // Nothing found + if (onCraft) { + event.setCancelled(true); + } + return; + } else { + for (SignWrapper swTmp : wrapps) { + AbstractMovecraftSign amsTmp = MovecraftSignRegistry.INSTANCE.get(swTmp.line(0)); + if (amsTmp != null) { + wrapper = swTmp; + ams = amsTmp; + break; + } + } + } + } + + if (ams == null) { + ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); + } + // Would be one more readable line if using Optionals but Nullables were wanted if (ams != null) { if (!eventType.isOnCraft()) { @@ -404,6 +432,29 @@ public void onSignClick(PlayerInteractEvent event) { ItemStack heldItem = event.getItem(); boolean onCraft = MathUtils.getCraftByPersistentBlockData(sign.getLocation()) != null; + AbstractMovecraftSign ams = null; + + // If the side is empty, we should try a different side, like, the next side that is not empty and which has a signHandler + if (wrapper.isEmpty()) { + SignWrapper[] wrapps = this.getSignWrappers(sign, true); + if (wrapps == null || wrapps.length == 0) { + // Nothing found + if (onCraft) { + event.setCancelled(true); + } + return; + } else { + for (SignWrapper swTmp : wrapps) { + AbstractMovecraftSign amsTmp = MovecraftSignRegistry.INSTANCE.get(swTmp.line(0)); + if (amsTmp != null) { + wrapper = swTmp; + ams = amsTmp; + break; + } + } + } + } + // Always cancel if on craft => Avoid clicking empty sides and entering edit mode if (onCraft && wrapper.isEmpty()) { event.setCancelled(true); @@ -427,8 +478,11 @@ public void onSignClick(PlayerInteractEvent event) { } } + if (ams == null) { + ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); + } + // Would be one more readable line if using Optionals but Nullables were wanted - AbstractMovecraftSign ams = MovecraftSignRegistry.INSTANCE.get(wrapper.line(0)); if (ams != null) { boolean success = ams.processSignClick(event.getAction(), wrapper, event.getPlayer()); // Always cancel, regardless of the success From ab7ae64a4473564e7fd6d41014d7661b666fe677 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Tue, 17 Sep 2024 18:33:39 +0200 Subject: [PATCH 53/55] fix compile errors --- .../src/main/java/net/countercraft/movecraft/Movecraft.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index 179e6b6ee..0a72e101b 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -58,7 +58,7 @@ public class Movecraft extends JavaPlugin { private SmoothTeleport smoothTeleport; private AsyncManager asyncManager; private WreckManager wreckManager; - private AbstractSignListener abstractSignListener; + private SignListener abstractSignListener; public static synchronized Movecraft getInstance() { return instance; From 9d850cefbecd6d1fd5783c038ce9d4f50dcb2d47 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:27:26 +0200 Subject: [PATCH 54/55] add getter for crafttype (cherry picked from commit c83b41ee0652e9f2b6751083150b000c7f954ab2) --- .../countercraft/movecraft/sign/AbstractCraftPilotSign.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java index c2d80313a..501cf4743 100644 --- a/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java +++ b/api/src/main/java/net/countercraft/movecraft/sign/AbstractCraftPilotSign.java @@ -14,4 +14,8 @@ public AbstractCraftPilotSign(final CraftType craftType) { this.craftType = craftType; } + public CraftType getCraftType() { + return this.craftType; + } + } From 9d3518333b54c88e4fe6e25c7d40fa03995be848 Mon Sep 17 00:00:00 2001 From: DerToaster98 <38782719+DerToaster98@users.noreply.github.com> Date: Mon, 30 Sep 2024 18:27:59 +0200 Subject: [PATCH 55/55] check if player actually is the pilot of that craft (cherry picked from commit 64800b87413241ca1fe8fc2c81461beaa1931f64) --- .../main/java/net/countercraft/movecraft/sign/ReleaseSign.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java index 4982825b7..d7b7bfa3e 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/sign/ReleaseSign.java @@ -2,6 +2,7 @@ import net.countercraft.movecraft.craft.Craft; import net.countercraft.movecraft.craft.CraftManager; +import net.countercraft.movecraft.craft.PilotedCraft; import net.countercraft.movecraft.events.CraftReleaseEvent; import org.bukkit.entity.Player; import org.bukkit.event.block.Action; @@ -21,7 +22,7 @@ protected boolean isSignValid(Action clickType, SignListener.SignWrapper sign, P @Override protected boolean internalProcessSign(Action clickType, SignListener.SignWrapper sign, Player player, @Nullable Craft craft) { - if (craft == null) { + if (craft == null || (craft instanceof PilotedCraft pc && pc.getPilot() != player)) { craft = CraftManager.getInstance().getCraftByPlayer(player); } if (craft != null) {