Add Pos and OptimizedAxisMovementLimiter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
Dieser Commit ist enthalten in:
Ursprung
8f54fa1ef0
Commit
a83b538b04
@ -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<Vector> possibleCollisions() {
|
||||
private List<Pos> 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<Vector> vectors = new ArrayList<>();
|
||||
List<Pos> 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<Vector> vectors = possibleCollisions();
|
||||
List<Pos> 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;
|
||||
|
||||
|
@ -49,6 +49,16 @@ public class Explosion {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Set<Material> 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<Vector> affectedBlocks = new HashSet<>();
|
||||
Set<Pos> 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);
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
29
BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java
Normale Datei
29
BauSystem_19/src/de/steamwar/bausystem/features/simulator/Pos.java
Normale Datei
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
@ -46,9 +46,13 @@ public class Simulator19 implements Simulator {
|
||||
}
|
||||
};
|
||||
|
||||
private static final Map<Vector, Material> BLOCK_TYPES_MAP = new HashMap<>();
|
||||
private static final Map<Vector, BlockData> BLOCK_DATA_MAP = new HashMap<>();
|
||||
private static final Map<Vector, VoxelShape> COLLISION_DATA_MAP = new HashMap<>();
|
||||
private static long accessed = 0;
|
||||
private static long cacheMisses = 0;
|
||||
private static long aired = 0;
|
||||
private static final Set<Pos> AIR_BLOCKS = new HashSet<>();
|
||||
private static final Map<Pos, Material> BLOCK_TYPES_MAP = new HashMap<>();
|
||||
private static final Map<Pos, BlockData> BLOCK_DATA_MAP = new HashMap<>();
|
||||
private static final Map<Pos, VoxelShape> 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> pos) {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -145,8 +145,7 @@ public class TNT {
|
||||
}
|
||||
}
|
||||
|
||||
if (onGround) {
|
||||
if (material == Material.SLIME_BLOCK) {
|
||||
if (onGround && material == Material.SLIME_BLOCK) {
|
||||
double cy = Math.abs(this.vy);
|
||||
if (cy < 0.1) {
|
||||
double cy2 = 0.4 + cy * 0.2;
|
||||
@ -154,7 +153,6 @@ public class TNT {
|
||||
this.vz = vz * cy2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkBlockCollision();
|
||||
float j = this.getVelocityMultiplier();
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
In neuem Issue referenzieren
Einen Benutzer sperren