diff --git a/build.gradle b/build.gradle index 8a3c38d..1fbdb91 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'fr.phylisiumstudio' -version = '1.0-BETA' +version = '1.1-BETA' repositories { mavenCentral() diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/BukkitMotionState.java b/src/main/java/fr/phylisiumstudio/bullet/BukkitMotionState.java similarity index 97% rename from src/main/java/fr/phylisiumstudio/soraxPhysic/BukkitMotionState.java rename to src/main/java/fr/phylisiumstudio/bullet/BukkitMotionState.java index 0032a50..db5599c 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/BukkitMotionState.java +++ b/src/main/java/fr/phylisiumstudio/bullet/BukkitMotionState.java @@ -1,8 +1,9 @@ -package fr.phylisiumstudio.soraxPhysic; +package fr.phylisiumstudio.bullet; import com.bulletphysics.dynamics.RigidBody; import com.bulletphysics.linearmath.MotionState; import com.bulletphysics.linearmath.Transform; +import fr.phylisiumstudio.soraxPhysic.SoraxPhysic; import fr.phylisiumstudio.soraxPhysic.models.RigidBlock; import org.bukkit.Bukkit; import org.bukkit.Location; diff --git a/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java b/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java new file mode 100644 index 0000000..ffd4df2 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/BulletWorldPhysics.java @@ -0,0 +1,447 @@ +package fr.phylisiumstudio.bullet; + +import com.bulletphysics.collision.broadphase.BroadphaseInterface; +import com.bulletphysics.collision.broadphase.DbvtBroadphase; +import com.bulletphysics.collision.dispatch.CollisionConfiguration; +import com.bulletphysics.collision.dispatch.CollisionDispatcher; +import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration; +import com.bulletphysics.collision.shapes.BoxShape; +import com.bulletphysics.collision.shapes.CompoundShape; +import com.bulletphysics.collision.shapes.ConvexHullShape; +import com.bulletphysics.collision.shapes.SphereShape; +import com.bulletphysics.dynamics.DiscreteDynamicsWorld; +import com.bulletphysics.dynamics.DynamicsWorld; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.dynamics.RigidBodyConstructionInfo; +import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver; +import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver; +import com.bulletphysics.linearmath.DefaultMotionState; +import com.bulletphysics.linearmath.Transform; +import com.bulletphysics.util.ObjectArrayList; +import fr.phylisiumstudio.logic.WorldPhysics; +import fr.phylisiumstudio.soraxPhysic.PhysicsManager; +import fr.phylisiumstudio.soraxPhysic.models.RigidBlock; +import org.bukkit.Chunk; +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; +import org.bukkit.util.Transformation; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.vecmath.Vector3f; +import java.util.*; +import java.util.concurrent.*; + +/** + * A world physics implementation using bullet physics + */ +public class BulletWorldPhysics extends WorldPhysics { + + private DynamicsWorld bulletWorld; + private final World bukkitWorld; + + private final List blocks; + + private float timespan = 1.0f / 20.0f; + private int maxSubSteps = 30; + private boolean timeFreeze = false; + + private final Logger logger = LoggerFactory.getLogger(BulletWorldPhysics.class); + + public BulletWorldPhysics(World bukkitWorld) { + this.bukkitWorld = bukkitWorld; + this.blocks = new ArrayList<>(); + + try { + BroadphaseInterface broadphase = new DbvtBroadphase(); + CollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration(); + CollisionDispatcher dispatcher = new CollisionDispatcher(collisionConfiguration); + ConstraintSolver solver = new SequentialImpulseConstraintSolver(); + + this.bulletWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + logger.info("BulletWorld for world " + bukkitWorld.getName() + " initialized"); + } catch (Exception e) { + logger.error("Error initializing BulletWorld for world " + bukkitWorld.getName() + ": " + e.getMessage(), e); + } + } + + /** + * Step the simulation + */ + @Override + public void stepSimulation() { + if (!isRunning()){ + return; + } + + if (bulletWorld == null) { + logger.error("bulletWorld is null"); + return; + } + + if (timeFreeze) return; + synchronized (PhysicsManager.lock) { + try{ + bulletWorld.stepSimulation(timespan, maxSubSteps); + } + catch (Exception e){ + logger.error("Error stepping simulation: " + e.getMessage(), e); + } + + } + } + + /** + * Get the unique id of the world (bukkit world) + * + * @return the unique id + */ + @Override + public UUID getUniqueId() { + return bukkitWorld.getUID(); + } + + /** + * Get the world name + * + * @return the world name + */ + @Override + public String getWorldName() { + return bukkitWorld.getName(); + } + + /** + * Get the blocks + * + * @return the blocks + */ + @Override + public List getBlocks() { + return this.blocks; + } + + /** + * Create a box + * + * @param location the location (must be in the same world) + * @param blockData the block data to use + * @param mass the mass of the block + * @param xScale the x scale + * @param yScale the y scale + * @param zScale the z scale + * @return the box + */ + @Override + public RigidBlock createBox(Location location, BlockData blockData, float mass, float xScale, float yScale, float zScale) { + assert location.getWorld().equals(bukkitWorld); + + BlockDisplay blockDisplay = bukkitWorld.spawn(location, BlockDisplay.class, display -> { + display.setBlock(blockData); + display.setRotation(0, 0); + display.setTeleportDuration(1); + + Transformation transformation = display.getTransformation(); + org.joml.Vector3f translation = new org.joml.Vector3f(-xScale/2, -yScale/2, -zScale/2); + org.joml.Vector3f scale = new org.joml.Vector3f(xScale, yScale, zScale); + display.setTransformation(new Transformation(translation, transformation.getLeftRotation(), scale, transformation.getRightRotation())); + }); + + Interaction interaction = bukkitWorld.spawn(location, Interaction.class, display -> { + display.setInteractionHeight(xScale); + display.setInteractionWidth(zScale); + display.setResponsive(true); + }); + + Vector3f position = new Vector3f(xScale/2, yScale/2, zScale/2); + BoxShape boxShape = new BoxShape(position); + + Transform transform = new Transform(); + transform.setIdentity(); + transform.origin.set((float) location.getX(), (float) location.getY(), (float) location.getZ()); + + Vector3f localInertia = new Vector3f(0, 0, 0); + boxShape.calculateLocalInertia(3, localInertia); + + + RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(mass, null, boxShape, localInertia); + RigidBody body = new RigidBody(constructionInfo); + body.setWorldTransform(transform); + body.setRestitution(0.0f); + + RigidBlock rigidBlock = new RigidBlock(body, blockDisplay, interaction); + BukkitMotionState motionState = new BukkitMotionState(rigidBlock); + body.setMotionState(motionState); + + synchronized (PhysicsManager.lock) { + bulletWorld.addRigidBody(body); + synchronized (blocks) { + blocks.add(rigidBlock); + } + } + + Chunk chunk = location.getChunk(); + Vector3f pos1 = new Vector3f(chunk.getX() * 16, 0, chunk.getZ() * 16); + Vector3f pos2 = new Vector3f(chunk.getX() * 16 + 16, 256, chunk.getZ() * 16 + 16); + convertChunk(pos1, pos2); + + return rigidBlock; + } + + /** + * Create a sphere + * + * @param location the location (must be in the same world) + * @param radius the radius + * @param mass the mass + * @return the sphere + */ + @Override + public RigidBlock createSphere(Location location, BlockData data, float radius, float mass) { + assert location.getWorld().equals(bukkitWorld); + float length = (float) (radius * Math.sqrt(2)); + + BlockDisplay blockDisplay = bukkitWorld.spawn(location, BlockDisplay.class, display -> { + display.setBlock(data); + display.setRotation(0, 0); + display.setInterpolationDuration(1); + display.setTeleportDuration(1); + + Transformation transformation = display.getTransformation(); + org.joml.Vector3f translation = new org.joml.Vector3f(-length/2, -length/2, -length/2); + org.joml.Vector3f scale = new org.joml.Vector3f(length, length, length); + display.setTransformation(new Transformation(translation, transformation.getLeftRotation(), scale, transformation.getRightRotation())); + }); + Interaction hitbox = bukkitWorld.spawn(location, Interaction.class, display -> { + display.setInteractionHeight(length); + display.setInteractionWidth(length); + display.setResponsive(true); + }); + SphereShape sphereShape = new SphereShape(radius); + + Transform transform = new Transform(); + transform.setIdentity(); + transform.origin.set((float) location.getX(), (float) location.getY(), (float) location.getZ()); + + Vector3f localInertia = new Vector3f(0, 0, 0); + sphereShape.calculateLocalInertia(3, localInertia); + + RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(mass, null, sphereShape, localInertia); + RigidBody body = new RigidBody(constructionInfo); + body.setWorldTransform(transform); + body.setRestitution(0.0f); + + RigidBlock rigidBlock = new RigidBlock(body, blockDisplay, hitbox); + BukkitMotionState motionState = new BukkitMotionState(rigidBlock); + body.setMotionState(motionState); + + synchronized (PhysicsManager.lock) { + bulletWorld.addRigidBody(body); + synchronized (blocks) { + blocks.add(rigidBlock); + } + } + + Chunk chunk = location.getChunk(); + Vector3f pos1 = new Vector3f(chunk.getX() * 16, 0, chunk.getZ() * 16); + Vector3f pos2 = new Vector3f(chunk.getX() * 16 + 16, 256, chunk.getZ() * 16 + 16); + convertChunk(pos1, pos2); + + return rigidBlock; + } + + /** + * Remove a block + * + * @param block the block to remove + */ + @Override + public void removeBlock(RigidBlock block) { + bulletWorld.removeRigidBody(block.getRigidBody()); + block.getBlockDisplay().remove(); + block.getInteraction().remove(); + synchronized (blocks) { + blocks.remove(block); + } + } + + /** + * Clear the world + */ + @Override + public void clear() { + synchronized (blocks) { + for (RigidBlock block : blocks) { + bulletWorld.removeRigidBody(block.getRigidBody()); + block.getBlockDisplay().remove(); + block.getInteraction().remove(); + } + blocks.clear(); + } + } + + + /** + * Get the block with the given id + * + * @param id the id + * @return the block + */ + @Override + @Nullable + public RigidBlock getBlock(UUID id) { + return this.blocks.stream().filter(block -> block.getUniqueId().equals(id)).findFirst().orElse(null); + } + + @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 startZ = (int) Math.min(pos1.z, pos2.z); + int startY = (int) Math.min(pos1.y, pos2.y); + + 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)); + + synchronized (compoundShape) { + compoundShape.addChildShape(transformShape, blockShape); + } + } + } + return null; + }); + futures.add(future); + } + + // Wait for all tasks to complete + for (Future future : futures) { + try { + future.get(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // 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); + } + } + + private RigidBody createStaticRigidBody(CompoundShape compoundShape) { + float mass = 0.0f; + Vector3f inertia = new Vector3f(0, 0, 0); + RigidBodyConstructionInfo chunkInfo = new RigidBodyConstructionInfo(mass, null, compoundShape, inertia); + RigidBody groundBlock = new RigidBody(chunkInfo); + Transform ntransform = new Transform(); + ntransform.setIdentity(); + groundBlock.setWorldTransform(ntransform); + return groundBlock; + } + + /** + * Get the time freeze + */ + @Override + public float getTimespan() { + return timespan; + } + + /** + * Set the time freeze + * + * @param timespan + */ + @Override + public void setTimespan(float timespan) { + this.timespan = timespan; + } + + /** + * Get Max substeps + */ + @Override + public int getMaxSubSteps() { + return maxSubSteps; + } + + /** + * Set Max substeps + * + * @param maxSubSteps + */ + @Override + public void setMaxSubSteps(int maxSubSteps) { + this.maxSubSteps = maxSubSteps; + } + + /** + * set freeze + * + * @param freeze the freeze + */ + @Override + public void setFreeze(boolean freeze) { + this.timeFreeze = freeze; + } + + /** + * is frozen + * + * @return is frozen + */ + @Override + public boolean isFrozen() { + return timeFreeze; + } + + /** + * Verify if the world can run + * + * @return if the world can run + */ + @Override + public boolean isRunning() { + return !bukkitWorld.getPlayers().isEmpty(); + } + + private boolean isAdjacentToAir(Block block) { + for (BlockFace face : BlockFace.values()) { + if (block.getRelative(face).getType().isAir()) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/fr/phylisiumstudio/bullet/PhysicsEngineFactory.java b/src/main/java/fr/phylisiumstudio/bullet/PhysicsEngineFactory.java new file mode 100644 index 0000000..0eeb41f --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/PhysicsEngineFactory.java @@ -0,0 +1,40 @@ +package fr.phylisiumstudio.bullet; + +import com.bulletphysics.collision.broadphase.BroadphaseInterface; +import com.bulletphysics.collision.broadphase.DbvtBroadphase; +import com.bulletphysics.collision.dispatch.CollisionConfiguration; +import com.bulletphysics.collision.dispatch.CollisionDispatcher; +import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration; +import com.bulletphysics.collision.shapes.BoxShape; +import com.bulletphysics.collision.shapes.CollisionShape; +import com.bulletphysics.collision.shapes.SphereShape; +import com.bulletphysics.dynamics.DiscreteDynamicsWorld; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.dynamics.RigidBodyConstructionInfo; +import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver; +import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver; +import com.bulletphysics.linearmath.Transform; +import fr.phylisiumstudio.logic.IPhysicsEngineFactory; + +import javax.vecmath.Vector3f; + +public class PhysicsEngineFactory implements IPhysicsEngineFactory { + @Override + public DiscreteDynamicsWorld createWorld() { + BroadphaseInterface broadphase = new DbvtBroadphase(); + CollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration(); + CollisionDispatcher dispatcher = new CollisionDispatcher(collisionConfiguration); + ConstraintSolver solver = new SequentialImpulseConstraintSolver(); + return new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + } + + @Override + public RigidBody createRigidBody(float mass, CollisionShape shape, Transform transform) { + Vector3f localInertia = new Vector3f(0, 0, 0); + shape.calculateLocalInertia(mass, localInertia); + RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(mass, null, shape, localInertia); + RigidBody body = new RigidBody(constructionInfo); + body.setWorldTransform(transform); + return body; + } +} diff --git a/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeAbstractFabric.java b/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeAbstractFabric.java new file mode 100644 index 0000000..64e8bed --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeAbstractFabric.java @@ -0,0 +1,10 @@ +package fr.phylisiumstudio.bullet.fabric; + +import com.bulletphysics.collision.shapes.CollisionShape; +import fr.phylisiumstudio.logic.fabric.AbstractFactory; + +/** + * Abstract factory for creating shapes + */ +public class ShapeAbstractFabric extends AbstractFactory { +} diff --git a/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeConstructor.java b/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeConstructor.java new file mode 100644 index 0000000..a34af56 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/fabric/ShapeConstructor.java @@ -0,0 +1,10 @@ +package fr.phylisiumstudio.bullet.fabric; + +import com.bulletphysics.collision.shapes.CollisionShape; +import fr.phylisiumstudio.logic.fabric.IConstructor; + +/** + * Abstract class for the shape constructor + */ +public abstract class ShapeConstructor implements IConstructor { +} diff --git a/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/CubeConstructor.java b/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/CubeConstructor.java new file mode 100644 index 0000000..52cb953 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/CubeConstructor.java @@ -0,0 +1,23 @@ +package fr.phylisiumstudio.bullet.fabric.constructors; + +import com.bulletphysics.collision.shapes.BoxShape; +import com.bulletphysics.collision.shapes.CollisionShape; +import fr.phylisiumstudio.bullet.fabric.ShapeConstructor; + +import javax.vecmath.Vector3f; + +/** + * Constructor for the cube shape + */ +public class CubeConstructor extends ShapeConstructor { + @Override + public String getName() { + return "cube"; + } + + @Override + public CollisionShape construct(Object... args) { + Vector3f size = (Vector3f) args[0]; + return new BoxShape(size); + } +} diff --git a/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/SphereConstructor.java b/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/SphereConstructor.java new file mode 100644 index 0000000..42305f1 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/bullet/fabric/constructors/SphereConstructor.java @@ -0,0 +1,21 @@ +package fr.phylisiumstudio.bullet.fabric.constructors; + +import com.bulletphysics.collision.shapes.CollisionShape; +import com.bulletphysics.collision.shapes.SphereShape; +import fr.phylisiumstudio.bullet.fabric.ShapeConstructor; + +/** + * Constructor for the sphere shape + */ +public class SphereConstructor extends ShapeConstructor { + @Override + public String getName() { + return "sphere"; + } + + @Override + public CollisionShape construct(Object... args) { + float radius = (float) args[0]; + return new SphereShape(radius); + } +} diff --git a/src/main/java/fr/phylisiumstudio/logic/IPhysicsEngineFactory.java b/src/main/java/fr/phylisiumstudio/logic/IPhysicsEngineFactory.java new file mode 100644 index 0000000..7645116 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/IPhysicsEngineFactory.java @@ -0,0 +1,31 @@ +package fr.phylisiumstudio.logic; + +import com.bulletphysics.collision.shapes.BoxShape; +import com.bulletphysics.collision.shapes.CollisionShape; +import com.bulletphysics.collision.shapes.SphereShape; +import com.bulletphysics.dynamics.DiscreteDynamicsWorld; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.linearmath.Transform; + +import javax.vecmath.Vector3f; + +/** + * Factory for creating physics engine objects + */ +public interface IPhysicsEngineFactory { + + /** + * Create a new dynamics world + * @return the new dynamics world + */ + T createWorld(); + + /** + * Create a new rigid body + * @param mass the mass of the rigid body + * @param shape the shape of the rigid body + * @param transform the transform of the rigid body + * @return the new rigid body + */ + C createRigidBody(float mass, W shape, V transform); +} diff --git a/src/main/java/fr/phylisiumstudio/logic/IPhysicsManager.java b/src/main/java/fr/phylisiumstudio/logic/IPhysicsManager.java new file mode 100644 index 0000000..b92595c --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/IPhysicsManager.java @@ -0,0 +1,26 @@ +package fr.phylisiumstudio.logic; + +import org.bukkit.World; + +/** + * Interface for the physics manager + */ +public interface IPhysicsManager { + /** + * Get the physics engine for a world + * @param world The world + * @return The physics engine + */ + WorldPhysics getWorldPhysics(World world); + + /** + * Setup the physics engine for a world + * @param world The world + */ + void setupPhysicsEngine(World world); + + /** + * Stop the physics engine + */ + void stop(); +} diff --git a/src/main/java/fr/phylisiumstudio/logic/ItemLinker.java b/src/main/java/fr/phylisiumstudio/logic/ItemLinker.java deleted file mode 100644 index f381540..0000000 --- a/src/main/java/fr/phylisiumstudio/logic/ItemLinker.java +++ /dev/null @@ -1,42 +0,0 @@ -package fr.phylisiumstudio.logic; - -import java.util.UUID; - -public class ItemLinker { - private final UUID id; - - private UUID firstRigidBody; - private UUID secondRigidBody; - - public ItemLinker(UUID id) { - this.id = id; - } - - public UUID getId() { - return id; - } - - public UUID getFirstRigidBody() { - return firstRigidBody; - } - - public void setFirstRigidBody(UUID firstRigidBody) { - this.firstRigidBody = firstRigidBody; - } - - public UUID getSecondRigidBody() { - return secondRigidBody; - } - - public void setSecondRigidBody(UUID secondRigidBody) { - this.secondRigidBody = secondRigidBody; - } - - public boolean hasFirstRigidBody() { - return firstRigidBody != null; - } - - public boolean hasSecondRigidBody() { - return secondRigidBody != null; - } -} diff --git a/src/main/java/fr/phylisiumstudio/logic/PhysicsThreadManager.java b/src/main/java/fr/phylisiumstudio/logic/PhysicsThreadManager.java new file mode 100644 index 0000000..f32ea51 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/PhysicsThreadManager.java @@ -0,0 +1,87 @@ +package fr.phylisiumstudio.logic; + +import fr.phylisiumstudio.logic.runnable.PhysicsThreadRunnable; + +import java.util.UUID; +import java.util.concurrent.*; + +public class PhysicsThreadManager { + private final ScheduledExecutorService executorService; + private final ConcurrentLinkedQueue physicsTasks; + private final ConcurrentLinkedQueue> futures; + + public PhysicsThreadManager(int maxThreads) { + this.executorService = Executors.newScheduledThreadPool(maxThreads); + this.physicsTasks = new ConcurrentLinkedQueue<>(); + this.futures = new ConcurrentLinkedQueue<>(); + + for (int i = 0; i < maxThreads; i++) { + PhysicsThreadRunnable physicsThreadRunnable = new PhysicsThreadRunnable(); + Future future = executorService.scheduleAtFixedRate(physicsThreadRunnable, 0, 50, TimeUnit.MILLISECONDS); + physicsTasks.add(physicsThreadRunnable); + futures.add(future); + } + } + + /** + * Submit a task to the executor service + * @param worldPhysics The world physics + */ + public void registerWorld(WorldPhysics worldPhysics) { + PhysicsThreadRunnable physicsThreadRunnable = getLeastBusyThread(); + physicsThreadRunnable.addWorld(worldPhysics); + } + + /** + * Cancel the task associated with the world + * @param worldId The world id + */ + public void unregisterWorld(UUID worldId) { + PhysicsThreadRunnable physicsThreadRunnable = getPhysicsTask(worldId); + if (physicsThreadRunnable != null) { + WorldPhysics worldPhysics = physicsThreadRunnable.getWorld(worldId); + physicsThreadRunnable.removeWorld(worldPhysics); + } + } + + /** + * Arrêter le gestionnaire de threads + */ + public void shutdown() { + for (Future future : this.futures) { + future.cancel(true); + } + this.futures.clear(); + + this.physicsTasks.clear(); + executorService.shutdown(); + try { + if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } + } catch (InterruptedException e) { + executorService.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + private PhysicsThreadRunnable getLeastBusyThread() { + PhysicsThreadRunnable leastBusy = null; + int minTasks = Integer.MAX_VALUE; + for (PhysicsThreadRunnable physicsThreadRunnable : physicsTasks) { + int tasks = physicsThreadRunnable.getWorldsSize(); + if (tasks < minTasks) { + minTasks = tasks; + leastBusy = physicsThreadRunnable; + } + } + return leastBusy; + } + + private PhysicsThreadRunnable getPhysicsTask(UUID worldId) { + return physicsTasks.stream() + .filter(physicsThread -> physicsThread.getWorlds().stream().anyMatch(world -> world.getUniqueId().equals(worldId))) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/fr/phylisiumstudio/logic/WorldManager.java b/src/main/java/fr/phylisiumstudio/logic/WorldManager.java new file mode 100644 index 0000000..c2e9f2f --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/WorldManager.java @@ -0,0 +1,108 @@ +package fr.phylisiumstudio.logic; + +import org.slf4j.Logger; + +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * Manage the worlds and the threads associated with them + */ +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()); + } + + /** + * Register a world with the physics engine + * @param world The world to register + */ + public void registerWorld(WorldPhysics world) { + if (world == null) { + throw new IllegalArgumentException("The world or its UID is null"); + } + if (worlds.containsKey(world.getUniqueId())) { + throw new IllegalArgumentException("The world is already registered"); + } + + worlds.put(world.getUniqueId(), world); + startWorldPhysics(world); + } + + /** + * Unregister a world from the physics engine + * @param world The world to unregister + */ + public void unregisterWorld(WorldPhysics world) { + if (world == null) { + throw new IllegalArgumentException("The world or its UID is null"); + } + if (!worlds.containsKey(world.getUniqueId())) { + throw new IllegalArgumentException("The world is not registered"); + } + + WorldPhysics worldPhysics = worlds.remove(world.getUniqueId()); + physicsThreadManager.unregisterWorld(worldPhysics.getUniqueId()); + } + + /** + * Start the physics for a world + * @param worldPhysics The world physics + */ + public void startWorldPhysics(WorldPhysics worldPhysics) { + if (worldPhysics == null) { + throw new IllegalArgumentException("The world physics is null"); + } + if (!worlds.containsKey(worldPhysics.getUniqueId())) { + throw new IllegalArgumentException("The world is not registered"); + } + + physicsThreadManager.registerWorld(worldPhysics); + } + + /** + * Get the world physics associated with the world + * @param worldId The world id + * @return The world physics + */ + public WorldPhysics getWorld(UUID worldId) { + return worlds.get(worldId); + } + + /** + * Check if a world is registered + * @param worldId The world id + * @return True if the world is registered + */ + public boolean isWorldRegistered(UUID worldId) { + return worlds.containsKey(worldId); + } + + /** + * Shutdown the world manager + */ + public void shutdown() { + physicsThreadManager.shutdown(); + worlds.clear(); + } + + public PhysicsThreadManager getPhysicsThreadManager() { + return physicsThreadManager; + } + + /** + * Get all the worlds being managed + * @return a copy of the worlds + */ + public ConcurrentMap getWorlds() { + return new ConcurrentHashMap<>(worlds); + } +} diff --git a/src/main/java/fr/phylisiumstudio/logic/WorldPhysics.java b/src/main/java/fr/phylisiumstudio/logic/WorldPhysics.java new file mode 100644 index 0000000..cb028a9 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/WorldPhysics.java @@ -0,0 +1,128 @@ +package fr.phylisiumstudio.logic; + +import fr.phylisiumstudio.soraxPhysic.models.RigidBlock; +import org.bukkit.Location; +import org.bukkit.block.data.BlockData; + +import javax.vecmath.Vector3f; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * Interface for the physics world + */ +public abstract class WorldPhysics { + + /** + * Step the simulation + */ + public abstract void stepSimulation(); + + /** + * Get the unique id of the world + * + * @return the unique id + */ + public abstract UUID getUniqueId(); + + /** + * Get the world name + * @return the world name + */ + public abstract String getWorldName(); + + /** + * Get the blocks + * @return the blocks + */ + public abstract List getBlocks(); + + /** + * Create a box + * + * @param location the location (must be in the same world) + * @param blockData the block data to use + * @param mass the mass of the block + * @param xScale the x scale + * @param yScale the y scale + * @param zScale the z scale + * @return the box + */ + public abstract RigidBlock createBox(Location location, BlockData blockData, float mass, float xScale, float yScale, float zScale); + + /** + * Create a sphere + * + * @param location the location (must be in the same world) + * @param radius the radius + * @param mass the mass + * @return the sphere + */ + public abstract RigidBlock createSphere(Location location, BlockData data, float radius, float mass); + + /** + * Remove a block + * @param block the block to remove + */ + public abstract void removeBlock(RigidBlock block); + + /** + * Clear the world + */ + public abstract void clear(); + + /** + * Get the block with the given id + * @param id the id + * @return the block + */ + public abstract RigidBlock getBlock(UUID id); + + /** + * convert region physics + * @param pos1 the first position + * @param pos2 the second position + */ + public abstract void convertChunk(Vector3f pos1, Vector3f pos2); + + /** + * Get the time span + */ + public abstract float getTimespan(); + + /** + * Set the time span + */ + public abstract void setTimespan(float timespan); + + /** + * Get Max substeps + */ + public abstract int getMaxSubSteps(); + + /** + * Set Max substeps + */ + public abstract void setMaxSubSteps(int maxSubSteps); + + /** + * set freeze + * @param freeze the freeze + */ + public abstract void setFreeze(boolean freeze); + + /** + * is frozen + * @return is frozen + */ + public abstract boolean isFrozen(); + + /** + * Verify if the world can run + * @return if the world can run + */ + public abstract boolean isRunning(); +} diff --git a/src/main/java/fr/phylisiumstudio/logic/fabric/AbstractFactory.java b/src/main/java/fr/phylisiumstudio/logic/fabric/AbstractFactory.java new file mode 100644 index 0000000..185fe06 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/fabric/AbstractFactory.java @@ -0,0 +1,91 @@ +package fr.phylisiumstudio.logic.fabric; + +import java.util.ArrayList; +import java.util.List; + +/** + * Abstract factory for creating objects + * @param the type of object to create + * @param the type of constructor + */ +public abstract class AbstractFactory> { + private final List constructors; + + /** + * Create a new abstract factory + */ + public AbstractFactory() { + this.constructors = new ArrayList<>(); + } + + /** + * Construct an object by name + * @param name the name of the object to construct + * @return the object + */ + public T construct(String name, Object... args) { + for (C constructor : constructors) { + if (constructor.getName().equals(name)) { + return constructor.construct(args); + } + } + return null; + } + + /** + * Get all the registered names + * @return the names + */ + public List getAllRegisteredNames() { + List names = new ArrayList<>(); + for (C constructor : constructors) { + names.add(constructor.getName()); + } + return names; + } + + /** + * Register a constructor + * @param constructor the constructor + */ + public void registerConstructor(C constructor) { + constructors.add(constructor); + } + + /** + * Unregister a constructor + * @param constructor the constructor + */ + public void unregisterConstructor(C constructor) { + constructors.remove(constructor); + } + + /** + * Get the constructor by name + * @param name the name + * @return the constructor + */ + public C getConstructor(String name) { + for (C constructor : constructors) { + if (constructor.getName().equals(name)) { + return constructor; + } + } + return null; + } + + /** + * Get all the constructors + * @return the constructors in a new list + */ + public List getConstructors() { + return new ArrayList<>(constructors); + } + + /** + * Clear all the constructors + */ + public void clear() { + constructors.clear(); + } +} diff --git a/src/main/java/fr/phylisiumstudio/logic/fabric/IConstructor.java b/src/main/java/fr/phylisiumstudio/logic/fabric/IConstructor.java new file mode 100644 index 0000000..a0ee388 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/fabric/IConstructor.java @@ -0,0 +1,6 @@ +package fr.phylisiumstudio.logic.fabric; + +public interface IConstructor { + String getName(); + T construct(Object... args); +} diff --git a/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java b/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java new file mode 100644 index 0000000..432dd06 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/logic/runnable/PhysicsThreadRunnable.java @@ -0,0 +1,88 @@ +package fr.phylisiumstudio.logic.runnable; + +import fr.phylisiumstudio.logic.WorldPhysics; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +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 + */ +public class PhysicsThreadRunnable implements Runnable { + private final UUID id = UUID.randomUUID(); + private final List worldPhysics; + private final Logger logger = LoggerFactory.getLogger(PhysicsThreadRunnable.class); + + private final Lock lock = new ReentrantLock(); + + /** + * Create a new physics task + */ + public PhysicsThreadRunnable() { + this.worldPhysics = new ArrayList<>(); + } + + @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() > 100) { + logger.warn("PhysicsTask #" + id + " is running slow: " + duration.toMillis() + "ms"); + } + } catch (NullPointerException e) { + logger.error("NullPointerException in world physics thread #" + id, e); + } catch (Exception e) { + logger.error("Error in world physics thread #" + id, e); + } + finally { + lock.unlock(); + } + } + + /** + * Add a world to the physics task + * @param worldPhysics The world to add + */ + public void addWorld(WorldPhysics worldPhysics) { + this.worldPhysics.add(worldPhysics); + } + + /** + * Remove a world from the physics task + * @param worldPhysics The world to remove + */ + public void removeWorld(WorldPhysics worldPhysics) { + this.worldPhysics.remove(worldPhysics); + } + + public int getWorldsSize() { + return this.worldPhysics.size(); + } + + public List getWorlds() { + return new ArrayList<>(this.worldPhysics); + } + + public WorldPhysics getWorld(UUID worldId) { + return this.worldPhysics.stream() + .filter(world -> world.getUniqueId().equals(worldId)) + .findFirst() + .orElse(null); + } +} diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/ItemLinkerManager.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/ItemLinkerManager.java deleted file mode 100644 index 45eace4..0000000 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/ItemLinkerManager.java +++ /dev/null @@ -1,51 +0,0 @@ -package fr.phylisiumstudio.soraxPhysic; - -import fr.phylisiumstudio.logic.ItemLinker; -import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -public class ItemLinkerManager { - private Map items; - - public ItemLinkerManager() { - this.items = new HashMap<>(); - } - - public ItemStack CreateLinker(UUID id){ - ItemLinker linker = new ItemLinker(id); - ItemStack item = new ItemStack(Material.STICK); - ItemMeta meta = item.getItemMeta(); - meta.setDisplayName("Linker"); - ArrayList lore = new ArrayList<>(); - lore.add(id.toString()); - meta.setLore(lore); - item.setItemMeta(meta); - - items.put(linker, item); - return item; - } - - public ItemLinker getLinker(UUID id){ - for(ItemLinker linker : items.keySet()){ - if(linker.getId().equals(id)){ - return linker; - } - } - return null; - } - - public ItemStack getItem(UUID id){ - for(ItemLinker linker : items.keySet()){ - if(linker.getId().equals(id)){ - return items.get(linker); - } - } - return null; - } -} diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/PhysicsManager.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/PhysicsManager.java index f78e785..dd45746 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/PhysicsManager.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/PhysicsManager.java @@ -1,383 +1,136 @@ package fr.phylisiumstudio.soraxPhysic; -import com.bulletphysics.collision.broadphase.BroadphaseInterface; -import com.bulletphysics.collision.broadphase.DbvtBroadphase; -import com.bulletphysics.collision.dispatch.CollisionConfiguration; -import com.bulletphysics.collision.dispatch.CollisionDispatcher; -import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration; -import com.bulletphysics.collision.shapes.BoxShape; -import com.bulletphysics.collision.shapes.CompoundShape; -import com.bulletphysics.collision.shapes.SphereShape; -import com.bulletphysics.dynamics.DiscreteDynamicsWorld; -import com.bulletphysics.dynamics.RigidBody; -import com.bulletphysics.dynamics.RigidBodyConstructionInfo; -import com.bulletphysics.dynamics.constraintsolver.*; -import com.bulletphysics.linearmath.Transform; +import fr.phylisiumstudio.logic.WorldManager; +import fr.phylisiumstudio.logic.WorldPhysics; import fr.phylisiumstudio.soraxPhysic.models.RigidBlock; import org.bukkit.*; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; -import org.bukkit.entity.*; -import org.bukkit.scheduler.BukkitTask; -import org.bukkit.util.Transformation; +import org.bukkit.block.data.BlockData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.vecmath.Vector3f; -import java.time.Duration; import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - -import static org.bukkit.block.BlockFace.NORTH; -import static org.bukkit.block.BlockFace.SOUTH; public class PhysicsManager { - private DiscreteDynamicsWorld dynamicsWorld; - private final World bukkitWorld; - - private final List rigidBlocks; - - private boolean timeFreeze = false; - private float timespan = 1/20f; - private int maxSubSteps = 30; - - private boolean running = true; - - private final ExecutorService executorService; - private final Object lock = new Object(); - + private final WorldManager worldManager; private final Logger logger = LoggerFactory.getLogger(PhysicsManager.class); - - public PhysicsManager(World bukkitWorld) { - this.bukkitWorld = bukkitWorld; - this.rigidBlocks = new ArrayList<>(); - - this.executorService = Executors.newSingleThreadExecutor(); - this.executorService.submit(this::SetupPhysiqueEngine); - } - - private void SetupPhysiqueEngine(){ - BroadphaseInterface broadphase = new DbvtBroadphase(); - CollisionConfiguration collisionConfiguration = new DefaultCollisionConfiguration(); - CollisionDispatcher dispatcher = new CollisionDispatcher(collisionConfiguration); - ConstraintSolver solver = new SequentialImpulseConstraintSolver(); - - this.dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); - this.runPhysics(); - } - - private void runPhysics() { - while (running) { - try { - synchronized (lock) { - if (dynamicsWorld == null){ - logger.error("Dynamics world is null"); - continue; - } - - if (!timeFreeze) { - dynamicsWorld.stepSimulation(timespan, maxSubSteps); - } - } - Thread.sleep(Duration.ofMillis(50)); - } catch (InterruptedException e) { - logger.info("Physics thread interrupted"); - running = false; - Thread.currentThread().interrupt(); - } catch (Exception e) { - running = false; - logger.error("Error in physics thread", e); - } - } - } - - public RigidBody createBoxShape(Location location, Material material, float mass, float xScale, float yScale, float zScale) { - BlockDisplay blockDisplay = bukkitWorld.spawn(location, BlockDisplay.class, display -> { - display.setBlock(material.createBlockData()); - display.setRotation(0, 0); - - Transformation transformation = display.getTransformation(); - org.joml.Vector3f translation = new org.joml.Vector3f(-xScale/2, -yScale/2, -zScale/2); - org.joml.Vector3f scale = new org.joml.Vector3f(xScale, yScale, zScale); - display.setTransformation(new Transformation(translation, transformation.getLeftRotation(), scale, transformation.getRightRotation())); - }); - Interaction hitbox = bukkitWorld.spawn(location, Interaction.class, display -> { - display.setInteractionHeight(xScale); - display.setInteractionWidth(zScale); - display.setResponsive(true); - }); - - Vector3f position = new Vector3f(xScale/2, yScale/2, zScale/2); - BoxShape boxShape = new BoxShape(position); - - Transform transform = new Transform(); - transform.setIdentity(); - transform.origin.set((float) location.getX(), (float) location.getY(), (float) location.getZ()); - - Vector3f localInertia = new Vector3f(0, 0, 0); - boxShape.calculateLocalInertia(3, localInertia); - - - RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(mass, null, boxShape, localInertia); - RigidBody body = new RigidBody(constructionInfo); - body.setWorldTransform(transform); - body.setRestitution(0.0f); - - RigidBlock rigidBlock = new RigidBlock(body, blockDisplay, hitbox); - BukkitMotionState motionState = new BukkitMotionState(rigidBlock); - body.setMotionState(motionState); - - synchronized (lock){ - dynamicsWorld.addRigidBody(body); - rigidBlocks.add(rigidBlock); - } - - ConvertChunk(location.getChunk()); - - return body; - } - - public void createsphereShape(Location location, Material material, float mass, float radius) { - BlockDisplay blockDisplay = bukkitWorld.spawn(location, BlockDisplay.class, display -> { - display.setBlock(material.createBlockData()); - display.setRotation(0, 0); - display.setInterpolationDuration(1); - display.setTeleportDuration(1); - - Transformation transformation = display.getTransformation(); - org.joml.Vector3f translation = new org.joml.Vector3f(-radius/2, -radius/2, -radius/2); - org.joml.Vector3f scale = new org.joml.Vector3f(radius, radius, radius); - display.setTransformation(new Transformation(translation, transformation.getLeftRotation(), scale, transformation.getRightRotation())); - }); - Interaction hitbox = bukkitWorld.spawn(location, Interaction.class, display -> { - display.setInteractionHeight(radius); - display.setInteractionWidth(radius); - display.setResponsive(true); - }); - - Transformation transformation = blockDisplay.getTransformation(); - - SphereShape boxShape = new SphereShape(radius); - - Transform transform = new Transform(); - transform.setIdentity(); - transform.origin.set((float) location.getX(), (float) location.getY(), (float) location.getZ()); - - Vector3f localInertia = new Vector3f(0, 0, 0); - boxShape.calculateLocalInertia(3, localInertia); - - RigidBodyConstructionInfo constructionInfo = new RigidBodyConstructionInfo(mass, null, boxShape, localInertia); - RigidBody body = new RigidBody(constructionInfo); - body.setWorldTransform(transform); - body.setRestitution(0.0f); - - RigidBlock rigidBlock = new RigidBlock(body, blockDisplay, hitbox); - BukkitMotionState motionState = new BukkitMotionState(rigidBlock); - body.setMotionState(motionState); - - synchronized (lock){ - dynamicsWorld.addRigidBody(body); - rigidBlocks.add(rigidBlock); - } - - ConvertChunk(location.getChunk()); - } - - public void ConvertChunk(Chunk chunk) { - CompoundShape compoundShape = new CompoundShape(); - final int numThreads = Runtime.getRuntime().availableProcessors(); // Nombre de threads à utiliser - ExecutorService executor = Executors.newFixedThreadPool(numThreads); - - // Liste pour stocker les futurs - List> futures = new ArrayList<>(); - - // Divisez le travail en tâches plus petites - for (int x = 0; x < 16; x++) { - final int currentX = x; - Future future = executor.submit(() -> { - for (int z = 0; z < 16; z++) { - for (int y = 0; y < 256; y++) { - int newX = chunk.getX() * 16 + currentX; - int newY = y; - int newZ = chunk.getZ() * 16 + z; - - Block block = chunk.getBlock(currentX, y, z); - if (block.getType() == Material.AIR || block.getType().isTransparent()) continue; - - // Vérifier si ce bloc est adjacent à un bloc d'air - if (isAdjacentToAir(chunk, currentX, y, z)) { - // Créer une BoxShape pour ce bloc - BoxShape blockShape = new BoxShape(new Vector3f(0.5f, 0.5f, 0.5f)); - - // Définir la transformation pour ce bloc - Transform transformShape = new Transform(); - transformShape.setIdentity(); - transformShape.origin.set(new Vector3f(newX + 0.5f, newY + 0.5f, newZ + 0.5f)); - - // Ajouter la BoxShape à la CompoundShape - synchronized (compoundShape) { - compoundShape.addChildShape(transformShape, blockShape); - } - } - } - } - return null; - }); - futures.add(future); - } - - // Attendez que toutes les tâches soient terminées - for (Future future : futures) { - try { - future.get(); - } catch (Exception e) { - e.printStackTrace(); - } + public static final Object lock = new Object(); + + + /** + * Create a new physics manager + */ + public PhysicsManager() { + this.worldManager = new WorldManager(); + } + + /** + * Register a world with the physics engine + * + * @param world The world to register + */ + public void registerWorld(WorldPhysics world) { + this.worldManager.registerWorld(world); + } + + /** + * Unregister a world from the physics engine + * @param world The world to unregister + */ + public void unregisterWorld(WorldPhysics world) { + this.worldManager.unregisterWorld(world); + } + + /** + * Create a box + * @param location The location of the box + * @param data The block data + * @param mass The mass of the box + * @param xScale The x scale of the box + * @param yScale The y scale of the box + * @param zScale The z scale of the box + * @return The rigid block + */ + public RigidBlock createBox(Location location, BlockData data, float mass, float xScale, float yScale, float zScale) { + WorldPhysics world = getWorldPhysics(location.getWorld().getUID()); + if (world == null) { + throw new IllegalArgumentException("The world is not managed by the physics engine"); } - - // Créer et ajouter la CompoundShape au monde dynamique - if (compoundShape.getNumChildShapes() > 0) { - RigidBody rigidBody = createStaticRigidBody(compoundShape); - dynamicsWorld.addRigidBody(rigidBody); + return world.createBox(location, data, mass, xScale, yScale, zScale); + } + + /** + * Create a sphere + * @param location The location of the sphere + * @param data The block data + * @param mass The mass of the sphere + * @param radius The radius of the sphere + */ + public void createSphere(Location location, BlockData data, float mass, float radius) { + WorldPhysics world = getWorldPhysics(location.getWorld().getUID()); + if (world == null) { + throw new IllegalArgumentException("The world is not managed by the physics engine"); } - - executor.shutdown(); + world.createSphere(location, data, radius, mass); } - private boolean isAdjacentToAir(Chunk chunk, int x, int y, int z) { - // Vérifier les blocs adjacents - return (isAir(chunk, x - 1, y, z) || isAir(chunk, x + 1, y, z) || - isAir(chunk, x, y - 1, z) || isAir(chunk, x, y + 1, z) || - isAir(chunk, x, y, z - 1) || isAir(chunk, x, y, z + 1)); - } - - private boolean isAir(Chunk chunk, int x, int y, int z) { - // Assurez-vous que les coordonnées sont dans les limites valides - if (x < 0 || x >= 16 || y < 0 || y >= 256 || z < 0 || z >= 16) { - return false; - } - Block block = chunk.getBlock(x, y, z); - return block.getType() == Material.AIR; - } - - public void linkRigidBody(RigidBody r1, RigidBody r2, BlockFace f1, BlockFace f2) { - Vector3f relativePosition1 = getOffsetFromBlockFace(f1); - Vector3f relativePosition2 = getOffsetFromBlockFace(f2); - - relativePosition2.negate(); - - Transform transform1 = new Transform(); - Transform transform2 = new Transform(); - r1.getMotionState().getWorldTransform(transform1); - r2.getMotionState().getWorldTransform(transform2); - - // Créer les transformations locales par rapport aux positions des faces - Transform localTransform1 = new Transform(); - localTransform1.setIdentity(); - localTransform1.origin.set(relativePosition1); - - Transform localTransform2 = new Transform(); - localTransform2.setIdentity(); - localTransform2.origin.set(relativePosition2); - - // Créer la contrainte générique pour lier les deux RigidBody - boolean useLinearReferenceFrameA = true; - Generic6DofConstraint constraint = new Generic6DofConstraint(r1, r2, localTransform1, localTransform2, useLinearReferenceFrameA); - - // Verrouiller tous les degrés de liberté - constraint.setLinearLowerLimit(new Vector3f(0, 0, 0)); - constraint.setLinearUpperLimit(new Vector3f(0, 0, 0)); - constraint.setAngularLowerLimit(new Vector3f(0, 0, 0)); - constraint.setAngularUpperLimit(new Vector3f(0, 0, 0)); - - dynamicsWorld.addConstraint(constraint, true); + /** + * Get the physics object for the given world + * @param uniqueId The unique id of the world + * @return The physics object or null if not found + */ + public WorldPhysics getWorldPhysics(UUID uniqueId) { + return worldManager.getWorld(uniqueId); } - private boolean blockExistsInWorld(int x, int y, int z) { - return dynamicsWorld.getCollisionObjectArray().stream().anyMatch(collisionObject -> { - Transform t = new Transform(); - collisionObject.getWorldTransform(t); - return t.origin.x == x && t.origin.y == y && t.origin.z == z; + /** + * Clear all the physics objects + */ + public void clear() { + worldManager.getWorlds().forEach((id, world) -> { + world.clear(); }); } - private RigidBody createStaticRigidBody(CompoundShape compoundShape) { - float mass = 0.0f; - Vector3f inertia = new Vector3f(0, 0, 0); - RigidBodyConstructionInfo chunkInfo = new RigidBodyConstructionInfo(mass, null, compoundShape, inertia); - RigidBody groundBlock = new RigidBody(chunkInfo); - Transform ntransform = new Transform(); - ntransform.setIdentity(); - groundBlock.setWorldTransform(ntransform); - return groundBlock; - } - - public void clearAll() { - synchronized (lock){ - for (RigidBlock rigidBlock : rigidBlocks) { - dynamicsWorld.removeRigidBody(rigidBlock.getRigidBody()); - rigidBlock.getBlockDisplay().remove(); - rigidBlock.getInteraction().remove(); - } - } - } - - public BukkitTask runOnMainThread(Runnable runnable){ - return Bukkit.getScheduler().runTask(SoraxPhysic.getInstance(), runnable); - } - - public BukkitTask runOnAsyncThread(Runnable runnable){ - return Bukkit.getScheduler().runTaskAsynchronously(SoraxPhysic.getInstance(), runnable); - } - - public void setTimeFreeze(boolean timeFreeze) { - synchronized (lock) { - this.timeFreeze = timeFreeze; - } - } - - public void setTimespan(float timespan) { - this.timespan = timespan; - } - - public void setMaxSubSteps(int maxSubSteps) { - this.maxSubSteps = maxSubSteps; - } - - public boolean isTimeFreeze() { - return timeFreeze; + /** + * Stop the physics engine + */ + public void stop() { + worldManager.shutdown(); } - public float getTimespan() { - return timespan; + /** + * Pause the physics simulation for all worlds + */ + public void pauseAllWorlds() { + worldManager.getWorlds().forEach((id, world) -> { + world.setFreeze(true); + }); } - public List getRigidBlocks() { - return rigidBlocks; + /** + * Resume the physics simulation for all worlds + */ + public void resumeAllWorlds() { + worldManager.getWorlds().forEach((id, world) -> { + world.setFreeze(false); + }); } - public World getBukkitWorld() { - return bukkitWorld; + /** + * Get all the worlds being simulated + * @return The list of worlds + */ + public List getAllWorlds() { + return new ArrayList<>(worldManager.getWorlds().values()); } - public void stop() { - running = false; - if (executorService != null && !executorService.isShutdown()) { - executorService.shutdownNow(); // Arrête les tâches en cours et rejette les nouvelles tâches - } + /** + * Check if a world is being simulated + * @param worldId The unique id of the world + * @return True if the world is being simulated + */ + public boolean isWorldSimulated(UUID worldId) { + return worldManager.isWorldRegistered(worldId); } - private Vector3f getOffsetFromBlockFace(BlockFace face) { - return switch (face) { - case NORTH -> new Vector3f(0, 0, -0.5f); - case SOUTH -> new Vector3f(0, 0, 0.5f); - case EAST -> new Vector3f(0.5f, 0, 0); - case WEST -> new Vector3f(-0.5f, 0, 0); - case UP -> new Vector3f(0, 0.5f, 0); - case DOWN -> new Vector3f(0, -0.5f, 0); - default -> new Vector3f(0, 0, 0); // Par défaut, aucune déviation - }; - } } diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/SoraxPhysic.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/SoraxPhysic.java index 9c9998e..2191eaf 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/SoraxPhysic.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/SoraxPhysic.java @@ -5,7 +5,8 @@ import com.sk89q.worldedit.session.SessionManager; import fr.phylisiumstudio.soraxPhysic.commands.PhysicsCommands; import fr.phylisiumstudio.soraxPhysic.listeners.RigidbodyListener; -import fr.phylisiumstudio.soraxPhysic.listeners.ToolsListener; +import fr.phylisiumstudio.soraxPhysic.listeners.PlayerActionListener; +import fr.phylisiumstudio.soraxPhysic.listeners.WorldListener; import org.bstats.bukkit.Metrics; import org.bstats.charts.SimplePie; import org.bukkit.Material; @@ -21,22 +22,20 @@ public final class SoraxPhysic extends JavaPlugin { private PaperCommandManager commandManager; private PhysicsManager physicsManager; - private ItemLinkerManager itemLinkerManager; @Override public void onEnable() { instance = this; - this.itemLinkerManager = new ItemLinkerManager(); - setupPhysics(); setupCommands(); setupListeners(); + setupBstats(); } @Override public void onDisable() { - this.physicsManager.clearAll(); + this.physicsManager.clear(); this.physicsManager.stop(); } @@ -51,7 +50,6 @@ private void setupCommands(){ commandManager = new PaperCommandManager(this); commandManager.enableUnstableAPI("help"); commandManager.registerDependency(PhysicsManager.class, physicsManager); - commandManager.registerDependency(ItemLinkerManager.class, itemLinkerManager); commandManager.registerDependency(SessionManager.class, WorldEdit.getInstance().getSessionManager()); commandManager.registerCommand(new PhysicsCommands()); @@ -75,16 +73,24 @@ private void setupCommands(){ } private void setupPhysics() { - World world = getServer().getWorlds().get(0); - physicsManager = new PhysicsManager(world); + physicsManager = new PhysicsManager(); } private void setupListeners() { - getServer().getPluginManager().registerEvents(new ToolsListener(physicsManager, this.itemLinkerManager), this); + getServer().getPluginManager().registerEvents(new WorldListener(physicsManager), this); + getServer().getPluginManager().registerEvents(new PlayerActionListener(physicsManager), this); getServer().getPluginManager().registerEvents(new RigidbodyListener(physicsManager, getServer()), this); } public static SoraxPhysic getInstance() { return instance; } + + /** + * Get the physics manager + * @return the physics manager + */ + public PhysicsManager getPhysicsManager() { + return physicsManager; + } } diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/commands/PhysicsCommands.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/commands/PhysicsCommands.java index 300a5d7..8bb089c 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/commands/PhysicsCommands.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/commands/PhysicsCommands.java @@ -3,30 +3,14 @@ import co.aikar.commands.BaseCommand; import co.aikar.commands.CommandHelp; import co.aikar.commands.annotation.*; -import com.bulletphysics.dynamics.RigidBody; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.IncompleteRegionException; -import com.sk89q.worldedit.LocalSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.regions.Region; import com.sk89q.worldedit.session.SessionManager; -import com.sk89q.worldedit.world.World; -import com.sk89q.worldedit.world.block.BlockState; -import fr.phylisiumstudio.soraxPhysic.ItemLinkerManager; +import fr.phylisiumstudio.logic.WorldPhysics; import fr.phylisiumstudio.soraxPhysic.PhysicsManager; import org.bukkit.Material; -import org.bukkit.block.Block; -import org.bukkit.block.BlockFace; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import javax.vecmath.Vector3f; @CommandAlias("physics") @Description("Physics related commands") @@ -34,9 +18,6 @@ public class PhysicsCommands extends BaseCommand { @Dependency private PhysicsManager physicsManager; - @Dependency - private ItemLinkerManager itemLinkerManager; - @Dependency private SessionManager sessionManager; @@ -48,7 +29,7 @@ private class CreateCommands extends BaseCommand { @Description("Create a box shape") @CommandPermission("physics.create.box") public void createBox(Player sender, Material block, float mass, float xscale, float yscale, float zscale){ - physicsManager.createBoxShape(sender.getEyeLocation(), block, mass, xscale, yscale, zscale); + physicsManager.createBox(sender.getEyeLocation(), block.createBlockData(), mass, xscale, yscale, zscale); sender.sendMessage("Box created"); } @@ -57,11 +38,11 @@ public void createBox(Player sender, Material block, float mass, float xscale, f @Description("Create a sphere shape") @CommandPermission("physics.create.sphere") public void createSphere(Player sender, Material block, float mass, float radius){ - physicsManager.createsphereShape(sender.getEyeLocation(), block, mass, radius); + physicsManager.createSphere(sender.getEyeLocation(), block.createBlockData(), mass, radius); sender.sendMessage("Sphere created"); } - @Subcommand("convert") + /*@Subcommand("convert") @Description("Convert the WorldEdit selection to physics shapes (beta feature do not use if you don't know what you are doing)") @CommandPermission("physics.create.convert") public void convertSelection(Player player, float impulse, float damping, float tau){ @@ -124,32 +105,32 @@ public void convertSelection(Player player, float impulse, float damping, float } player.sendMessage("Selection converted"); - } - } - - @Subcommand("tools") - @Description("Don't work for the moment") - private class ToolsCommands extends BaseCommand { - @Subcommand("linker") - public void createLinker(Player sender){ - ItemStack linker = itemLinkerManager.CreateLinker(sender.getUniqueId()); - sender.getInventory().addItem(linker); - } + }*/ } @Subcommand("clear") @Description("Clear all shapes in the physics engine") @CommandPermission("physics.clear") public void clearShapes(Player sender){ - physicsManager.clearAll(); - sender.sendMessage("Shapes cleared"); + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(sender.getWorld().getUID()); + if(worldPhysics == null){ + throw new IllegalArgumentException("The world is not managed by the physics engine"); + } + worldPhysics.clear(); + sender.sendMessage("Shapes cleared for the world " + sender.getWorld().getName()); } @Subcommand("freeze") @Description("Freeze the time in the physics engine") @CommandPermission("physics.freeze") public void freezeTime(Player sender){ - physicsManager.setTimeFreeze(!physicsManager.isTimeFreeze()); + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(sender.getWorld().getUID()); + if(worldPhysics == null){ + throw new IllegalArgumentException("The world is not managed by the physics engine"); + } + boolean freeze = !worldPhysics.isFrozen(); + worldPhysics.setFreeze(freeze); + sender.sendMessage("Time " + (freeze ? "frozen" : "unfrozen") + " for the world " + sender.getWorld().getName()); } @Subcommand("timespan") @@ -157,8 +138,12 @@ public void freezeTime(Player sender){ @Description("Set the timespan for the physics engine (do not touch if you don't know what you are doing)") @CommandPermission("physics.timespan") public void setTimeSpan(Player sender, float timespan){ - physicsManager.setTimespan(timespan); - sender.sendMessage("Timespan set to " + timespan); + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(sender.getWorld().getUID()); + if(worldPhysics == null){ + throw new IllegalArgumentException("The world is not managed by the physics engine"); + } + worldPhysics.setTimespan(timespan); + sender.sendMessage("Timespan set to " + timespan + "s for the world " + sender.getWorld().getName()); } @Subcommand("substeps") @@ -166,15 +151,25 @@ public void setTimeSpan(Player sender, float timespan){ @Description("Set the number of substeps for the physics engine (do not touch if you don't know what you are doing)") @CommandPermission("physics.substeps") public void setMaxSubSteps(Player sender, int maxSubSteps){ - physicsManager.setMaxSubSteps(maxSubSteps); - sender.sendMessage("Max substeps set to " + maxSubSteps); + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(sender.getWorld().getUID()); + if(worldPhysics == null){ + throw new IllegalArgumentException("The world is not managed by the physics engine"); + } + worldPhysics.setMaxSubSteps(maxSubSteps); + sender.sendMessage("Max substeps set to " + maxSubSteps + " for the world " + sender.getWorld().getName()); } @Subcommand("chunk") @Description("Convert the chunk where the player is standing to a physics chunk") @CommandPermission("physics.chunk") public void convertChunk(Player sender){ - physicsManager.ConvertChunk(sender.getLocation().getChunk()); + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(sender.getWorld().getUID()); + if(worldPhysics == null){ + throw new IllegalArgumentException("The world is not managed by the physics engine"); + } + Vector3f pos1 = new Vector3f(sender.getLocation().getBlockX(), sender.getLocation().getBlockY(), sender.getLocation().getBlockZ()); + Vector3f pos2 = new Vector3f(sender.getLocation().getBlockX() + 16, sender.getLocation().getBlockY() + 16, sender.getLocation().getBlockZ() + 16); + worldPhysics.convertChunk(pos1, pos2); sender.sendMessage("Chunk converted"); } diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolHeld.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolHeld.java deleted file mode 100644 index c843aaf..0000000 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolHeld.java +++ /dev/null @@ -1,9 +0,0 @@ -package fr.phylisiumstudio.soraxPhysic.consumers; - -import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; -import org.bukkit.inventory.ItemStack; - -@FunctionalInterface -public interface ToolHeld { - void onToolHeld(ItemStack itemStack, PlayerInventorySlotChangeEvent event); -} diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolUnheld.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolUnheld.java deleted file mode 100644 index 822e667..0000000 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/consumers/ToolUnheld.java +++ /dev/null @@ -1,9 +0,0 @@ -package fr.phylisiumstudio.soraxPhysic.consumers; - -import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; -import org.bukkit.inventory.ItemStack; - -@FunctionalInterface -public interface ToolUnheld { - void onToolUnheld(ItemStack itemStack, PlayerInventorySlotChangeEvent event); -} diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/ToolsListener.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/PlayerActionListener.java similarity index 60% rename from src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/ToolsListener.java rename to src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/PlayerActionListener.java index 3fae8f6..3448bf9 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/ToolsListener.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/PlayerActionListener.java @@ -1,59 +1,24 @@ package fr.phylisiumstudio.soraxPhysic.listeners; -import com.bulletphysics.dynamics.RigidBody; -import fr.phylisiumstudio.logic.ItemLinker; -import fr.phylisiumstudio.soraxPhysic.ItemLinkerManager; import fr.phylisiumstudio.soraxPhysic.PhysicsManager; -import fr.phylisiumstudio.soraxPhysic.consumers.ToolHeld; -import fr.phylisiumstudio.soraxPhysic.consumers.ToolUnheld; import fr.phylisiumstudio.soraxPhysic.event.LeftClickRigidblockEvent; import fr.phylisiumstudio.soraxPhysic.event.RightClickRigidblockEvent; import fr.phylisiumstudio.soraxPhysic.models.RigidBlock; -import io.papermc.paper.event.player.PlayerInventorySlotChangeEvent; import org.bukkit.Location; import org.bukkit.Particle; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.inventory.ItemStack; import org.bukkit.util.Vector; import javax.vecmath.Vector3f; -import java.util.HashMap; -import java.util.Map; -public class ToolsListener implements Listener { +public class PlayerActionListener implements Listener { private final PhysicsManager physicsManager; - private final ItemLinkerManager itemLinkerManager; - private final Map toolHeldMap; - private final Map toolUnheldMap; - - public ToolsListener(PhysicsManager physicsManager, ItemLinkerManager itemLinkerManager) { + public PlayerActionListener(PhysicsManager physicsManager) { this.physicsManager = physicsManager; - this.itemLinkerManager = itemLinkerManager; - - this.toolHeldMap = new HashMap<>(); - this.toolUnheldMap = new HashMap<>(); - - } - - @EventHandler - public void onToolHeld(PlayerInventorySlotChangeEvent event){ - ItemStack newItem = event.getNewItemStack(); - ItemStack oldItem = event.getOldItemStack(); - - Integer newUUID = newItem.hashCode(); - Integer oldUUID = oldItem.hashCode(); - - if (toolHeldMap.containsKey(newUUID)){ - toolHeldMap.get(newUUID).onToolHeld(newItem, event); - } - - if (toolUnheldMap.containsKey(oldUUID)){ - toolUnheldMap.get(oldUUID).onToolUnheld(oldItem, event); - } } @EventHandler(priority = EventPriority.LOW) @@ -67,17 +32,17 @@ public void onRightClickRigidbody(RightClickRigidblockEvent event){ Location playerLocation = event.getPlayer().getEyeLocation(); Vector playerDirection = playerLocation.getDirection(); playerDirection.normalize(); - Vector direction = clickedPosition.subtract(playerLocation.toVector()); + Vector direction = playerLocation.getDirection().subtract(clickedPosition); Vector3f impulse = new Vector3f((float) direction.getX(), (float) direction.getY(), (float) direction.getZ()); - impulse.scale(5); + impulse.scale(5f); org.joml.Vector3f impulseJoml = new org.joml.Vector3f(impulse.x, impulse.y, impulse.z); org.joml.Vector3f clickedPositionJoml = new org.joml.Vector3f((float) clickedPosition.getX(), (float) clickedPosition.getY(), (float) clickedPosition.getZ()); rigidBlock.applyImpulse(clickedPositionJoml, impulseJoml); } - @EventHandler + /*@EventHandler public void onLeftClickRigidbody(LeftClickRigidblockEvent event){ Vector clickedPosition = event.getClickLocation().toVector(); RigidBlock rigidBlock = event.getRigidBlock(); @@ -91,18 +56,10 @@ public void onLeftClickRigidbody(LeftClickRigidblockEvent event){ Vector direction = playerDirection.subtract(clickedPosition); Vector3f impulse = new Vector3f((float) direction.getX(), (float) direction.getY(), (float) direction.getZ()); - impulse.scale(5); + impulse.scale(0.1f); org.joml.Vector3f impulseJoml = new org.joml.Vector3f(impulse.x, impulse.y, impulse.z); org.joml.Vector3f clickedPositionJoml = new org.joml.Vector3f((float) clickedPosition.getX(), (float) clickedPosition.getY(), (float) clickedPosition.getZ()); rigidBlock.applyImpulse(clickedPositionJoml, impulseJoml); - } - - public void registerToolHeld(Integer uuid, ToolHeld toolHeld){ - toolHeldMap.put(uuid, toolHeld); - } - - public void registerToolUnheld(Integer uuid, ToolUnheld toolUnheld){ - toolUnheldMap.put(uuid, toolUnheld); - } + }*/ } diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/RigidbodyListener.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/RigidbodyListener.java index 0b6f190..d833109 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/RigidbodyListener.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/RigidbodyListener.java @@ -1,6 +1,7 @@ package fr.phylisiumstudio.soraxPhysic.listeners; import com.bulletphysics.dynamics.RigidBody; +import fr.phylisiumstudio.logic.WorldPhysics; import fr.phylisiumstudio.soraxPhysic.PhysicsManager; import fr.phylisiumstudio.soraxPhysic.event.LeftClickRigidblockEvent; import fr.phylisiumstudio.soraxPhysic.event.RightClickRigidblockEvent; @@ -19,8 +20,8 @@ import java.util.List; public class RigidbodyListener implements Listener { - private PhysicsManager physicsManager; - private Server server; + private final PhysicsManager physicsManager; + private final Server server; public RigidbodyListener(PhysicsManager physicsManager, Server server) { this.physicsManager = physicsManager; @@ -33,7 +34,9 @@ public void onEntityInteract(PlayerInteractAtEntityEvent event){ return; } - List rigidBlocks = physicsManager.getRigidBlocks(); + WorldPhysics world = this.physicsManager.getWorldPhysics(event.getPlayer().getWorld().getUID()); + + List rigidBlocks = world.getBlocks(); World bukkitWorld = event.getPlayer().getWorld(); RigidBlock rigidBlock = rigidBlocks.stream() @@ -64,8 +67,9 @@ public void onEntityDamageByEntity(EntityDamageByEntityEvent event){ return; } - List rigidBlocks = physicsManager.getRigidBlocks(); - World bukkitWorld = hitbox.getWorld(); + WorldPhysics worldPhysics = this.physicsManager.getWorldPhysics(player.getWorld().getUID()); + + List rigidBlocks = worldPhysics.getBlocks(); RigidBlock rigidBlock = rigidBlocks.stream() .filter(motionState -> motionState.getInteraction().equals(hitbox)) @@ -79,7 +83,7 @@ public void onEntityDamageByEntity(EntityDamageByEntityEvent event){ if (blockDisplay == null || body == null) { return; } - Location clickedLocation = event.getDamageSource().getDamageLocation(); + Location clickedLocation = event.getEntity().getLocation(); LeftClickRigidblockEvent rightClickRigidbodyEvent = new LeftClickRigidblockEvent(player, rigidBlock, clickedLocation); this.server.getPluginManager().callEvent(rightClickRigidbodyEvent); diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/WorldListener.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/WorldListener.java new file mode 100644 index 0000000..46bd382 --- /dev/null +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/listeners/WorldListener.java @@ -0,0 +1,29 @@ +package fr.phylisiumstudio.soraxPhysic.listeners; + +import fr.phylisiumstudio.bullet.BulletWorldPhysics; +import fr.phylisiumstudio.logic.WorldPhysics; +import fr.phylisiumstudio.soraxPhysic.PhysicsManager; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.world.WorldInitEvent; +import org.bukkit.event.world.WorldUnloadEvent; + +public class WorldListener implements Listener { + private final PhysicsManager physicsManager; + + public WorldListener(PhysicsManager physicsManager) { + this.physicsManager = physicsManager; + } + + @EventHandler + public void onWorldUnload(WorldUnloadEvent event) { + WorldPhysics worldPhysics = physicsManager.getWorldPhysics(event.getWorld().getUID()); + physicsManager.unregisterWorld(worldPhysics); + } + + @EventHandler + public void onWorldInit(WorldInitEvent event) { + BulletWorldPhysics worldPhysics = new BulletWorldPhysics(event.getWorld()); + physicsManager.registerWorld(worldPhysics); + } +} diff --git a/src/main/java/fr/phylisiumstudio/soraxPhysic/models/RigidBlock.java b/src/main/java/fr/phylisiumstudio/soraxPhysic/models/RigidBlock.java index 386604c..d6a6062 100644 --- a/src/main/java/fr/phylisiumstudio/soraxPhysic/models/RigidBlock.java +++ b/src/main/java/fr/phylisiumstudio/soraxPhysic/models/RigidBlock.java @@ -9,9 +9,9 @@ public class RigidBlock { private final UUID uniqueId; - private RigidBody rigidBody; - private BlockDisplay blockDisplay; - private Interaction interaction; + private final RigidBody rigidBody; + private final BlockDisplay blockDisplay; + private final Interaction interaction; public RigidBlock(RigidBody rigidBody, BlockDisplay blockDisplay, Interaction interaction) { this.rigidBody = rigidBody; diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 1bfda29..34d82af 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,3 +2,5 @@ name: SoraxPhysic version: '1.0-SNAPSHOT' main: fr.phylisiumstudio.soraxPhysic.SoraxPhysic api-version: '1.21' +author: SoraxDubbing +load: STARTUP