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

Use affine transformations for low-level movement logic #716

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions Movecraft/src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ commands:
usage: /craftinfo [player] [page]
libraries:
- org.roaringbitmap:RoaringBitmap:1.0.6
- org.ejml:ejml-simple:0.43
1 change: 1 addition & 0 deletions api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ dependencies {
api(libs.it.unimi.dsi.fastutil)
api(libs.net.kyori.adventure.api)
api(libs.net.kyori.adventure.platform.bukkit)
api(libs.org.ejml.simple.library)
testImplementation(libs.org.junit.jupiter.junit.jupiter.api)
testImplementation(libs.junit.junit)
testImplementation(libs.org.hamcrest.hamcrest.library)
Expand Down
30 changes: 27 additions & 3 deletions api/src/main/java/net/countercraft/movecraft/WorldHandler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package net.countercraft.movecraft;

import net.countercraft.movecraft.craft.Craft;
import net.countercraft.movecraft.util.AffineTransformation;
import net.countercraft.movecraft.util.hitboxes.HitBox;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
Expand All @@ -11,11 +13,33 @@
import org.jetbrains.annotations.Nullable;

public abstract class WorldHandler {
public abstract void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originLocation, @NotNull MovecraftRotation rotation);
public abstract void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation newLocation, @NotNull World world);
@Deprecated(forRemoval = true)
public void rotateCraft(@NotNull Craft craft, @NotNull MovecraftLocation originLocation, @NotNull MovecraftRotation rotation){
transformHitBox(
craft.getHitBox(),
AffineTransformation.of(originLocation)
.mult(AffineTransformation.of(rotation))
.mult(AffineTransformation.of(originLocation.scalarMultiply(-1))),
craft.getWorld(),
craft.getWorld());
}

@Deprecated(forRemoval = true)
public void translateCraft(@NotNull Craft craft, @NotNull MovecraftLocation displacement, @NotNull World world){
transformHitBox(craft.getHitBox(), AffineTransformation.of(displacement), craft.getWorld(), world);
}
public abstract void transformHitBox(@NotNull HitBox hitbox, @NotNull AffineTransformation transformation, @NotNull World originWorld, @NotNull World destinationWorld);
public abstract void setBlockFast(@NotNull Location location, @NotNull BlockData data);
public abstract void setBlockFast(@NotNull Location location, @NotNull MovecraftRotation rotation, @NotNull BlockData data);

