From a83b538b04314934c05662e69bd51067b067aec4 Mon Sep 17 00:00:00 2001 From: yoyosource Date: Wed, 30 Aug 2023 21:19:17 +0200 Subject: [PATCH] Add Pos and OptimizedAxisMovementLimiter Signed-off-by: yoyosource --- .../simulator/AxisMovementLimiter.java | 18 +- .../features/simulator/Explosion.java | 26 +- .../OptimizedAxisMovementLimiter.java | 299 ++++++++++++++++++ .../bausystem/features/simulator/Pos.java | 29 ++ .../features/simulator/Simulator19.java | 59 +++- .../bausystem/features/simulator/TNT.java | 24 +- 6 files changed, 409 insertions(+), 46 deletions(-) create mode 100644 BauSystem_19/src/de/steamwar/bausystem/features/simulator/OptimizedAxisMovementLimiter.java create mode 100644 BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java index 7aeb4e06..41eec617 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/AxisMovementLimiter.java @@ -21,7 +21,6 @@ package de.steamwar.bausystem.features.simulator; import org.bukkit.Axis; import org.bukkit.util.BoundingBox; -import org.bukkit.util.Vector; import org.bukkit.util.VoxelShape; import java.util.ArrayList; @@ -78,7 +77,7 @@ public class AxisMovementLimiter { } } - private List possibleCollisions() { + private List possibleCollisions() { int minX = TNT.floor(this.minX); int maxX = TNT.floor(this.maxX); int minY = TNT.floor(this.minY) - 1; @@ -86,26 +85,27 @@ public class AxisMovementLimiter { int minZ = TNT.floor(this.minZ); int maxZ = TNT.floor(this.maxZ); - List vectors = new ArrayList<>(); + List poss = new ArrayList<>(); for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { for (int z = minZ; z <= maxZ; z++) { - vectors.add(new Vector(x, y, z)); + poss.add(new Pos(x, y, z)); } } } - return vectors; + return poss; } + // TODO: This can be optimized by optimizing the x,y,z loop layout public double run() { BoundingBox movementBoundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ); - List vectors = possibleCollisions(); + List poss = possibleCollisions(); Double collision = null; - for (Vector vector : vectors) { - VoxelShape voxelShape = Simulator19.getVoxelShape(vector); + for (Pos pos : poss) { + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { - boundingBox = boundingBox.clone().shift(vector); + boundingBox = boundingBox.clone().shift(pos.x, pos.y, pos.z); boolean collides = boundingBox.overlaps(movementBoundingBox); if (!collides) continue; diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Explosion.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Explosion.java index 8cd01ff1..468d3c73 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Explosion.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Explosion.java @@ -49,6 +49,16 @@ public class Explosion { } } + private static final Set WATER_LOGABLE = new HashSet<>(); + + static { + for (Material material : Material.values()) { + if (Waterlogged.class.isAssignableFrom(material.data)) { + WATER_LOGABLE.add(material); + } + } + } + private static final Random RANDOM = new Random(); private static final float POWER = 4.0F; @@ -68,7 +78,7 @@ public class Explosion { } public void calculate() { - Set affectedBlocks = new HashSet<>(); + Set affectedBlocks = new HashSet<>(); for (int i = 0; i < FACE_BLOCKS.length; i += 3) { double d = FACE_BLOCKS[i + 0]; double e = FACE_BLOCKS[i + 1]; @@ -84,19 +94,17 @@ public class Explosion { int y = TNT.floor(n); int z = TNT.floor(o); - BlockData blockData = Simulator19.getBlockData(x, y, z); - if (blockData != null) { - h -= (blockData.getMaterial().getBlastResistance() + 0.3F) * 0.3F; - } - if (blockData instanceof Waterlogged) { - Waterlogged waterlogged = (Waterlogged) blockData; + Material material = Simulator19.getBlockType(x, y, z); + h -= (material.getBlastResistance() + 0.3F) * 0.3F; + if (WATER_LOGABLE.contains(material)) { + Waterlogged waterlogged = (Waterlogged) Simulator19.getBlockData(x, y, z); if (waterlogged.isWaterlogged()) { h = 0.0F; } } if (h > 0.0F) { - affectedBlocks.add(new Vector(x, y, z)); + affectedBlocks.add(new Pos(x, y, z)); } m += d * 0.30000001192092896; @@ -104,7 +112,7 @@ public class Explosion { o += f * 0.30000001192092896; } } - affectedBlocks.forEach(Simulator19::clearBlock); + Simulator19.clearBlocks(affectedBlocks); float q = POWER * 2.0F; int k = floor(x - q - 1.0D); diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/OptimizedAxisMovementLimiter.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/OptimizedAxisMovementLimiter.java new file mode 100644 index 00000000..3ecbf8bd --- /dev/null +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/OptimizedAxisMovementLimiter.java @@ -0,0 +1,299 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.simulator; + +import org.bukkit.Axis; +import org.bukkit.util.BoundingBox; +import org.bukkit.util.VoxelShape; + +public class OptimizedAxisMovementLimiter { + + private Axis axis; + private double movement; + + private double minX; + private double maxX; + private double minY; + private double maxY; + private double minZ; + private double maxZ; + + private int minIX; + private int maxIX; + private int minIY; + private int maxIY; + private int minIZ; + private int maxIZ; + + private BoundingBox movementBoundingBox; + + public OptimizedAxisMovementLimiter(double x, double y, double z, Axis axis, double movement) { + this.axis = axis; + this.movement = movement; + + // Calculate the min and max values for the movement + minX = x; + maxX = x + 0.98; + + minY = y; + maxY = y + 0.98; + + minZ = z; + maxZ = z + 0.98; + + switch (axis) { + case X: + if (movement < 0) { + minX += movement; + } else if (movement > 0) { + maxX += movement; + } + break; + case Y: + if (movement < 0) { + minY += movement; + } else if (movement > 0) { + maxY += movement; + } + break; + case Z: + if (movement < 0) { + minZ += movement; + } else if (movement > 0) { + maxZ += movement; + } + break; + } + + minIX = TNT.floor(minX); + maxIX = TNT.floor(maxX); + minIY = TNT.floor(minY) - 1; + maxIY = TNT.floor(maxY); + minIZ = TNT.floor(minZ); + maxIZ = TNT.floor(maxZ); + + movementBoundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ); + } + + public double run() { + if (movement == 0.0) return 0.0; + switch (axis) { + case X: + if (movement < 0.0) { + return runNX(); + } else { + return runPX(); + } + case Y: + if (movement < 0.0) { + return runNY(); + } else { + return runPY(); + } + case Z: + default: + if (movement < 0.0) { + return runNZ(); + } else { + return runPZ(); + } + } + } + + private double runNX() { + Double collision = null; + for (int x = maxIX - 1; x >= minIX; x--) { + for (int y = minIY; y < maxIY; y++) { + for (int z = minIZ; z < maxIZ; z++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMaxX(); + if (collision == null) { + collision = value; + } else { + collision = Math.max(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minX); + } + + private double runPX() { + Double collision = null; + for (int x = minIX; x < maxIX; x++) { + for (int y = minIY; y < maxIY; y++) { + for (int z = minIZ; z < maxIZ; z++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMinX() - 0.98; + if (collision == null) { + collision = value; + } else { + collision = Math.min(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minX); + } + + private double runNY() { + Double collision = null; + for (int y = maxIY - 1; y >= minIY; y--) { + for (int x = minIX; x < maxIX; x++) { + for (int z = minIZ; z < maxIZ; z++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMaxY(); + if (collision == null) { + collision = value; + } else { + collision = Math.max(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minY); + } + + private double runPY() { + Double collision = null; + for (int y = minIY; y < maxIY; y++) { + for (int x = minIX; x < maxIX; x++) { + for (int z = minIZ; z < maxIZ; z++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMinY() - 0.98; + if (collision == null) { + collision = value; + } else { + collision = Math.min(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minY); + } + + private double runNZ() { + Double collision = null; + for (int z = maxIZ - 1; z >= minIZ; z--) { + for (int x = minIX; x < maxIX; x++) { + for (int y = minIY; y < maxIY; y++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMaxZ(); + if (collision == null) { + collision = value; + } else { + collision = Math.max(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minZ); + } + + private double runPZ() { + Double collision = null; + for (int z = minIZ; z < maxIZ; z++) { + for (int x = minIX; x < maxIX; x++) { + for (int y = minIY; y < maxIY; y++) { + Pos pos = new Pos(x, y, z); + VoxelShape voxelShape = Simulator19.getVoxelShape(pos); + for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) { + boundingBox = boundingBox.clone().shift(x, y, z); + if (!boundingBox.overlaps(movementBoundingBox)) continue; + + double value = boundingBox.getMinZ() - 0.98; + if (collision == null) { + collision = value; + } else { + collision = Math.min(value, collision); + } + } + } + } + if (collision != null) { + break; + } + } + if (collision == null) { + return movement; + } + return movement + (collision - minZ); + } +} diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java new file mode 100644 index 00000000..831c09f5 --- /dev/null +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java @@ -0,0 +1,29 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.simulator; + +import lombok.Data; + +@Data +public class Pos { + public final int x; + public final int y; + public final int z; +} diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java index 276150b0..1f499509 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/Simulator19.java @@ -46,9 +46,13 @@ public class Simulator19 implements Simulator { } }; - private static final Map BLOCK_TYPES_MAP = new HashMap<>(); - private static final Map BLOCK_DATA_MAP = new HashMap<>(); - private static final Map COLLISION_DATA_MAP = new HashMap<>(); + private static long accessed = 0; + private static long cacheMisses = 0; + private static long aired = 0; + private static final Set AIR_BLOCKS = new HashSet<>(); + private static final Map BLOCK_TYPES_MAP = new HashMap<>(); + private static final Map BLOCK_DATA_MAP = new HashMap<>(); + private static final Map COLLISION_DATA_MAP = new HashMap<>(); @Override public synchronized void run() { @@ -66,30 +70,55 @@ public class Simulator19 implements Simulator { } tntList.removeAll(remove); } - System.out.println("Time: " + (System.currentTimeMillis() - time) + "ms"); + System.out.println("Time: " + (System.currentTimeMillis() - time) + "ms " + cacheMisses + "/" + accessed + "/" + aired); + AIR_BLOCKS.clear(); BLOCK_TYPES_MAP.clear(); BLOCK_DATA_MAP.clear(); COLLISION_DATA_MAP.clear(); + accessed = 0; + cacheMisses = 0; + aired = 0; } - public static Material getBlockType(int x, int y, int z) { - Vector vector = new Vector(x, y, z); - return BLOCK_TYPES_MAP.computeIfAbsent(vector, v -> WORLD.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getType()); + public static Material getBlockType(int x, int y, int z) { // Get BlockType of Chunk Data array? + accessed++; + Pos pos = new Pos(x, y, z); + if (AIR_BLOCKS.contains(pos)) { + return Material.AIR; + } + return BLOCK_TYPES_MAP.computeIfAbsent(pos, v -> { + cacheMisses++; + return WORLD.getBlockAt(x, y, z).getType(); + }); } public static BlockData getBlockData(int x, int y, int z) { - Vector vector = new Vector(x, y, z); - return BLOCK_DATA_MAP.computeIfAbsent(vector, v -> WORLD.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getBlockData()); + accessed++; + return BLOCK_DATA_MAP.computeIfAbsent(new Pos(x, y, z), v -> { + cacheMisses++; + return WORLD.getBlockAt(x, y, z).getBlockData(); + }); } - public static VoxelShape getVoxelShape(Vector vector) { - return COLLISION_DATA_MAP.computeIfAbsent(vector, v -> WORLD.getBlockAt(v.getBlockX(), v.getBlockY(), v.getBlockZ()).getCollisionShape()); + public static VoxelShape getVoxelShape(Pos pos) { + accessed++; + if (AIR_BLOCKS.contains(pos)) { + return AIR_VOXEL_SHAPE; + } + return COLLISION_DATA_MAP.computeIfAbsent(pos, v -> { + cacheMisses++; + return WORLD.getBlockAt(pos.x, pos.y, pos.z).getCollisionShape(); + }); } - public static void clearBlock(Vector vector) { - BLOCK_TYPES_MAP.put(vector, Material.AIR); - BLOCK_DATA_MAP.put(vector, AIR_BLOCK_DATA); - COLLISION_DATA_MAP.put(vector, AIR_VOXEL_SHAPE); + public static void clearBlock(Pos pos) { + aired++; + AIR_BLOCKS.add(pos); + BLOCK_DATA_MAP.put(pos, AIR_BLOCK_DATA); + } + + public static void clearBlocks(Set pos) { + } } diff --git a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java index a3389c1d..cbc71ab7 100644 --- a/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java +++ b/BauSystem_19/src/de/steamwar/bausystem/features/simulator/TNT.java @@ -145,14 +145,12 @@ public class TNT { } } - if (onGround) { - if (material == Material.SLIME_BLOCK) { - double cy = Math.abs(this.vy); - if (cy < 0.1) { - double cy2 = 0.4 + cy * 0.2; - this.vx = vx * cy2; - this.vz = vz * cy2; - } + if (onGround && material == Material.SLIME_BLOCK) { + double cy = Math.abs(this.vy); + if (cy < 0.1) { + double cy2 = 0.4 + cy * 0.2; + this.vx = vx * cy2; + this.vz = vz * cy2; } } @@ -167,16 +165,16 @@ public class TNT { return movement; } - double mY = new AxisMovementLimiter(x, y, z, Axis.Y, movement.getY()).run(); + double mY = new OptimizedAxisMovementLimiter(x, y, z, Axis.Y, movement.getY()).run(); boolean bl = Math.abs(movement.getX()) < Math.abs(movement.getZ()); if (bl) { - double mZ = new AxisMovementLimiter(x, y + mY, z, Axis.Z, movement.getZ()).run(); - double mX = new AxisMovementLimiter(x, y + mY, z + mZ, Axis.X, movement.getX()).run(); + double mZ = new OptimizedAxisMovementLimiter(x, y + mY, z, Axis.Z, movement.getZ()).run(); + double mX = new OptimizedAxisMovementLimiter(x, y + mY, z + mZ, Axis.X, movement.getX()).run(); return new Vector(mX, mY, mZ); } else { - double mX = new AxisMovementLimiter(x, y + mY, z, Axis.X, movement.getX()).run(); - double mZ = new AxisMovementLimiter(x + mX, y + mY, z, Axis.Z, movement.getZ()).run(); + double mX = new OptimizedAxisMovementLimiter(x, y + mY, z, Axis.X, movement.getX()).run(); + double mZ = new OptimizedAxisMovementLimiter(x + mX, y + mY, z, Axis.Z, movement.getZ()).run(); return new Vector(mX, mY, mZ); } }