diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/CLine.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/CLine.java index 1f877cce..a7628c40 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/CLine.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/CLine.java @@ -19,21 +19,23 @@ package de.steamwar.entity; +import lombok.AccessLevel; import lombok.Getter; import org.bukkit.Location; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Display; import org.bukkit.entity.Player; -import org.bukkit.util.Consumer; import org.bukkit.util.Transformation; import org.bukkit.util.Vector; import org.joml.Quaternionf; import org.joml.Vector3f; import java.util.Objects; +import java.util.function.Consumer; /** * Can be used for Axis Aligned Lines of near infinite length. + * The line is a single display, which is shown to players based on their view distance. */ @Getter public class CLine extends CEntity { @@ -42,6 +44,8 @@ public class CLine extends CEntity { private static final float offset = 1 / 1024f; private static final Vector offsetVec = new Vector(offset, offset, offset); + @Getter(AccessLevel.NONE) + private LineDisplay line; private Location from; private Location to; private float width = DEFAULT_WIDTH; @@ -99,9 +103,7 @@ public class CLine extends CEntity { if (hide == this.hide) return; this.hide = hide; if (hide) { - if (startLine != null) startLine.hide(true); - if (middleLine != null) middleLine.hide(true); - if (endLine != null) endLine.hide(true); + if (line != null) line.hide(true); } else { tick(); } @@ -109,148 +111,26 @@ public class CLine extends CEntity { @Override void tick() { - if (from == null || to == null) { - if (startLine != null) startLine.hide(true); - if (middleLine != null) middleLine.hide(true); - if (endLine != null) endLine.hide(true); - return; - } else { - if (startLine != null) startLine.hide(false); - if (middleLine != null) middleLine.hide(false); - if (endLine != null) endLine.hide(false); - } - if (hide) return; - updateStart(); - updateMiddle(); - updateEnd(); - } - - private RBlockDisplay startLine; - - private void updateStart() { - Vector vec = to.clone().subtract(from).toVector(); - if (vec.length() > 35) vec.normalize().multiply(35); - - if (startLine == null) { - startLine = new RBlockDisplay(server, new Location(null, 0, 0, 0)); - startLine.setBrightness(new Display.Brightness(15, 15)); - startLine.setViewRange(560); - startLine.setBlock(blockData); - entities.add(startLine); - } else { - startLine.hide(false); - } - - startLine.move(from.clone().subtract(offsetVec)); - startLine.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf(0, 0, 0, 1), addWidth(vec).toVector3f(), new Quaternionf(0, 0, 0, 1))); - } - - private RBlockDisplay middleLine; - - private void updateMiddle() { - Vector vec = to.clone().subtract(from).toVector(); - if (vec.length() <= 70) { - if (middleLine != null) middleLine.hide(true); + if (from == null || to == null || hide) { + if (line != null) line.hide(true); return; } - if (vec.length() > 280) vec.normalize().multiply(280); - else vec = vec.clone().normalize().multiply(vec.length() - 60); - if (middleLine == null) { - middleLine = new RBlockDisplay(server, new Location(null, 0, 0, 0)); - middleLine.setBrightness(new Display.Brightness(15, 15)); - middleLine.setViewRange(560); - middleLine.setBlock(blockData); - entities.add(middleLine); + if (line == null) { + line = new LineDisplay(server); + line.setBrightness(new Display.Brightness(15, 15)); + line.setViewRange(560); + line.setBlock(blockData); + entities.add(line); } else { - middleLine.hide(false); + line.hide(false); } - Player player = server.getPlayers().stream().findFirst().orElse(null); - if (player == null) return; - - Vector tempVector = vec.clone().normalize().multiply(30); - Location from = this.from.clone().add(tempVector); - Location to = this.to.clone().subtract(tempVector); - - Vector lineVec = to.clone().subtract(from).toVector(); - Vector playerVec = player.getLocation().toVector().subtract(from.toVector()); - double lineVecDotItself = lineVec.dot(lineVec); - Vector projectionVec = lineVec.clone().multiply(lineVec.dot(playerVec)).divide(new Vector(lineVecDotItself, lineVecDotItself, lineVecDotItself)); - - Vector moveVec = from.toVector().add(projectionVec); - if (moveVec.getX() < from.getX()) { - moveVec.setX(from.getX()); - } - if (moveVec.getX() > to.getX()) { - moveVec.setX(to.getX()); - } - if (moveVec.getY() < from.getY()) { - moveVec.setY(from.getY()); - } - if (moveVec.getY() > to.getY()) { - moveVec.setY(to.getY()); - } - if (moveVec.getZ() < from.getZ()) { - moveVec.setZ(from.getZ()); - } - if (moveVec.getZ() > to.getZ()) { - moveVec.setZ(to.getZ()); - } - - Vector translation = vec.clone().divide(new Vector(2, 2, 2)); - translation.setX(-translation.getX()); - translation.setY(-translation.getY()); - translation.setZ(-translation.getZ()); - - Vector first = moveVec.clone().add(translation).subtract(from.toVector()); - if (first.getX() < 0) { - translation.setX(translation.getX() - first.getX()); - } - if (first.getY() < 0) { - translation.setY(translation.getY() - first.getY()); - } - if (first.getZ() < 0) { - translation.setZ(translation.getZ() - first.getZ()); - } - - Vector second = to.toVector().subtract(moveVec.clone().subtract(translation)); - if (second.getX() < 0) { - translation.setX(translation.getX() + second.getX()); - } - if (second.getY() < 0) { - translation.setY(translation.getY() + second.getY()); - } - if (second.getZ() < 0) { - translation.setZ(translation.getZ() + second.getZ()); - } - - middleLine.move(moveVec.toLocation(player.getWorld()).subtract(offsetVec)); - middleLine.setTransform(new Transformation(translation.toVector3f(), new Quaternionf(0, 0, 0, 1), addWidth(vec).toVector3f(), new Quaternionf(0, 0, 0, 1))); - } - - private RBlockDisplay endLine; - - private void updateEnd() { Vector vec = to.clone().subtract(from).toVector(); - if (vec.length() <= 35) { - if (endLine != null) endLine.hide(true); - return; - } - if (vec.length() > 35) vec.normalize().multiply(35); - - if (endLine == null) { - endLine = new RBlockDisplay(server, new Location(null, 0, 0, 0)); - endLine.setBrightness(new Display.Brightness(15, 15)); - endLine.setViewRange(560); - endLine.setBlock(blockData); - entities.add(endLine); - } else { - endLine.hide(false); - } - - endLine.move(to.clone().subtract(offsetVec)); - endLine.setTransform(new Transformation(vec.toVector3f().negate(), new Quaternionf(0, 0, 0, 1), addWidth(vec).toVector3f(), new Quaternionf(0, 0, 0, 1))); + line.setSpan(from, to); + line.move(from.clone().subtract(offsetVec)); + line.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf(0, 0, 0, 1), addWidth(vec).toVector3f(), new Quaternionf(0, 0, 0, 1))); + server.updateViewers(line); } private Vector addWidth(Vector vector) { @@ -267,4 +147,31 @@ public class CLine extends CEntity { vector.add(offsetVec).add(offsetVec); return vector; } + + /** + * The line is one display that is visible for a player as soon as any chunk it passes through is in the view distance, + * independent of the chunk its origin is in. + */ + private static class LineDisplay extends RBlockDisplay { + + private int minChunkX, maxChunkX, minChunkZ, maxChunkZ; + + private LineDisplay(REntityServer server) { + super(server, new Location(null, 0, 0, 0)); + } + + private void setSpan(Location from, Location to) { + minChunkX = Math.floorDiv((int) Math.floor(Math.min(from.getX(), to.getX())), 16); + maxChunkX = Math.floorDiv((int) Math.floor(Math.max(from.getX(), to.getX())), 16); + minChunkZ = Math.floorDiv((int) Math.floor(Math.min(from.getZ(), to.getZ())), 16); + maxChunkZ = Math.floorDiv((int) Math.floor(Math.max(from.getZ(), to.getZ())), 16); + } + + @Override + protected boolean canView(Player player, int playerChunkX, int playerChunkZ, int entityChunkX, int entityChunkZ) { + int distanceX = Math.max(0, Math.max(minChunkX - playerChunkX, playerChunkX - maxChunkX)); + int distanceZ = Math.max(0, Math.max(minChunkZ - playerChunkZ, playerChunkZ - maxChunkZ)); + return inViewDistance(player, Math.max(distanceX, distanceZ)); + } + } } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java index d1c7cc98..28d9744a 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/RDisplay.java @@ -143,8 +143,8 @@ public abstract class RDisplay extends REntity { * A view range of 1 equals 64 blocks. This is only a comparison, so huge values like {@link Float#MAX_VALUE} are cheap. */ @Override - protected boolean canView(Player player, int chunkDistance) { - return chunkDistance <= Math.ceil(viewRange * 64.0 / 16.0); + protected boolean canView(Player player, int playerChunkX, int playerChunkZ, int entityChunkX, int entityChunkZ) { + return chunkDistance(playerChunkX, playerChunkZ, entityChunkX, entityChunkZ) <= Math.ceil(viewRange * 64.0 / 16.0); } private static final EntityDataAccessor viewRangeWatcher = new EntityDataAccessor<>(17, EntityDataSerializers.FLOAT); diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java index 5d6f422f..8b6697ed 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntity.java @@ -129,9 +129,21 @@ public class REntity { * Whether the given player should see this entity. * Defaults to the view distance of the player. * - * @param chunkDistance the larger of the chunk distances between the player and the entity on the x and z axis + * @param playerChunkX the chunk the player is in + * @param entityChunkX the chunk the entity is in, the entity itself might not be moved there yet */ - protected boolean canView(Player player, int chunkDistance) { + protected boolean canView(Player player, int playerChunkX, int playerChunkZ, int entityChunkX, int entityChunkZ) { + return inViewDistance(player, chunkDistance(playerChunkX, playerChunkZ, entityChunkX, entityChunkZ)); + } + + /** + * @return the larger of the chunk distances on the x and z axis + */ + protected static int chunkDistance(int chunkX1, int chunkZ1, int chunkX2, int chunkZ2) { + return Math.max(Math.abs(chunkX1 - chunkX2), Math.abs(chunkZ1 - chunkZ2)); + } + + protected boolean inViewDistance(Player player, int chunkDistance) { return chunkDistance <= server.viewRadius(player); } diff --git a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java index 046c55f5..005dbf42 100644 --- a/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java +++ b/SpigotCore/SpigotCore_Main/src/de/steamwar/entity/REntityServer.java @@ -227,8 +227,7 @@ public class REntityServer implements Listener { private boolean inView(REntity entity, Player player, int entityChunkX, int entityChunkZ) { Location location = lastLocation.get(player); - int distance = Math.max(Math.abs(posToChunk(location.getX()) - entityChunkX), Math.abs(posToChunk(location.getZ()) - entityChunkZ)); - return entity.canView(player, distance); + return entity.canView(player, posToChunk(location.getX()), posToChunk(location.getZ()), entityChunkX, entityChunkZ); } private void showTo(REntity entity, Player player) {