more refactors

This commit is contained in:
dfsek
2021-06-23 10:05:29 -07:00
parent 7495a6b4a1
commit bb888380aa
86 changed files with 169 additions and 104 deletions

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.profiler;
public class ProfileFrame implements AutoCloseable {
private final Runnable action;
public ProfileFrame(Runnable action) {
this.action = action;
}
@Override
public void close() {
action.run();
}
}

View File

@@ -0,0 +1,56 @@
package com.dfsek.terra.api.profiler;
import java.util.Map;
public interface Profiler {
/**
* Push a frame to this profiler.
*
* @param frame ID of frame.
*/
void push(String frame);
/**
* Pop a frame from this profiler.
*
* @param frame ID of frame. Must match ID
* at the top of the profiler stack.
*/
void pop(String frame);
/**
* Start profiling.
*/
void start();
/**
* Stop profiling.
*/
void stop();
/**
* Get the profiler data.
*
* @return Profiler data.
*/
Map<String, Timings> getTimings();
/**
* Return a {@link AutoCloseable} implementation that
* may be used in a try-with-resources statement for
* more intuitive profiling, with auto-push/pop.
*
* @param frame ID of frame.
* @return {@link AutoCloseable} implementation for use
* in try-with-resources.
*/
default ProfileFrame profile(String frame) {
push(frame);
return new ProfileFrame(() -> pop(frame));
}
/**
* Clear the profiler data.
*/
void reset();
}

View File

