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

Fix/Add tracked locations rotating on subcrafts #728

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
43521e7
move trackedlocations to subcraft and back to the parent on release
DerToaster98 Jan 9, 2025
bea513c
Ugly hack to allow transfering it to a different craft
DerToaster98 Jan 9, 2025
6ee8f0c
Transfer the same *object*
DerToaster98 Jan 9, 2025
9b93697
update trackedlocation map to concurrent map
DerToaster98 Jan 12, 2025
1ed6851
for some odd reason, the pilot event for subcraft fires twice sometimes
DerToaster98 Jan 12, 2025
49a1bf4
use computeIfAbsent and HashSet in favor of Set.of()
DerToaster98 Jan 12, 2025
b4bcb84
reinforce transfer logic
DerToaster98 Jan 17, 2025
2492f1e
Fix trackedlocation rotation
DerToaster98 Jan 17, 2025
299c160
comment out code => this will rotate ALL aprent craft's trackedlocati…
DerToaster98 Jan 17, 2025
3c8d300
just to be safe and to keep it working with normal rotations
DerToaster98 Jan 17, 2025
873035c
transfer the object if possible to keep compatibility with moving sub…
DerToaster98 Jan 17, 2025
fd33db1
actually run the code
DerToaster98 Jan 17, 2025
7effbfb
temporary "debug" stuff
DerToaster98 Jan 17, 2025
b987822
no need to call reset()
DerToaster98 Jan 17, 2025
19ff28d
throw exception when the effective location changes while transferring
DerToaster98 Jan 17, 2025
e2f7a03
make trackedlocations a bit more stupid
DerToaster98 Jan 18, 2025
de7d7e8
move tracked locations
DerToaster98 Jan 18, 2025
8d312f1
clean up
DerToaster98 Jan 18, 2025
df0b690
remove debug println's
DerToaster98 Jan 18, 2025
ae18c94
Add craftOrigin helper object
DerToaster98 Jan 26, 2025
41c4ca3
update trackedLocation implementation to be based on the enw referenc…
DerToaster98 Jan 26, 2025
073059f
update implementation of translate and rotate for subcrafts
DerToaster98 Jan 26, 2025
f3af15c
forgot to rotate origin too
DerToaster98 Jan 26, 2025
5adcaeb
correct typo
DerToaster98 Jan 26, 2025
3f6425c
careful, the order here is very important => Origin needs to be rotat…
DerToaster98 Jan 26, 2025
8150cef
fix rotation on trackedlocations
DerToaster98 Jan 26, 2025
714316f
Use MovecraftLocation in TrackedLocation
DerToaster98 Jan 27, 2025
e0bfea0
Use MovecraftLocation in CraftOrigin
DerToaster98 Jan 27, 2025
4a018e5
Simplify lambda
DerToaster98 Jan 27, 2025
00a0f3f
Merge branch 'main' into tracked-location-subcraft-support
DerToaster98 Jan 27, 2025
97387ee
fixes and use movecraftlocation as origin now
DerToaster98 Jan 31, 2025
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 @@ -165,14 +165,16 @@ protected void execute() {
}

// Rotates the craft's tracked locations, and all parent craft's.
craft.getCraftOrigin().rotate(originPoint, rotation);
Craft temp = craft;
do {
for (Set<TrackedLocation> locations : craft.getTrackedLocations().values()) {
// recursion through all subcrafts is not necessary as the trackedlocations are transferred to the subcraft
//do {
for (Set<TrackedLocation> locations : temp.getTrackedLocations().values()) {
for (TrackedLocation location : locations) {
location.rotate(rotation, originPoint);
location.rotate(rotation);
}
}
} while (temp instanceof SubCraft && (temp = ((SubCraft) temp).getParent()) != null);
//} while (temp instanceof SubCraft && (temp = ((SubCraft) temp).getParent()) != null);

updates.add(new CraftRotateCommand(getCraft(),originPoint, rotation));
//rotate entities in the craft
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.countercraft.movecraft.Movecraft;
import net.countercraft.movecraft.MovecraftChunk;
import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.TrackedLocation;
import net.countercraft.movecraft.async.AsyncTask;
import net.countercraft.movecraft.config.Settings;
import net.countercraft.movecraft.craft.ChunkManager;
Expand Down Expand Up @@ -318,6 +319,9 @@ protected void execute() throws InterruptedException, ExecutionException {
}
}

// Update the reference location for trackedlocations
craft.getCraftOrigin().translate(dx, dy, dz);

