Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Feature] Proper access to craft via UUID #673

Merged
merged 16 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@
import net.countercraft.movecraft.features.contacts.ContactsCommand;
import net.countercraft.movecraft.features.contacts.ContactsManager;
import net.countercraft.movecraft.features.contacts.ContactsSign;
import net.countercraft.movecraft.listener.BlockListener;
import net.countercraft.movecraft.listener.InteractListener;
import net.countercraft.movecraft.listener.PlayerListener;
import net.countercraft.movecraft.listener.*;
import net.countercraft.movecraft.localisation.I18nSupport;
import net.countercraft.movecraft.mapUpdater.MapUpdateManager;
import net.countercraft.movecraft.processing.WorldManager;
Expand Down Expand Up @@ -221,6 +219,8 @@ public void onEnable() {
getServer().getPluginManager().registerEvents(new SubcraftRotateSign(), this);
getServer().getPluginManager().registerEvents(new TeleportSign(), this);
getServer().getPluginManager().registerEvents(new ScuttleSign(), this);
getServer().getPluginManager().registerEvents(new CraftPilotListener(), this);
getServer().getPluginManager().registerEvents(new CraftReleaseListener(), this);

var contactsManager = new ContactsManager();
contactsManager.runTaskTimerAsynchronously(this, 0, 20);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@
import org.bukkit.block.data.BlockData;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;

Expand Down Expand Up @@ -75,7 +72,10 @@ public abstract class BaseCraft implements Craft {

private final CraftDataTagContainer dataTagContainer = new CraftDataTagContainer();

private final UUID uuid = UUID.randomUUID();

public BaseCraft(@NotNull CraftType type, @NotNull World world) {
Hidden.uuidToCraft.put(uuid, this);
this.type = type;
this.w = world;
hitBox = new SetHitBox();
Expand Down Expand Up @@ -559,4 +559,21 @@ public double getTotalFuel() {
public CraftDataTagContainer getDataTagContainer() {
return dataTagContainer;
}

public UUID getUuid() {
return this.uuid;
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof BaseCraft))
return false;

return this.getUUID().equals(((BaseCraft) obj).getUUID());
}

@Override
public int hashCode() {
return this.getUUID().hashCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
import java.util.UUID;

public class PlayerCraftImpl extends BaseCraft implements PlayerCraft {
private final UUID id = UUID.randomUUID();
private final int hashCode = id.hashCode();
private final Player pilot;
private boolean pilotLocked;
private double pilotLockedX;
Expand All @@ -30,12 +28,7 @@ public boolean equals(Object obj) {
if (!(obj instanceof PlayerCraftImpl))
return false;

return id.equals(((PlayerCraftImpl) obj).id);
}

@Override
public int hashCode() {
return hashCode;
return super.equals(obj);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package net.countercraft.movecraft.listener;

import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.events.CraftPilotEvent;
import net.countercraft.movecraft.util.MathUtils;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;

public class CraftPilotListener implements Listener {

@EventHandler(ignoreCancelled = true)
public void onCraftPilot(@NotNull CraftPilotEvent event) {
// Walk through all signs and set a UUID in there
final Craft craft = event.getCraft();

// Now, find all signs on the craft...
for (MovecraftLocation mLoc : craft.getHitBox()) {
Block block = mLoc.toBukkit(craft.getWorld()).getBlock();
// Only interested in signs, if no sign => continue
// TODO: Just limit to signs?
// Edit: That's useful for dispensers too to flag TNT and the like, but for that one could use a separate listener
if (!(block.getState() instanceof Sign))
continue;
// Sign located!
Sign tile = (Sign) block.getState();

craft.markTileStateWithUUID(tile);
tile.update();
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package net.countercraft.movecraft.listener;

import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.craft.SubCraft;
import net.countercraft.movecraft.events.CraftReleaseEvent;
import net.countercraft.movecraft.util.MathUtils;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;

public class CraftReleaseListener implements Listener {

@EventHandler
public void onDisassembly(@NotNull CraftReleaseEvent event) {
// Walk through all signs and set a UUID in there
final Craft craft = event.getCraft();

// Now, find all signs on the craft...
for (MovecraftLocation mLoc : craft.getHitBox()) {
Block block = mLoc.toBukkit(craft.getWorld()).getBlock();
// Only interested in signs, if no sign => continue
if (!(block.getState() instanceof Sign))
continue;
// Sign located!
Sign tile = (Sign) block.getState();

craft.removeUUIDMarkFromTile(tile);

tile.update();
}
}
}
31 changes: 30 additions & 1 deletion api/src/main/java/net/countercraft/movecraft/craft/Craft.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,36 @@
import net.countercraft.movecraft.craft.type.CraftType;
import net.countercraft.movecraft.processing.MovecraftWorld;
import net.countercraft.movecraft.util.Counter;
import net.countercraft.movecraft.util.MathUtils;
import net.countercraft.movecraft.util.hitboxes.HitBox;
import net.countercraft.movecraft.util.hitboxes.MutableHitBox;
import net.kyori.adventure.audience.Audience;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Sign;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;

import java.util.Map;
import java.util.*;

public interface Craft {

// Java disallows private or protected fields in interfaces, this is a workaround
class Hidden {
// Concurrent so we don't have problems when accessing async (useful for addon plugins that want to do stuff async, for example NPC crafts with complex off-thread pathfinding)
protected static final Map<UUID, Craft> uuidToCraft = Collections.synchronizedMap(new WeakHashMap<>());
}
public static Craft getCraftByUUID(final UUID uuid) {
return Hidden.uuidToCraft.getOrDefault(uuid, null);
}

public default UUID getUUID() {
return null;
}

@Deprecated
boolean isNotProcessing();

Expand Down Expand Up @@ -276,4 +292,17 @@ public default <T> T getDataTag(CraftDataTagKey<T> tagKey) {
}
return container.get(this, tagKey);
}

public default void markTileStateWithUUID(TileState tile) {
// Add the marker
tile.getPersistentDataContainer().set(
MathUtils.KEY_CRAFT_UUID,
PersistentDataType.STRING,
this.getUUID().toString()
);
}

public default void removeUUIDMarkFromTile(TileState tile) {
tile.getPersistentDataContainer().remove(MathUtils.KEY_CRAFT_UUID);
}
}
17 changes: 17 additions & 0 deletions api/src/main/java/net/countercraft/movecraft/craft/SubCraft.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package net.countercraft.movecraft.craft;

import net.countercraft.movecraft.util.MathUtils;
import org.bukkit.block.TileState;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;

public interface SubCraft extends Craft {
Expand All @@ -8,4 +11,18 @@ public interface SubCraft extends Craft {
Craft getParent();

void setParent(@NotNull Craft parent);

@Override
default void removeUUIDMarkFromTile(TileState tile) {
Craft parent = this.getParent();
if (parent != null) {
tile.getPersistentDataContainer().set(
MathUtils.KEY_CRAFT_UUID,
PersistentDataType.STRING,
parent.getUUID().toString()
);
} else {
Craft.super.removeUUIDMarkFromTile(tile);
}
}
}
41 changes: 41 additions & 0 deletions api/src/main/java/net/countercraft/movecraft/util/MathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.util.hitboxes.HitBox;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.World;
import org.bukkit.WorldBorder;
import org.bukkit.block.Block;
import org.bukkit.block.TileState;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashSet;
import java.util.OptionalInt;
import java.util.Set;
import java.util.UUID;

public class MathUtils {

Expand Down Expand Up @@ -232,6 +237,42 @@ public static Craft fastNearestCraftToLoc(@NotNull Set<Craft> crafts, @NotNull L
return result;
}

// TODO: Move somewhere else!
public static final NamespacedKey KEY_CRAFT_UUID = new NamespacedKey("movecraft", "craft-uuid");
public static Craft getCraftByPersistentBlockData(@NotNull Location loc) {
Block block = loc.getBlock();
if (!(block.getState() instanceof TileState))
return null;

TileState blockEntity = (TileState)block.getState();

if (blockEntity.getPersistentDataContainer().has(KEY_CRAFT_UUID, PersistentDataType.STRING)) {
String value = blockEntity.getPersistentDataContainer().get(KEY_CRAFT_UUID, PersistentDataType.STRING);
try {
UUID uuid = UUID.fromString(value);
Craft result = Craft.getCraftByUUID(uuid);
if (result == null) {
// Remove invalid entry!
blockEntity.getPersistentDataContainer().remove(KEY_CRAFT_UUID);
blockEntity.update();
} else if (!result.getHitBox().inBounds(loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) {
// Remove invalid entry!
blockEntity.getPersistentDataContainer().remove(KEY_CRAFT_UUID);
blockEntity.update();
result = null;
}
return result;
} catch(IllegalArgumentException iae) {
// Remove invalid entry!
blockEntity.getPersistentDataContainer().remove(KEY_CRAFT_UUID);
blockEntity.update();
return null;
}

}
return null;
}

@Nullable
public static Set<Craft> craftsNearLocFast(@NotNull Set<Craft> crafts, @NotNull Location loc) {
Set<Craft> result = new HashSet<>(crafts.size(), 1);
Expand Down