@@ -0,0 +1,73 @@
package com.dfsek.terra.api.profiler;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Timings {
private final Map<String, Timings> subItems = new HashMap<>();
private final List<Long> timings = new ArrayList<>();
public void addTime(long time) {
timings.add(time);
}
public List<Long> getTimings() {
return timings;
}
public double average() {
return (double) timings.stream().reduce(0L, Long::sum) / timings.size();
}
public long max() {
return timings.stream().mapToLong(Long::longValue).max().orElse(0L);
}
public long min() {
return timings.stream().mapToLong(Long::longValue).min().orElse(0L);
}
public double sum() {
return timings.stream().mapToDouble(Long::doubleValue).sum();
}
public Timings getSubItem(String id) {
return subItems.computeIfAbsent(id, s -> new Timings());
}
private String toString(int indent, Timings parent, Set<Integer> branches) {
StringBuilder builder = new StringBuilder();
builder.append((double) min() / 1000000).append("ms min / ").append(average() / 1000000).append("ms avg / ")
.append((double) max() / 1000000).append("ms max (").append(timings.size()).append(" samples, ")
.append((sum() / parent.sum()) * 100).append("% of parent)");
List<String> frames = new ArrayList<>();
Set<Integer> newBranches = new HashSet<>(branches);
newBranches.add(indent);
subItems.forEach((id, timings) -> frames.add(id + ": " + timings.toString(indent + 1, this, newBranches)));
for(int i = 0; i < frames.size(); i++) {
builder.append('\n');
for(int j = 0; j < indent; j++) {
if(branches.contains(j)) builder.append("");
else builder.append(" ");
}
if(i == frames.size() - 1 && !frames.get(i).contains("\n")) builder.append("└───");
else builder.append("├───");
builder.append(frames.get(i));
}
return builder.toString();
}
@Override
public String toString() {
return toString(1, this, Collections.emptySet());
}
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.structure.buffer;
import com.dfsek.terra.api.vector.Location;
public interface Buffer {
Buffer addItem(BufferedItem item, Location location);
Location getOrigin();
String getMark(Location location);
Buffer setMark(String mark, Location location);
}

View File

@@ -0,0 +1,13 @@
package com.dfsek.terra.api.structure.buffer;
import com.dfsek.terra.api.vector.Location;
import com.dfsek.terra.api.world.Chunk;
public interface BufferedItem {
void paste(Location origin);
default void paste(Chunk chunk, Location origin) {
origin.setWorld(chunk.getWorld()); // Fabric weirdness
paste(origin);
}
}

View File

@@ -0,0 +1,55 @@
package com.dfsek.terra.api.structure.rotation;
import net.jafama.FastMath;
public enum Rotation {
CW_90(90), CW_180(180), CCW_90(270), NONE(0);
private final int degrees;
Rotation(int degrees) {
this.degrees = degrees;
}
public static Rotation fromDegrees(int deg) {
switch(FastMath.floorMod(deg, 360)) {
case 0:
return Rotation.NONE;
case 90:
return Rotation.CW_90;
case 180:
return Rotation.CW_180;
case 270:
return Rotation.CCW_90;
default:
throw new IllegalArgumentException();
}
}
public int getDegrees() {
return degrees;
}
public Rotation inverse() {
switch(this) {
case NONE:
return NONE;
case CCW_90:
return CW_90;
case CW_90:
return CCW_90;
case CW_180:
return CW_180;
default:
throw new IllegalArgumentException();
}
}
public Rotation rotate(Rotation rotation) {
return fromDegrees(this.getDegrees() + rotation.getDegrees());
}
public enum Axis {
X, Y, Z
}
}

View File

@@ -0,0 +1,291 @@
package com.dfsek.terra.api.structure.rotation;
import com.dfsek.terra.api.vector.Vector2;
import com.dfsek.terra.api.block.Axis;
import com.dfsek.terra.api.block.BlockData;
import com.dfsek.terra.api.block.BlockFace;
import com.dfsek.terra.api.block.data.Directional;
import com.dfsek.terra.api.block.data.MultipleFacing;
import com.dfsek.terra.api.block.data.Orientable;
import com.dfsek.terra.api.block.data.Rail;
import com.dfsek.terra.api.block.data.RedstoneWire;
import com.dfsek.terra.api.block.data.Rotatable;
import com.dfsek.terra.api.block.data.Wall;
import com.google.common.collect.Sets;
import net.jafama.FastMath;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
public class RotationUtil {
private static final Set<BlockFace> CARDINALS = Sets.newHashSet(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST);
/**
* Rotate and mirror a coordinate pair.
*
* @param orig Vector to rotate.
* @param r Rotation
*/
public static void rotateVector(Vector2 orig, Rotation r) {
Vector2 copy = orig.clone();
switch(r) {
case CW_90:
copy.setX(orig.getZ()).setZ(-orig.getX());
break;
case CCW_90:
copy.setX(-orig.getZ()).setZ(orig.getX());
break;
case CW_180:
copy.multiply(-1);
break;
}
orig.setX(copy.getX());
orig.setZ(copy.getZ());
}
/**
* Get the BlockFace with rotation and mirrors applied to it
*
* @param f BlockFace to apply rotation to
* @param r Rotation
* @return Rotated BlockFace
*/
public static BlockFace getRotatedFace(BlockFace f, Rotation r) {
BlockFace n = f;
int rotateNum = r.getDegrees() / 90;
int rn = faceRotation(f);
if(rn >= 0) {
n = fromRotation(faceRotation(n) + 4 * rotateNum);
}
return n;
}
/**
* Get an integer representation of a BlockFace, to perform math on.
*
* @param f BlockFace to get integer for
* @return integer representation of BlockFace
*/
public static int faceRotation(BlockFace f) {
switch(f) {
case NORTH:
return 0;
case NORTH_NORTH_EAST:
return 1;
case NORTH_EAST:
return 2;
case EAST_NORTH_EAST:
return 3;
case EAST:
return 4;
case EAST_SOUTH_EAST:
return 5;
case SOUTH_EAST:
return 6;
case SOUTH_SOUTH_EAST:
return 7;
case SOUTH:
return 8;
case SOUTH_SOUTH_WEST:
return 9;
case SOUTH_WEST:
return 10;
case WEST_SOUTH_WEST:
return 11;
case WEST:
return 12;
case WEST_NORTH_WEST:
return 13;
case NORTH_WEST:
return 14;
case NORTH_NORTH_WEST:
return 15;
default:
return -1;
}
}
/**
* Convert integer to BlockFace representation
*
* @param r integer to get BlockFace for
* @return BlockFace represented by integer.
*/
public static BlockFace fromRotation(int r) {
switch(FastMath.floorMod(r, 16)) {
case 0:
return BlockFace.NORTH;
case 1:
return BlockFace.NORTH_NORTH_EAST;
case 2:
return BlockFace.NORTH_EAST;
case 3:
return BlockFace.EAST_NORTH_EAST;
case 4:
return BlockFace.EAST;
case 5:
return BlockFace.EAST_SOUTH_EAST;
case 6:
return BlockFace.SOUTH_EAST;
case 7:
return BlockFace.SOUTH_SOUTH_EAST;
case 8:
return BlockFace.SOUTH;
case 9:
return BlockFace.SOUTH_SOUTH_WEST;
case 10:
return BlockFace.SOUTH_WEST;
case 11:
return BlockFace.WEST_SOUTH_WEST;
case 12:
return BlockFace.WEST;
case 13:
return BlockFace.WEST_NORTH_WEST;
case 14:
return BlockFace.NORTH_WEST;
case 15:
return BlockFace.NORTH_NORTH_WEST;
default:
throw new IllegalArgumentException();
}
}
public static Axis getRotatedAxis(Axis orig, Rotation r) {
Axis other = orig;
final boolean shouldSwitch = r.equals(Rotation.CW_90) || r.equals(Rotation.CCW_90);
switch(orig) {
case X:
if(shouldSwitch) other = Axis.Z;
break;
case Z:
if(shouldSwitch) other = Axis.X;
break;
}
return other;
}
/**
* Method to rotate the incredibly obnoxious Rail.Shape enum
*
* @param orig Original shape
* @param r Rotate
* @return Rotated/mirrored shape
*/
@SuppressWarnings("fallthrough")
public static Rail.Shape getRotatedRail(Rail.Shape orig, Rotation r) {
switch(r) {
case CCW_90:
switch(orig) {
case NORTH_WEST:
return Rail.Shape.SOUTH_WEST;
case NORTH_SOUTH:
return Rail.Shape.EAST_WEST;
case SOUTH_WEST:
return Rail.Shape.SOUTH_EAST;
case SOUTH_EAST:
return Rail.Shape.NORTH_EAST;
case EAST_WEST:
return Rail.Shape.NORTH_SOUTH;
case NORTH_EAST:
return Rail.Shape.NORTH_WEST;
case ASCENDING_EAST:
return Rail.Shape.ASCENDING_NORTH;
case ASCENDING_WEST:
return Rail.Shape.ASCENDING_SOUTH;
case ASCENDING_NORTH:
return Rail.Shape.ASCENDING_WEST;
case ASCENDING_SOUTH:
return Rail.Shape.ASCENDING_EAST;
}
case CW_90:
switch(orig) {
case NORTH_WEST:
return Rail.Shape.NORTH_EAST;
case NORTH_SOUTH:
return Rail.Shape.EAST_WEST;
case SOUTH_WEST:
return Rail.Shape.NORTH_WEST;
case SOUTH_EAST:
return Rail.Shape.SOUTH_WEST;
case EAST_WEST:
return Rail.Shape.NORTH_SOUTH;
case NORTH_EAST:
return Rail.Shape.SOUTH_EAST;
case ASCENDING_EAST:
return Rail.Shape.ASCENDING_SOUTH;
case ASCENDING_WEST:
return Rail.Shape.ASCENDING_NORTH;
case ASCENDING_NORTH:
return Rail.Shape.ASCENDING_EAST;
case ASCENDING_SOUTH:
return Rail.Shape.ASCENDING_WEST;
}
case CW_180:
switch(orig) {
case NORTH_WEST:
return Rail.Shape.SOUTH_EAST;
case NORTH_SOUTH:
return Rail.Shape.NORTH_SOUTH;
case SOUTH_WEST:
return Rail.Shape.NORTH_EAST;
case SOUTH_EAST:
return Rail.Shape.NORTH_WEST;
case EAST_WEST:
return Rail.Shape.EAST_WEST;
case NORTH_EAST:
return Rail.Shape.SOUTH_WEST;
case ASCENDING_EAST:
return Rail.Shape.ASCENDING_WEST;
case ASCENDING_WEST:
return Rail.Shape.ASCENDING_EAST;
case ASCENDING_NORTH:
return Rail.Shape.ASCENDING_SOUTH;
case ASCENDING_SOUTH:
return Rail.Shape.ASCENDING_NORTH;
}
}
return orig;
}
public static void rotateBlockData(BlockData data, Rotation r) {
if(data instanceof Rotatable) {
BlockFace rt = getRotatedFace(((Rotatable) data).getRotation(), r);
((Rotatable) data).setRotation(rt);
} else if(data instanceof Directional) {
BlockFace rt = getRotatedFace(((Directional) data).getFacing(), r);
((Directional) data).setFacing(rt);
} else if(data instanceof MultipleFacing) {
MultipleFacing mfData = (MultipleFacing) data;
Map<BlockFace, Boolean> faces = new EnumMap<>(BlockFace.class);
for(BlockFace f : mfData.getAllowedFaces()) {
faces.put(f, mfData.hasFace(f));
}
for(Map.Entry<BlockFace, Boolean> face : faces.entrySet()) {
mfData.setFace(getRotatedFace(face.getKey(), r), face.getValue());
}
} else if(data instanceof Rail) {
Rail.Shape newShape = getRotatedRail(((Rail) data).getShape(), r);
((Rail) data).setShape(newShape);
} else if(data instanceof Orientable) {
Axis newAxis = getRotatedAxis(((Orientable) data).getAxis(), r);
((Orientable) data).setAxis(newAxis);
} else if(data instanceof RedstoneWire) {
Map<BlockFace, RedstoneWire.Connection> connections = new EnumMap<>(BlockFace.class);
RedstoneWire rData = (RedstoneWire) data;
for(BlockFace f : rData.getAllowedFaces()) {
connections.put(f, rData.getFace(f));
}
for(Map.Entry<BlockFace, RedstoneWire.Connection> e : connections.entrySet()) {
rData.setFace(getRotatedFace(e.getKey(), r), e.getValue());
}
} else if(data instanceof Wall) {
Wall wallData = (Wall) data;
Map<BlockFace, Wall.Height> faces = new EnumMap<>(BlockFace.class);
for(BlockFace b : CARDINALS) faces.put(b, wallData.getHeight(b));
for(Map.Entry<BlockFace, Wall.Height> face : faces.entrySet()) {
wallData.setHeight(getRotatedFace(face.getKey(), r), face.getValue());
}
}
}
}

View File

@@ -0,0 +1,36 @@
package com.dfsek.terra.api.util;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.Random;
public interface Range extends Iterable<Integer> {
boolean isInRange(int test);
int getMax();
ConstantRange setMax(int max);
int getMin();
ConstantRange setMin(int min);
int getRange();
ConstantRange multiply(int mult);
ConstantRange reflect(int pt);
int get(Random r);
ConstantRange intersects(ConstantRange other);
ConstantRange add(int add);
ConstantRange sub(int sub);
@NotNull
@Override
Iterator<Integer> iterator();
}

View File

@@ -0,0 +1,52 @@
package com.dfsek.terra.api.util.generic.either;
import java.util.Optional;
import java.util.function.Consumer;
public final class Either<L, R> {
private final L left;
private final R right;
private final boolean leftPresent;
private Either(L left, R right, boolean leftPresent) {
this.left = left;
this.right = right;
this.leftPresent = leftPresent;
}
public static <L1, R1> Either<L1, R1> left(L1 left) {
return new Either<>(left, null, true);
}
public static <L1, R1> Either<L1, R1> right(R1 right) {
return new Either<>(null, right, false);
}
public Optional<L> getLeft() {
if(leftPresent) return Optional.of(left);
return Optional.empty();
}
public Optional<R> getRight() {
if(!leftPresent) return Optional.of(right);
return Optional.empty();
}
public Either<L, R> ifLeft(Consumer<L> action) {
if(leftPresent) action.accept(left);
return this;
}
public Either<L, R> ifRight(Consumer<R> action) {
if(!leftPresent) action.accept(right);
return this;
}
public boolean hasLeft() {
return leftPresent;
}
public boolean hasRight() {
return !leftPresent;
}
}

View File

@@ -0,0 +1,34 @@
package com.dfsek.terra.api.util.generic.pair;
public final class ImmutablePair<L, R> {
private final L left;
private final R right;
private static final ImmutablePair<?, ?> NULL = new ImmutablePair<>(null, null);
private ImmutablePair(L left, R right) {
this.left = left;
this.right = right;
}
public static <L1, R1> ImmutablePair<L1, R1> of(L1 left, R1 right) {
return new ImmutablePair<>(left, right);
}
public R getRight() {
return right;
}
public L getLeft() {
return left;
}
@SuppressWarnings("unchecked")
public static <L1, R1> ImmutablePair<L1, R1> ofNull() {
return (ImmutablePair<L1, R1>) NULL;
}
public Pair<L, R> mutable() {
return Pair.of(left, right);
}
}

View File

@@ -0,0 +1,35 @@
package com.dfsek.terra.api.util.generic.pair;
public class Pair<L, R> {
private L left;
private R right;
private Pair(L left, R right) {
this.left = left;
this.right = right;
}
public static <L1, R1> Pair<L1, R1> of(L1 left, R1 right) {
return new Pair<>(left, right);
}
public L getLeft() {
return left;
}
public void setLeft(L left) {
this.left = left;
}
public R getRight() {
return right;
}
public void setRight(R right) {
this.right = right;
}
public ImmutablePair<L, R> immutable() {
return ImmutablePair.of(left, right);
}
}

View File

@@ -0,0 +1,35 @@
package com.dfsek.terra.api.util.mutable;
import org.jetbrains.annotations.NotNull;
public class MutableBoolean implements MutablePrimitive<Boolean> {
private boolean value;
public MutableBoolean() {
this.value = false;
}
public MutableBoolean(boolean value) {
this.value = value;
}
@Override
public Boolean get() {
return value;
}
@Override
public void set(Boolean value) {
this.value = value;
}
public boolean invert() {
value = !value;
return value;
}
@Override
public int compareTo(@NotNull Boolean o) {
return Boolean.compare(value, o);
}
}

View File

@@ -0,0 +1,46 @@
package com.dfsek.terra.api.util.mutable;
import org.jetbrains.annotations.NotNull;
public class MutableDouble extends MutableNumber<Double> {
private static final long serialVersionUID = -2218110876763640053L;
public MutableDouble(Double value) {
super(value);
}
@Override
public void increment() {
add(1d);
}
@Override
public void decrement() {
subtract(1d);
}
@Override
public void add(Double add) {
value += add;
}
@Override
public void multiply(Double mul) {
value *= mul;
}
@Override
public void subtract(Double sub) {
value -= sub;
}
@Override
public void divide(Double divide) {
value /= divide;
}
@Override
public int compareTo(@NotNull Double o) {
return Double.compare(value, o);
}
}

View File

@@ -0,0 +1,48 @@
package com.dfsek.terra.api.util.mutable;
import org.jetbrains.annotations.NotNull;
public class MutableInteger extends MutableNumber<Integer> {
private static final long serialVersionUID = -4427935901819632745L;
public MutableInteger(Integer value) {
super(value);
}
public void increment() {
add(1);
}
public void decrement() {
subtract(1);
}
@Override
public void add(Integer add) {
value += add;
}
@Override
public void multiply(Integer mul) {
value *= mul;
}
@Override
public void subtract(Integer sub) {
value -= sub;
}
@Override
public void divide(Integer divide) {
value /= divide;
}
public void add(int add) {
value += add;
}
@Override
public int compareTo(@NotNull Integer o) {
return Integer.compare(value, o);
}
}

View File

@@ -0,0 +1,53 @@
package com.dfsek.terra.api.util.mutable;
public abstract class MutableNumber<T extends Number> extends Number implements MutablePrimitive<T> {
private static final long serialVersionUID = 8619508342781664393L;
protected T value;
public MutableNumber(T value) {
this.value = value;
}
public abstract void increment();
public abstract void decrement();
public abstract void add(T add);
public abstract void multiply(T mul);
public abstract void subtract(T sub);
public abstract void divide(T divide);
@Override
public T get() {
return value;
}
@Override
public void set(T value) {
this.value = value;
}
@Override
public int intValue() {
return value.intValue();
}
@Override
public long longValue() {
return value.longValue();
}
@Override
public float floatValue() {
return value.floatValue();
}
@Override
public double doubleValue() {
return value.doubleValue();
}
}

View File

@@ -0,0 +1,7 @@
package com.dfsek.terra.api.util.mutable;
public interface MutablePrimitive<T> extends Comparable<T>{
T get();
void set(T value);
}

View File

@@ -0,0 +1,4 @@
/**
* Mutable objects containing primitive types.
*/
package com.dfsek.terra.api.util.mutable;

View File

@@ -11,6 +11,8 @@ public interface Vector2 extends Cloneable {
*/
double getX();
Vector2 clone();
Vector2 setX(double x);
/**

View File

@@ -0,0 +1,14 @@
package com.dfsek.terra.api.world;
import com.dfsek.terra.api.math.Range;
import com.dfsek.terra.api.vector.Location;
import com.dfsek.terra.api.block.Block;
import com.dfsek.terra.api.world.Chunk;
import java.util.List;
public interface Flora {
List<Block> getValidSpawnsAt(Chunk chunk, int x, int z, Range check);
boolean plant(Location l);
}