@Deprecated(forRemoval = true)
public @Nullable Location getAccessLocation(@NotNull InventoryView inventoryView){
// Not needed for 1.20+, remove when dropping support for 1.18.2
return null;
}
@Deprecated(forRemoval = true)
public void setAccessLocation(@NotNull InventoryView inventoryView, @NotNull Location location){
// Not needed for 1.20+, remove when dropping support for 1.18.2
}
public static @NotNull String getPackageName(@NotNull String minecraftVersion) {
String[] parts = minecraftVersion.split("\\.");
if (parts.length < 2)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package net.countercraft.movecraft.util;

import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.MovecraftRotation;
import org.bukkit.block.structure.Mirror;
import org.ejml.simple.SimpleMatrix;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

public final class AffineTransformation {
public static @NotNull AffineTransformation UNIT = new AffineTransformation(SimpleMatrix.identity(4), MovecraftRotation.NONE);
private final SimpleMatrix backingMatrix;
private final MovecraftRotation rotation;

private AffineTransformation(SimpleMatrix backingMatrix, MovecraftRotation rotation) {
this.backingMatrix = backingMatrix;
this.rotation = rotation;
}

public static @NotNull AffineTransformation of(MovecraftLocation translation) {
var ret = SimpleMatrix.identity(4);
ret.set(3, 0, translation.getX());
ret.set(3, 1, translation.getY());
ret.set(3, 2, translation.getZ());

return new AffineTransformation(ret, MovecraftRotation.NONE);
}

public static @NotNull AffineTransformation of(MovecraftRotation rotation) {
var ret = SimpleMatrix.identity(4);
switch (rotation) {
case NONE:
break;
case CLOCKWISE:
ret.set(0, 0, 0);
ret.set(1, 1, 0);
ret.set(0, 1, -1);
ret.set(1, 0, 1);
break;
case ANTICLOCKWISE:
ret.set(0, 0, 0);
ret.set(1, 1, 0);
ret.set(0, 1, 1);
ret.set(1, 0, -1);
break;
}

return new AffineTransformation(ret, rotation);
}

public @NotNull AffineTransformation mult(AffineTransformation other) {
// Currently, MovecraftRotation does not support 180 degree rotations
// To work around this, we simply prefer the other transformations rotation
// TODO: Implement 180 degree MovecraftRotation

return new AffineTransformation(backingMatrix.mult(other.backingMatrix), other.rotation == MovecraftRotation.NONE ? rotation : other.rotation);
}

public @NotNull MovecraftLocation apply(MovecraftLocation location) {
var transformed = backingMatrix.mult(new SimpleMatrix(new double[]{location.getX(), location.getY(), location.getZ(), 1}));

return new MovecraftLocation((int) transformed.get(0), (int) transformed.get(1), (int) transformed.get(2));
}

public @NotNull MovecraftRotation extractRotation() {
return rotation;
}

public @NotNull Mirror extractMirror() {
return Mirror.NONE;
}

@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj == null || obj.getClass() != this.getClass()) return false;
var that = (AffineTransformation) obj;
return Objects.equals(this.backingMatrix, that.backingMatrix) &&
Objects.equals(this.rotation, that.rotation);
}

@Override
public int hashCode() {
return Objects.hash(backingMatrix, rotation);
}

@Override
public String toString() {
return "AffineTransformation[" +
"backingMatrix=" + backingMatrix + ", " +
"rotation=" + rotation + ']';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@
import com.google.common.collect.UnmodifiableIterator;
import net.countercraft.movecraft.MovecraftLocation;
import net.countercraft.movecraft.exception.EmptyHitBoxException;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.stream.Stream;

public interface HitBox extends Iterable<MovecraftLocation>{
int getMinX();
Expand Down Expand Up @@ -94,6 +96,12 @@ default Set<MovecraftLocation> asSet(){
return new HitBoxSetView(this);
}

@NotNull
@Contract(pure = true)
default Stream<MovecraftLocation> stream(){
return asSet().stream();
}

@NotNull
HitBox difference(HitBox other);

Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ it-unimi-dsi-fastutil = "8.5.13"
junit-junit = "4.13.2"
net-kyori-adventure-api = "4.17.0"
net-kyori-adventure-platform-bukkit = "4.3.2"
org-ejml-library = "0.42"
org-hamcrest-hamcrest-library = "1.3"
org-jetbrains-annotations = "24.1.0"
org-junit-jupiter-junit-jupiter-api = "5.10.2"
Expand All @@ -21,6 +22,7 @@ it-unimi-dsi-fastutil = { module = "it.unimi.dsi:fastutil", version.ref = "it-un
junit-junit = { module = "junit:junit", version.ref = "junit-junit" }
net-kyori-adventure-api = { module = "net.kyori:adventure-api", version.ref = "net-kyori-adventure-api" }
net-kyori-adventure-platform-bukkit = { module = "net.kyori:adventure-platform-bukkit", version.ref = "net-kyori-adventure-platform-bukkit" }
org-ejml-simple-library = { module = "org.ejml:ejml-simple", version.ref = "org-ejml-library" }
org-hamcrest-hamcrest-library = { module = "org.hamcrest:hamcrest-library", version.ref = "org-hamcrest-hamcrest-library" }
org-jetbrains-annotations = { module = "org.jetbrains:annotations", version.ref = "org-jetbrains-annotations" }
org-junit-jupiter-junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "org-junit-jupiter-junit-jupiter-api" }
Expand Down
Loading