SteamWar/BauSystem2.0
Archiviert
12
0

Commits vergleichen

...
Dieses Repository wurde am 2024-08-05 archiviert. Du kannst Dateien ansehen und es klonen, aber nicht pushen oder Issues/Pull-Requests öffnen.

37 Commits

Autor SHA1 Nachricht Datum
yoyosource
b787b96913 Fix AxisMovementLimiter19 a bit
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-16 14:00:15 +02:00
yoyosource
4d26ba5166 Fix fuse calculation and maybe exposure
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-16 12:51:41 +02:00
yoyosource
35c37b9c06 Hotfix RBlockServer
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-15 17:26:54 +02:00
yoyosource
7f7081adfe Rename to *19
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 16:55:33 +02:00
yoyosource
85527f7004 Fix show and hide of TNTSimulator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 16:32:03 +02:00
yoyosource
63e8b96ec9 Fix show and hide of TNTSimulator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 16:15:53 +02:00
yoyosource
8a3cce0a3a Add RBlockServer
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 15:19:05 +02:00
yoyosource
2774b29342 Add optimistic recalc on block break and place
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 13:32:22 +02:00
yoyosource
7e2780f326 Fix OutOfMemory
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-09 13:22:16 +02:00
yoyosource
e49724e593 Fix Water/Lava exposure
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-08 22:12:11 +02:00
yoyosource
c92d610ca9 Fix many inconveniences
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-08 20:46:00 +02:00
yoyosource
4fbbdf3a49 Add initial Multi Tick calculation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-08 20:16:51 +02:00
yoyosource
d09c4970a0 Remove freeze check
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add initial MultiTick implementation

Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-07 21:28:18 +02:00
yoyosource
1af342d2c3 Implement advanced TNTSimulator PreviewRecord
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-07 15:35:45 +02:00
yoyosource
12d0ecbf0e Setup Record for Simulator use and Trace use
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-06 21:27:21 +02:00
yoyosource
853aae1b8e Optimize imports
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-06 17:59:28 +02:00
yoyosource
5ea2b52985 Fix TNTSimulatorListener.hideShow for SimulatorCursor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Remove prewview for 1.20

Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-02 16:24:21 +02:00
yoyosource
6b727dd915 Merge branch 'master' into SimulatorPreview
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-02 15:40:45 +02:00
yoyosource
d5d25e9fb0 Update Record
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-02 13:10:57 +02:00
yoyosource
0b8a34bbe5 Merge branch 'master' into SimulatorPreview
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-09-02 12:13:58 +02:00
yoyosource
6ebc9b1068 Fix AxisMovementLimiter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Fix Simulator19
Update many things for dynamic preview

Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-01 19:03:33 +02:00
yoyosource
6409748d32 Fix AxisMovementLimiter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-09-01 16:26:07 +02:00
yoyosource
02c68a8b0b Push current state
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-08-31 21:30:34 +02:00
yoyosource
e42e123aaa Merge branch 'master' into SimulatorPreview 2023-08-31 17:45:14 +02:00
yoyosource
9d91c4e3eb Add basic Simulator Preview
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-08-31 16:53:45 +02:00
yoyosource
bb489fd185 Add basic Simulator Preview
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-08-31 16:53:21 +02:00
yoyosource
a83b538b04 Add Pos and OptimizedAxisMovementLimiter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-08-30 21:19:17 +02:00
yoyosource
8f54fa1ef0 Update code
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-08-27 20:48:45 +02:00
yoyosource
fb3db5d4dd Merge branch 'master' into SimulatorPreview
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-08-27 15:04:34 +02:00
yoyosource
faf62a6ee7 Add basic Explosion
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-04-16 13:00:17 +02:00
yoyosource
a2cdcc0b47 Merge branch 'master' into SimulatorPreview 2023-04-16 12:15:58 +02:00
yoyosource
5f6696885d Add 400 tnt test
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-04-10 21:15:09 +02:00
yoyosource
8d4f1bf2da Update simulator preview
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-04-10 18:50:36 +02:00
yoyosource
ff0d3fe2e5 Merge branch 'master' into SimulatorPreview 2023-04-10 09:15:37 +02:00
yoyosource
92ed98a8a1 Fix Simulator19
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-04-04 18:32:45 +02:00
yoyosource
89ee198968 Update TNT and cache of Block and BlockData for faster get
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-03-19 21:09:34 +01:00
yoyosource
a68f45d824 Add basic TNT
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: yoyosource <yoyosource@nidido.de>
2023-03-19 20:23:16 +01:00
31 geänderte Dateien mit 2039 neuen und 154 gelöschten Zeilen

Datei anzeigen

@ -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 {
}

Datei anzeigen

@ -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);
}
}
}
}

Datei anzeigen

@ -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;
}
}
}
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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);
};
}
}

Datei anzeigen

@ -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 +
'}';
}
}

Datei anzeigen

@ -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 {
}

Datei anzeigen

@ -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));
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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);
}

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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));
});
}
}

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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);

Datei anzeigen

@ -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()));
}
}

Datei anzeigen

@ -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;
}
}

Datei anzeigen

@ -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();

Datei anzeigen

@ -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) {

Datei anzeigen

@ -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);
}
}

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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;
});

Datei anzeigen

@ -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);
}

Datei anzeigen

@ -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);

Datei anzeigen

@ -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);
}
}
}

Datei anzeigen

@ -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;
}

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;

Datei anzeigen

@ -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;
}
}