diff --git a/api/src/main/java/ru/dragonestia/msb3/api/boot/DefaultBootstrap.java b/api/src/main/java/ru/dragonestia/msb3/api/boot/DefaultBootstrap.java index edc547a..935f4f0 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/boot/DefaultBootstrap.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/boot/DefaultBootstrap.java @@ -1,7 +1,14 @@ package ru.dragonestia.msb3.api.boot; import lombok.extern.log4j.Log4j2; +import net.kyori.adventure.text.Component; +import net.minestom.server.coordinate.Point; +import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.GameMode; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import ru.dragonestia.msb3.api.entity.debug.DebugLine; +import ru.dragonestia.msb3.api.entity.debug.DebugMarker; import ru.dragonestia.msb3.api.module.FlatWorldModule; import ru.dragonestia.msb3.api.module.MotdModule; import ru.dragonestia.msb3.api.module.PrometheusMetricsModule; @@ -9,6 +16,7 @@ import ru.dragonestia.msb3.api.module.ResourcePackRepositoryModule; import team.unnamed.creative.ResourcePack; import java.net.InetSocketAddress; +import java.util.concurrent.atomic.AtomicInteger; @Log4j2 public class DefaultBootstrap extends ServerInitializer { @@ -20,7 +28,7 @@ public class DefaultBootstrap extends ServerInitializer { @Override public void onDefaultModulesLoaded() { MotdModule.init("logo.png", "msb3 server"); - FlatWorldModule.init(GameMode.ADVENTURE); + FlatWorldModule.init(GameMode.CREATIVE); PrometheusMetricsModule.init(new InetSocketAddress("0.0.0.0", 7500)); } @@ -33,4 +41,44 @@ public class DefaultBootstrap extends ServerInitializer { public void onResourcePackCompiled(ResourcePack resourcePack) { ResourcePackRepositoryModule.init("0.0.0.0", 7270); } + + @Override + public void onServerStarted() { + var instance = FlatWorldModule.getWorld().getInstance(); + + int radius = 20; + int length = 8; + var center = new Vec(0, 40, 0); + for (int i = -20; i < 40; i++) { + var rad = Math.toRadians(360.0 / 30 * i); + var normal = new Vec(Math.sin(rad), (i - 15.0) / 7, Math.cos(rad)).normalize(); + var startPos = center.add(normal.mul(radius, 1, radius)); + var endPos = startPos.add(normal.mul(length)); + drawLine(instance, startPos, endPos); + } + + drawLine(instance, new Vec(3, 50, 0), new Vec(3, 60, 0)); + drawLine(instance, new Vec(-3, 60, 0), new Vec(-3, 50, 0)); + drawLine(instance, new Vec(10, 60, 3), new Vec(-10, 60, 3)); + drawLine(instance, new Vec(-10, 60, -3), new Vec(10, 60, -3)); + drawLine(instance, new Vec(0, 57, 10), new Vec(0, 57, -10)); + drawLine(instance, new Vec(0, 53, -10), new Vec(0, 53, 10)); + } + + private static final AtomicInteger I = new AtomicInteger(1); + + private void drawLine(Instance instance, Point startPos, Point endPos) { + var id = I.getAndIncrement(); + { + var marker = new DebugMarker("test", Block.RED_CONCRETE, Component.text("[%s] START".formatted(id))); + marker.setInstance(instance, startPos); + } + + { + var marker = new DebugMarker("test", Block.RED_CONCRETE, Component.text("[%s] END".formatted(id))); + marker.setInstance(instance, endPos); + } + + DebugLine.spawn(instance, startPos, endPos, Block.LIME_CONCRETE, "test"); + } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java b/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java new file mode 100644 index 0000000..531cc6b --- /dev/null +++ b/api/src/main/java/ru/dragonestia/msb3/api/debug/Graph.java @@ -0,0 +1,129 @@ +package ru.dragonestia.msb3.api.debug; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.minestom.server.coordinate.Vec; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +public class Graph { + + private final static AtomicInteger freeId = new AtomicInteger(1); + + private final Map nodes = new HashMap<>(); + + public Node addNode(Vec pos) { + var node = new Node(pos); + nodes.put(node.getId(), node); + return node; + } + + public boolean addNodeLink(Node from, Node to) { + return from.addLink(to) && to.addLink(from); + } + + public void removeLink(Node from, Node to) { + from.removeLink(to); + to.removeLink(from); + } + + public void removeNode(Node node) { + nodes.remove(node.getId()); + node.getLinks().forEach(other -> removeLink(other, node)); + } + + public Collection getNodes() { + return nodes.values(); + } + + public Optional findNode(int id) { + return Optional.ofNullable(nodes.get(id)); + } + + public SerializedGraph serialize() { + var nodes = new ArrayList(); + var links = new HashSet(); + + for (var node: getNodes()) { + nodes.add(new SerializedNode(node.id, node.position.x(), node.position.y(), node.position.z())); + + for (var otherNode: node.getLinks()) { + if (links.contains(new IntPair(otherNode.getId(), node.getId()))) continue; + links.add(new IntPair(node.getId(), otherNode.getId())); + } + } + return new SerializedGraph(nodes, links); + } + + public static Graph deserialize(SerializedGraph serializedGraph) { + var graph = new Graph(); + for (var node: serializedGraph.nodes) { + graph.addNode(new Vec(node.x(), node.y(), node.z())); + } + for (var link: serializedGraph.links) { + graph.addNodeLink(graph.findNode(link.a).orElseThrow(), graph.findNode(link.b).orElseThrow()); + } + return graph; + } + + @RequiredArgsConstructor + public static class Node { + + @Getter private final int id = freeId.getAndIncrement(); + @Getter private final Vec position; + private final Map links = new HashMap<>(); + + private boolean addLink(Node node) { + if (id == node.getId()) return false; + return links.put(node, position.distance(node.position)) == null; + } + + private void removeLink(Node node) { + links.remove(node); + node.links.remove(this); + } + + public boolean hasLink(Node node) { + if (id == node.getId()) return true; + return links.containsKey(node); + } + + public Set getLinks() { + return links.keySet(); + } + + public Collection getLinksWithDistance() { + return links.entrySet().stream() + .map(entry -> new DistancePair(entry.getKey(), entry.getValue())) + .toList(); + } + + public double getDistanceTo(Node node) { + return links.getOrDefault(node, Double.NaN); + } + + @Override + public int hashCode() { + return id; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) return true; + if (obj == null) return false; + if (obj instanceof Node other) { + return id == other.id; + } + return false; + } + } + + public record SerializedGraph(List nodes, Set links) {} + + public record SerializedNode(int id, double x, double y, double z) {} + + public record IntPair(int a, int b) {} + + public record DistancePair(Node node, double distance) {} +} diff --git a/api/src/main/java/ru/dragonestia/msb3/api/entity/debug/DebugLine.java b/api/src/main/java/ru/dragonestia/msb3/api/entity/debug/DebugLine.java index 17aa1de..1edf950 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/entity/debug/DebugLine.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/entity/debug/DebugLine.java @@ -2,63 +2,52 @@ package ru.dragonestia.msb3.api.entity.debug; import lombok.Getter; import lombok.extern.log4j.Log4j2; -import net.kyori.adventure.text.format.TextColor; import net.minestom.server.coordinate.Point; import net.minestom.server.coordinate.Vec; import net.minestom.server.entity.EntityType; -import net.minestom.server.network.packet.server.play.ParticlePacket; -import net.minestom.server.particle.Particle; +import net.minestom.server.entity.metadata.display.BlockDisplayMeta; +import net.minestom.server.instance.Instance; +import net.minestom.server.instance.block.Block; +import ru.dragonestia.msb3.api.math.Quaternion; +@Getter @Log4j2 public class DebugLine extends DebugRendererEntity { - @Getter private Point startPos; - @Getter private Point endPos; - private double dist; - private long lastTick = 0; - private final Particle particle; + private Vec normalWithLength; + private Block block; - public DebugLine(String layerName, Point start, Point end, TextColor color) { - super(EntityType.TEXT_DISPLAY, layerName); - this.startPos = start; - this.endPos = end; - updateDist(); + protected DebugLine(String layerName, Vec normalWithLength, Block block) { + super(EntityType.BLOCK_DISPLAY, layerName); - particle = Particle.DUST.withColor(color).withScale(1); + var meta = getMeta(); + meta.setBrightnessOverride(15); + meta.setBrightness(15, 15); + + updateNormalWithLength(normalWithLength); + updateBlock(block); } - @Override - public void tick(long time) { - super.tick(time); - - if (time - lastTick < 500) return; - lastTick = time; - - if (getViewers().isEmpty()) return; - - double currentDist = 0; - while ((currentDist += 0.7) < dist) { - var pos = Vec.fromPoint(endPos.sub(startPos)) - .normalize() - .mul(currentDist) - .add(startPos); - - var pk = new ParticlePacket(particle, pos.x(), pos.y(), pos.z(), 0, 0, 0, 0, 1); - getViewers().forEach(player -> player.sendPacket(pk)); - } + public void updateNormalWithLength(Vec newValue) { + normalWithLength = newValue; + var meta = getMeta(); + meta.setScale(new Vec(0.1, normalWithLength.length(), 0.1)); + meta.setLeftRotation(Quaternion.DEFAULT + .rotate(newValue.normalize()) + .toFloatArray()); } - private void updateDist() { - dist = endPos.distance(startPos); + public void updateBlock(Block block) { + getMeta().setBlockState(this.block = block); } - public void setStartPos(Point startPos) { - this.startPos = startPos; - updateDist(); + private BlockDisplayMeta getMeta() { + return (BlockDisplayMeta) getEntityMeta(); } - public void setEndPos(Point endPos) { - this.endPos = endPos; - updateDist(); + public static DebugLine spawn(Instance instance, Point start, Point end, Block block, String layerName) { + var entity = new DebugLine(layerName, Vec.fromPoint(end.sub(start)), block); + entity.setInstance(instance, start); + return entity; } } diff --git a/api/src/main/java/ru/dragonestia/msb3/api/math/Quaternion.java b/api/src/main/java/ru/dragonestia/msb3/api/math/Quaternion.java index 1f49256..16df674 100644 --- a/api/src/main/java/ru/dragonestia/msb3/api/math/Quaternion.java +++ b/api/src/main/java/ru/dragonestia/msb3/api/math/Quaternion.java @@ -2,17 +2,73 @@ package ru.dragonestia.msb3.api.math; import net.minestom.server.coordinate.Vec; -public record Quaternion(float w, float x, float y, float z) { +public record Quaternion(double x, double y, double z, double w) { - public static Quaternion fromVec(Vec vec, double angle) { - double halfAngle = angle / 2.0; - double sinHalfAngle = Math.sin(halfAngle); + public static final Quaternion DEFAULT = new Quaternion(0, 0, 0, 1); - double qX = vec.x() * sinHalfAngle; - double qY = vec.y() * sinHalfAngle; - double qZ = vec.z() * sinHalfAngle; - double qW = Math.cos(halfAngle); + public static Quaternion fromVec (Vec vec, double realPart) { + return new Quaternion(vec.x(), vec.y(), vec.z(), realPart); + } - return new Quaternion((float) qW, (float) qX, (float) qY, (float) qZ); + public float[] toFloatArray() { + return new float[] { (float) x, (float) y, (float) z, (float) w }; + } + + public double length() { + return Math.sqrt(x * x + y * y + z * z + w * w); + } + + public Quaternion normalize() { + var length = length(); + return new Quaternion(x / length, y / length, z / length, w / length); + } + + public Quaternion conjugate() { + return new Quaternion(-x, -y, -z, w); + } + + public double dotProduct(Quaternion target) { + return (x * target.x + y * target.y + z * target.z + w * target.w + ); + } + + public Quaternion mul(Quaternion target) { + return new Quaternion( + w() * target.x() + x() * target.w() + y() * target.z() - z() * target.y(), + w() * target.y() - x() * target.z() + y() * target.w() + z() * target.x(), + w() * target.z() + x() * target.y() - y() * target.x() + z() * target.w(), + w() * target.w() - x() * target.x() - y() * target.y() - z() * target.z() + ); + } + + public Quaternion add(Quaternion target) { + return new Quaternion( + x + target.x, + y + target.y, + z + target.z, + w + target.w + ); + } + + public Quaternion sub(Quaternion target) { + return new Quaternion( + x - target.x, + y - target.y, + z - target.z, + w - target.w + ); + } + + public Quaternion rotate(Vec normal) { + var defaultQuaternion = new Quaternion(0, 0, 0, 1); + var defaultVector = new Vec(0, 1, 0); + + var feta = Math.acos(defaultVector.dot(normal)); + var r = defaultVector.cross(normal); + if (r.isZero()){ + r = new Vec(0, 0, 1); + } + var rotation = fromVec(r.normalize().mul(Math.sin(feta / 2)), Math.cos(feta / 2)); + return defaultQuaternion.mul(rotation); } }