if (!collisionBox.isEmpty() && craft.getType().getBoolProperty(CraftType.CRUISE_ON_PILOT)) {
CraftManager.getInstance().release(craft, CraftReleaseEvent.Reason.EMPTY, false);
for (MovecraftLocation location : oldHitBox) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;

Expand Down Expand Up @@ -79,7 +80,7 @@ public abstract class BaseCraft implements Craft {
private String name = "";
@NotNull
private MovecraftLocation lastTranslation = new MovecraftLocation(0, 0, 0);
private Map<NamespacedKey, Set<TrackedLocation>> trackedLocations = new HashMap<>();
private Map<NamespacedKey, Set<TrackedLocation>> trackedLocations = new ConcurrentHashMap<>();

@NotNull
private final CraftDataTagContainer dataTagContainer;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
package net.countercraft.movecraft.listener;

import com.google.common.base.Predicates;
import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.TrackedLocation;
import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.craft.SubCraft;
import net.countercraft.movecraft.events.CraftPilotEvent;
import net.countercraft.movecraft.events.CraftReleaseEvent;
import net.countercraft.movecraft.util.hitboxes.HitBox;
import org.bukkit.NamespacedKey;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;

import java.util.*;
import java.util.function.Predicate;

public class CraftPilotListener implements Listener {

@EventHandler(ignoreCancelled = true)
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCraftPilot(@NotNull CraftPilotEvent event) {
// Walk through all signs and set a UUID in there
final Craft craft = event.getCraft();
Expand All @@ -30,6 +40,91 @@ public void onCraftPilot(@NotNull CraftPilotEvent event) {
craft.markTileStateWithUUID(tile);
tile.update();
}

// Tracked locations => Modify correctly with subcrafts
if (craft instanceof SubCraft subCraft && subCraft.getParent() != null) {
final Craft parent = subCraft.getParent();
if (parent.getWorld() != subCraft.getWorld()) {
return;
}
transferTrackedLocations(parent, subCraft, (trackedLocation) -> {
MovecraftLocation absolute = trackedLocation.getAbsoluteLocation();
if (subCraft.getHitBox().inBounds(absolute)) {
if (subCraft.getHitBox().contains(absolute)) {
return true;
}
}
return false;
}, true);
}
}

@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onCraftRelease(@NotNull CraftReleaseEvent event) {
if (event.getReason() != CraftReleaseEvent.Reason.SUB_CRAFT) {
return;
}
Craft released = event.getCraft();
if (!(released instanceof SubCraft)) {
return;
}
SubCraft subCraft = (SubCraft) released;
if (subCraft.getParent() == null) {
return;
}

// Attention: SquadronCrafts are also subcrafts! We need to make sure that they are at least intersecting the parent when we add back the tracked locations
final HitBox parentHitBox = subCraft.getParent().getHitBox();
final HitBox subCraftHitBox = subCraft.getHitBox();

if (!parentHitBox.inBounds(subCraftHitBox.getMinX(), subCraftHitBox.getMinY(), subCraftHitBox.getMinZ())) {
if (!parentHitBox.inBounds(subCraftHitBox.getMaxX(), subCraftHitBox.getMaxY(), subCraftHitBox.getMaxZ())) {
// Subcraft cant possible be within its parent anymore
return;
}
}

transferTrackedLocations(subCraft, subCraft.getParent(), Predicates.alwaysTrue(), true);
}

/*
* Transfers TrackedLocations from a craft A to a craft B with a optional filter.
* This MOVES the tracked locations, so keep that in mind
*/
private static void transferTrackedLocations(final Craft a, final Craft b, Predicate<TrackedLocation> filterArgument, boolean move) {
final MovecraftLocation bMidPoint = b.getHitBox().getMidPoint();

for (Map.Entry<NamespacedKey, Set<TrackedLocation>> entry : a.getTrackedLocations().entrySet()) {
final Set<TrackedLocation> bTrackedLocations = b.getTrackedLocations().computeIfAbsent(entry.getKey(), k -> new HashSet<>());
final Set<TrackedLocation> aTrackedLocations = entry.getValue();

if (aTrackedLocations.isEmpty()) {
continue;
}

// Commented out code: previous attempt to actually transfer the tracked locations, which technically is unnecessary unless for subcrafts like squadrons that actually move!
List<TrackedLocation> transferred = new ArrayList<>();
aTrackedLocations.forEach(trackedLocation -> {
if (filterArgument.test(trackedLocation)) {
if (move) {
// Technically this (the reset call) is not necessary, but we will keep it here for potential extensions by third party addons
final MovecraftLocation absoluteLocation = trackedLocation.getAbsoluteLocation();
trackedLocation.reset(b, absoluteLocation);
if (!(bTrackedLocations.add(trackedLocation))) {
trackedLocation.reset(a, absoluteLocation);
} else {
transferred.add(trackedLocation);
}
if (!absoluteLocation.equals(trackedLocation.getAbsoluteLocation())) {
throw new IllegalStateException("Somehow the previous and transferred absolute locations are NOT the same! This should NEVER happen!");
}
} else {
bTrackedLocations.add(trackedLocation);
}
}
});
aTrackedLocations.removeAll(transferred);
}
}

}
65 changes: 51 additions & 14 deletions api/src/main/java/net/countercraft/movecraft/TrackedLocation.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,44 +4,81 @@
import net.countercraft.movecraft.util.MathUtils;
import org.jetbrains.annotations.NotNull;

import java.lang.ref.WeakReference;

public class TrackedLocation {
private MovecraftLocation offSet;
private final Craft craft;

private int dx;
private int dy;
private int dz;

private WeakReference<Craft> craft;

/**
* Creates a new TrackedLocation instance which tracks a location about a craft's midpoint.
* @param craft The craft that's that tied to the location.
* @param craft The craft this trackedlocation belongs to
* @param location The absolute position to track. This location will be stored as a relative
* location to the craft's central hitbox.
*/
public TrackedLocation(@NotNull Craft craft, @NotNull MovecraftLocation location) {
this.craft = craft;
MovecraftLocation midPoint = craft.getHitBox().getMidPoint();
offSet = location.subtract(midPoint);
this.craft = new WeakReference<>(craft);
reinit(location);
}

protected void reinit(@NotNull MovecraftLocation location) {
reinit(location.getX(), location.getY(), location.getZ());
}

protected void reinit(final int x, final int y, final int z) {
Craft craft = this.getCraft();
Craft.CraftOrigin origin = craft.getCraftOrigin();
this.dx = x - origin.getX();
this.dy = y - origin.getY();
this.dz = z - origin.getZ();
}

/**
* Rotates the stored location.
* @param rotation A clockwise or counter-clockwise direction to rotate.
*/
public void rotate(MovecraftRotation rotation, MovecraftLocation origin) {
offSet = MathUtils.rotateVec(rotation, getAbsoluteLocation().subtract(origin));
public void rotate(MovecraftRotation rotation) {
MovecraftLocation newVector = MathUtils.rotateVec(rotation, new MovecraftLocation(this.dx, this.dy, this.dz));

this.dx = newVector.getX();
this.dy = newVector.getY();
this.dz = newVector.getZ();
}

/**
* Gets the stored absolute location.
* @return Returns the absolute location instead of a vector.
*/
public MovecraftLocation getAbsoluteLocation() {
MovecraftLocation midPoint = craft.getHitBox().getMidPoint();
return offSet.add(midPoint);
Craft craft = this.getCraft();
Craft.CraftOrigin origin = craft.getCraftOrigin();

int x = origin.getX() + this.dx;
int y = origin.getY() + this.dy;
int z = origin.getZ() + this.dz;

return new MovecraftLocation(x, y, z);
}

/**
* Gets the stored location as a position vector relative to the midpoint.
* @return Returns the absolute location instead of a vector.
* NEVER USE THIS UNLESS ABSOLUTELY NECESSARY
* @param craft
* @param location
*/
public MovecraftLocation getOffSet() {
return offSet;
public void reset(@NotNull Craft craft, @NotNull MovecraftLocation location) {
this.craft = new WeakReference<>(craft);
reinit(location);
}

public Craft getCraft() {
if (this.craft.get() == null) {
throw new RuntimeException("Craft of tracked location is null! This indicates that the craft object was destroyed but somehow the tracked location is still around!");
}
return this.craft.get();
}

}
68 changes: 68 additions & 0 deletions api/src/main/java/net/countercraft/movecraft/craft/Craft.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
import org.bukkit.block.data.BlockData;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector3i;

import javax.naming.Name;
import java.util.*;

public interface Craft {
Expand All @@ -51,12 +53,74 @@ public interface Craft {
CraftDataTagKey<Counter<RequiredBlockEntry>> MOVEBLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "moveblocks"), craft -> new Counter<>());
CraftDataTagKey<Integer> NON_NEGLIGIBLE_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-blocks"), Craft::getOrigBlockCount);
CraftDataTagKey<Integer> NON_NEGLIGIBLE_SOLID_BLOCKS = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "non-negligible-solid-blocks"), Craft::getOrigBlockCount);
CraftDataTagKey<CraftOrigin> CRAFT_ORIGIN = CraftDataTagRegistry.INSTANCE.registerTagKey(new NamespacedKey("movecraft", "craft-origin"), CraftOrigin::new);

// 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 class CraftOrigin {

private int x;
private int y;
private int z;

public CraftOrigin(final @NotNull Craft craft) {

}

public void translate(int dx, int dy, int dz) {
this.x += dx;
this.y += dy;
this.z += dz;
}

public void rotate(final MovecraftLocation rotationPoint, final MovecraftRotation rotation) {
MovecraftLocation oldAbsolute = this.getLocation();
MovecraftLocation vector = oldAbsolute.subtract(rotationPoint);
MovecraftLocation vectorRotated = MathUtils.rotateVec(rotation, vector);
MovecraftLocation newAbsolute = rotationPoint.add(vectorRotated);
this.copyValues(newAbsolute);
}

public void copyValues(final MovecraftLocation location) {
this.x = location.getX();
this.y = location.getY();
this.z = location.getZ();
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return y;
}

public void setY(int y) {
this.y = y;
}

public int getZ() {
return z;
}

public void setZ(int z) {
this.z = z;
}

public MovecraftLocation getLocation() {
return new MovecraftLocation(x, y, z);
}

}

public static Craft getCraftByUUID(final UUID uuid) {
return Hidden.uuidToCraft.getOrDefault(uuid, null);
}
Expand Down Expand Up @@ -293,4 +357,8 @@ public default void removeUUIDMarkFromTile(TileState tile) {
}

Map<NamespacedKey, Set<TrackedLocation>> getTrackedLocations();

public default CraftOrigin getCraftOrigin() {
return this.getDataTag(CRAFT_ORIGIN);
}
}