mirror of
https://github.com/Minestom/Minestom.git
synced 2025-02-02 21:41:41 +01:00
Remove dependency from instance/entity for collisions
This commit is contained in:
parent
60f583b179
commit
df64ce9653
@ -3,11 +3,7 @@ package net.minestom.server.collision;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@ -20,13 +16,12 @@ final class BlockCollision {
|
||||
* <p>
|
||||
* Works by getting all the full blocks that an entity could interact with.
|
||||
* All bounding boxes inside the full blocks are checked for collisions with the entity.
|
||||
*
|
||||
* @param entity the entity to move
|
||||
* @return the result of physics simulation
|
||||
*/
|
||||
static PhysicsResult handlePhysics(@NotNull Entity entity, @NotNull Vec entityVelocity,
|
||||
static PhysicsResult handlePhysics(@NotNull BoundingBox boundingBox,
|
||||
@NotNull Vec entityVelocity, @NotNull Pos entityPosition,
|
||||
@NotNull Block.Getter getter,
|
||||
@Nullable PhysicsResult lastPhysicsResult) {
|
||||
final var faces = entity.getBoundingBox().faces();
|
||||
final var faces = boundingBox.faces();
|
||||
Vec remainingMove = entityVelocity;
|
||||
|
||||
// Allocate once and update values
|
||||
@ -39,14 +34,13 @@ final class BlockCollision {
|
||||
|
||||
// Check cache to see if the entity is standing on a block without moving.
|
||||
// If the entity isn't moving and the block below hasn't changed, return
|
||||
if (lastPhysicsResult != null && entity.getInstance() != null) {
|
||||
if (lastPhysicsResult != null) {
|
||||
if (lastPhysicsResult.collisionY()
|
||||
&& Math.signum(remainingMove.y()) == Math.signum(lastPhysicsResult.originalDelta().y())
|
||||
&& lastPhysicsResult.collidedBlockY() != null
|
||||
&& entity.getInstance().getChunk(lastPhysicsResult.collidedBlockY().chunkX(), lastPhysicsResult.collidedBlockY().chunkZ()) != null
|
||||
&& entity.getInstance().getBlock(lastPhysicsResult.collidedBlockY(), Block.Getter.Condition.TYPE) == lastPhysicsResult.blockTypeY()
|
||||
&& getter.getBlock(lastPhysicsResult.collidedBlockY(), Block.Getter.Condition.TYPE) == lastPhysicsResult.blockTypeY()
|
||||
&& remainingMove.x() == 0 && remainingMove.z() == 0
|
||||
&& entity.getPosition().samePoint(lastPhysicsResult.newPosition())
|
||||
&& entityPosition.samePoint(lastPhysicsResult.newPosition())
|
||||
&& lastPhysicsResult.blockTypeY() != Block.AIR) {
|
||||
remainingMove = remainingMove.withY(0);
|
||||
foundCollisionY = true;
|
||||
@ -65,16 +59,16 @@ final class BlockCollision {
|
||||
|
||||
if (remainingMove.isZero())
|
||||
if (lastPhysicsResult != null)
|
||||
return new PhysicsResult(entity.getPosition(), Vec.ZERO, lastPhysicsResult.isOnGround(),
|
||||
return new PhysicsResult(entityPosition, Vec.ZERO, lastPhysicsResult.isOnGround(),
|
||||
lastPhysicsResult.collisionX(), lastPhysicsResult.collisionY(), lastPhysicsResult.collisionZ(),
|
||||
entityVelocity, lastPhysicsResult.collidedBlockY(), lastPhysicsResult.blockTypeY());
|
||||
else
|
||||
return new PhysicsResult(entity.getPosition(), Vec.ZERO, false, false, false, false, entityVelocity, null, Block.AIR);
|
||||
return new PhysicsResult(entityPosition, Vec.ZERO, false, false, false, false, entityVelocity, null, Block.AIR);
|
||||
|
||||
// Query faces to get the points needed for collision
|
||||
Vec[] allFaces = faces.get(new Vec(Math.signum(remainingMove.x()), Math.signum(remainingMove.y()), Math.signum(remainingMove.z())));
|
||||
|
||||
PhysicsResult res = handlePhysics(entity, remainingMove, entity.getPosition(), allFaces, finalResult);
|
||||
PhysicsResult res = handlePhysics(boundingBox, remainingMove, entityPosition, getter, allFaces, finalResult);
|
||||
|
||||
// Loop until no collisions are found.
|
||||
// When collisions are found, the collision axis is set to 0
|
||||
@ -107,7 +101,7 @@ final class BlockCollision {
|
||||
|
||||
allFaces = faces.get(new Vec(Math.signum(remainingMove.x()), Math.signum(remainingMove.y()), Math.signum(remainingMove.z())));
|
||||
|
||||
res = handlePhysics(entity, res.newVelocity(), res.newPosition(), allFaces, finalResult);
|
||||
res = handlePhysics(boundingBox, res.newVelocity(), res.newPosition(), getter, allFaces, finalResult);
|
||||
}
|
||||
|
||||
final double newDeltaX = foundCollisionX ? 0 : entityVelocity.x();
|
||||
@ -119,21 +113,11 @@ final class BlockCollision {
|
||||
foundCollisionX, foundCollisionY, foundCollisionZ, entityVelocity, collisionYBlock, blockYType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a physics step until a boundary is found
|
||||
*
|
||||
* @param entity the entity to move
|
||||
* @param deltaPosition the movement vector
|
||||
* @param entityPosition the position of the entity
|
||||
* @param allFaces point list to use for collision checking
|
||||
* @param finalResult place to store final result of collision
|
||||
* @return result of physics calculation
|
||||
*/
|
||||
private static PhysicsResult handlePhysics(@NotNull Entity entity, @NotNull Vec deltaPosition, Pos entityPosition,
|
||||
@NotNull Vec[] allFaces, @NotNull SweepResult finalResult) {
|
||||
final Instance instance = entity.getInstance();
|
||||
final Chunk originChunk = entity.getChunk();
|
||||
final BoundingBox boundingBox = entity.getBoundingBox();
|
||||
private static PhysicsResult handlePhysics(@NotNull BoundingBox boundingBox,
|
||||
@NotNull Vec deltaPosition, Pos entityPosition,
|
||||
@NotNull Block.Getter getter,
|
||||
@NotNull Vec[] allFaces,
|
||||
@NotNull SweepResult finalResult) {
|
||||
|
||||
double remainingX = deltaPosition.x();
|
||||
double remainingY = deltaPosition.y();
|
||||
@ -153,47 +137,47 @@ final class BlockCollision {
|
||||
// Checks can be limited by checking if we moved across an axis line
|
||||
|
||||
// Pass through (0, 0, 0)
|
||||
checkBoundingBox(pointBefore.blockX(), pointBefore.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointBefore.blockX(), pointBefore.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
|
||||
if (pointBefore.blockX() != pointAfter.blockX()) {
|
||||
// Pass through (+1, 0, 0)
|
||||
checkBoundingBox(pointAfter.blockX(), pointBefore.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointAfter.blockX(), pointBefore.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
|
||||
// Checks for moving through 4 blocks
|
||||
if (pointBefore.blockY() != pointAfter.blockY())
|
||||
// Pass through (+1, +1, 0)
|
||||
checkBoundingBox(pointAfter.blockX(), pointAfter.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointAfter.blockX(), pointAfter.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
|
||||
if (pointBefore.blockZ() != pointAfter.blockZ())
|
||||
// Pass through (+1, 0, +1)
|
||||
checkBoundingBox(pointAfter.blockX(), pointBefore.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointAfter.blockX(), pointBefore.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
}
|
||||
|
||||
if (pointBefore.blockY() != pointAfter.blockY()) {
|
||||
// Pass through (0, +1, 0)
|
||||
checkBoundingBox(pointBefore.blockX(), pointAfter.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointBefore.blockX(), pointAfter.blockY(), pointBefore.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
|
||||
// Checks for moving through 4 blocks
|
||||
if (pointBefore.blockZ() != pointAfter.blockZ())
|
||||
// Pass through (0, +1, +1)
|
||||
checkBoundingBox(pointBefore.blockX(), pointAfter.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointBefore.blockX(), pointAfter.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
}
|
||||
|
||||
if (pointBefore.blockZ() != pointAfter.blockZ()) {
|
||||
// Pass through (0, 0, +1)
|
||||
checkBoundingBox(pointBefore.blockX(), pointBefore.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointBefore.blockX(), pointBefore.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
}
|
||||
|
||||
// Pass through (+1, +1, +1)
|
||||
if (pointBefore.blockX() != pointAfter.blockX()
|
||||
&& pointBefore.blockY() != pointAfter.blockY()
|
||||
&& pointBefore.blockZ() != pointAfter.blockZ())
|
||||
checkBoundingBox(pointAfter.blockX(), pointAfter.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, instance, originChunk, finalResult);
|
||||
checkBoundingBox(pointAfter.blockX(), pointAfter.blockY(), pointAfter.blockZ(), deltaPosition, entityPosition, boundingBox, getter, finalResult);
|
||||
}
|
||||
} else {
|
||||
// When large moves are done we need to ray-cast to find all blocks that could intersect with the movement
|
||||
for (Vec point : allFaces) {
|
||||
RayUtils.RaycastCollision(deltaPosition, point.add(entityPosition), instance, originChunk, boundingBox, entityPosition, finalResult);
|
||||
RayUtils.RaycastCollision(deltaPosition, point.add(entityPosition), getter, boundingBox, entityPosition, finalResult);
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,22 +228,15 @@ final class BlockCollision {
|
||||
* @param entityVelocity entity movement vector
|
||||
* @param entityPosition entity position
|
||||
* @param boundingBox entity bounding box
|
||||
* @param instance entity instance
|
||||
* @param originChunk entity chunk
|
||||
* @param getter block getter
|
||||
* @param finalResult place to store final result of collision
|
||||
* @return true if entity finds collision, other false
|
||||
*/
|
||||
static boolean checkBoundingBox(int blockX, int blockY, int blockZ,
|
||||
Vec entityVelocity, Pos entityPosition, BoundingBox boundingBox,
|
||||
Instance instance, Chunk originChunk, SweepResult finalResult) {
|
||||
final Chunk c = ChunkUtils.retrieve(instance, originChunk, blockX, blockZ);
|
||||
Block.Getter getter, SweepResult finalResult) {
|
||||
// Don't step if chunk isn't loaded yet
|
||||
final Block checkBlock;
|
||||
if (ChunkUtils.isLoaded(c)) {
|
||||
checkBlock = c.getBlock(blockX, blockY, blockZ, Block.Getter.Condition.TYPE);
|
||||
} else {
|
||||
checkBlock = Block.STONE; // Generic full block
|
||||
}
|
||||
final Block checkBlock = getter.getBlock(blockX, blockY, blockZ, Block.Getter.Condition.TYPE);
|
||||
boolean hitBlock = false;
|
||||
if (checkBlock.isSolid()) {
|
||||
final Vec blockPos = new Vec(blockX, blockY, blockZ);
|
||||
|
@ -3,12 +3,11 @@ package net.minestom.server.collision;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.WorldBorder;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.chunk.ChunkCache;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -30,7 +29,12 @@ public final class CollisionUtils {
|
||||
*/
|
||||
public static PhysicsResult handlePhysics(@NotNull Entity entity, @NotNull Vec entityVelocity,
|
||||
@Nullable PhysicsResult lastPhysicsResult) {
|
||||
return BlockCollision.handlePhysics(entity, entityVelocity, lastPhysicsResult);
|
||||
final BoundingBox boundingBox = entity.getBoundingBox();
|
||||
final Pos currentPosition = entity.getPosition();
|
||||
final Block.Getter getter = new ChunkCache(entity.getInstance(), entity.getChunk(), Block.STONE);
|
||||
return BlockCollision.handlePhysics(boundingBox,
|
||||
entityVelocity, currentPosition,
|
||||
getter, lastPhysicsResult);
|
||||
}
|
||||
|
||||
public static PhysicsResult handlePhysics(@NotNull Entity entity, @NotNull Vec entityVelocity) {
|
||||
|
@ -3,20 +3,10 @@ package net.minestom.server.collision;
|
||||
import net.minestom.server.coordinate.Point;
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.coordinate.Vec;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
|
||||
class RayUtils {
|
||||
/**
|
||||
* @param rayDirection Ray vector
|
||||
* @param rayStart Ray start point
|
||||
* @param instance entity instance
|
||||
* @param originChunk entity chunk
|
||||
* @param boundingBox entity bounding box
|
||||
* @param entityCentre position of entity
|
||||
* @param finalResult place to store final result of collision
|
||||
*/
|
||||
public static void RaycastCollision(Vec rayDirection, Point rayStart, Instance instance, Chunk originChunk, BoundingBox boundingBox, Pos entityCentre, SweepResult finalResult) {
|
||||
final class RayUtils {
|
||||
public static void RaycastCollision(Vec rayDirection, Point rayStart, Block.Getter getter, BoundingBox boundingBox, Pos entityCentre, SweepResult finalResult) {
|
||||
// This works by finding all the x, y and z grid line intersections and calculating the value of the point at that intersection
|
||||
// Finding all the intersections will give us all the full blocks that are traversed by the ray
|
||||
|
||||
@ -53,7 +43,7 @@ class RayUtils {
|
||||
|
||||
// Check for collisions with the found block
|
||||
// If a collision was found, break
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, instance, originChunk, finalResult))
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -79,7 +69,7 @@ class RayUtils {
|
||||
zi -= zFix;
|
||||
zStepsCompleted++;
|
||||
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, instance, originChunk, finalResult))
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -105,7 +95,7 @@ class RayUtils {
|
||||
yi -= yFix;
|
||||
yStepsCompleted++;
|
||||
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, instance, originChunk, finalResult))
|
||||
if (BlockCollision.checkBoundingBox(xi, yi, zi, rayDirection, entityCentre, boundingBox, getter, finalResult))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ import net.minestom.server.utils.ArrayUtils;
|
||||
import net.minestom.server.utils.PacketUtils;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
import net.minestom.server.utils.block.BlockIterator;
|
||||
import net.minestom.server.utils.chunk.ChunkCache;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import net.minestom.server.utils.entity.EntityUtils;
|
||||
import net.minestom.server.utils.player.PlayerUtils;
|
||||
@ -630,8 +631,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
// TODO do not call every tick (it is pretty expensive)
|
||||
final Pos position = this.position;
|
||||
final BoundingBox boundingBox = this.boundingBox;
|
||||
final Instance instance = this.instance;
|
||||
Chunk chunk = currentChunk;
|
||||
ChunkCache cache = new ChunkCache(instance, currentChunk);
|
||||
|
||||
final int minX = (int) Math.floor(boundingBox.minX() + position.x());
|
||||
final int maxX = (int) Math.ceil(boundingBox.maxX() + position.x());
|
||||
@ -643,9 +643,7 @@ public class Entity implements Viewable, Tickable, Schedulable, Snapshotable, Ev
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
chunk = ChunkUtils.retrieve(instance, chunk, x, z);
|
||||
if (!ChunkUtils.isLoaded(chunk)) continue;
|
||||
final Block block = chunk.getBlock(x, y, z, Block.Getter.Condition.CACHED);
|
||||
final Block block = cache.getBlock(x, y, z, Block.Getter.Condition.CACHED);
|
||||
if (block == null) continue;
|
||||
final BlockHandler handler = block.handler();
|
||||
if (handler != null) {
|
||||
|
@ -0,0 +1,40 @@
|
||||
package net.minestom.server.utils.chunk;
|
||||
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import static net.minestom.server.utils.chunk.ChunkUtils.getChunkCoordinate;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ChunkCache implements Block.Getter {
|
||||
private final Instance instance;
|
||||
private Chunk chunk;
|
||||
|
||||
private final Block defaultBlock;
|
||||
|
||||
public ChunkCache(Instance instance, Chunk chunk,
|
||||
Block defaultBlock) {
|
||||
this.instance = instance;
|
||||
this.chunk = chunk;
|
||||
this.defaultBlock = defaultBlock;
|
||||
}
|
||||
|
||||
public ChunkCache(Instance instance, Chunk chunk) {
|
||||
this(instance, chunk, Block.AIR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @UnknownNullability Block getBlock(int x, int y, int z, @NotNull Condition condition) {
|
||||
Chunk chunk = this.chunk;
|
||||
final int chunkX = getChunkCoordinate(x);
|
||||
final int chunkZ = getChunkCoordinate(z);
|
||||
if (chunk == null || chunk.getChunkX() != chunkX || chunk.getChunkZ() != chunkZ) {
|
||||
this.chunk = chunk = this.instance.getChunk(chunkX, chunkZ);
|
||||
}
|
||||
return chunk != null ? chunk.getBlock(x, y, z, condition) : defaultBlock;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user