From 05073b24d21f2e1b37b59977bea914bad386347b Mon Sep 17 00:00:00 2001 From: SoraxDubbing Date: Wed, 11 Sep 2024 10:42:19 +0200 Subject: [PATCH] improve performance --- .../bullet/BulletWorldPhysics.java | 170 +++++++----------- .../phylisiumstudio/logic/WorldManager.java | 5 + .../logic/runnable/PhysicsThreadRunnable.java | 10 +- 3 files changed, 76 insertions(+), 109 deletions(-) diff --git a/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java b/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java index 2a18359..ffd4df2 100644 --- a/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java +++ b/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java @@ -25,6 +25,7 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; import org.bukkit.block.data.BlockData; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.Interaction; @@ -300,48 +301,73 @@ public RigidBlock getBlock(UUID id) { @Override public void convertChunk(Vector3f pos1, Vector3f pos2) { + CompoundShape compoundShape = new CompoundShape(); + final int numThreads = Runtime.getRuntime().availableProcessors(); + int startX = (int) Math.min(pos1.x, pos2.x); - int endX = (int) Math.max(pos1.x, pos2.x); int startZ = (int) Math.min(pos1.z, pos2.z); - int endZ = (int) Math.max(pos1.z, pos2.z); + int startY = (int) Math.min(pos1.y, pos2.y); - Set uniqueVertices = new HashSet<>(); + int endX = (int) Math.max(pos1.x, pos2.x); + int endZ = (int) Math.max(pos1.z, pos2.z); + int endY = (int) Math.max(pos1.y, pos2.y); + + try (ExecutorService executor = Executors.newFixedThreadPool(numThreads)) { + List> futures = new ArrayList<>(); + + // Divide the work into smaller tasks + for (int x = startX; x < endX; x++) { + final int currentX = x; + Future future = executor.submit(() -> { + for (int z = startZ; z < endZ; z++) { + for (int y = startY; y < endY; y++) { + Block block = bukkitWorld.getBlockAt(currentX, y, z); + if (block.getType().isAir() || !isAdjacentToAir(block)) { + continue; + } + BoxShape blockShape = new BoxShape(new Vector3f(0.5f, 0.5f, 0.5f)); + Transform transformShape = new Transform(); + transformShape.setIdentity(); + transformShape.origin.set(new Vector3f(currentX + 0.5f, y + 0.5f, z + 0.5f)); - // Find the surface blocks in the chunk and collect their unique vertices - for (int x = startX; x <= endX; x++) { - for (int z = startZ; z <= endZ; z++) { - for (int y = bukkitWorld.getMaxHeight() - 1; y >= 0; y--) { - Block block = bukkitWorld.getBlockAt(x, y, z); - if (!block.getType().isAir() && isAdjacentToAir(x, y, z)) { - addBlockVertices(uniqueVertices, new Vector3f(x, y, z)); + synchronized (compoundShape) { + compoundShape.addChildShape(transformShape, blockShape); + } + } } - } + return null; + }); + futures.add(future); } - } - // Merge adjacent blocks into larger quads - Set mergedVertices = mergeAdjacentBlocks(uniqueVertices); - ObjectArrayList vertices = new ObjectArrayList<>(mergedVertices.size()); - vertices.addAll(mergedVertices); - - // Create a convex hull shape using the collected unique vertices - ConvexHullShape convexHullShape = new ConvexHullShape(vertices); + // Wait for all tasks to complete + for (Future future : futures) { + try { + future.get(); + } catch (Exception e) { + e.printStackTrace(); + } + } - // Create a compound shape and add the convex hull shape to it - CompoundShape compoundShape = new CompoundShape(); - Transform transform = new Transform(); - transform.setIdentity(); - compoundShape.addChildShape(transform, convexHullShape); + // Create and add the CompoundShape to the dynamics world + if (compoundShape.getNumChildShapes() > 0) { + RigidBody rigidBody = createStaticRigidBody(compoundShape); + bulletWorld.addRigidBody(rigidBody); + } + } catch (Exception e) { + logger.error("Error creating executor service: " + e.getMessage(), e); + } + } - // Create a rigid body with the compound shape + private RigidBody createStaticRigidBody(CompoundShape compoundShape) { float mass = 0.0f; Vector3f inertia = new Vector3f(0, 0, 0); - DefaultMotionState motionState = new DefaultMotionState(transform); - RigidBodyConstructionInfo rbInfo = new RigidBodyConstructionInfo(mass, motionState, compoundShape, inertia); - RigidBody body = new RigidBody(rbInfo); - - // Add the rigid body to the physics world - bulletWorld.addRigidBody(body); + RigidBodyConstructionInfo chunkInfo = new RigidBodyConstructionInfo(mass, null, compoundShape, inertia); + RigidBody groundBlock = new RigidBody(chunkInfo); + Transform ntransform = new Transform(); + ntransform.setIdentity(); + groundBlock.setWorldTransform(ntransform); + return groundBlock; } /** @@ -410,84 +436,12 @@ public boolean isRunning() { return !bukkitWorld.getPlayers().isEmpty(); } - private boolean isAdjacentToAir(int x, int y, int z) { - return bukkitWorld.getBlockAt(x + 1, y, z).getType().isAir() || - bukkitWorld.getBlockAt(x - 1, y, z).getType().isAir() || - bukkitWorld.getBlockAt(x, y + 1, z).getType().isAir() || - bukkitWorld.getBlockAt(x, y - 1, z).getType().isAir() || - bukkitWorld.getBlockAt(x, y, z + 1).getType().isAir() || - bukkitWorld.getBlockAt(x, y, z - 1).getType().isAir(); - } - - private void addBlockVertices(Set vertices, Vector3f blockPos) { - float x = blockPos.x; - float y = blockPos.y; - float z = blockPos.z; - - // Add the 8 vertices of the block - vertices.add(new Vector3f(x, y, z)); - vertices.add(new Vector3f(x + 1, y, z)); - vertices.add(new Vector3f(x, y + 1, z)); - vertices.add(new Vector3f(x, y, z + 1)); - vertices.add(new Vector3f(x + 1, y + 1, z)); - vertices.add(new Vector3f(x, y + 1, z + 1)); - vertices.add(new Vector3f(x + 1, y, z + 1)); - vertices.add(new Vector3f(x + 1, y + 1, z + 1)); - } - - private Set mergeAdjacentBlocks(Set vertices) { - int chunkWidth = 16; - int chunkHeight = 256; - int chunkDepth = 16; - - boolean[][][] grid = new boolean[chunkWidth][chunkHeight][chunkDepth]; - - // Mark the grid cells corresponding to surface blocks - for (Vector3f vertex : vertices) { - int x = (int) vertex.x; - int y = (int) vertex.y; - int z = (int) vertex.z; - if (x >= 0 && x < chunkWidth && y >= 0 && y < chunkHeight && z >= 0 && z < chunkDepth) { - grid[x][y][z] = true; - } - } - - Set mergedVertices = new HashSet<>(); - - // Greedy meshing algorithm - for (int x = 0; x < chunkWidth; x++) { - for (int y = 0; y < chunkHeight; y++) { - for (int z = 0; z < chunkDepth; z++) { - if (grid[x][y][z]) { - // Find the extent of the quad in the x direction - int width = 1; - while (x + width < chunkWidth && grid[x + width][y][z]) { - width++; - } - - // Find the extent of the quad in the z direction - int depth = 1; - while (z + depth < chunkDepth && grid[x][y][z + depth]) { - depth++; - } - - // Add the vertices of the quad - mergedVertices.add(new Vector3f(x, y, z)); - mergedVertices.add(new Vector3f(x + width, y, z)); - mergedVertices.add(new Vector3f(x, y, z + depth)); - mergedVertices.add(new Vector3f(x + width, y, z + depth)); - - // Mark the cells as processed - for (int i = 0; i < width; i++) { - for (int j = 0; j < depth; j++) { - grid[x + i][y][z + j] = false; - } - } - } - } + private boolean isAdjacentToAir(Block block) { + for (BlockFace face : BlockFace.values()) { + if (block.getRelative(face).getType().isAir()) { + return true; } } - - return mergedVertices; + return false; } } diff --git a/src/main/java/fr/phylisiumstudio/logic/WorldManager.java b/src/main/java/fr/phylisiumstudio/logic/WorldManager.java index 7413577..c2e9f2f 100644 --- a/src/main/java/fr/phylisiumstudio/logic/WorldManager.java +++ b/src/main/java/fr/phylisiumstudio/logic/WorldManager.java @@ -1,5 +1,7 @@ package fr.phylisiumstudio.logic; +import org.slf4j.Logger; + import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -10,9 +12,12 @@ public class WorldManager { private final ConcurrentMap worlds; private final PhysicsThreadManager physicsThreadManager; + private final Logger logger = org.slf4j.LoggerFactory.getLogger(WorldManager.class); public WorldManager() { this.worlds = new ConcurrentHashMap<>(); + int availableProcessors = Runtime.getRuntime().availableProcessors(); + logger.info("Available processors: {}", availableProcessors); this.physicsThreadManager = new PhysicsThreadManager(Runtime.getRuntime().availableProcessors()); } diff --git a/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java b/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java index 9df2e92..432dd06 100644 --- a/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java +++ b/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java @@ -12,6 +12,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * Runnable task for physics simulation @@ -21,6 +23,8 @@ public class PhysicsThreadRunnable implements Runnable { private final List worldPhysics; private final Logger logger = LoggerFactory.getLogger(PhysicsThreadRunnable.class); + private final Lock lock = new ReentrantLock(); + /** * Create a new physics task */ @@ -31,13 +35,14 @@ public PhysicsThreadRunnable() { @Override public void run() { try { + lock.lock(); Instant start = Instant.now(); for (WorldPhysics worldPhysic : this.worldPhysics) { worldPhysic.stepSimulation(); } Instant end = Instant.now(); Duration duration = Duration.between(start, end); - if (duration.toMillis() > 50) { + if (duration.toMillis() > 100) { logger.warn("PhysicsTask #" + id + " is running slow: " + duration.toMillis() + "ms"); } } catch (NullPointerException e) { @@ -45,6 +50,9 @@ public void run() { } catch (Exception e) { logger.error("Error in world physics thread #" + id, e); } + finally { + lock.unlock(); + } } /**