Commits vergleichen
37 Commits
master
...
SimulatorP
Autor | SHA1 | Datum | |
---|---|---|---|
|
b787b96913 | ||
|
4d26ba5166 | ||
|
35c37b9c06 | ||
|
7f7081adfe | ||
|
85527f7004 | ||
|
63e8b96ec9 | ||
|
8a3cce0a3a | ||
|
2774b29342 | ||
|
7e2780f326 | ||
|
e49724e593 | ||
|
c92d610ca9 | ||
|
4fbbdf3a49 | ||
|
d09c4970a0 | ||
|
1af342d2c3 | ||
|
12d0ecbf0e | ||
|
853aae1b8e | ||
|
5ea2b52985 | ||
|
6b727dd915 | ||
|
d5d25e9fb0 | ||
|
0b8a34bbe5 | ||
|
6ebc9b1068 | ||
|
6409748d32 | ||
|
02c68a8b0b | ||
|
e42e123aaa | ||
|
9d91c4e3eb | ||
|
bb489fd185 | ||
|
a83b538b04 | ||
|
8f54fa1ef0 | ||
|
fb3db5d4dd | ||
|
faf62a6ee7 | ||
|
a2cdcc0b47 | ||
|
5f6696885d | ||
|
8d4f1bf2da | ||
|
ff0d3fe2e5 | ||
|
92ed98a8a1 | ||
|
89ee198968 | ||
|
a68f45d824 |
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
public class Simulator15 implements Simulator {
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.VoxelShape;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AxisMovementLimiter19 {
|
||||
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
private Axis axis;
|
||||
private double movement;
|
||||
|
||||
private double minX;
|
||||
private double maxX;
|
||||
private double minY;
|
||||
private double maxY;
|
||||
private double minZ;
|
||||
private double maxZ;
|
||||
|
||||
public AxisMovementLimiter19(double x, double y, double z, Axis axis, double movement) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
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;
|
||||
}
|
||||
// System.out.println(axis + " " + minX + " -> " + maxX + " " + minY + " -> " + maxY + " " + minZ + " -> " + maxZ);
|
||||
}
|
||||
|
||||
private List<Pos19> possibleCollisions() {
|
||||
int minX = TNT19.floor(this.minX);
|
||||
int maxX = TNT19.floor(this.maxX);
|
||||
int minY = TNT19.floor(this.minY) - 1;
|
||||
int maxY = TNT19.floor(this.maxY);
|
||||
int minZ = TNT19.floor(this.minZ);
|
||||
int maxZ = TNT19.floor(this.maxZ);
|
||||
|
||||
List<Pos19> poss = new ArrayList<>();
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
poss.add(new Pos19(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
return poss;
|
||||
}
|
||||
|
||||
// TODO: This can be optimized by optimizing the x,y,z loop layout
|
||||
public double run(SimulatorData19 simulatorData) {
|
||||
if (movement == 0.0) return 0.0;
|
||||
|
||||
BoundingBox movementBoundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
List<Pos19> poss = possibleCollisions();
|
||||
|
||||
Double collision = null;
|
||||
for (Pos19 pos : poss) {
|
||||
VoxelShape voxelShape = simulatorData.getVoxelShape(pos);
|
||||
for (BoundingBox boundingBox : voxelShape.getBoundingBoxes()) {
|
||||
boundingBox = boundingBox.clone().shift(pos.x, pos.y, pos.z);
|
||||
boolean collides = boundingBox.overlaps(movementBoundingBox);
|
||||
if (!collides) continue;
|
||||
|
||||
double value;
|
||||
switch (axis) {
|
||||
case X:
|
||||
if (movement < 0) {
|
||||
value = boundingBox.getMaxX();
|
||||
} else {
|
||||
value = boundingBox.getMinX() - 0.98;
|
||||
}
|
||||
break;
|
||||
case Y:
|
||||
if (movement < 0) {
|
||||
value = boundingBox.getMaxY();
|
||||
} else {
|
||||
value = boundingBox.getMinY() - 0.98;
|
||||
}
|
||||
break;
|
||||
case Z:
|
||||
if (movement < 0) {
|
||||
value = boundingBox.getMaxZ();
|
||||
} else {
|
||||
value = boundingBox.getMinZ() - 0.98;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected value: " + axis);
|
||||
}
|
||||
|
||||
if (collision == null) {
|
||||
collision = value;
|
||||
} else {
|
||||
if (movement < 0) {
|
||||
collision = Math.max(collision, value);
|
||||
} else {
|
||||
collision = Math.min(collision, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (collision == null) {
|
||||
return movement;
|
||||
} else {
|
||||
switch (axis) {
|
||||
case X:
|
||||
// System.out.println(axis + " " + movement + " " + x + " " + collision);
|
||||
return collision - x;
|
||||
case Y:
|
||||
// System.out.println(axis + " " + movement + " " + y + " " + collision);
|
||||
return collision - y;
|
||||
case Z:
|
||||
// System.out.println(axis + " " + movement + " " + z + " " + collision);
|
||||
return collision - z;
|
||||
default:
|
||||
throw new IllegalStateException("Unexpected value: " + axis);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class Explosion19 {
|
||||
|
||||
private static final double[] FACE_BLOCKS = new double[(16 * 16 * 2 + 14 * 16 * 2 + 14 * 14 * 2) * 3];
|
||||
|
||||
static {
|
||||
int index = 0;
|
||||
for (int x = 0; x < 16; ++x) {
|
||||
for (int y = 0; y < 16; ++y) {
|
||||
for (int z = 0; z < 16; ++z) {
|
||||
if (x == 0 || x == 15 || y == 0 || y == 15 || z == 0 || z == 15) {
|
||||
double dX = x / 15.0F * 2.0F - 1.0F;
|
||||
double dY = y / 15.0F * 2.0F - 1.0F;
|
||||
double dZ = z / 15.0F * 2.0F - 1.0F;
|
||||
double length = Math.sqrt(dX * dX + dY * dY + dZ * dZ);
|
||||
FACE_BLOCKS[index++] = dX / length;
|
||||
FACE_BLOCKS[index++] = dY / length;
|
||||
FACE_BLOCKS[index++] = dZ / length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
private final TNT19 tnt;
|
||||
private final double x;
|
||||
private final double y;
|
||||
private final double z;
|
||||
|
||||
|
||||
public Explosion19(TNT19 tnt, double x, double y, double z) {
|
||||
this.tnt = tnt;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void calculate(SimulatorData19 simulatorData) {
|
||||
Set<Pos19> 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];
|
||||
double f = FACE_BLOCKS[i + 2];
|
||||
|
||||
float h = POWER * (0.7F + RANDOM.nextFloat() * 0.6F);
|
||||
double m = x;
|
||||
double n = y;
|
||||
double o = z;
|
||||
|
||||
for (; h > 0.0F; h -= 0.22500001F) {
|
||||
int x = TNT19.floor(m);
|
||||
int y = TNT19.floor(n);
|
||||
int z = TNT19.floor(o);
|
||||
|
||||
Material material = simulatorData.getBlockType(x, y, z);
|
||||
if (!material.isAir()) {
|
||||
h -= (material.getBlastResistance() + 0.3F) * 0.3F;
|
||||
}
|
||||
if (WATER_LOGABLE.contains(material)) {
|
||||
Waterlogged waterlogged = (Waterlogged) simulatorData.getBlockData(x, y, z);
|
||||
if (waterlogged.isWaterlogged()) {
|
||||
h = 0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
if (h > 0.0F) {
|
||||
affectedBlocks.add(new Pos19(x, y, z));
|
||||
}
|
||||
|
||||
m += d * 0.30000001192092896;
|
||||
n += e * 0.30000001192092896;
|
||||
o += f * 0.30000001192092896;
|
||||
}
|
||||
}
|
||||
simulatorData.clearBlocks(affectedBlocks);
|
||||
|
||||
float q = POWER * 2.0F;
|
||||
int k = floor(x - q - 1.0D);
|
||||
int l = floor(x + q + 1.0D);
|
||||
int r = floor(y - q - 1.0D);
|
||||
int s = floor(y + q + 1.0D);
|
||||
int t = floor(z - q - 1.0D);
|
||||
int u = floor(z + q + 1.0D);
|
||||
|
||||
for (TNT19 currentTNT : simulatorData.tntList) {
|
||||
if (currentTNT == tnt) continue;
|
||||
if (!(currentTNT.getX() >= k && currentTNT.getY() >= r && currentTNT.getZ() >= t && currentTNT.getX() <= l && currentTNT.getY() <= s && currentTNT.getZ() <= u)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double x = currentTNT.getX() - this.x;
|
||||
double y = currentTNT.getY() - this.y;
|
||||
double z = currentTNT.getZ() - this.z;
|
||||
double aa = Math.sqrt(x * x + y * y + z * z);
|
||||
double w = aa / q;
|
||||
if (w > 1.0) {
|
||||
continue;
|
||||
}
|
||||
if (aa == 0.0) {
|
||||
continue;
|
||||
}
|
||||
x /= aa;
|
||||
y /= aa;
|
||||
z /= aa;
|
||||
|
||||
double ab = getExposure(simulatorData, currentTNT.getX(), currentTNT.getY(), currentTNT.getZ());
|
||||
double ac = (1.0 - w) * ab;
|
||||
currentTNT.setVx(currentTNT.getVx() + x * ac);
|
||||
currentTNT.setVy(currentTNT.getVy() + y * ac);
|
||||
currentTNT.setVz(currentTNT.getVz() + z * ac);
|
||||
}
|
||||
}
|
||||
|
||||
private static int floor(double value) {
|
||||
int i = (int) value;
|
||||
return value < (double) i ? i - 1 : i;
|
||||
}
|
||||
|
||||
private static final double SIZE = 0.98;
|
||||
private static final double MIN_POINT = 0.01;
|
||||
private static final double MAX_POINT = SIZE + MIN_POINT;
|
||||
private static final double EXPOSURE_CONSTANT_1 = 1.0 / (SIZE * 2 + 1.0);
|
||||
private static final double EXPOSURE_CONSTANT_2 = (1.0 - Math.floor(1.0 / EXPOSURE_CONSTANT_1) * EXPOSURE_CONSTANT_1) / 2.0;
|
||||
|
||||
private float getExposure(SimulatorData19 simulatorData, double x, double y, double z) {
|
||||
float blockMisses = 0;
|
||||
float blockTotal = 0;
|
||||
|
||||
for (double k = 0.0; k <= 1.0; k += EXPOSURE_CONSTANT_1) {
|
||||
for (double l = 0.0; l <= 1.0; l += EXPOSURE_CONSTANT_1) {
|
||||
for (double m = 0.0; m <= 1.0; m += EXPOSURE_CONSTANT_1) {
|
||||
double dx = lerp(k, MIN_POINT, MAX_POINT) + EXPOSURE_CONSTANT_2 + x;
|
||||
double dy = lerp(l, 0.0, SIZE) + y;
|
||||
double dz = lerp(m, MIN_POINT, MAX_POINT) + EXPOSURE_CONSTANT_2 + z;
|
||||
|
||||
if (rayTrace(simulatorData, dx, dy, dz, this.x, this.y, this.z)) {
|
||||
blockMisses++;
|
||||
}
|
||||
blockTotal++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return blockMisses / blockTotal;
|
||||
}
|
||||
|
||||
public static double lerp(double delta, double start, double end) {
|
||||
return start + delta * (end - start);
|
||||
}
|
||||
|
||||
public static boolean rayTrace(SimulatorData19 simulatorData, double sX, double sY, double sZ, double dX, double dY, double dZ) {
|
||||
double x = sX;
|
||||
double y = sY;
|
||||
double z = sZ;
|
||||
int oX = sX > dX ? -1 : 1;
|
||||
int oY = sY > dY ? -1 : 1;
|
||||
int oZ = sZ > dZ ? -1 : 1;
|
||||
while (true) {
|
||||
Material material = simulatorData.getBlockType(floor(x), floor(y), floor(z));
|
||||
if (material != Material.LAVA && material != Material.WATER && material != Material.AIR) {
|
||||
return true;
|
||||
}
|
||||
|
||||
double cX = x - (floor(x) + oX);
|
||||
double cY = y - (floor(y) + oY);
|
||||
double cZ = z - (floor(z) + oZ);
|
||||
|
||||
double maxChange = Math.min(Math.abs(cX), Math.min(Math.abs(cY), Math.abs(cZ)));
|
||||
x += Math.signum(cX) * maxChange;
|
||||
y += Math.signum(cY) * maxChange;
|
||||
z += Math.signum(cZ) * maxChange;
|
||||
|
||||
if (floor(x) == floor(dX) && floor(y) == floor(dY) && floor(z) == floor(dZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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.preview;
|
||||
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.VoxelShape;
|
||||
|
||||
public class OptimizedAxisMovementLimiter19 {
|
||||
|
||||
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 OptimizedAxisMovementLimiter19(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 = TNT19.floor(minX);
|
||||
maxIX = TNT19.floor(maxX);
|
||||
minIY = TNT19.floor(minY) - 1;
|
||||
maxIY = TNT19.floor(maxY);
|
||||
minIZ = TNT19.floor(minZ);
|
||||
maxIZ = TNT19.floor(maxZ);
|
||||
|
||||
movementBoundingBox = new BoundingBox(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
}
|
||||
|
||||
public double run(SimulatorData19 simulatorData) {
|
||||
if (movement == 0.0) return 0.0;
|
||||
switch (axis) {
|
||||
case X:
|
||||
if (movement < 0.0) {
|
||||
return runNX(simulatorData);
|
||||
} else {
|
||||
return runPX(simulatorData);
|
||||
}
|
||||
case Y:
|
||||
if (movement < 0.0) {
|
||||
return runNY(simulatorData);
|
||||
} else {
|
||||
return runPY(simulatorData);
|
||||
}
|
||||
case Z:
|
||||
default:
|
||||
if (movement < 0.0) {
|
||||
return runNZ(simulatorData);
|
||||
} else {
|
||||
return runPZ(simulatorData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private double runNX(SimulatorData19 simulatorData) {
|
||||
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++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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(SimulatorData19 simulatorData) {
|
||||
Double collision = null;
|
||||
for (int x = minIX; x < maxIX; x++) {
|
||||
for (int y = minIY; y < maxIY; y++) {
|
||||
for (int z = minIZ; z < maxIZ; z++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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(SimulatorData19 simulatorData) {
|
||||
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++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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(SimulatorData19 simulatorData) {
|
||||
Double collision = null;
|
||||
for (int y = minIY; y < maxIY; y++) {
|
||||
for (int x = minIX; x < maxIX; x++) {
|
||||
for (int z = minIZ; z < maxIZ; z++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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(SimulatorData19 simulatorData) {
|
||||
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++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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(SimulatorData19 simulatorData) {
|
||||
Double collision = null;
|
||||
for (int z = minIZ; z < maxIZ; z++) {
|
||||
for (int x = minIX; x < maxIX; x++) {
|
||||
for (int y = minIY; y < maxIY; y++) {
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
VoxelShape voxelShape = simulatorData.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);
|
||||
}
|
||||
}
|
@ -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.preview;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Pos19 {
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.features.simulator.TNTData;
|
||||
import de.steamwar.bausystem.features.tracer.show.Record;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class Simulator19 implements Simulator {
|
||||
|
||||
protected static final World WORLD = Bukkit.getWorlds().get(0);
|
||||
|
||||
@Override
|
||||
public BukkitTask run(Pair<Integer, Map<Integer, List<List<Pair<TNTData, Integer>>>>> toCalculate, Consumer<PreviewRecord> consumer) { // TODO: Implement multi tick calculation max 40 ms per tick
|
||||
if (toCalculate == null) return null;
|
||||
|
||||
BukkitRunnable bukkitRunnable = new BukkitRunnable() {
|
||||
private SimulatorData19 simulatorData = new SimulatorData19();
|
||||
private PreviewRecord previewRecord = new PreviewRecord();
|
||||
private int currentTick = 0;
|
||||
private Map<TNT19, Record.TNTRecord> recordMap = new IdentityHashMap<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
long time = System.currentTimeMillis();
|
||||
while (!simulatorData.tntList.isEmpty() || currentTick <= toCalculate.getKey()) {
|
||||
if (System.currentTimeMillis() - time >= 40) break;
|
||||
|
||||
List<List<Pair<TNTData, Integer>>> toSpawnInTick = toCalculate.getValue().get(currentTick);
|
||||
if (toSpawnInTick != null) {
|
||||
int finalCurrentTick = currentTick;
|
||||
toSpawnInTick.forEach(pairs -> {
|
||||
AtomicBoolean hasSomeLeft = new AtomicBoolean(true);
|
||||
while(hasSomeLeft.get()) {
|
||||
hasSomeLeft.set(false);
|
||||
pairs.forEach(pair -> {
|
||||
if (pair.getValue() > 0) {
|
||||
hasSomeLeft.set(true);
|
||||
TNT19 tnt = new TNT19(pair.getKey().location.getX(), pair.getKey().location.getY(), pair.getKey().location.getZ());
|
||||
if (!pair.getKey().xVelocity) tnt.setVx(0.0);
|
||||
if (!pair.getKey().yVelocity) tnt.setVy(0.0);
|
||||
if (!pair.getKey().zVelocity) tnt.setVz(0.0);
|
||||
tnt.setFuse(tnt.getFuse());
|
||||
simulatorData.tntList.add(tnt);
|
||||
pair.setValue(pair.getValue() - 1);
|
||||
Record.TNTRecord record = previewRecord.getRecord().spawn(finalCurrentTick);
|
||||
record.source(pair.getKey().location.toVector(), new Vector(tnt.getVx(), tnt.getVy(), tnt.getVz()), tnt.getFuse());
|
||||
recordMap.put(tnt, record);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
currentTick++;
|
||||
|
||||
List<TNT19> remove = new ArrayList<>();
|
||||
for (TNT19 tnt : simulatorData.tntList) {
|
||||
// System.out.println("CALC: " + simulatorData.blockTypesMap.size() + "/" + simulatorData.blockDataMap.size() + "/" + simulatorData.collisionDataMap.size() + "/" + simulatorData.airBlocks.size() + " " + recordMap.size());
|
||||
if (tnt.tick(simulatorData)) {
|
||||
remove.add(tnt);
|
||||
recordMap.remove(tnt).explode(new Vector(tnt.getX(), tnt.getY(), tnt.getZ()), new Vector(tnt.getVx(), tnt.getVy(), tnt.getVz()), tnt.getFuse());
|
||||
} else {
|
||||
recordMap.get(tnt).location(new Vector(tnt.getX(), tnt.getY(), tnt.getZ()), new Vector(tnt.getVx(), tnt.getVy(), tnt.getVz()), tnt.getFuse());
|
||||
}
|
||||
}
|
||||
simulatorData.tntList.removeAll(remove);
|
||||
}
|
||||
|
||||
System.out.println("Time: " + (System.currentTimeMillis() - time) + "ms " + simulatorData.blockTypesMap.size() + "/" + simulatorData.blockDataMap.size() + "/" + simulatorData.collisionDataMap.size() + "/" + simulatorData.airBlocks.size() + " " + recordMap.size());
|
||||
if (simulatorData.tntList.isEmpty() && currentTick > toCalculate.getKey()) {
|
||||
previewRecord.setDestroyedBlocks(simulatorData.airBlocks());
|
||||
previewRecord.setAccessedBlocks(simulatorData.accessedBlocks());
|
||||
consumer.accept(previewRecord);
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
return bukkitRunnable.runTaskTimer(BauSystem.getInstance(), 0, 1);
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.bukkit.util.VoxelShape;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SimulatorData19 {
|
||||
|
||||
private static final BlockData AIR_BLOCK_DATA = Material.AIR.createBlockData();
|
||||
private static final VoxelShape AIR_VOXEL_SHAPE = new VoxelShape() {
|
||||
@Override
|
||||
public Collection<BoundingBox> getBoundingBoxes() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean overlaps(BoundingBox boundingBox) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
long accessed = 0;
|
||||
long cacheMisses = 0;
|
||||
long aired = 0;
|
||||
final List<TNT19> tntList = new ArrayList<>();
|
||||
final Set<Pos19> airBlocks = new HashSet<>();
|
||||
final Map<Pos19, Material> blockTypesMap = new HashMap<>();
|
||||
final Map<Pos19, BlockData> blockDataMap = new HashMap<>();
|
||||
final Map<Pos19, VoxelShape> collisionDataMap = new HashMap<>();
|
||||
|
||||
public Material getBlockType(int x, int y, int z) { // Get BlockType of Chunk Data array?
|
||||
accessed++;
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
if (airBlocks.contains(pos)) {
|
||||
return Material.AIR;
|
||||
}
|
||||
return blockTypesMap.computeIfAbsent(pos, v -> {
|
||||
cacheMisses++;
|
||||
return Simulator19.WORLD.getBlockAt(x, y, z).getType();
|
||||
});
|
||||
}
|
||||
|
||||
public BlockData getBlockData(int x, int y, int z) {
|
||||
accessed++;
|
||||
Pos19 pos = new Pos19(x, y, z);
|
||||
if (airBlocks.contains(pos)) {
|
||||
return AIR_BLOCK_DATA;
|
||||
}
|
||||
return blockDataMap.computeIfAbsent(pos, v -> {
|
||||
cacheMisses++;
|
||||
return Simulator19.WORLD.getBlockAt(x, y, z).getBlockData();
|
||||
});
|
||||
}
|
||||
|
||||
public VoxelShape getVoxelShape(Pos19 pos) {
|
||||
accessed++;
|
||||
if (airBlocks.contains(pos)) {
|
||||
return AIR_VOXEL_SHAPE;
|
||||
}
|
||||
return collisionDataMap.computeIfAbsent(pos, v -> {
|
||||
cacheMisses++;
|
||||
return Simulator19.WORLD.getBlockAt(pos.x, pos.y, pos.z).getCollisionShape();
|
||||
});
|
||||
}
|
||||
|
||||
public void clearBlock(Pos19 pos) {
|
||||
aired++;
|
||||
airBlocks.add(pos);
|
||||
blockTypesMap.remove(pos);
|
||||
blockDataMap.remove(pos);
|
||||
collisionDataMap.remove(pos);
|
||||
}
|
||||
|
||||
public void clearBlocks(Set<Pos19> poss) {
|
||||
poss.forEach(this::clearBlock);
|
||||
}
|
||||
|
||||
public Set<Vector> airBlocks() {
|
||||
return airBlocks.stream().map(pos -> new Vector(pos.x, pos.y, pos.z)).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Predicate<Vector> accessedBlocks() {
|
||||
return vector -> {
|
||||
Pos19 pos = new Pos19(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
|
||||
return airBlocks.contains(pos) || blockTypesMap.containsKey(pos) || blockDataMap.containsKey(pos) || collisionDataMap.containsKey(pos);
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,327 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.world.level.RayTrace;
|
||||
import net.minecraft.world.phys.MovingObjectPosition;
|
||||
import net.minecraft.world.phys.Vec3D;
|
||||
import org.bukkit.Axis;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.*;
|
||||
import org.bukkit.craftbukkit.v1_19_R2.CraftWorld;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TNT19 {
|
||||
|
||||
private static final Random R = new Random();
|
||||
|
||||
// Bottom left corner
|
||||
private double x;
|
||||
private double y;
|
||||
private double z;
|
||||
|
||||
private double vx;
|
||||
private double vy;
|
||||
private double vz;
|
||||
|
||||
private int fuse;
|
||||
|
||||
private boolean onGround = false;
|
||||
private float fallDistance = 0;
|
||||
|
||||
private boolean horizontalCollision = false;
|
||||
private boolean verticalCollision = false;
|
||||
private Vector movementMultiplier = new Vector(0, 0, 0);
|
||||
|
||||
public TNT19(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
|
||||
double seed = R.nextDouble() * 6.2831854820251465;
|
||||
this.vx = -Math.sin(seed) * 0.02;
|
||||
this.vy = 0.20000000298023224;
|
||||
this.vz = -Math.cos(seed) * 0.02;
|
||||
|
||||
this.fuse = 80;
|
||||
}
|
||||
|
||||
public boolean tick(SimulatorData19 simulatorData) {
|
||||
this.vy -= 0.04;
|
||||
|
||||
move(simulatorData, new Vector(this.vx, this.vy, this.vz));
|
||||
|
||||
this.vx *= 0.98;
|
||||
this.vy *= 0.98;
|
||||
this.vz *= 0.98;
|
||||
|
||||
if (onGround) {
|
||||
this.vx *= 0.7;
|
||||
this.vy *= -0.5;
|
||||
this.vz *= 0.7;
|
||||
}
|
||||
|
||||
this.fuse--;
|
||||
if (this.fuse <= 0) {
|
||||
Explosion19 explosion = new Explosion19(this, x, y + 0.98 * 0.0625, z);
|
||||
explosion.calculate(simulatorData);
|
||||
return true;
|
||||
} else {
|
||||
// TODO: Update Water and Lava Flowing
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void move(SimulatorData19 simulatorData, Vector movement) {
|
||||
if (movementMultiplier.lengthSquared() > 1.0E-7) {
|
||||
movement.multiply(movementMultiplier);
|
||||
movementMultiplier = new Vector(0, 0, 0);
|
||||
vx = 0;
|
||||
vy = 0;
|
||||
vz = 0;
|
||||
}
|
||||
|
||||
Vector vec3d = adjustMovementForCollisions(simulatorData, movement);
|
||||
// System.out.println(movement + " " + vec3d);
|
||||
double lengthSquared = vec3d.lengthSquared();
|
||||
if (lengthSquared > 1.0E-7) {
|
||||
if (fallDistance != 0.0F && lengthSquared >= 1.0) {
|
||||
// TODO: This could be wrong
|
||||
MovingObjectPosition movingObjectPosition = ((CraftWorld) Simulator19.WORLD).getHandle().a(new RayTrace(new Vec3D(x, y, z), new Vec3D(x + vec3d.getX(), y + vec3d.getY(), z + vec3d.getZ()), RayTrace.BlockCollisionOption.d, RayTrace.FluidCollisionOption.d, null));
|
||||
if (movingObjectPosition.c() != MovingObjectPosition.EnumMovingObjectType.a) {
|
||||
onLanding();
|
||||
}
|
||||
}
|
||||
|
||||
this.x += vec3d.getX();
|
||||
this.y += vec3d.getY();
|
||||
this.z += vec3d.getZ();
|
||||
}
|
||||
|
||||
boolean bl = !approximatelyEquals(movement.getX(), vec3d.getX());
|
||||
boolean bl2 = !approximatelyEquals(movement.getZ(), vec3d.getZ());
|
||||
this.horizontalCollision = bl || bl2;
|
||||
this.verticalCollision = movement.getY() != vec3d.getY();
|
||||
|
||||
this.onGround = this.verticalCollision && movement.getY() < 0.0D;
|
||||
Vector blockPos = getLandingPos(simulatorData);
|
||||
Material material = simulatorData.getBlockType(blockPos.getBlockX(), blockPos.getBlockY(), blockPos.getBlockZ());
|
||||
BlockData blockData = simulatorData.getBlockData(blockPos.getBlockX(), blockPos.getBlockY(), blockPos.getBlockZ());
|
||||
fall(vec3d.getY(), onGround);
|
||||
|
||||
if (horizontalCollision) {
|
||||
this.vx = bl ? 0.0 : this.vx;
|
||||
this.vz = bl2 ? 0.0 : this.vz;
|
||||
}
|
||||
|
||||
if (movement.getY() != vec3d.getY()) {
|
||||
if (material == Material.SLIME_BLOCK) {
|
||||
if (this.vy < 0.0) {
|
||||
this.vy = -this.vy * 0.8;
|
||||
}
|
||||
} else if (blockData instanceof Bed) {
|
||||
if (this.vy < 0.0) {
|
||||
this.vy = -this.vy * 0.6600000262260437 * 0.8;
|
||||
}
|
||||
} else {
|
||||
this.vy = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
checkBlockCollision(simulatorData);
|
||||
float j = this.getVelocityMultiplier(simulatorData);
|
||||
this.vx *= j;
|
||||
this.vz *= j;
|
||||
}
|
||||
|
||||
private Vector adjustMovementForCollisions(SimulatorData19 simulatorData, Vector movement) {
|
||||
if (movement.lengthSquared() == 0.0) {
|
||||
return movement;
|
||||
}
|
||||
|
||||
double mY = new AxisMovementLimiter19(x, y, z, Axis.Y, movement.getY()).run(simulatorData);
|
||||
|
||||
boolean bl = Math.abs(movement.getX()) < Math.abs(movement.getZ());
|
||||
if (bl) {
|
||||
double mZ = new AxisMovementLimiter19(x, y + mY, z, Axis.Z, movement.getZ()).run(simulatorData);
|
||||
double mX = new AxisMovementLimiter19(x, y + mY, z + mZ, Axis.X, movement.getX()).run(simulatorData);
|
||||
return new Vector(mX, mY, mZ);
|
||||
} else {
|
||||
double mX = new AxisMovementLimiter19(x, y + mY, z, Axis.X, movement.getX()).run(simulatorData);
|
||||
double mZ = new AxisMovementLimiter19(x + mX, y + mY, z, Axis.Z, movement.getZ()).run(simulatorData);
|
||||
return new Vector(mX, mY, mZ);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean approximatelyEquals(double a, double b) {
|
||||
return Math.abs(b - a) < 9.999999747378752E-6;
|
||||
}
|
||||
|
||||
public static int floor(double value) {
|
||||
int i = (int)value;
|
||||
return value < (double)i ? i - 1 : i;
|
||||
}
|
||||
|
||||
private Vector getLandingPos(SimulatorData19 simulatorData) {
|
||||
int x = floor(this.x);
|
||||
int y = floor(this.y - 0.2F);
|
||||
int z = floor(this.z);
|
||||
|
||||
if (simulatorData.getBlockType(x, y, z).isAir()) {
|
||||
BlockData blockData = simulatorData.getBlockData(x, y - 1, z);
|
||||
if (blockData instanceof Fence || blockData instanceof Wall || blockData instanceof Gate) {
|
||||
return new Vector(x, y - 1, z);
|
||||
}
|
||||
}
|
||||
return new Vector(x, y, z);
|
||||
}
|
||||
|
||||
private void fall(double heightDifference, boolean onGround) {
|
||||
if (onGround) {
|
||||
if (fallDistance > 0.0F) {
|
||||
// TODO: onLandedUpon
|
||||
}
|
||||
this.onLanding();
|
||||
} else if (heightDifference < 0.0) {
|
||||
this.fallDistance -= (float)heightDifference;
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBlockCollision(SimulatorData19 simulatorData) {
|
||||
int x1 = floor(x + 1.0E-7);
|
||||
int y1 = floor(y + 1.0E-7);
|
||||
int z1 = floor(z + 1.0E-7);
|
||||
int x2 = floor(x + 0.98 - 1.0E-7);
|
||||
int y2 = floor(y + 0.98 - 1.0E-7);
|
||||
int z2 = floor(z + 0.98 - 1.0E-7);
|
||||
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int z = z1; z <= z2; z++) {
|
||||
Material material = simulatorData.getBlockType(x, y, z);
|
||||
|
||||
if (material == Material.POWDER_SNOW) {
|
||||
slowMovement(new Vector(0.8999999761581421, 1.5, 0.8999999761581421));
|
||||
} else if (material == Material.HONEY_BLOCK) {
|
||||
if (isSliding(new Vector(x, y, z))) {
|
||||
updateSlidingVelocity();
|
||||
}
|
||||
} else if (material == Material.COBWEB) {
|
||||
slowMovement(new Vector(0.25, 0.05000000074505806, 0.25));
|
||||
} else if (material == Material.BUBBLE_COLUMN) {
|
||||
boolean drag = ((BubbleColumn) simulatorData.getBlockData(x, y, z)).isDrag();
|
||||
if (simulatorData.getBlockType(x, y + 1, z).isAir()) {
|
||||
if (drag) {
|
||||
this.vy = Math.max(-0.9, vy - 0.03);
|
||||
} else {
|
||||
this.vy = Math.min(1.8, vy + 0.1);
|
||||
}
|
||||
} else {
|
||||
if (drag) {
|
||||
this.vy = Math.max(-0.3, vy - 0.03);
|
||||
} else {
|
||||
this.vy = Math.min(0.7, vy + 0.06);
|
||||
}
|
||||
onLanding();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void slowMovement(Vector movementMultiplier) {
|
||||
onLanding();
|
||||
this.movementMultiplier = movementMultiplier;
|
||||
}
|
||||
|
||||
private void updateSlidingVelocity() {
|
||||
if (vy < -0.13) {
|
||||
double d = -0.05 / vy;
|
||||
vx *= d;
|
||||
vz *= d;
|
||||
}
|
||||
vy = -0.05;
|
||||
onLanding();
|
||||
}
|
||||
|
||||
private boolean isSliding(Vector pos) {
|
||||
if (onGround) return false;
|
||||
if (y > pos.getY() + 0.9375 - 1.0E-7) return false;
|
||||
if (vy >= -0.08) return false;
|
||||
double d = Math.abs(pos.getX() + 0.5 - x);
|
||||
double e = Math.abs(pos.getZ() + 0.5 - z);
|
||||
double f = 0.4375 + 0.98 / 2.0;
|
||||
return d + 1.0E-7 > f || e + 1.0E-7 > f;
|
||||
}
|
||||
|
||||
private float getVelocityMultiplier(SimulatorData19 simulatorData) {
|
||||
Material material = simulatorData.getBlockType(floor(x), floor(y), floor(z));
|
||||
float f = 1F;
|
||||
if (material == Material.SOUL_SAND) {
|
||||
f = 0.5F;
|
||||
} else if (material == Material.HONEY_BLOCK) {
|
||||
f = 0.4F;
|
||||
}
|
||||
if (material != Material.WATER && material != Material.BUBBLE_COLUMN) {
|
||||
if (f != 1) return f;
|
||||
material = simulatorData.getBlockType(floor(x), floor(y - 0.5000001), floor(z));
|
||||
if (material == Material.SOUL_SAND) {
|
||||
f = 0.5F;
|
||||
} else if (material == Material.HONEY_BLOCK) {
|
||||
f = 0.4F;
|
||||
}
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private void onLanding() {
|
||||
this.fallDistance = 0.0F;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TNT{" +
|
||||
"x=" + x +
|
||||
", y=" + y +
|
||||
", z=" + z +
|
||||
", vx=" + vx +
|
||||
", vy=" + vy +
|
||||
", vz=" + vz +
|
||||
", fuse=" + fuse +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
public class Simulator20 implements Simulator {
|
||||
}
|
@ -44,7 +44,7 @@ public class SimulatorCommand extends SWCommand {
|
||||
|
||||
@Register(description = "SIMULATOR_HELP")
|
||||
public void genericCommand(@Validator Player p) {
|
||||
SimulatorCursor.hide(p, null);
|
||||
SimulatorCursor.hide(p);
|
||||
SWUtils.giveItemToPlayer(p, SimulatorStorage.getWand(p));
|
||||
}
|
||||
|
||||
|
@ -44,13 +44,10 @@ public class SimulatorCursor {
|
||||
private Map<Player, REntityServer> rEntityServerMap = new HashMap<>();
|
||||
|
||||
public void show(Player player, TNTSimulator tntSimulator, RayTraceUtils.RRayTraceResult result) {
|
||||
REntityServer cursor = rEntityServerMap.get(player);
|
||||
REntityServer cursor = rEntityServerMap.remove(player);
|
||||
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
tntSimulator.show(player);
|
||||
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
@ -67,7 +64,7 @@ public class SimulatorCursor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (SimulatorStorage.getSimulator(player.getInventory().getItemInOffHand()) != null && result.getHitPosition().distanceSquared(player.getLocation().toVector()) < 25) {
|
||||
if (SimulatorStorage.getSimulator(player.getInventory().getItemInOffHand()) != null && result.getHitPosition().distanceSquared(player.getLocation().toVector()) < 16) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -84,21 +81,6 @@ public class SimulatorCursor {
|
||||
if (cursor == null) return;
|
||||
|
||||
cursor.close();
|
||||
SimulatorStorage.getSimulatorNames().forEach(s -> {
|
||||
SimulatorStorage.getSimulator(s).hide(player);
|
||||
});
|
||||
}
|
||||
|
||||
public void hide(Player player, TNTSimulator tntSimulator) {
|
||||
REntityServer cursor = rEntityServerMap.get(player);
|
||||
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
if (tntSimulator != null) {
|
||||
tntSimulator.hide(player);
|
||||
}
|
||||
rEntityServerMap.remove(player);
|
||||
}
|
||||
|
||||
public static Vector getPos(Player player, RayTraceUtils.RRayTraceResult result) {
|
||||
@ -140,4 +122,8 @@ public class SimulatorCursor {
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public boolean has(Player player) {
|
||||
return rEntityServerMap.containsKey(player);
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ public class SimulatorStorage implements Enable, Disable {
|
||||
if (content.isEmpty()) continue;
|
||||
TNTSimulator tntSimulator = new TNTSimulator();
|
||||
for (YAPIONObject element : content.streamObject().collect(Collectors.toList())) {
|
||||
tntSimulator.getTntElementList().add(new TNTElement(element, null, tntSimulator.getEntityServer()));
|
||||
tntSimulator.getTntElementList().add(new TNTElement(element, tntSimulator, null, tntSimulator.getEntityServer()));
|
||||
}
|
||||
tntSimulators.put(newName, tntSimulator);
|
||||
}
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.RequiredArgsConstructor;
|
||||
import org.bukkit.Location;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class TNTData {
|
||||
public final Location location;
|
||||
public final int fuse;
|
||||
public final boolean xVelocity;
|
||||
public final boolean yVelocity;
|
||||
public final boolean zVelocity;
|
||||
}
|
@ -23,6 +23,8 @@ import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.configplayer.Config;
|
||||
import de.steamwar.bausystem.features.simulator.gui.TNTElementGUI;
|
||||
import de.steamwar.bausystem.features.simulator.gui.TNTSimulatorGui;
|
||||
import de.steamwar.bausystem.features.simulator.preview.PreviewRecord;
|
||||
import de.steamwar.bausystem.features.simulator.preview.Simulator;
|
||||
import de.steamwar.bausystem.features.simulator.tnt.SimulatorElement;
|
||||
import de.steamwar.bausystem.features.simulator.tnt.TNTElement;
|
||||
import de.steamwar.bausystem.features.simulator.tnt.TNTGroup;
|
||||
@ -37,6 +39,9 @@ import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.util.Vector;
|
||||
import yapion.hierarchy.types.YAPIONArray;
|
||||
import yapion.hierarchy.types.YAPIONObject;
|
||||
import yapion.hierarchy.types.YAPIONType;
|
||||
@ -46,18 +51,27 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
public class TNTSimulator {
|
||||
|
||||
@Getter
|
||||
private Set<Player> players = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private REntityServer entityServer = new REntityServer();
|
||||
|
||||
@Getter
|
||||
private Material material = Material.TNT;
|
||||
|
||||
@Getter
|
||||
private List<SimulatorElement> tntElementList = new ArrayList<>();
|
||||
|
||||
public TNTSimulator() {
|
||||
private List<Player> toShow = new ArrayList<>();
|
||||
private BukkitTask currentlyCalculating = null;
|
||||
|
||||
@Getter
|
||||
private PreviewRecord previewRecord = null;
|
||||
|
||||
public TNTSimulator() {
|
||||
}
|
||||
|
||||
public TNTSimulator(YAPIONObject yapionObject) {
|
||||
@ -65,9 +79,9 @@ public class TNTSimulator {
|
||||
YAPIONArray yapionArray = yapionObject.getArrayOrDefault("tntElements", new YAPIONArray());
|
||||
for (YAPIONObject element : yapionArray.streamObject().collect(Collectors.toList())) {
|
||||
if (element.containsKey("elements", YAPIONType.ARRAY)) {
|
||||
tntElementList.add(new TNTGroup(element, entityServer));
|
||||
tntElementList.add(new TNTGroup(element, this, entityServer));
|
||||
} else {
|
||||
tntElementList.add(new TNTElement(element, null, entityServer));
|
||||
tntElementList.add(new TNTElement(element, this, null, entityServer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,10 +113,65 @@ public class TNTSimulator {
|
||||
entityServer.removePlayer(player);
|
||||
players.remove(player);
|
||||
}
|
||||
hidePreview(player);
|
||||
}
|
||||
|
||||
void _hide(Player player) {
|
||||
players.remove(player);
|
||||
public void showPreview(Player player) {
|
||||
if (previewRecord != null) {
|
||||
previewRecord.show(player);
|
||||
return;
|
||||
}
|
||||
toShow.add(player);
|
||||
calcPreview(true);
|
||||
}
|
||||
|
||||
public void hidePreview(Player player) {
|
||||
if (previewRecord != null && previewRecord.hide(player)) {
|
||||
previewRecord = null;
|
||||
if (currentlyCalculating != null) {
|
||||
currentlyCalculating.cancel();
|
||||
currentlyCalculating = null;
|
||||
}
|
||||
}
|
||||
toShow.remove(player);
|
||||
}
|
||||
|
||||
public boolean hasPreview(Player player) {
|
||||
return previewRecord != null && previewRecord.has(player);
|
||||
}
|
||||
|
||||
public void calcPreview(boolean force, Vector changedBlock) {
|
||||
if (previewRecord == null || previewRecord.getAccessedBlocks().test(changedBlock)) {
|
||||
calcPreview(force);
|
||||
}
|
||||
}
|
||||
|
||||
public void calcPreview(boolean force) {
|
||||
if (!force && previewRecord == null) {
|
||||
return;
|
||||
}
|
||||
if (currentlyCalculating != null) {
|
||||
System.out.println("Cancelled");
|
||||
currentlyCalculating.cancel();
|
||||
}
|
||||
currentlyCalculating = Simulator.impl.run(locations(true), newRecord -> {
|
||||
currentlyCalculating = null;
|
||||
|
||||
PreviewRecord oldRecord = previewRecord;
|
||||
previewRecord = newRecord;
|
||||
if (newRecord == null) {
|
||||
toShow.clear();
|
||||
return;
|
||||
}
|
||||
if (oldRecord != null) {
|
||||
oldRecord.getPlayers().forEach(player -> {
|
||||
oldRecord.hide(player);
|
||||
newRecord.show(player);
|
||||
});
|
||||
}
|
||||
toShow.forEach(newRecord::show);
|
||||
toShow.clear();
|
||||
});
|
||||
}
|
||||
|
||||
public List<REntity> getEntities() {
|
||||
@ -119,6 +188,7 @@ public class TNTSimulator {
|
||||
|
||||
public void clear() {
|
||||
new ArrayList<>(tntElementList).forEach(this::remove);
|
||||
calcPreview(false);
|
||||
}
|
||||
|
||||
public void remove(SimulatorElement element) {
|
||||
@ -141,6 +211,7 @@ public class TNTSimulator {
|
||||
}
|
||||
element.close();
|
||||
tntElementList.remove(element);
|
||||
calcPreview(false);
|
||||
}
|
||||
|
||||
public void change() {
|
||||
@ -150,6 +221,7 @@ public class TNTSimulator {
|
||||
((TNTGroup) simulatorElement).getElements().forEach(SimulatorElement::change);
|
||||
}
|
||||
});
|
||||
calcPreview(false);
|
||||
}
|
||||
|
||||
public void edit(Player player, RayTraceUtils.RRayTraceResult result) {
|
||||
@ -199,19 +271,41 @@ public class TNTSimulator {
|
||||
return;
|
||||
}
|
||||
|
||||
TNTElement tntElement = new TNTElement(SimulatorCursor.getPos(player, result), null, entityServer);
|
||||
TNTElement tntElement = new TNTElement(this, SimulatorCursor.getPos(player, result), null, entityServer);
|
||||
tntElementList.add(tntElement);
|
||||
TNTElementGUI.open(player, tntElement, null);
|
||||
}
|
||||
|
||||
public Pair<Integer, Map<Integer, List<List<Pair<TNTData, Integer>>>>> locations(boolean allowFrozen) {
|
||||
Map<Integer, Map<Integer, Set<Pair<TNTData, Integer>>>> result = new HashMap<>();
|
||||
for (SimulatorElement element : tntElementList) {
|
||||
if (element.locations(result) && !allowFrozen) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
AtomicInteger maxTick = new AtomicInteger(0);
|
||||
Map<Integer, List<List<Pair<TNTData, Integer>>>> toSpawn = new HashMap<>();
|
||||
result.forEach((integer, integerSetMap) -> {
|
||||
List<Pair<Integer, Set<Pair<TNTData, Integer>>>> internal = new ArrayList<>();
|
||||
integerSetMap.forEach((integer1, pairs) -> {
|
||||
internal.add(new Pair<>(integer1, pairs));
|
||||
});
|
||||
internal.sort(Comparator.comparingInt(Pair::getKey));
|
||||
|
||||
toSpawn.put(integer, internal.stream().map(Pair::getValue).map(ArrayList::new).peek(Collections::shuffle).collect(Collectors.toList()));
|
||||
|
||||
if (maxTick.get() < integer) {
|
||||
maxTick.set(integer);
|
||||
}
|
||||
});
|
||||
return new Pair<>(maxTick.get(), toSpawn);
|
||||
}
|
||||
|
||||
public void start(Player p) {
|
||||
Region region = Region.getRegion(p.getLocation());
|
||||
Map<Integer, Map<Integer, Set<Pair<Runnable, Integer>>>> result = new HashMap<>();
|
||||
boolean regionFrozen = false;
|
||||
for (SimulatorElement element : tntElementList) {
|
||||
regionFrozen |= element.locations(result, region, p.getLocation());
|
||||
}
|
||||
if (regionFrozen) {
|
||||
Pair<Integer, Map<Integer, List<List<Pair<TNTData, Integer>>>>> result = locations(false);
|
||||
if (result == null) {
|
||||
BauSystem.MESSAGE.send("SIMULATOR_REGION_FROZEN", p);
|
||||
return;
|
||||
}
|
||||
@ -228,29 +322,13 @@ public class TNTSimulator {
|
||||
Recorder.INSTANCE.set(region, new SingleTraceRecorder(region));
|
||||
}
|
||||
|
||||
AtomicInteger maxTick = new AtomicInteger(0);
|
||||
Map<Integer, List<List<Pair<Runnable, Integer>>>> toSpawn = new HashMap<>();
|
||||
result.forEach((integer, integerSetMap) -> {
|
||||
List<Pair<Integer, Set<Pair<Runnable, Integer>>>> internal = new ArrayList<>();
|
||||
integerSetMap.forEach((integer1, pairs) -> {
|
||||
internal.add(new Pair<>(integer1, pairs));
|
||||
});
|
||||
internal.sort(Comparator.comparingInt(Pair::getKey));
|
||||
|
||||
toSpawn.put(integer, internal.stream().map(Pair::getValue).map(ArrayList::new).peek(Collections::shuffle).collect(Collectors.toList()));
|
||||
|
||||
if (maxTick.get() < integer) {
|
||||
maxTick.set(integer);
|
||||
}
|
||||
});
|
||||
|
||||
AtomicInteger currentTick = new AtomicInteger(0);
|
||||
BauSystem.runTaskTimer(BauSystem.getInstance(), bukkitTask -> {
|
||||
int tick = currentTick.get();
|
||||
if (tick > maxTick.get()) bukkitTask.cancel();
|
||||
if (tick > result.getKey()) bukkitTask.cancel();
|
||||
currentTick.incrementAndGet();
|
||||
|
||||
List<List<Pair<Runnable, Integer>>> toSpawnInTick = toSpawn.get(tick);
|
||||
List<List<Pair<TNTData, Integer>>> toSpawnInTick = result.getValue().get(tick);
|
||||
if (toSpawnInTick == null) return;
|
||||
toSpawnInTick.forEach(pairs -> {
|
||||
AtomicBoolean hasSomeLeft = new AtomicBoolean(true);
|
||||
@ -259,7 +337,7 @@ public class TNTSimulator {
|
||||
pairs.forEach(pair -> {
|
||||
if (pair.getValue() > 0) {
|
||||
hasSomeLeft.set(true);
|
||||
pair.getKey().run();
|
||||
spawn(pair.getKey());
|
||||
pair.setValue(pair.getValue() - 1);
|
||||
}
|
||||
});
|
||||
@ -267,4 +345,13 @@ public class TNTSimulator {
|
||||
});
|
||||
}, 1, 1);
|
||||
}
|
||||
|
||||
private void spawn(TNTData tntData) {
|
||||
SimulatorStorage.WORLD.spawn(tntData.location, TNTPrimed.class, tntPrimed -> {
|
||||
tntPrimed.setFuseTicks(tntData.fuse);
|
||||
if (!tntData.xVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setX(0));
|
||||
if (!tntData.yVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setY(0));
|
||||
if (!tntData.zVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setZ(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -19,25 +19,40 @@
|
||||
|
||||
package de.steamwar.bausystem.features.simulator;
|
||||
|
||||
import com.comphenix.tinyprotocol.Reflection;
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.Permission;
|
||||
import de.steamwar.bausystem.features.simulator.gui.SimulatorSelectionGUI;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import de.steamwar.bausystem.utils.ItemUtils;
|
||||
import de.steamwar.bausystem.utils.PlayerMovementWrapper;
|
||||
import de.steamwar.bausystem.utils.RayTraceUtils;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Linked
|
||||
public class TNTSimulatorListener implements Listener {
|
||||
|
||||
private Map<Player, Pair<TNTSimulator, Boolean>> currentSimulator = new HashMap<>();
|
||||
|
||||
private boolean permissionCheck(Player player) {
|
||||
if (!Permission.hasPermission(player, Permission.WORLD)) {
|
||||
BauSystem.MESSAGE.send("SIMULATOR_NO_PERMS", player);
|
||||
@ -46,85 +61,165 @@ public class TNTSimulatorListener implements Listener {
|
||||
return true;
|
||||
}
|
||||
|
||||
private Pair<TNTSimulator, Boolean> getSimulator(ItemStack offHand, ItemStack mainHand) {
|
||||
if (ItemUtils.isItem(offHand, "simulator")) {
|
||||
return new Pair<>(SimulatorStorage.getSimulator(offHand), true);
|
||||
}
|
||||
if (ItemUtils.isItem(mainHand, "simulator")) {
|
||||
return new Pair<>(SimulatorStorage.getSimulator(mainHand), false);
|
||||
}
|
||||
return new Pair<>(null, false);
|
||||
}
|
||||
|
||||
private Pair<TNTSimulator, Boolean> getSimulator(Player player) {
|
||||
return getSimulator(player.getInventory().getItemInOffHand(), player.getInventory().getItemInMainHand());
|
||||
}
|
||||
|
||||
static RayTraceUtils.RRayTraceResult trace(Player player, Location to, TNTSimulator simulator) {
|
||||
return RayTraceUtils.traceREntity(player, to, simulator.getEntities());
|
||||
}
|
||||
|
||||
private static final Class<?> base = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying");
|
||||
private static final Reflection.FieldAccessor<Double> x = Reflection.getField(base, double.class, 0);
|
||||
private static final Reflection.FieldAccessor<Double> y = Reflection.getField(base, double.class, 1);
|
||||
private static final Reflection.FieldAccessor<Double> z = Reflection.getField(base, double.class, 2);
|
||||
private static final Class<?> position = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying$PacketPlayInPosition");
|
||||
private static final Class<?> positionLook = Reflection.getClass("{nms.network.protocol.game}.PacketPlayInFlying$PacketPlayInPositionLook");
|
||||
|
||||
{
|
||||
BiFunction<Player, Object, Object> positionSetter = (player, o) -> {
|
||||
if (player.getGameMode() == GameMode.SPECTATOR) return o;
|
||||
Pair<TNTSimulator, Boolean> tntSimulator = currentSimulator.get(player);
|
||||
if (tntSimulator == null) return o;
|
||||
if (!tntSimulator.getKey().hasPreview(player)) return o;
|
||||
if (!tntSimulator.getKey().getPreviewRecord().isAir(x.get(o), y.get(o), z.get(o))) return o;
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
SimulatorCursor.hide(player);
|
||||
PlayerMovementWrapper.impl.setPosition(player, o);
|
||||
}, 0L);
|
||||
return null;
|
||||
};
|
||||
|
||||
TinyProtocol.instance.addFilter(position, positionSetter);
|
||||
TinyProtocol.instance.addFilter(positionLook, positionSetter);
|
||||
}
|
||||
|
||||
private void hideShow(Player player, Pair<TNTSimulator, Boolean> simulatorPair) {
|
||||
Pair<TNTSimulator, Boolean> tntSimulatorPair = currentSimulator.get(player);
|
||||
TNTSimulator tntSimulator = tntSimulatorPair == null ? null : tntSimulatorPair.getKey();
|
||||
TNTSimulator simulator = simulatorPair.getKey();
|
||||
|
||||
if (Objects.equals(simulatorPair, tntSimulatorPair)) {
|
||||
if (simulatorPair.getKey() != null) {
|
||||
SimulatorCursor.show(player, simulator, trace(player, player.getLocation(), simulator));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tntSimulator != null) {
|
||||
tntSimulator.hide(player);
|
||||
currentSimulator.remove(player);
|
||||
}
|
||||
if (simulator == null) {
|
||||
SimulatorCursor.hide(player);
|
||||
return;
|
||||
}
|
||||
|
||||
currentSimulator.put(player, simulatorPair);
|
||||
simulator.show(player);
|
||||
SimulatorCursor.show(player, simulator, trace(player, player.getLocation(), simulator));
|
||||
if (simulatorPair.getValue()) {
|
||||
simulator.showPreview(player);
|
||||
} else {
|
||||
simulator.hidePreview(player);
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerMove(PlayerMoveEvent e) {
|
||||
if (ItemUtils.isItem(e.getPlayer().getInventory().getItemInMainHand(), "simulator")) {
|
||||
simulatorShowHide(e.getPlayer(), i -> null, PlayerInventory::getItemInMainHand, e.getTo());
|
||||
} else {
|
||||
SimulatorCursor.hide(e.getPlayer());
|
||||
}
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getPlayer());
|
||||
hideShow(e.getPlayer(), simulatorPair);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerItemHeld(PlayerItemHeldEvent e) {
|
||||
simulatorShowHide(e.getPlayer(), i -> i.getItem(e.getPreviousSlot()), i -> i.getItem(e.getNewSlot()), e.getPlayer().getLocation());
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getPlayer().getInventory().getItemInOffHand(), e.getPlayer().getInventory().getItem(e.getNewSlot()));
|
||||
hideShow(e.getPlayer(), simulatorPair);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDropItem(PlayerDropItemEvent e) {
|
||||
simulatorShowHide(e.getPlayer(), i -> e.getItemDrop().getItemStack(), i -> null, e.getPlayer().getLocation());
|
||||
}
|
||||
|
||||
private TNTSimulator simulatorShowHide(Player player, Function<PlayerInventory, ItemStack> oldItemFunction, Function<PlayerInventory, ItemStack> newItemFunction, Location location) {
|
||||
TNTSimulator oldSimulator = SimulatorStorage.getSimulator(oldItemFunction.apply(player.getInventory()));
|
||||
SimulatorCursor.hide(player, oldSimulator);
|
||||
|
||||
TNTSimulator simulator = SimulatorStorage.getSimulator(newItemFunction.apply(player.getInventory()));
|
||||
if (simulator == null) return null;
|
||||
|
||||
SimulatorCursor.show(player, simulator, trace(player, location, simulator));
|
||||
return simulator;
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getPlayer().getInventory().getItemInOffHand(), new ItemStack(Material.AIR));
|
||||
hideShow(e.getPlayer(), simulatorPair);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent e) {
|
||||
if (ItemUtils.isItem(e.getPlayer().getInventory().getItemInMainHand(), "simulator")) {
|
||||
simulatorShowHide(e.getPlayer(), i -> null, PlayerInventory::getItemInMainHand, e.getPlayer().getLocation());
|
||||
}
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getPlayer());
|
||||
hideShow(e.getPlayer(), simulatorPair);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||
hideShow(e.getPlayer(), new Pair<>(null, false));
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
SimulatorCursor.hide(event.getPlayer(), null);
|
||||
SimulatorStorage.getSimulatorNames().forEach(s -> {
|
||||
SimulatorStorage.getSimulator(s)._hide(event.getPlayer());
|
||||
});
|
||||
public void onPlayerSwapHandItems(PlayerSwapHandItemsEvent e) {
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getOffHandItem(), e.getMainHandItem());
|
||||
hideShow(e.getPlayer(), simulatorPair);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!ItemUtils.isItem(event.getItem(), "simulator")) {
|
||||
public void onPlayerInteract(PlayerInteractEvent e) {
|
||||
if (!ItemUtils.isItem(e.getItem(), "simulator")) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
if (!permissionCheck(event.getPlayer())) {
|
||||
Pair<TNTSimulator, Boolean> simulatorPair = getSimulator(e.getPlayer());
|
||||
if (!SimulatorCursor.has(e.getPlayer()) && simulatorPair.getValue()) {
|
||||
return;
|
||||
}
|
||||
TNTSimulator simulator = SimulatorStorage.getSimulator(event.getItem());
|
||||
e.setCancelled(true);
|
||||
if (!permissionCheck(e.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
TNTSimulator simulator = simulatorPair.getKey();
|
||||
|
||||
switch (event.getAction()) {
|
||||
switch (e.getAction()) {
|
||||
case LEFT_CLICK_BLOCK:
|
||||
case LEFT_CLICK_AIR:
|
||||
if (simulator == null) {
|
||||
if (simulator == null || simulatorPair.getValue()) {
|
||||
return;
|
||||
}
|
||||
simulator.start(event.getPlayer());
|
||||
simulator.start(e.getPlayer());
|
||||
break;
|
||||
case RIGHT_CLICK_BLOCK:
|
||||
case RIGHT_CLICK_AIR:
|
||||
if (simulator == null) {
|
||||
SimulatorSelectionGUI.open(event.getPlayer(), event.getItem());
|
||||
SimulatorSelectionGUI.open(e.getPlayer(), e.getItem());
|
||||
} else {
|
||||
simulator.edit(event.getPlayer(), trace(event.getPlayer(), event.getPlayer().getLocation(), simulator));
|
||||
simulator.edit(e.getPlayer(), trace(e.getPlayer(), e.getPlayer().getLocation(), simulator));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockPlace(BlockPlaceEvent event) {
|
||||
currentSimulator.values().forEach(simulator -> {
|
||||
simulator.getKey().calcPreview(false, event.getBlock().getLocation().toVector());
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onBlockBreak(BlockBreakEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
currentSimulator.values().forEach(simulator -> {
|
||||
simulator.getKey().calcPreview(false, event.getBlock().getLocation().toVector());
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ public class TNTElementGUI {
|
||||
tntSimulator.getTntElementList().remove(tntElement);
|
||||
Vector vector = tntElement.getOwnPosition().clone();
|
||||
int tickOffset = tntElement.getOwnTickOffset();
|
||||
TNTGroup tntGroup = new TNTGroup(vector);
|
||||
TNTGroup tntGroup = new TNTGroup(tntSimulator, vector);
|
||||
tntGroup.setTickOffset(tickOffset);
|
||||
tntGroup.add(tntElement);
|
||||
tntElement.setTickOffset(0);
|
||||
@ -318,7 +318,7 @@ public class TNTElementGUI {
|
||||
tntSimulator.getTntElementList().add(tntGroup);
|
||||
|
||||
// Add new TNT
|
||||
TNTElement newElement = new TNTElement(new Vector(0, 0, 0), tntGroup, tntSimulator.getEntityServer());
|
||||
TNTElement newElement = new TNTElement(tntSimulator, new Vector(0, 0, 0), tntGroup, tntSimulator.getEntityServer());
|
||||
newElement.setTickOffset(1);
|
||||
tntGroup.add(newElement);
|
||||
|
||||
@ -334,7 +334,7 @@ public class TNTElementGUI {
|
||||
inv.setItem(25, new SWItem(Material.DISPENSER, BauSystem.MESSAGE.parse("SIMULATOR_TNT_SPAWN_ADD_TNT", player), clickType -> {
|
||||
if (clickType == ClickType.DOUBLE_CLICK) return;
|
||||
Vector vector = tntElement.getOwnPosition().clone();
|
||||
TNTElement newElement = new TNTElement(vector, null, tntSimulator.getEntityServer());
|
||||
TNTElement newElement = new TNTElement(tntSimulator, vector, null, tntSimulator.getEntityServer());
|
||||
if (tntElement.hasParent()) {
|
||||
newElement.setTickOffset(tntElement.getOwnTickOffset() + 1);
|
||||
tntElement.getParent().add(newElement);
|
||||
|
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import de.steamwar.bausystem.features.tracer.show.EntityShowMode;
|
||||
import de.steamwar.bausystem.features.tracer.show.Record;
|
||||
import de.steamwar.bausystem.features.tracer.show.ShowModeParameter;
|
||||
import de.steamwar.bausystem.utils.RBlockServer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class PreviewRecord {
|
||||
|
||||
private static final BlockData AIR_BLOCK_DATA = Material.AIR.createBlockData();
|
||||
private static final World WORLD = Bukkit.getWorlds().get(0);
|
||||
|
||||
private Set<Vector> destroyedBlocks;
|
||||
private RBlockServer rBlockServer = new RBlockServer();
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private Predicate<Vector> accessedBlocks;
|
||||
|
||||
@Getter
|
||||
private Record record = new Record(null, (region, tntPosition) -> {});
|
||||
|
||||
private Map<Player, EntityShowMode> showModeMap = new HashMap<>();
|
||||
|
||||
public void setDestroyedBlocks(Set<Vector> airBlocks) {
|
||||
destroyedBlocks = airBlocks;
|
||||
airBlocks.forEach(vector -> {
|
||||
rBlockServer.setBlock(vector.toLocation(WORLD), AIR_BLOCK_DATA);
|
||||
});
|
||||
}
|
||||
|
||||
public Set<Player> getPlayers() {
|
||||
return new HashSet<>(showModeMap.keySet());
|
||||
}
|
||||
|
||||
public void show(Player player) {
|
||||
showModeMap.computeIfAbsent(player, p -> {
|
||||
rBlockServer.addPlayer(p);
|
||||
ShowModeParameter showModeParameter = new ShowModeParameter();
|
||||
showModeParameter.enableWater();
|
||||
showModeParameter.enableCount();
|
||||
showModeParameter.setTntPositionMaterial(Material.WHITE_STAINED_GLASS);
|
||||
showModeParameter.setExplosePositionMaterial(Material.RED_STAINED_GLASS);
|
||||
EntityShowMode entityShowMode = new EntityShowMode(p, showModeParameter, 10);
|
||||
record.getTnt().forEach(tntRecord -> tntRecord.getPositions().forEach(entityShowMode::show));
|
||||
return entityShowMode;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean hide(Player player) {
|
||||
EntityShowMode showMode = showModeMap.remove(player);
|
||||
if (showMode != null) {
|
||||
showMode.hide();
|
||||
rBlockServer.removePlayer(player);
|
||||
}
|
||||
if (showModeMap.isEmpty()) {
|
||||
rBlockServer.close();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean has(Player player) {
|
||||
return showModeMap.containsKey(player);
|
||||
}
|
||||
|
||||
public boolean isAir(double x, double y, double z) {
|
||||
Vector vec = new Vector(x, y, z);
|
||||
return destroyedBlocks.contains(new Vector(vec.getBlockX(), vec.getBlockY(), vec.getBlockZ()));
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.preview;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.features.simulator.TNTData;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import de.steamwar.core.VersionDependent;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface Simulator {
|
||||
Simulator impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
|
||||
|
||||
default BukkitTask run(Pair<Integer, Map<Integer, List<List<Pair<TNTData, Integer>>>>> toCalculate, Consumer<PreviewRecord> finished) {
|
||||
return null;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
|
||||
package de.steamwar.bausystem.features.simulator.tnt;
|
||||
|
||||
import de.steamwar.bausystem.features.simulator.TNTData;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import de.steamwar.entity.REntity;
|
||||
@ -46,7 +47,7 @@ public interface SimulatorElement {
|
||||
void remove(TNTElement tntElement);
|
||||
|
||||
SWItem menu(Player p);
|
||||
boolean locations(Map<Integer, Map<Integer, Set<Pair<Runnable, Integer>>>> result, Region region, Location location); // Ticks to subtick order to spawning runnable to count of activations
|
||||
boolean locations(Map<Integer, Map<Integer, Set<Pair<TNTData, Integer>>>> result); // Ticks to subtick order to spawning runnable to count of activations
|
||||
int tntCount();
|
||||
int tick();
|
||||
|
||||
|
@ -22,6 +22,8 @@ package de.steamwar.bausystem.features.simulator.tnt;
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.features.simulator.OrderUtils;
|
||||
import de.steamwar.bausystem.features.simulator.SimulatorStorage;
|
||||
import de.steamwar.bausystem.features.simulator.TNTData;
|
||||
import de.steamwar.bausystem.features.simulator.TNTSimulator;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.region.flags.Flag;
|
||||
import de.steamwar.bausystem.region.flags.flagvalues.FreezeMode;
|
||||
@ -54,6 +56,7 @@ public class TNTElement implements SimulatorElement {
|
||||
private RFallingBlockEntity entity;
|
||||
TNTGroup tntGroup = null;
|
||||
|
||||
private final TNTSimulator tntSimulator;
|
||||
private final Vector position;
|
||||
private int fuseTicks = 80;
|
||||
private int count = 1;
|
||||
@ -71,14 +74,16 @@ public class TNTElement implements SimulatorElement {
|
||||
private Material material = Material.TNT;
|
||||
private boolean disabled = false;
|
||||
|
||||
public TNTElement(Vector position, TNTGroup tntGroup, REntityServer entityServer) {
|
||||
public TNTElement(TNTSimulator tntSimulator, Vector position, TNTGroup tntGroup, REntityServer entityServer) {
|
||||
this.tntSimulator = tntSimulator;
|
||||
this.entityServer = entityServer;
|
||||
this.tntGroup = tntGroup;
|
||||
this.position = position;
|
||||
initEntity();
|
||||
}
|
||||
|
||||
public TNTElement(YAPIONObject yapionObject, TNTGroup tntGroup, REntityServer entityServer) {
|
||||
public TNTElement(YAPIONObject yapionObject, TNTSimulator tntSimulator, TNTGroup tntGroup, REntityServer entityServer) {
|
||||
this.tntSimulator = tntSimulator;
|
||||
this.entityServer = entityServer;
|
||||
this.tntGroup = tntGroup;
|
||||
this.position = new Vector(yapionObject.getDoubleOrDefault("x", yapionObject.getDoubleOrDefault("positionX", 0)), yapionObject.getDoubleOrDefault("y", yapionObject.getDoubleOrDefault("positionY", 0)), yapionObject.getDoubleOrDefault("z", yapionObject.getDoubleOrDefault("positionZ", 0)));
|
||||
@ -146,6 +151,7 @@ public class TNTElement implements SimulatorElement {
|
||||
this.position.setY(position.getY());
|
||||
this.position.setZ(position.getZ());
|
||||
_updatePosition();
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
void _updatePosition() {
|
||||
@ -167,6 +173,7 @@ public class TNTElement implements SimulatorElement {
|
||||
@Override
|
||||
public void remove(TNTElement tntElement) {
|
||||
entity.die();
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -189,31 +196,15 @@ public class TNTElement implements SimulatorElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean locations(Map<Integer, Map<Integer, Set<Pair<Runnable, Integer>>>> result, Region region, Location radius) {
|
||||
public boolean locations(Map<Integer, Map<Integer, Set<Pair<TNTData, Integer>>>> result) {
|
||||
if (disabled) return false;
|
||||
Location location = getPosition().toLocation(SimulatorStorage.WORLD);
|
||||
if (region.isGlobal() && location.distanceSquared(radius) > 10000) {
|
||||
return false;
|
||||
}
|
||||
if (!region.inRegion(location, RegionType.NORMAL, RegionExtensionType.NORMAL)) {
|
||||
return false;
|
||||
}
|
||||
Region thisRegion = Region.getRegion(location);
|
||||
if (thisRegion.getFlagStorage().get(Flag.FREEZE) == FreezeMode.ACTIVE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
result.computeIfAbsent(getTickOffset(), ignore -> new HashMap<>())
|
||||
.computeIfAbsent(OrderUtils.order(order), ignore -> new HashSet<>())
|
||||
.add(new Pair<>(() -> {
|
||||
SimulatorStorage.WORLD.spawn(location, TNTPrimed.class, tntPrimed -> {
|
||||
tntPrimed.setFuseTicks(fuseTicks);
|
||||
if (!xVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setX(0));
|
||||
if (!yVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setY(0));
|
||||
if (!zVelocity) tntPrimed.setVelocity(tntPrimed.getVelocity().setZ(0));
|
||||
});
|
||||
}, count));
|
||||
return false;
|
||||
.add(new Pair<>(new TNTData(location, fuseTicks, xVelocity, yVelocity, zVelocity), count));
|
||||
return thisRegion.getFlagStorage().get(Flag.FREEZE) == FreezeMode.ACTIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -230,6 +221,7 @@ public class TNTElement implements SimulatorElement {
|
||||
if (count < 0) count = 0;
|
||||
if (count > 400) count = 400;
|
||||
this.count = count;
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public int getOwnTickOffset() {
|
||||
@ -246,19 +238,23 @@ public class TNTElement implements SimulatorElement {
|
||||
public void setTickOffset(int tickOffset) {
|
||||
if (getTickOffset() - this.tickOffset + tickOffset < 0) {
|
||||
this.tickOffset = -this.getParentTickOffset();
|
||||
tntSimulator.calcPreview(false);
|
||||
return;
|
||||
}
|
||||
if (getTickOffset() - this.tickOffset + tickOffset > 400) {
|
||||
this.tickOffset = 400 - this.getParentTickOffset();
|
||||
tntSimulator.calcPreview(false);
|
||||
return;
|
||||
}
|
||||
this.tickOffset = tickOffset;
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public void setFuseTicks(int fuseTicks) {
|
||||
if (fuseTicks < 0) fuseTicks = 0;
|
||||
if (fuseTicks > 160) fuseTicks = 160;
|
||||
this.fuseTicks = fuseTicks;
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public Vector getOwnPosition() {
|
||||
@ -267,6 +263,7 @@ public class TNTElement implements SimulatorElement {
|
||||
|
||||
public void setOrder(Material material) {
|
||||
this.order = material;
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public void setMaterial(Material material) {
|
||||
@ -284,6 +281,7 @@ public class TNTElement implements SimulatorElement {
|
||||
public void setDisabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
_updatePosition();
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public void align(Vector offset) {
|
||||
|
@ -20,6 +20,8 @@
|
||||
package de.steamwar.bausystem.features.simulator.tnt;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.features.simulator.TNTData;
|
||||
import de.steamwar.bausystem.features.simulator.TNTSimulator;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.shared.Pair;
|
||||
import de.steamwar.entity.REntity;
|
||||
@ -42,24 +44,27 @@ import java.util.stream.Collectors;
|
||||
@Getter
|
||||
public class TNTGroup implements SimulatorElement {
|
||||
|
||||
private final TNTSimulator tntSimulator;
|
||||
private final Vector position;
|
||||
private int tickOffset = 0;
|
||||
private Material material = Material.BARREL;
|
||||
private boolean disabled = false;
|
||||
private List<TNTElement> elements = new ArrayList<>();
|
||||
|
||||
public TNTGroup(Vector position) {
|
||||
public TNTGroup(TNTSimulator tntSimulator, Vector position) {
|
||||
this.tntSimulator = tntSimulator;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public TNTGroup(YAPIONObject yapionObject, REntityServer entityServer) {
|
||||
public TNTGroup(YAPIONObject yapionObject, TNTSimulator tntSimulator, REntityServer entityServer) {
|
||||
this.tntSimulator = tntSimulator;
|
||||
this.position = new Vector(yapionObject.getDoubleOrDefault("x", 0), yapionObject.getDoubleOrDefault("y", 0), yapionObject.getDoubleOrDefault("z", 0));
|
||||
this.tickOffset = yapionObject.getIntOrDefault("tickOffset", 0);
|
||||
this.material = Material.getMaterial(yapionObject.getStringOrDefault("material", "BARREL"));
|
||||
this.disabled = yapionObject.getBooleanOrDefault("disabled", false);
|
||||
YAPIONArray elements = yapionObject.getArrayOrDefault("elements", new YAPIONArray());
|
||||
for (YAPIONObject element : elements.streamObject().collect(Collectors.toList())) {
|
||||
TNTElement tntElement = new TNTElement(element, this, entityServer);
|
||||
TNTElement tntElement = new TNTElement(element, tntSimulator, this, entityServer);
|
||||
this.elements.add(tntElement);
|
||||
tntElement._updatePosition();
|
||||
}
|
||||
@ -85,6 +90,7 @@ public class TNTGroup implements SimulatorElement {
|
||||
public void add(TNTElement tntElement) {
|
||||
tntElement.tntGroup = this;
|
||||
elements.add(tntElement);
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,6 +118,7 @@ public class TNTGroup implements SimulatorElement {
|
||||
this.position.setY(position.getY());
|
||||
this.position.setZ(position.getZ());
|
||||
elements.forEach(TNTElement::_updatePosition);
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -140,14 +147,15 @@ public class TNTGroup implements SimulatorElement {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean locations(Map<Integer, Map<Integer, Set<Pair<Runnable, Integer>>>> result, Region region, Location location) {
|
||||
public boolean locations(Map<Integer, Map<Integer, Set<Pair<TNTData, Integer>>>> result) {
|
||||
if (disabled) return false;
|
||||
boolean frozen = false;
|
||||
for (TNTElement element : elements) {
|
||||
if (element.locations(result, region, location)) {
|
||||
return true;
|
||||
if (element.locations(result)) {
|
||||
frozen = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return frozen;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -173,6 +181,7 @@ public class TNTGroup implements SimulatorElement {
|
||||
}
|
||||
}
|
||||
this.tickOffset = tickOffset;
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
|
||||
public void setMaterial(Material material) {
|
||||
@ -182,5 +191,6 @@ public class TNTGroup implements SimulatorElement {
|
||||
public void setDisabled(boolean disabled) {
|
||||
this.disabled = disabled;
|
||||
elements.forEach(TNTElement::_updatePosition);
|
||||
tntSimulator.calcPreview(false);
|
||||
}
|
||||
}
|
||||
|
@ -36,10 +36,10 @@ public class TNTPosition extends Position {
|
||||
private final boolean source;
|
||||
private final boolean exploded;
|
||||
|
||||
public TNTPosition(Record.TNTRecord record, TNTPrimed entity, Vector previousLocation, Vector velocity, Vector updateVelocity, boolean source, boolean exploded) {
|
||||
super(entity.getLocation().toVector());
|
||||
public TNTPosition(Record.TNTRecord record, Vector position, int fuseTicks, Vector previousLocation, Vector velocity, Vector updateVelocity, boolean source, boolean exploded) {
|
||||
super(position);
|
||||
this.record = record;
|
||||
this.fuseTicks = entity.getFuseTicks();
|
||||
this.fuseTicks = fuseTicks;
|
||||
this.previousLocation = previousLocation;
|
||||
this.velocity = velocity;
|
||||
this.updateVelocity = updateVelocity;
|
||||
|
@ -81,14 +81,14 @@ public abstract class AutoTraceRecorder implements TraceRecorder {
|
||||
startRecording();
|
||||
}
|
||||
if (recording) {
|
||||
getRecord(tntPrimed).source(tntPrimed);
|
||||
getRecord(tntPrimed).source(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void tick(TNTPrimed tntPrimed) {
|
||||
if (recording) {
|
||||
getRecord(tntPrimed).location(tntPrimed);
|
||||
getRecord(tntPrimed).location(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public abstract class AutoTraceRecorder implements TraceRecorder {
|
||||
if (recording) {
|
||||
Record.TNTRecord tntRecord = getRecord(tntPrimed);
|
||||
if (inBuildRegion) tntRecord.setInBuildArea(true);
|
||||
tntRecord.explode(tntPrimed);
|
||||
tntRecord.explode(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
}
|
||||
lastExplosion = 0;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ package de.steamwar.bausystem.features.tracer.record;
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.features.tracer.show.Record;
|
||||
import de.steamwar.bausystem.features.tracer.show.StoredRecords;
|
||||
import de.steamwar.bausystem.features.tracer.show.TraceShowManager;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.region.utils.RegionExtensionType;
|
||||
import de.steamwar.bausystem.region.utils.RegionType;
|
||||
@ -100,7 +101,7 @@ public class Recorder implements Listener {
|
||||
public void set(Region region, TraceRecorder traceRecorder) {
|
||||
regionTraceRecorderMap.put(region, traceRecorder);
|
||||
traceRecorder.recordSupplier(() -> {
|
||||
Record record = new Record(region);
|
||||
Record record = new Record(region, TraceShowManager::show);
|
||||
StoredRecords.add(region, record);
|
||||
return record;
|
||||
});
|
||||
|
@ -56,19 +56,19 @@ public class SimpleTraceRecorder implements TraceRecorder, ActiveTracer {
|
||||
|
||||
@Override
|
||||
public void spawn(TNTPrimed tntPrimed) {
|
||||
getRecord(tntPrimed).source(tntPrimed);
|
||||
getRecord(tntPrimed).source(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(TNTPrimed tntPrimed) {
|
||||
getRecord(tntPrimed).location(tntPrimed);
|
||||
getRecord(tntPrimed).location(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void explode(TNTPrimed tntPrimed, boolean inBuildRegion) {
|
||||
Record.TNTRecord tntRecord = getRecord(tntPrimed);
|
||||
if (inBuildRegion) tntRecord.setInBuildArea(true);
|
||||
tntRecord.explode(tntPrimed);
|
||||
tntRecord.explode(tntPrimed.getLocation().toVector(), tntPrimed.getVelocity(), tntPrimed.getFuseTicks());
|
||||
recordMap.remove(tntPrimed);
|
||||
}
|
||||
|
||||
|
@ -166,11 +166,11 @@ public class EntityShowMode implements ShowMode<TNTPosition> {
|
||||
private REntity createEntity(Vector position, PositionType positionType) {
|
||||
Material material;
|
||||
if (positionType == PositionType.TNT) {
|
||||
material = Material.TNT;
|
||||
material = showModeParameter.getTntPositionMaterial();
|
||||
} else if (positionType == PositionType.EXPLODE) {
|
||||
material = Material.RED_STAINED_GLASS;
|
||||
material = showModeParameter.getExplosePositionMaterial();
|
||||
} else {
|
||||
material = Material.WHITE_STAINED_GLASS;
|
||||
material = showModeParameter.getUpdatePositionMaterial();
|
||||
}
|
||||
RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, position.toLocation(player.getWorld()), material);
|
||||
entity.setNoGravity(true);
|
||||
|
@ -31,6 +31,7 @@ import org.bukkit.util.Vector;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class Record {
|
||||
@ -38,15 +39,12 @@ public class Record {
|
||||
@Getter
|
||||
private final List<TNTRecord> tnt = new ArrayList<>();
|
||||
private final Region region;
|
||||
private final BiConsumer<Region, TNTPosition> showFunction;
|
||||
|
||||
public int size() {
|
||||
return tnt.size();
|
||||
}
|
||||
|
||||
public void showAll(ShowMode<TNTPosition> traceShowMode) {
|
||||
tnt.forEach(tntRecord -> tntRecord.getPositions().forEach(traceShowMode::show));
|
||||
}
|
||||
|
||||
public TNTRecord spawn(long offset) {
|
||||
TNTRecord record = new TNTRecord(this, offset, region);
|
||||
tnt.add(record);
|
||||
@ -102,30 +100,30 @@ public class Record {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public void source(TNTPrimed tntPrimed) {
|
||||
add(tntPrimed, true, false);
|
||||
public void source(Vector location, Vector velocity, int fuseTicks) {
|
||||
add(location, velocity, fuseTicks, true, false);
|
||||
}
|
||||
|
||||
public void location(TNTPrimed tntPrimed) {
|
||||
add(tntPrimed, false, false);
|
||||
public void location(Vector location, Vector velocity, int fuseTicks) {
|
||||
add(location, velocity, fuseTicks, false, false);
|
||||
}
|
||||
|
||||
public void explode(TNTPrimed tntPrimed) {
|
||||
add(tntPrimed, false, true);
|
||||
public void explode(Vector location, Vector velocity, int fuseTicks) {
|
||||
add(location, velocity, fuseTicks, false, true);
|
||||
record.checkMicroMotion();
|
||||
}
|
||||
|
||||
private void add(TNTPrimed tntPrimed, boolean source, boolean exploded) {
|
||||
private void add(Vector location, Vector velocity, int fuseTicks, boolean source, boolean exploded) {
|
||||
TNTPosition position;
|
||||
if (positions.isEmpty()) {
|
||||
position = new TNTPosition(this, tntPrimed, null, tntPrimed.getVelocity(), null, source, exploded);
|
||||
position = new TNTPosition(this, location, fuseTicks, null, velocity, null, source, exploded);
|
||||
} else {
|
||||
TNTPosition tntPosition = positions.get(positions.size() - 1);
|
||||
Vector lastVelocity = tntPrimed.getLocation().toVector().clone().subtract(tntPosition.getLocation());
|
||||
position = new TNTPosition(this, tntPrimed, positions.get(positions.size() - 1).getLocation(), tntPrimed.getVelocity(), lastVelocity, source, exploded);
|
||||
Vector lastVelocity = location.clone().subtract(tntPosition.getLocation());
|
||||
position = new TNTPosition(this, location, fuseTicks, positions.get(positions.size() - 1).getLocation(), velocity, lastVelocity, source, exploded);
|
||||
}
|
||||
positions.add(position);
|
||||
TraceShowManager.show(region, position);
|
||||
record.showFunction.accept(region, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
package de.steamwar.bausystem.features.tracer.show;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Material;
|
||||
|
||||
@Getter
|
||||
public class ShowModeParameter {
|
||||
@ -34,6 +36,15 @@ public class ShowModeParameter {
|
||||
private boolean ticksSinceStart = false;
|
||||
private boolean microMotion = false;
|
||||
|
||||
@Setter
|
||||
private Material tntPositionMaterial = Material.TNT;
|
||||
|
||||
@Setter
|
||||
private Material updatePositionMaterial = Material.WHITE_STAINED_GLASS;
|
||||
|
||||
@Setter
|
||||
private Material explosePositionMaterial = Material.RED_STAINED_GLASS;
|
||||
|
||||
public void enableWater() {
|
||||
this.water = true;
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ public class TraceShowManager implements Listener {
|
||||
}
|
||||
|
||||
/* Only to be called by record */
|
||||
static void show(Region region, TNTPosition tnt) {
|
||||
public static void show(Region region, TNTPosition tnt) {
|
||||
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.get(region);
|
||||
if (regionalShowModes == null) {
|
||||
return;
|
||||
|
@ -118,7 +118,7 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
|
||||
if (hidden.containsKey(region) && hidden.get(region).contains(player)) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
PlayerMovementWrapper.impl.setPosition(player, o);
|
||||
}, 1L);
|
||||
}, 0L);
|
||||
return null;
|
||||
}
|
||||
return o;
|
||||
|
207
BauSystem_Main/src/de/steamwar/bausystem/utils/RBlockServer.java
Normale Datei
207
BauSystem_Main/src/de/steamwar/bausystem/utils/RBlockServer.java
Normale Datei
@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.utils;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.core.FlatteningWrapper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class RBlockServer implements Listener {
|
||||
|
||||
private final HashMap<Long, Map<Location, BlockData>> blocks = new HashMap<>();
|
||||
private final HashMap<Long, Set<Player>> players = new HashMap<>();
|
||||
private final HashMap<Player, Location> lastLocation = new HashMap<>();
|
||||
private final HashMap<Player, Integer> viewDistance = new HashMap<>();
|
||||
|
||||
public RBlockServer() {
|
||||
BauSystem.getInstance().getServer().getPluginManager().registerEvents(this, BauSystem.getInstance());
|
||||
}
|
||||
|
||||
public void addPlayer(Player player) {
|
||||
Location location = player.getLocation();
|
||||
this.lastLocation.put(player, location);
|
||||
this.viewDistance.put(player, this.viewRadius(player));
|
||||
this.forChunkInView(player, location, (x, z) -> {
|
||||
this.addPlayerToChunk(player, x, z);
|
||||
});
|
||||
}
|
||||
|
||||
public void removePlayer(Player player) {
|
||||
this.forChunkInView(player, this.lastLocation.remove(player), (x, z) -> {
|
||||
this.removePlayerFromChunk(player, x, z);
|
||||
});
|
||||
this.viewDistance.remove(player);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
Player[] players = lastLocation.keySet().toArray(new Player[0]);
|
||||
for (Player player : players) {
|
||||
removePlayer(player);
|
||||
}
|
||||
blocks.clear();
|
||||
|
||||
HandlerList.unregisterAll(this);
|
||||
}
|
||||
|
||||
public void setBlock(Location location, BlockData blockData) {
|
||||
long chunkId = posToId(location.getX(), location.getZ());
|
||||
if (blockData == null) {
|
||||
Map<Location, BlockData> chunkData = blocks.get(chunkId);
|
||||
if (chunkData == null) return;
|
||||
chunkData.remove(location);
|
||||
if (chunkData.isEmpty()) {
|
||||
blocks.remove(chunkId);
|
||||
}
|
||||
BlockData data = location.getBlock().getBlockData();
|
||||
players.getOrDefault(chunkId, Collections.emptySet()).forEach(player -> {
|
||||
player.sendBlockChange(location, data);
|
||||
});
|
||||
} else {
|
||||
blocks.computeIfAbsent(chunkId, i -> new HashMap<>()).put(location, blockData);
|
||||
players.getOrDefault(chunkId, Collections.emptySet()).forEach(player -> {
|
||||
player.sendBlockChange(location, blockData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(
|
||||
ignoreCancelled = true,
|
||||
priority = EventPriority.MONITOR
|
||||
)
|
||||
public void onMove(PlayerMoveEvent e) {
|
||||
Player player = e.getPlayer();
|
||||
Location from = this.lastLocation.get(player);
|
||||
Location to = e.getTo();
|
||||
if (from != null && to != null) {
|
||||
int fromX = this.posToChunk(from.getX());
|
||||
int fromZ = this.posToChunk(from.getZ());
|
||||
int toX = this.posToChunk(to.getX());
|
||||
int toZ = this.posToChunk(to.getZ());
|
||||
if (fromX != toX || fromZ != toZ) {
|
||||
this.lastLocation.put(player, to);
|
||||
int toViewDistance = this.viewRadius(player);
|
||||
this.forChunkInView(player, from, (x, z) -> {
|
||||
if (Math.abs(x - toX) > toViewDistance || Math.abs(z - toZ) > toViewDistance) {
|
||||
this.removePlayerFromChunk(player, x, z);
|
||||
}
|
||||
|
||||
});
|
||||
this.viewDistance.put(player, toViewDistance);
|
||||
this.forChunkInView(player, to, (x, z) -> {
|
||||
this.addPlayerToChunk(player, x, z);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(
|
||||
ignoreCancelled = true,
|
||||
priority = EventPriority.MONITOR
|
||||
)
|
||||
public void onPlayerInteract(PlayerInteractEvent event) {
|
||||
if (!event.hasBlock()) return;
|
||||
Block block = event.getClickedBlock();
|
||||
long chunkId = posToId(block.getX(), block.getZ());
|
||||
if (!blocks.containsKey(chunkId)) return;
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
this.addPlayerToChunk(event.getPlayer(), posToChunk(block.getX()), posToChunk(block.getZ()));
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent e) {
|
||||
Player player = e.getPlayer();
|
||||
Location location = this.lastLocation.remove(player);
|
||||
if (location != null) {
|
||||
this.forChunkInView(player, location, (x, z) -> {
|
||||
long id = this.chunkToId(x, z);
|
||||
Set<Player> playersInChunk = this.players.get(id);
|
||||
playersInChunk.remove(player);
|
||||
if (playersInChunk.isEmpty()) {
|
||||
this.players.remove(id);
|
||||
}
|
||||
|
||||
});
|
||||
this.viewDistance.remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
private void forChunkInView(Player player, Location location, BiConsumer<Integer, Integer> func) {
|
||||
int chunkX = this.posToChunk(location.getX());
|
||||
int chunkZ = this.posToChunk(location.getZ());
|
||||
int viewDistance = this.viewDistance.get(player);
|
||||
|
||||
for(int x = chunkX - viewDistance; x <= chunkX + viewDistance; ++x) {
|
||||
for(int z = chunkZ - viewDistance; z <= chunkZ + viewDistance; ++z) {
|
||||
func.accept(x, z);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void addPlayerToChunk(Player player, int x, int z) {
|
||||
long id = this.chunkToId(x, z);
|
||||
this.players.computeIfAbsent(id, i -> new HashSet<>()).add(player);
|
||||
blocks.getOrDefault(id, Collections.emptyMap()).forEach(player::sendBlockChange);
|
||||
}
|
||||
|
||||
private void removePlayerFromChunk(Player player, int x, int z) {
|
||||
long id = this.chunkToId(x, z);
|
||||
Set<Player> playersInChunk = this.players.get(id);
|
||||
playersInChunk.remove(player);
|
||||
if (playersInChunk.isEmpty()) {
|
||||
this.players.remove(id);
|
||||
}
|
||||
|
||||
blocks.getOrDefault(id, Collections.emptyMap()).forEach((location, block) -> {
|
||||
player.sendBlockChange(location, location.getBlock().getBlockData());
|
||||
});
|
||||
}
|
||||
|
||||
private int posToChunk(double coord) {
|
||||
return (int)(coord / 16.0) - (coord < 0.0 ? 1 : 0);
|
||||
}
|
||||
|
||||
private int viewRadius(Player player) {
|
||||
return FlatteningWrapper.impl.getViewDistance(player);
|
||||
}
|
||||
|
||||
private long posToId(double x, double z) {
|
||||
return this.chunkToId(this.posToChunk(x), this.posToChunk(z));
|
||||
}
|
||||
|
||||
private long chunkToId(int x, int z) {
|
||||
return ((long)x << 32) + (long)z;
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren