Trace Refactor #233

Zusammengeführt
YoyoNow hat 121 Commits von TracerGUI nach master 2024-04-21 16:03:26 +02:00 zusammengeführt
45 geänderte Dateien mit 3356 neuen und 3385 gelöschten Zeilen

10
.gitignore vendored
Datei anzeigen

@ -1,5 +1,7 @@
# Package Files
# Build files
*.jar
**/bin
**/build
# Gradle
.gradle
@ -10,6 +12,10 @@ steamwar.properties
# IntelliJ IDEA
.idea
*.iml
plugin.yml
# Other
lib
lib
#linkage
LinkageUtils.java

Datei anzeigen

@ -1,39 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import net.minecraft.server.v1_15_R1.EntityTNTPrimed;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_15_R1.CraftWorld;
import org.bukkit.entity.TNTPrimed;
import java.util.stream.Stream;
public class TNTPrimedIterator15 implements TNTPrimedIterator {
private static final CraftWorld WORLD = (CraftWorld) Bukkit.getWorlds().get(0);
@Override
public Stream<TNTPrimed> iterator() {
return WORLD.getHandle().entitiesById.values().stream()
.filter(EntityTNTPrimed.class::isInstance)
.map(entity -> (TNTPrimed) entity.getBukkitEntity());
}
}

Datei anzeigen

@ -1,46 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import com.comphenix.tinyprotocol.Reflection;
import net.minecraft.server.level.WorldServer;
import net.minecraft.world.level.entity.LevelEntityGetter;
import org.bukkit.Bukkit;
import org.bukkit.entity.TNTPrimed;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
public class TNTPrimedIterator18 implements TNTPrimedIterator {
private static final Reflection.MethodInvoker getWorld = Reflection.getMethod(Reflection.getClass("{obc}.CraftWorld"), "getHandle");
private static final Reflection.MethodInvoker getWorldEntities = Reflection.getTypedMethod(WorldServer.class, null, LevelEntityGetter.class);
private static final Reflection.MethodInvoker getIterable = Reflection.getTypedMethod(LevelEntityGetter.class, null, Iterable.class);
private static final Reflection.MethodInvoker getBukkitEntity = Reflection.getTypedMethod(Reflection.getClass("{nms.world.entity}.Entity"), "getBukkitEntity", null);
@Override
public Stream<TNTPrimed> iterator() {
return StreamSupport.stream(((Iterable<?>) getIterable.invoke(getWorldEntities.invoke(getWorld.invoke(Bukkit.getWorlds().get(0))))).spliterator(), false)
.map(getBukkitEntity::invoke)
.filter(TNTPrimed.class::isInstance)
.map(TNTPrimed.class::cast);
}
}

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei-Diff unterdrückt, da er zu groß ist Diff laden

Datei anzeigen

@ -39,7 +39,10 @@ import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class Loader implements Listener {
@ -78,6 +81,10 @@ public class Loader implements Listener {
}
if (currentElement >= elements.size()) {
currentElement = 0;
if (stage == Stage.SINGLE) {
stage = Stage.PAUSE;
return;
}
}
while (currentElement < elements.size()) {
@ -92,6 +99,20 @@ public class Loader implements Listener {
}, 0, 1);
}
public void single() {
if (stage == Stage.END) return;
if (stage == Stage.RUNNING) return;
stage = Stage.SINGLE;
if (recorder != null) {
recorder.stop();
recorder = null;
}
if (elements.isEmpty()) {
BauSystem.MESSAGE.send("LOADER_NOTHING_RECORDED", p);
stop();
}
}
public void start() {
if (stage == Stage.END) return;
if (stage == Stage.RUNNING) return;
@ -349,6 +370,7 @@ public class Loader implements Listener {
public enum Stage implements EnumDisplay {
SETUP("LOADER_SETUP"),
RUNNING("LOADER_RUNNING"),
SINGLE("LOADER_SINGLE"),
PAUSE("LOADER_PAUSE"),
END("LOADER_END");

Datei anzeigen

@ -20,10 +20,8 @@
package de.steamwar.bausystem.features.loader;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeValidator;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -105,6 +103,14 @@ public class LoaderCommand extends SWCommand implements Listener {
loader.setTicksBetweenBlocks(delay);
}
@Register(value = "single", description = "LOADER_HELP_SINGLE")
public void singleLoader(@Validator Player p) {
Loader loader = Loader.getLoader(p);
if (loaderNullCheck(loader, p)) return;
loader.single();
BauSystem.MESSAGE.send("LOADER_SINGLE_CMD", p);
}
@EventHandler
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
event.getNewSpectator().forEach(player -> {

Datei anzeigen

@ -20,9 +20,6 @@
package de.steamwar.bausystem.features.script.lua.libs;
import de.steamwar.bausystem.features.loader.Loader;
import de.steamwar.bausystem.features.tracer.record.ActiveTracer;
import de.steamwar.bausystem.features.tracer.record.AutoTraceRecorder;
import de.steamwar.bausystem.features.tracer.record.Recorder;
import de.steamwar.bausystem.region.GlobalRegion;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.flags.Flag;
@ -71,13 +68,13 @@ public class RegionLib implements LuaLib {
table.set("freeze", getter(() -> region.get().getPlain(Flag.FREEZE, FreezeMode.class) == FreezeMode.ACTIVE));
table.set("protect", getter(() -> region.get().getPlain(Flag.PROTECT, ProtectMode.class) == ProtectMode.ACTIVE));
LuaValue traceLib = LuaValue.tableOf();
traceLib.set("active", getter(() -> !region.get().isGlobal() && Recorder.INSTANCE.get(region.get()) instanceof ActiveTracer));
traceLib.set("auto", getter(() -> !region.get().isGlobal() && Recorder.INSTANCE.get(region.get()) instanceof AutoTraceRecorder));
traceLib.set("status", getter(() -> Recorder.INSTANCE.get(region.get()).scriptState()));
traceLib.set("time", getter(() -> Recorder.INSTANCE.get(region.get()).scriptTime()));
//LuaValue traceLib = LuaValue.tableOf();
//traceLib.set("active", getter(() -> !region.get().isGlobal() && Recorder.INSTANCE.get(region.get()) instanceof ActiveTracer));
//traceLib.set("auto", getter(() -> !region.get().isGlobal() && Recorder.INSTANCE.get(region.get()) instanceof AutoTraceRecorder));
//traceLib.set("status", getter(() -> Recorder.INSTANCE.get(region.get()).scriptState()));
//traceLib.set("time", getter(() -> Recorder.INSTANCE.get(region.get()).scriptTime()));
table.set("trace", traceLib);
//table.set("trace", traceLib);
Loader loader = Loader.getLoader(player);
table.set("loader", getter(() -> loader == null ? "OFF" : loader.getStage().name()));

Datei anzeigen

@ -23,8 +23,7 @@ import de.steamwar.bausystem.features.simulator.data.Simulator;
import de.steamwar.bausystem.features.simulator.data.SimulatorElement;
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.features.tracer.record.Recorder;
import de.steamwar.bausystem.features.tracer.record.SingleTraceRecorder;
import de.steamwar.bausystem.features.tracer.TraceRecorder;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.TickEndEvent;
import de.steamwar.bausystem.utils.TickStartEvent;
@ -69,6 +68,20 @@ public class SimulatorExecutor implements Listener {
@Override
public void accept(World world) {
currentlyRunning.remove(simulator);
if (simulator.isAutoTrace()) {
simulator.getGroups()
.stream()
.map(SimulatorGroup::getElements)
.flatMap(List::stream)
.map(SimulatorElement::getPosition)
.map(pos -> pos.toLocation(WORLD))
.map(Region::getRegion)
.distinct()
.forEach(region -> {
TraceRecorder.instance.stopRecording(region);
});
}
}
});
@ -82,9 +95,7 @@ public class SimulatorExecutor implements Listener {
.map(Region::getRegion)
.distinct()
.forEach(region -> {
if (Recorder.INSTANCE.isDisabled(region)) {
Recorder.INSTANCE.set(region, new SingleTraceRecorder(region));
}
TraceRecorder.instance.startRecording(region);
});
}
return true;

Datei anzeigen

@ -20,9 +20,7 @@
package de.steamwar.bausystem.features.slaves.laufbau;
import com.sk89q.worldedit.EditSession;
import de.steamwar.bausystem.features.slaves.laufbau.states.FilteringTracesState;
import de.steamwar.bausystem.features.slaves.laufbau.states.LaufbauState;
import de.steamwar.bausystem.features.slaves.laufbau.states.CreatingLaufState;
import de.steamwar.bausystem.features.slaves.laufbau.states.ProcessingTracesState;
import de.steamwar.bausystem.utils.WorldEditUtils;
import lombok.Getter;
@ -40,9 +38,7 @@ public class Laufbau {
private Location pos1;
private Location pos2;
private FilteringTracesState filteringTracesState = null;
private ProcessingTracesState processingTracesState = null;
private CreatingLaufState creatingLaufState = null;
private LaufbauState active;
private List<BlockBoundingBox> elements;
@ -62,8 +58,6 @@ public class Laufbau {
int zFactor = (int) (Math.abs(selectionSize.getZ()) / 9.875);
factor = Math.max(Math.max(xFactor, Math.max(yFactor, zFactor)), 8);
filteringTracesState = new FilteringTracesState(world, this::inRegion);
editSession = WorldEditUtils.getEditSession(player);
elements = BlockBoundingBox.elements.stream().filter(blockBoundingBox -> {
@ -86,49 +80,32 @@ public class Laufbau {
return -Double.compare(o1.blockData.getMaterial().getBlastResistance(), o2.blockData.getMaterial().getBlastResistance());
});
}
}
private LaufbauState getActive() {
if (creatingLaufState != null) {
return creatingLaufState;
}
if (processingTracesState != null) {
return processingTracesState;
}
return filteringTracesState;
active = new ProcessingTracesState(world, this::inRegion, editSession, elements, factor);
}
private void createNextState() {
if (creatingLaufState != null) {
return;
}
if (processingTracesState != null) {
creatingLaufState = new CreatingLaufState(processingTracesState.getBlocks(), processingTracesState.getCuboidIntersectionCache(), world, editSession, elements, factor);
return;
}
processingTracesState = new ProcessingTracesState(filteringTracesState.getTntPositions(), this::inRegion, factor);
if (active == null) return;
active = active.getNextState();
}
public String actionBarMessage(Player p) {
return getActive().actionBarMessage(p);
return active.actionBarMessage(p);
}
public boolean hasNext() {
return getActive().hasNext();
if (active == null) return false;
return active.hasNext();
}
public void next() {
LaufbauState state = getActive();
LaufbauState state = active;
state.next();
if (!state.hasNext()) {
createNextState();
}
}
private boolean inRegion(Location location, int expansion) {
return inRegion(location.toVector(), expansion);
}
private boolean inRegion(Vector location, int expansion) {
if (location.getBlockX() >= pos1.getBlockX() - expansion) {
if (location.getBlockY() >= pos1.getBlockY() - expansion) {

Datei anzeigen

@ -196,4 +196,9 @@ public class CreatingLaufState implements LaufbauState {
}
}
}
@Override
public LaufbauState getNextState() {
return null;
}
}

Datei anzeigen

@ -1,91 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.slaves.laufbau.states;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.features.tracer.show.Record;
import de.steamwar.bausystem.features.tracer.show.StoredRecords;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiPredicate;
public class FilteringTracesState implements LaufbauState {
private long start = System.currentTimeMillis();
private World world;
private BiPredicate<Location, Integer> inRegionCheck;
private int totalRecord;
private List<Record> recordList;
private List<Record.TNTRecord> tntRecords = new ArrayList<>();
@Getter
private List<TNTPosition> tntPositions = new ArrayList<>();
public FilteringTracesState(World world, BiPredicate<Location, Integer> inRegionCheck) {
recordList = new ArrayList<>(StoredRecords.getRecords());
totalRecord = recordList.size();
this.world = world;
this.inRegionCheck = inRegionCheck;
}
@Override
public String actionBarMessage(Player p) {
return BauSystem.MESSAGE.parse("LAUFBAU_SIMPLE_PROGRESS", p, BauSystem.MESSAGE.parse("LAUFBAU_STATE_FILTERING_TRACES", p), totalRecord - recordList.size(), totalRecord, eta(p, start, totalRecord - recordList.size(), totalRecord));
}
@Override
public boolean hasNext() {
return !recordList.isEmpty() || !tntRecords.isEmpty();
}
@Override
public void next() {
if (tntRecords.isEmpty()) {
Record record = recordList.remove(0);
tntRecords.addAll(record.getTnt());
}
if (tntRecords.isEmpty()) {
return;
}
Record.TNTRecord tntRecord = tntRecords.remove(0);
tntRecord.getPositions().forEach(tntPosition -> {
if (FlatteningWrapper.impl.inWater(world, tntPosition.getLocation())) {
return;
}
if (inRegionCheck.test(tntPosition.getLocation().toLocation(world), 1)) {
tntPositions.add(tntPosition);
}
if (tntPosition.getPreviousLocation() != null && inRegionCheck.test(tntPosition.getPreviousLocation().toLocation(world), 1)) {
tntPositions.add(tntPosition);
}
});
}
}

Datei anzeigen

@ -45,4 +45,6 @@ public interface LaufbauState {
}
return LocalTime.ofNanoOfDay(eta).format(DateTimeFormatter.ofPattern(BauSystem.MESSAGE.parse("TIME", p)));
}
LaufbauState getNextState();
}

Datei anzeigen

@ -1,115 +1,128 @@
/*
* This file is a part of the SteamWar software.
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
* Copyright (C) 2024 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 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.
* 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/>.
* 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.slaves.laufbau.states;
import com.sk89q.worldedit.EditSession;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.slaves.laufbau.BlockBoundingBox;
import de.steamwar.bausystem.features.slaves.laufbau.Cuboid;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.bausystem.features.tracer.TraceManager;
import de.steamwar.bausystem.region.Point;
import lombok.Getter;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;
public class ProcessingTracesState implements LaufbauState {
private long start = System.currentTimeMillis();
private final long start = System.currentTimeMillis();
private final World world;
private final BiPredicate<Vector, Integer> inRegionCheck;
private final EditSession editSession;
private final List<BlockBoundingBox> elements;
private final int factor;
private int totalCuboids;
private List<TNTPosition> tntPositionList;
private BiPredicate<Vector, Integer> inRegionCheck;
private int factor;
private final List<TNTPoint> TNTPoints;
private final int totalTntRecords;
private List<Cuboid> toExpand = new ArrayList<>();
private final Set<Point> affectedBlocks = new HashSet<>();
private final Map<Point, Set<Cuboid>> cuboidsPerChunk = new HashMap<>();
private int cuboidsDone = 0;
@Getter
private Set<Point> blocks = new HashSet<>();
@Getter
private Map<Point, Set<Cuboid>> cuboidIntersectionCache = new HashMap<>();
public ProcessingTracesState(List<TNTPosition> tntPositionList, BiPredicate<Vector, Integer> inRegionCheck, int factor) {
this.tntPositionList = tntPositionList;
this.totalCuboids = tntPositionList.stream().mapToInt(tntPosition -> tntPosition.getPreviousLocation() == null ? 1 : 3).sum();
public ProcessingTracesState(World world, BiPredicate<Vector, Integer> inRegionCheck, EditSession editSession, List<BlockBoundingBox> elements, int factor) {
this.world = world;
this.inRegionCheck = inRegionCheck;
this.editSession = editSession;
this.elements = elements;
this.factor = factor;
// TODO: Optimize only retrieving traces inside of the affected regions!
TNTPoints = TraceManager.instance.getAll()
.stream()
.flatMap(trace -> trace.getHistories().stream())
.flatMap(Collection::stream)
.collect(Collectors.toList());
totalTntRecords = TNTPoints.size();
}
@Override
public String actionBarMessage(Player p) {
return BauSystem.MESSAGE.parse("LAUFBAU_SIMPLE_PROGRESS", p, BauSystem.MESSAGE.parse("LAUFBAU_STATE_PROCESSING_TRACES", p), cuboidsDone, totalCuboids, eta(p, start, cuboidsDone, totalCuboids));
return BauSystem.MESSAGE.parse("LAUFBAU_SIMPLE_PROGRESS", p, BauSystem.MESSAGE.parse("LAUFBAU_STATE_PROCESSING_TRACES", p), totalTntRecords - TNTPoints.size(), totalTntRecords, eta(p, start, totalTntRecords - TNTPoints.size(), totalTntRecords));
}
private boolean inRegion(Vector location, int expansion) {
return inRegionCheck.test(location, expansion);
}
@Override
public boolean hasNext() {
return !tntPositionList.isEmpty() || !toExpand.isEmpty();
return !TNTPoints.isEmpty();
}
@Override
public void next() {
if (!toExpand.isEmpty()) {
Cuboid cuboid = toExpand.remove(0);
expandCuboid(cuboid);
} else {
TNTPosition tntPosition = tntPositionList.remove(0);
createCuboid(tntPosition);
}
}
TNTPoint current = TNTPoints.remove(0);
if (FlatteningWrapper.impl.inWater(world, current.getLocation().toVector())) return;
if (!(inRegion(current.getLocation().toVector(), 1) || (current.getPrevious().isPresent() && inRegion(current.getPrevious().get().getLocation().toVector(), 1))))
return;
private void createCuboid(TNTPosition tntPosition) {
Vector location = tntPosition.getLocation();
Vector previousLocation = tntPosition.getPreviousLocation();
if (previousLocation == null) {
toExpand.add(new Cuboid(location.getX() - 0.49, location.getY(), location.getZ() - 0.49, 0.98, 0.98, 0.98));
} else {
Vector movement = location.clone().subtract(previousLocation);
toExpand.add(new Cuboid(previousLocation.getX() - 0.49, Math.min(previousLocation.getY(), location.getY()), previousLocation.getZ() - 0.49, 0.98, Math.abs(movement.getY()) + 0.98, 0.98));
if (Math.abs(tntPosition.getUpdateVelocity().getX()) >= Math.abs(tntPosition.getUpdateVelocity().getZ())) {
toExpand.add(new Cuboid(Math.min(previousLocation.getX(), location.getX()) - 0.49, location.getY(), previousLocation.getZ() - 0.49, Math.abs(movement.getX()) + 0.98, 0.98, 0.98));
toExpand.add(new Cuboid(location.getX() - 0.49, location.getY(), Math.min(previousLocation.getZ(), location.getZ()) - 0.49, 0.98, 0.98, Math.abs(movement.getZ()) + 0.98));
Location location = current.getLocation();
if (current.getPrevious().isPresent()) {
Vector velocity = current.getPrevious().get().getVelocity();
Location previousLocation = current.getPrevious().get().getLocation();
Location movement = location.clone().subtract(previousLocation);
calculateCuboid(new Cuboid(previousLocation.getX() - 0.49, Math.min(previousLocation.getY(), location.getY()), previousLocation.getZ() - 0.49, 0.98, Math.abs(movement.getY()) + 0.98, 0.98));
if (velocity.getX() >= velocity.getZ()) {
calculateCuboid(new Cuboid(Math.min(previousLocation.getX(), location.getX()) - 0.49, location.getY(), previousLocation.getZ() - 0.49, Math.abs(movement.getX()) + 0.98, 0.98, 0.98));
calculateCuboid(new Cuboid(location.getX() - 0.49, location.getY(), Math.min(previousLocation.getZ(), location.getZ()) - 0.49, 0.98, 0.98, Math.abs(movement.getZ()) + 0.98));
} else {
toExpand.add(new Cuboid(previousLocation.getX() - 0.49, location.getY(), Math.min(previousLocation.getZ(), location.getZ()) - 0.49, 0.98, 0.98, Math.abs(movement.getZ()) + 0.98));
toExpand.add(new Cuboid(Math.min(previousLocation.getX(), location.getX()) - 0.49, location.getY(), location.getZ() - 0.49, Math.abs(movement.getX()) + 0.98, 0.98, 0.98));
calculateCuboid(new Cuboid(previousLocation.getX() - 0.49, location.getY(), Math.min(previousLocation.getZ(), location.getZ()) - 0.49, 0.98, 0.98, Math.abs(movement.getZ()) + 0.98));
calculateCuboid(new Cuboid(Math.min(previousLocation.getX(), location.getX()) - 0.49, location.getY(), location.getZ() - 0.49, Math.abs(movement.getX()) + 0.98, 0.98, 0.98));
}
} else {
calculateCuboid(new Cuboid(location.getX() - 0.49, location.getY(), location.getZ() - 0.49, 0.98, 0.98, 0.98));
}
}
private void expandCuboid(Cuboid cuboid) {
cuboidsDone++;
private void calculateCuboid(Cuboid cuboid) {
for (double x = cuboid.getX() - 2; x < cuboid.getX() + cuboid.getDx() + 2; x++) {
for (double y = cuboid.getY() - 2; y < cuboid.getY() + cuboid.getDy() + 2; y++) {
for (double z = cuboid.getZ() - 2; z < cuboid.getZ() + cuboid.getDz() + 2; z++) {
Vector location = new Vector(x, y, z);
if (inRegionCheck.test(location, 0)) {
Point point = new Point(location.getBlockX(), location.getBlockY(), location.getBlockZ());
blocks.add(point);
cuboidIntersectionCache.computeIfAbsent(point.divide(factor), __ -> new HashSet<>())
if (inRegion(location, 0)) {
affectedBlocks.add(new Point(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
cuboidsPerChunk.computeIfAbsent(new Point(location.getBlockX() / factor, location.getBlockY() / factor, location.getBlockZ() / factor), __ -> new HashSet<>())
.add(cuboid);
}
}
}
}
}
@Override
public LaufbauState getNextState() {
return new CreatingLaufState(affectedBlocks, cuboidsPerChunk, world, editSession, elements, factor);
}
}

Datei anzeigen

@ -0,0 +1,227 @@
/*
* 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.tracer;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.region.utils.RegionExtensionType;
import de.steamwar.bausystem.region.utils.RegionType;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.util.Vector;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.List;
import java.util.Optional;
/**
* Recording of a tnt at a specific tick
*/
@Getter
public class TNTPoint implements Externalizable {
/**
* Unique number to identify records being of the same tnt
*/
private int tntId;
/**
* Whether this is a record of a tnt explosion or an entity
*/
private boolean explosion;
/**
* Whether this is a record of a tnt that was in water
*/
private boolean inWater;
/**
* Whether this record was taken after the first tnt exploded
*/
private boolean afterFirstExplosion;
/**
* Whether this record has destroyed blocks in build area
*/
private boolean destroyedBuildArea;
/**
* Whether this record has destroyed blocks in testblock area
*/
private boolean destroyedTestBlock;
/**
* Tick offset, from this record being taken to the start of the trace
*/
private long ticksSinceStart;
/**
* Fuse ticks of the recorded tnt (0 if this is an explosion)
*/
private int fuse;
/**
* Location of the recorded tnt
*/
private Location location;
/**
* Velocity of the recorded tnt
*/
private Vector velocity;
/**
* List of all tnt records, that are represent the same tnt
*/
private List<TNTPoint> history;
/**
* Constructor for deserialization only !! Do not Call !!
*/
public TNTPoint() {
}
public TNTPoint(int tntId, TNTPrimed tnt, boolean explosion, boolean afterFirstExplosion, long ticksSinceStart,
List<TNTPoint> history, List<Block> destroyedBlocks) {
this.tntId = tntId;
this.explosion = explosion;
this.inWater = tnt.isInWater();
this.afterFirstExplosion = afterFirstExplosion;
this.ticksSinceStart = ticksSinceStart;
fuse = tnt.getFuseTicks();
location = tnt.getLocation();
velocity = tnt.getVelocity();
this.history = history;
boolean buildDestroy = false;
boolean testblockDestroy = false;
for (Block destroyedBlock : destroyedBlocks) {
if (Region.getRegion(destroyedBlock.getLocation()).inRegion(destroyedBlock.getLocation(), RegionType.BUILD,
RegionExtensionType.EXTENSION)) {
buildDestroy = true;
}
if (Region.getRegion(destroyedBlock.getLocation()).inRegion(destroyedBlock.getLocation(),
RegionType.TESTBLOCK, RegionExtensionType.EXTENSION)) {
testblockDestroy = true;
}
}
destroyedBuildArea = buildDestroy;
destroyedTestBlock = testblockDestroy;
}
/**
* Method for getting the next record of the tnt represented by this record
*
* @return the next record
*/
public Optional<TNTPoint> getNext() {
int index = history.indexOf(this);
return index == history.size() - 1 ? Optional.empty() : Optional.of(history.get(index + 1));
}
/**
* Method for getting the previous record of the tnt represented by this record
*
* @return the previous record
*/
public Optional<TNTPoint> getPrevious() {
int index = history.indexOf(this);
return index == 0 ? Optional.empty() : Optional.of(history.get(index - 1));
}
/**
* Internal methode for setting the history of this record
* during deserialization.
*
* @param history
*/
void setHistory(List<TNTPoint> history) {
this.history = history;
}
@Override
public void writeExternal(ObjectOutput objectOutput) throws IOException {
objectOutput.writeInt(tntId);
objectOutput.writeBoolean(explosion);
objectOutput.writeBoolean(inWater);
objectOutput.writeBoolean(afterFirstExplosion);
objectOutput.writeBoolean(destroyedBuildArea);
objectOutput.writeBoolean(destroyedTestBlock);
objectOutput.writeLong(ticksSinceStart);
objectOutput.writeInt(fuse);
objectOutput.writeDouble(location.getX());
objectOutput.writeDouble(location.getY());
objectOutput.writeDouble(location.getZ());
objectOutput.writeDouble(velocity.getX());
objectOutput.writeDouble(velocity.getY());
objectOutput.writeDouble(velocity.getZ());
}
@Override
public void readExternal(ObjectInput objectInput) throws IOException {
tntId = objectInput.readInt();
explosion = objectInput.readBoolean();
inWater = objectInput.readBoolean();
afterFirstExplosion = objectInput.readBoolean();
destroyedBuildArea = objectInput.readBoolean();
destroyedTestBlock = objectInput.readBoolean();
ticksSinceStart = objectInput.readLong();
fuse = objectInput.readInt();
double locX = objectInput.readDouble();
double locY = objectInput.readDouble();
double locZ = objectInput.readDouble();
location = new Location(Bukkit.getWorlds().get(0), locX, locY, locZ);
double velX = objectInput.readDouble();
double velY = objectInput.readDouble();
double velZ = objectInput.readDouble();
velocity = new Vector(velX, velY, velZ);
}
@Override
public String toString() {
return "TNTPoint{" +
"tntId=" + tntId +
", explosion=" + explosion +
", inWater=" + inWater +
", afterFirstExplosion=" + afterFirstExplosion +
", destroyedBuildArea=" + destroyedBuildArea +
", destroyedTestBlock=" + destroyedTestBlock +
", ticksSinceStart=" + ticksSinceStart +
", fuse=" + fuse +
", location=" + location +
", velocity=" + velocity +
'}';
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TNTPoint)) return false;
TNTPoint record = (TNTPoint) obj;
if (record.isExplosion() != explosion) return false;
if (!record.getLocation().equals(location)) return false;
if (!record.getVelocity().equals(velocity)) return false;
return true;
}
}

Datei anzeigen

@ -1,62 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer;
import de.steamwar.bausystem.features.tracer.show.Record;
import de.steamwar.bausystem.shared.Position;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.util.Vector;
@Getter
public class TNTPosition extends Position {
private final Record.TNTRecord record;
private final int fuseTicks;
private final long timeTicks;
private final Vector previousLocation;
private final Vector velocity;
private final Vector updateVelocity;
private final boolean source;
private final boolean exploded;
@Setter
private boolean microMotion;
public TNTPosition(Record.TNTRecord record, TNTPrimed entity, long timeTicks, Vector previousLocation, Vector velocity, Vector updateVelocity, boolean source, boolean exploded) {
super(entity.getLocation().toVector());
this.record = record;
this.fuseTicks = entity.getFuseTicks();
this.timeTicks = timeTicks;
this.previousLocation = previousLocation;
this.velocity = velocity;
this.updateVelocity = updateVelocity;
this.source = source;
this.exploded = exploded;
}
@Override
public String toString() {
return "Position{" +
"location=" + super.getLocation() +
'}';
}
}

Datei anzeigen

@ -0,0 +1,339 @@
/*
* 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.tracer;
import de.steamwar.bausystem.features.tracer.rendering.BundleFilter;
import de.steamwar.bausystem.features.tracer.rendering.PlayerTraceShowData;
import de.steamwar.bausystem.features.tracer.rendering.TraceEntity;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import de.steamwar.bausystem.region.Region;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import lombok.Cleanup;
import lombok.Getter;
import lombok.SneakyThrows;
import org.bukkit.entity.Player;
import java.io.*;
import java.lang.ref.SoftReference;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
public class Trace {
/**
* UUID of the trace used for
*/
@Getter
private final UUID uuid;
/**
* File the records are saved in
*/
@Getter
private final File recordsSaveFile;
/**
* File the metadata are saved in
*/
@Getter
private final File metadataSaveFile;
/**
* Region the trace was recorded in
*/
@Getter
private final Region region;
/**
* Date the trace was recorded at
*/
@Getter
private final Date date;
/**
* Records of TNTs, making up the trace
*/
private SoftReference<List<TNTPoint>> records;
/**
* A map of all REntityServers rendering this trace
*/
private final Map<Player, REntityServer> entityServerMap = new HashMap<>();
/**
* Constructor for the creation of a new trace
*
* @param region the region the trace is created at
* @param recordList the list for the records making up this trace
*/
@SneakyThrows
public Trace(Region region, List<TNTPoint> recordList) {
this.uuid = UUID.randomUUID();
recordsSaveFile = new File(TraceManager.tracesFolder, uuid + ".records");
this.region = region;
this.date = new Date();
records = new SoftReference<>(recordList);
metadataSaveFile = new File(TraceManager.tracesFolder, uuid + ".meta");
@Cleanup
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(metadataSaveFile));
outputStream.writeUTF(uuid.toString());
outputStream.writeUTF(region.getName());
outputStream.writeObject(date);
}
/**
* Constructor for serialising a trace from the file system
*
* @param metadataSaveFile the file for this traces metadata
*/
@SneakyThrows
public Trace(File metadataSaveFile) {
String uuid = null;
Region region = null;
Date date = null;
this.metadataSaveFile = metadataSaveFile;
try {
@Cleanup
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(metadataSaveFile));
uuid = inputStream.readUTF();
region = Region.getREGION_MAP().get(inputStream.readUTF());
date = (Date) inputStream.readObject();
inputStream.close();
} finally {
this.uuid = UUID.fromString(uuid);
this.region = region;
this.date = date;
recordsSaveFile = new File(TraceManager.tracesFolder, uuid + ".records");
this.records = new SoftReference<>(null);
}
}
/**
* Gets the histories of all tnts in this trace
*
* @return the histories of this trace
*/
public Set<List<TNTPoint>> getHistories() {
Set<List<TNTPoint>> histories = new HashSet<>();
for (TNTPoint record : getRecords()) {
histories.add(record.getHistory());
}
return histories;
}
/**
* List of all used ids
*/
public List<String> getUsedIds() {
return getRecords()
.stream()
.map(TNTPoint::getTntId)
.map(i -> i + "")
.collect(Collectors.toList());
}
/**
* Renders this traces
*
* @param player The player the trace is rendered to
* @param playerTraceShowData The showData for modifying the rendering
*/
public void render(Player player, PlayerTraceShowData playerTraceShowData) {
REntityServer entityServer = entityServerMap.get(player);
if (entityServer != null) {
entityServer.getEntities().forEach(REntity::die);
} else {
entityServer = new REntityServer();
entityServer.addPlayer(player);
entityServer.setCallback((p, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) return;
if (rEntity instanceof TraceEntity) {
((TraceEntity) rEntity).printIntoChat(p);
}
});
entityServerMap.put(player, entityServer);
}
render(getRecords(), entityServer, playerTraceShowData);
}
/**
* Renders specific records to this trace rendering
*
* @param records The records to be rendered
* @param player The player the records are rendered to
* @param playerTraceShowData The showData for modifying the rendering
*/
protected void render(List<TNTPoint> records, Player player, PlayerTraceShowData playerTraceShowData) {
render(records, entityServerMap.get(player), playerTraceShowData);
}
/**
* Internal methode to render records to a REntityServer
*
* @param records Records to render
* @param entityServer The show server the trace will be renderd to
* @param playerTraceShowData The showData for modifying the rendering
*/
private void render(List<TNTPoint> records, REntityServer entityServer, PlayerTraceShowData playerTraceShowData) {
if (records.isEmpty()) {
return;
}
Set<ViewFlag> flagList = playerTraceShowData.getEffectiveViewFlags();
// Apply filters
Stream<TNTPoint> workingRecordsStream = records.stream();
for (ViewFlag flag : flagList) {
workingRecordsStream = flag.filter(workingRecordsStream);
}
List<TNTPoint> workingRecords = workingRecordsStream.collect(Collectors.toList());
// Bundle records at unique positions
List<List<TNTPoint>> bundles = bundleRecords(workingRecords, playerTraceShowData.getBundleFilter());
// Render bundled records
List<TraceEntity> entities = new LinkedList<>();
for (List<TNTPoint> bundle : bundles) {
entities.add(new TraceEntity(entityServer, bundle.get(0).getLocation(), bundle.get(0).isExplosion(), bundle, this));
}
// Apply modifiers
for (ViewFlag flag : flagList) {
flag.modify(entityServer, entities);
}
}
/**
* Bundles the passed TNTRecords based on whether they are at the same location
*
* @param records The TNTRecords that are supposed to be bundled
* @param filter A filter specefieng whether records can be bundled
* @return A list of bundles
*/
private List<List<TNTPoint>> bundleRecords(List<TNTPoint> records, BundleFilter filter) {
if (filter == BundleFilter.NONE) {
return records.stream().map(List::of).collect(Collectors.toList());
}
List<List<TNTPoint>> bundles = new ArrayList<>();
recordsLoop:
for (TNTPoint record : records) {
for (int i = bundles.size() - 1; i >= 0; i--) {
List<TNTPoint> bundle = bundles.get(i);
Boolean filterResult = filter.function.apply(record, bundle.get(0));
if (filterResult == null) {
ArrayList<TNTPoint> newBundle = new ArrayList<>();
newBundle.add(record);
bundles.add(newBundle);
continue recordsLoop;
} else if (filterResult) {
bundle.add(record);
continue recordsLoop;
}
}
ArrayList<TNTPoint> newBundle = new ArrayList<>();
newBundle.add(record);
bundles.add(newBundle);
}
return bundles;
}
/**
* Hides this trail for the given player
*
* @param player
*/
public void hide(Player player) {
REntityServer entityServer = entityServerMap.remove(player);
if (entityServer == null) {
return;
}
entityServer.removePlayer(player);
if (entityServer.getPlayers().isEmpty()) {
entityServer.close();
}
}
/**
* Hides this trace for all players
*/
public void hide() {
entityServerMap.forEach((player, entityServer) -> {
entityServer.close();
});
entityServerMap.clear();
}
/**
* Loads the records of this trace from storage to memory
*/
@SneakyThrows
private void loadRecords() {
List<TNTPoint> records = new ArrayList<>();
FileInputStream fileInputStream = new FileInputStream(recordsSaveFile);
@Cleanup
ObjectInputStream inputStream = new ObjectInputStream(new GZIPInputStream(fileInputStream));
while (fileInputStream.getChannel().position() < recordsSaveFile.length()) {
records.add((TNTPoint) inputStream.readObject());
}
Map<Integer, List<TNTPoint>> histories = new HashMap<>();
for (TNTPoint record : records) {
int tntId = record.getTntId();
List<TNTPoint> history = histories.computeIfAbsent(tntId, id -> new ArrayList<>());
history.add(record);
record.setHistory(history);
}
this.records = new SoftReference<>(records);
}
public List<TNTPoint> getRecords() {
if (records.get() == null) {
loadRecords();
}
return records.get();
}
@Override
public String toString() {
return "Trace{" +
"uuid=" + uuid +
", region=" + region +
", creationTime=" + date +
", recordsSaveFile=" + recordsSaveFile.getName() +
", records=" + getRecords() +
'}';
}
}

Datei anzeigen

@ -1,44 +1,43 @@
/*
* This file is a part of the SteamWar software.
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
* 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 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.
* 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/>.
* 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.tracer;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.tracer.record.*;
import de.steamwar.bausystem.features.tracer.show.*;
import de.steamwar.bausystem.features.tracer.rendering.BundleFilter;
import de.steamwar.bausystem.features.tracer.rendering.PlayerTraceShowData;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import de.steamwar.bausystem.features.tracer.rendering.dynamicflags.AtFlag;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.shared.ShowMode;
import de.steamwar.command.PreviousArguments;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeMapper;
import de.steamwar.command.TypeValidator;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import lombok.AllArgsConstructor;
import net.md_5.bungee.api.chat.ClickEvent;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Linked
public class TraceCommand extends SWCommand {
@ -47,199 +46,226 @@ public class TraceCommand extends SWCommand {
super("trace", "trail");
}
@LinkedInstance
public Recorder recorder;
@Register(value = {"start"}, description = "TRACE_COMMAND_HELP_START")
public void startCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
recorder.set(region, new SimpleTraceRecorder());
BauSystem.MESSAGE.send("TRACE_MESSAGE_START", p);
@Register(value = "start", description = "TRACE_COMMAND_HELP_START")
public void start(@Validator Player player) {
Region region = Region.getRegion(player.getLocation());
TraceRecorder.instance.startRecording(region);
BauSystem.MESSAGE.send("TRACE_MESSAGE_START", player);
}
@Register(value = {"single"}, description = "TRACE_COMMAND_HELP_SINGLE")
public void singleCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
recorder.set(region, new SingleTraceRecorder(region));
BauSystem.MESSAGE.send("TRACE_MESSAGE_SINGLE", p);
@Register(value = "stop", description = "TRACE_COMMAND_HELP_STOP")
public void stop(@Validator Player player) {
Region region = Region.getRegion(player.getLocation());
TraceRecorder.instance.stopRecording(region);
BauSystem.MESSAGE.send("TRACE_MESSAGE_STOP", player);
}
@Register(value = {"stop"}, description = "TRACE_COMMAND_HELP_STOP")
public void stopCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
recorder.remove(region);
BauSystem.MESSAGE.send("TRACE_MESSAGE_STOP", p);
}
@Register(value = {"auto"}, description = "TRACE_COMMAND_HELP_AUTO")
@Register({"toggleauto"})
public void autoCommand(@Validator Player p, @OptionalValue("-explode") @StaticValue({"-explode", "-ignite"}) String type) {
Region region = Region.getRegion(p.getLocation());
switch (type) {
case "-explode":
recorder.set(region, new AutoExplodeTraceRecorder());
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_IDLE_EXPLODE", p);
break;
case "-ignite":
recorder.set(region, new AutoIgniteTraceRecorder());
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_IDLE_IGNITE", p);
break;
default:
break;
@Register(value = "auto", description = "TRACE_COMMAND_HELP_AUTO")
public void auto(@Validator Player player) {
Region region = Region.getRegion(player.getLocation());
if (TraceRecorder.instance.toggleAutoTrace(region)) {
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_START", player);
} else {
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_STOP", player);
}
}
@Register(value = {"autoremove"}, description = "TRACE_COMMAND_HELP_AUTO_REMOVE")
@Register(value = {"autodelete"})
public void setAutoDeleteMode(@Validator Player p, @StaticValue({"-always", "-never", "-no_build_destroy", "-build_destroy", "-no_testblock_destroy", "-testblock_destroy"}) String destroyMode) {
Region region = Region.getRegion(p.getLocation());
TraceRecorder recorder = this.recorder.get(region);
if (!(recorder instanceof AutoTraceRecorder) || recorder instanceof SingleTraceRecorder) {
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_DELETE_INVALID", p);
return;
}
AutoTraceRecorder autoTraceRecorder = (AutoTraceRecorder) recorder;
switch (destroyMode) {
case "-always":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.ALWAYS);
break;
case "-never":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.NEVER);
break;
case "-no_build_destroy":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.NO_BUILD_DESTROY);
break;
case "-build_destroy":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.BUILD_DESTROY);
break;
case "-no_testblock_destroy":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.NO_TESTBLOCK_DESTROY);
break;
case "-testblock_destroy":
autoTraceRecorder.setTraceRecordAutoDeletion(TraceRecordAutoDeletion.TESTBLOCK_DESTROY);
break;
default:
return;
}
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_DELETE_" + autoTraceRecorder.getTraceRecordAutoDeletion().name(), p);
@Register(value = "show", description = "TRACE_COMMAND_HELP_SHOW")
public void show(@Validator Player player, @OptionalValue("DEFAULT") BundleFilter bundleFilter, ViewFlag... flags) {
showInternal(player, bundleFilter, flags);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW", player);
}
@Register(value = {"show"}, description = "TRACE_COMMAND_HELP_SHOW_AT")
public void showAtCommand(@Validator Player p, @OptionalValue("time") @StaticValue({"time", "fuse"}) String type, @StaticValue("at") String __, @Min(intValue = 0) int at) {
internalSetShowFilter(p, "TRACE_MESSAGE_SHOW_AT", type, at, at);
@Register(value = {"show", "at"}, description = "TRACE_COMMAND_HELP_SHOW_AT_WITH")
public void showAt(@Validator Player player, @Min(intValue = 0) int time, @StaticValue("with") String with, @OptionalValue("DEFAULT") BundleFilter bundleFilter, ViewFlag... flags) {
showInternal(player, time, time, bundleFilter, flags);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_AT", player, time);
}
@Register(value = {"show"}, description = "TRACE_COMMAND_HELP_SHOW_FROM")
public void showFromCommand(@Validator Player p, @OptionalValue("time") @StaticValue({"time", "fuse"}) String type, @StaticValue("from") String __, @Min(intValue = 0) int from) {
if (from == 0) {
TraceShowManager.setShowFilter(p, null);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW", p);
return;
}
internalSetShowFilter(p, "TRACE_MESSAGE_SHOW_FROM", type, from, Integer.MAX_VALUE);
@Register(value = {"show", "from"}, description = "TRACE_COMMAND_HELP_SHOW_FROM_WITH")
public void showFromTo(@Validator Player player, @Min(intValue = 0) int from, @StaticValue("with") String with, @OptionalValue("DEFAULT") BundleFilter bundleFilter, ViewFlag... flags) {
showInternal(player, from, Integer.MAX_VALUE, bundleFilter, flags);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_FROM", player, from);
}
@Register(value = {"show"}, description = "TRACE_COMMAND_HELP_SHOW_FROM_TO")
public void showFromToCommand(@Validator Player p, @OptionalValue("time") @StaticValue({"time", "fuse"}) String type, @StaticValue("from") String __, @Min(intValue = 0) int from, @StaticValue("to") String ___, @Min(intValue = 0) int to) {
internalSetShowFilter(p, "TRACE_MESSAGE_SHOW_FROM_TO", type, from, to);
}
private void internalSetShowFilter(Player p, String message, String type, int from, int to) {
@Register(value = {"show", "from"}, description = "TRACE_COMMAND_HELP_SHOW_FROM_TO_WITH")
public void showFromTo(@Validator Player player, @Min(intValue = 0) int from, @StaticValue("to") String toString, int to, @StaticValue("with") String with, @OptionalValue("DEFAULT") BundleFilter bundleFilter, ViewFlag... flags) {
if (to < from) {
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_TO_SMALLER", p);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_TO_SMALLER", player);
return;
}
TraceShowManager.setShowFilter(p, tntPosition -> {
switch (type) {
case "time":
return tntPosition.getTimeTicks() >= from && tntPosition.getTimeTicks() <= to;
case "fuse":
return tntPosition.getFuseTicks() >= from && tntPosition.getFuseTicks() <= to;
default:
return true;
}
});
BauSystem.MESSAGE.send(message, p, type, from, to);
showInternal(player, from, to, bundleFilter, flags);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_FROM_TO", player, from, to);
}
// /trace show at 0
// /trace show raw -auto at 0
@Register(value = {"show"}, description = "TRACE_COMMAND_HELP_SHOW")
public void showCommand(@Validator Player p, @OptionalValue("entity") ShowModeType showModeType, ShowModeParameterType... showModeParameterTypes) {
Region region = Region.getRegion(p.getLocation());
ShowModeParameter showModeParameter = new ShowModeParameter();
if (region.getWaterLevel() != 0) { // Enable Water by default for regions with WaterLevel e.g. WarShip
showModeParameter.enableWater();
@Register(value = {"show", "at"}, description = "TRACE_COMMAND_HELP_SHOW_AT")
public void showAt(@Validator Player player, @Min(intValue = 0) int time) {
TraceManager.instance.renderAt(player, time, time);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_AT", player, time);
}
@Register(value = {"show", "from"}, description = "TRACE_COMMAND_HELP_SHOW_FROM")
public void showFrom(@Validator Player player, @Min(intValue = 0) int from) {
TraceManager.instance.renderAt(player, from, Integer.MAX_VALUE);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_FROM", player, from);
}
@Register(value = {"show", "from"}, description = "TRACE_COMMAND_HELP_SHOW_FROM_TO")
public void showFromTo(@Validator Player player, @Min(intValue = 0) int from, @StaticValue("to") String toString, int to) {
TraceManager.instance.renderAt(player, from, to);
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_FROM_TO", player, from, to);
}
private void showInternal(Player player, BundleFilter bundleFilter, ViewFlag... flags) {
PlayerTraceShowData playerTraceShowData = new PlayerTraceShowData(bundleFilter, flags);
TraceManager.instance.show(player, playerTraceShowData);
}
private void showInternal(Player player, int from, int to, BundleFilter bundleFilter, ViewFlag... flags) {
PlayerTraceShowData playerTraceShowData = new PlayerTraceShowData(bundleFilter, flags);
playerTraceShowData.addViewFlag(new AtFlag(from, to));
TraceManager.instance.show(player, playerTraceShowData);
}
@Register(value = "hide", description = "TRACE_COMMAND_HELP_HIDE")
public void hide(@Validator Player player) {
TraceManager.instance.hide(player);
BauSystem.MESSAGE.send("TRACE_MESSAGE_HIDE", player);
}
@Register(value = "clear")
public void clear(@Validator Player player) {
TraceManager.instance.clear(Region.getRegion(player.getLocation()));
BauSystem.MESSAGE.send("TRACE_MESSAGE_CLEAR", player);
}
@Register(value = "delete", description = "TRACE_COMMAND_HELP_DELETE")
public void delete(@Validator Player player, Trace trace) {
TraceManager.instance.remove(trace);
BauSystem.MESSAGE.send("TRACE_MESSAGE_DELETE", player);
}
@Register(value = "isolate", description = "TRACE_COMMAND_HELP_ISOLATE")
public void isolate(@Validator Player player, Trace trace, @ErrorMessage("TRACE_RECORD_ID_INVALID") TNTPoint... records) {
TraceManager.instance.isolate(player, records);
BauSystem.MESSAGE.send("TRACE_MESSAGE_ISOLATE", player);
}
@Register(value = "share", description = "TRACE_COMMAND_HELP_SHARE")
public void share(@Validator Player player) {
BauSystem.MESSAGE.broadcast("TRACE_MESSAGE_SHARE", "TRACE_MESSAGE_SHARE_HOVER", new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/trace follow " + player.getName()), player.getName());
}
@Register(value = "follow", description = "TRACE_COMMAND_HELP_FOLLOW")
public void follow(@Validator Player player, Player toFollow) {
if (player == toFollow) {
BauSystem.MESSAGE.send("TRACE_MESSAGE_FOLLOW_SELF", player);
return;
}
for (ShowModeParameterType showModeParameterType : showModeParameterTypes) {
if (showModeParameterType == ShowModeParameterType.WATER && region.getWaterLevel() != 0) {
showModeParameter.disableWater();
} else {
showModeParameterType.getShowModeParameterConsumer().accept(showModeParameter);
}
}
TraceShowManager.show(p, showModeType.showModeBiFunction.apply(p, showModeParameter));
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW", p);
TraceManager.instance.follow(player, toFollow);
BauSystem.MESSAGE.send("TRACE_MESSAGE_FOLLOW", player, toFollow.getName());
}
@Register(value = {"hide"}, description = "TRACE_COMMAND_HELP_HIDE")
public void hideCommand(@Validator Player p) {
TraceShowManager.hide(p);
BauSystem.MESSAGE.send("TRACE_MESSAGE_HIDE", p);
@Register(value = "unfollow", description = "TRACE_COMMAND_HELP_UNFOLLOW")
public void unfollow(@Validator Player player) {
TraceManager.instance.unfollow(player);
BauSystem.MESSAGE.send("TRACE_MESSAGE_UNFOLLOW", player);
}
@Register(value = {"delete"}, description = "TRACE_COMMAND_HELP_DELETE")
@Register({"clear"})
public void deleteCommand(@Validator Player p) {
Region region = Region.getRegion(p.getLocation());
StoredRecords.clear(region);
BauSystem.MESSAGE.send("TRACE_MESSAGE_DELETE", p);
}
@AllArgsConstructor
private enum ShowModeType {
ENTITY((player, showModeParameter) -> new EntityShowMode(player, showModeParameter, 16)),
RAW((player, showModeParameter) -> new EntityShowMode(player, showModeParameter, -1));
private BiFunction<Player, ShowModeParameter, ShowMode<TNTPosition>> showModeBiFunction;
}
@ClassMapper(value = ShowModeParameterType.class, local = true)
public TypeMapper<ShowModeParameterType> showModeParameterTypesTypeMapper() {
Map<ShowModeParameterType, List<String>> showModeParameterTypeListMap = new EnumMap<>(ShowModeParameterType.class);
for (ShowModeParameterType value : ShowModeParameterType.values()) {
showModeParameterTypeListMap.put(value, value.getTabCompletes());
}
Map<String, ShowModeParameterType> showModeParameterTypesMap = new HashMap<>();
showModeParameterTypeListMap.forEach((k, v) -> v.forEach(s -> showModeParameterTypesMap.put(s, k)));
return new TypeMapper<ShowModeParameterType>() {
@ClassMapper(value = Trace.class, local = true)
public TypeMapper<Trace> traceClassMapper() {
return new TypeMapper<>() {
@Override
public ShowModeParameterType map(CommandSender commandSender, PreviousArguments previousArguments, String s) {
return showModeParameterTypesMap.get(s);
public Trace map(CommandSender commandSender, String[] previousArguments, String s) {
return TraceManager.instance.get(Integer.parseInt(s)).orElse(null);
}
@Override
public List<String> tabCompletes(CommandSender commandSender, PreviousArguments previousArguments, String s) {
Set<ShowModeParameterType> showModeParameterTypeSet = new HashSet<>();
Arrays.stream(previousArguments.userArgs).map(showModeParameterTypesMap::get).forEach(showModeParameterTypeSet::add);
showModeParameterTypeSet.remove(null);
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
return TraceManager.instance.getAllIds().stream()
.map(Object::toString)
.collect(Collectors.toList());
}
};
}
Set<ShowModeParameterType> removed = showModeParameterTypeSet.stream()
.map(ShowModeParameterType::getRemoved)
.map(Supplier::get)
.flatMap(Arrays::stream)
.collect(Collectors.toSet());
@ClassMapper(value = TNTPoint.class, local = true)
public TypeMapper<TNTPoint> recordMapper() {
return new TypeMapper<>() {
@Override
public TNTPoint map(CommandSender commandSender, PreviousArguments previousArguments, String s) {
Trace trace = previousArguments.getFirst(Trace.class).orElse(null);
if (trace == null) return null;
List<String> tabCompletes = new ArrayList<>();
for (Map.Entry<ShowModeParameterType, List<String>> entry : showModeParameterTypeListMap.entrySet()) {
if (removed.contains(entry.getKey()) || showModeParameterTypeSet.contains(entry.getKey())) {
continue;
int id = Integer.parseInt(s);
return trace.getRecords().stream()
.filter(record -> record.getTntId() == id)
.findFirst()
.orElse(null);
}
// TODO change when new command framework update
@Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
Trace trace = previousArguments.getFirst(Trace.class).orElse(null);
if (trace == null) return null;
return trace.getUsedIds();
}
};
}
@ClassMapper(value = BundleFilter.class, local = true)
public TypeMapper<BundleFilter> bundleFilterClassMapper() {
return new TypeMapper<>() {
@Override
public BundleFilter map(CommandSender commandSender, String[] previousArguments, String s) {
for (BundleFilter filter : BundleFilter.values()) {
if (s.equals(filter.toString())) {
return filter;
}
tabCompletes.addAll(entry.getValue());
}
return tabCompletes;
return null;
}
@Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
if (s.length() == 0) {
return new ArrayList<>();
}
return Arrays.stream(BundleFilter.values())
.map(Enum::toString)
.collect(Collectors.toList());
}
};
}
@ClassMapper(value = ViewFlag.class, local = true)
public TypeMapper<ViewFlag> viewFlagClassMapper() {
return new TypeMapper<ViewFlag>() {
@Override
public ViewFlag map(CommandSender commandSender, String[] previousArguments, String s) {
for (ViewFlag flag : ViewFlag.flags) {
if (s.equals("--" + flag.name)) {
return flag;
}
for (String alias : flag.aliases) {
if (s.equals("-" + alias)) {
return flag;
}
}
}
return null;
}
@Override
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
return ViewFlag.flags.stream()
.flatMap(viewFlag -> Stream.concat(Stream.of("--" + viewFlag.name),
Arrays.stream(viewFlag.aliases).map(name -> "-" + name)))
.collect(Collectors.toList());
}
};
}

Datei anzeigen

@ -0,0 +1,388 @@
/*
* 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.tracer;
import de.steamwar.bausystem.features.tracer.rendering.BundleFilter;
import de.steamwar.bausystem.features.tracer.rendering.PlayerTraceShowData;
import de.steamwar.bausystem.features.tracer.rendering.dynamicflags.AtFlag;
import de.steamwar.bausystem.features.tracer.rendering.dynamicflags.IsolateFlag;
import de.steamwar.bausystem.region.Region;
import de.steamwar.linkage.Linked;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;
@Linked
public class TraceManager implements Listener {
public static TraceManager instance;
{
instance = this;
}
public static File tracesFolder = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "traces");
public TraceManager() {
if (!tracesFolder.exists())
tracesFolder.mkdir();
File[] traceFiles = tracesFolder.listFiles();
if (traceFiles == null)
return;
for (File traceFile : traceFiles) {
if (traceFile.getName().contains(".records"))
continue;
add(new Trace(traceFile));
}
}
/**
* List of all current traces
*/
private final Map<Region, Map<Integer, Trace>> tracesByRegion = new HashMap<>();
private final Map<Region, Map<Player, PlayerTraceShowData>> showDataPerRegionPerPlayer = new HashMap<>();
private final Map<Player, Set<Player>> followerMap = new HashMap<>();
/**
* Utility variable to keep track of the next open trace id;
*/
private int nextOpenId = 0;
/**
* Adds a new trace to the global record
*
* @param trace Trace to be added
* @return id of the created trace
*/
protected int add(Trace trace) {
showDataPerRegionPerPlayer.getOrDefault(trace.getRegion(), Collections.emptyMap()).forEach((player, playerTraceShowData) -> {
trace.render(player, playerTraceShowData);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
trace.render(follower, playerTraceShowData);
});
});
tracesByRegion.computeIfAbsent(trace.getRegion(), region -> new HashMap<>()).put(nextOpenId, trace);
nextOpenId++;
return nextOpenId;
}
/**
* Get the id of the given trace
*
* @param trace
*/
public int getId(Trace trace) {
for (Map.Entry<Integer, Trace> entry : tracesByRegion.getOrDefault(trace.getRegion(), Collections.emptyMap()).entrySet()) {
if (entry.getValue() == trace) return entry.getKey();
}
return -1;
}
/**
* Renders only the given records to the specified trace
*
* @param trace
* @param recordsToAdd
*/
protected void showPartial(Trace trace, List<TNTPoint> recordsToAdd) {
showDataPerRegionPerPlayer.getOrDefault(trace.getRegion(), Collections.emptyMap()).forEach((player, playerTraceShowData) -> {
trace.render(recordsToAdd, player, playerTraceShowData);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
trace.render(recordsToAdd, follower, playerTraceShowData);
});
});
}
protected Map<Player, PlayerTraceShowData> getTraceShowDataPlayerMapping(Region region) {
return showDataPerRegionPerPlayer.getOrDefault(region, new HashMap<>());
}
/**
* Removes the trace with the given id
*
* @param trace the trace to be removed
*/
public boolean remove(Trace trace) {
Map<Integer, Trace> traces = tracesByRegion.getOrDefault(trace.getRegion(), Collections.emptyMap());
Integer traceId = traces.entrySet().stream()
.filter(entry -> entry.getValue() == trace)
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
if (traceId == null) return false;
traces.remove(traceId);
trace.hide();
return true;
}
/**
* Clears all traces
*/
public void clear(Region region) {
showDataPerRegionPerPlayer.getOrDefault(region, new HashMap<>())
.keySet()
.forEach(player -> {
Set<Player> players = followerMap.getOrDefault(player, Collections.emptySet());
tracesByRegion.get(region).values().forEach(trace -> {
trace.hide(player);
players.forEach(trace::hide);
});
});
tracesByRegion.getOrDefault(region, new HashMap<>())
.forEach((i, trace) -> {
if (trace.getRegion() != region) return;
trace.getMetadataSaveFile().delete();
trace.getRecordsSaveFile().delete();
});
tracesByRegion.getOrDefault(region, new HashMap<>()).clear();
}
/**
* Methode to get all traces in a certain region
*
* @param region Region to look for traces in
* @return All traces recorded in the given Region
*/
public Collection<Trace> get(Region region) {
return tracesByRegion.getOrDefault(region, Collections.emptyMap()).values();
}
/**
* Methode to get the trace with specific id
*
* @param index index of the trace
* @return the trace with given id
*/
public Optional<Trace> get(int index) {
for (Map.Entry<Region, Map<Integer, Trace>> intermediate : tracesByRegion.entrySet()) {
if (intermediate.getValue().containsKey(index)) {
return Optional.ofNullable(intermediate.getValue().get(index));
}
}
return Optional.empty();
}
/**
* Methode to get all traces
*
* @return internal list of all current traces
*/
public Collection<Trace> getAll() {
return tracesByRegion.values().stream().map(Map::values).flatMap(Collection::stream).collect(Collectors.toList());
}
/**
* @return all ids of active traces
*/
public Set<Integer> getAllIds() {
return tracesByRegion.values().stream().map(Map::keySet).flatMap(Collection::stream).collect(Collectors.toSet());
}
/**
* Shows traces for the player of the current Region
*
* @param player
* @param playerTraceShowData
*/
public void show(Player player, PlayerTraceShowData playerTraceShowData) {
unfollow(player);
Region region = Region.getRegion(player.getLocation());
showDataPerRegionPerPlayer.computeIfAbsent(region, ignored -> new HashMap<>()).put(player, playerTraceShowData);
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.render(player, playerTraceShowData);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
trace.render(follower, playerTraceShowData);
});
});
}
/**
* Hides traces for the player of the current Region
*
* @param player
*/
public void hide(Player player) {
unfollow(player);
Region region = Region.getRegion(player.getLocation());
PlayerTraceShowData previous = showDataPerRegionPerPlayer.getOrDefault(region, Collections.emptyMap()).remove(player);
if (previous == null) return;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.hide(player);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(trace::hide);
});
}
/**
* Makes the given player
*
* @param follower
* @param following
* @return
*/
public boolean follow(Player follower, Player following) {
if (followerMap.containsKey(follower)) return false;
if (followerMap.entrySet().stream().anyMatch(playerSetEntry -> playerSetEntry.getValue().contains(follower))) {
unfollow(follower);
}
followerMap.computeIfAbsent(following, ignored -> new HashSet<>()).add(follower);
showDataPerRegionPerPlayer.forEach((region, playerPlayerTraceShowDataMap) -> {
if (playerPlayerTraceShowDataMap.containsKey(follower)) {
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> trace.hide(follower));
}
PlayerTraceShowData playerTraceShowData = playerPlayerTraceShowDataMap.get(following);
if (playerTraceShowData == null) return;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.render(follower, playerTraceShowData);
});
});
return true;
}
public void unfollow(Player follower) {
if (followerMap.containsKey(follower)) return;
List<Player> toRemove = new ArrayList<>();
Set<Player> toHide = new HashSet<>();
followerMap.forEach((player, players) -> {
if (players.remove(follower)) toHide.add(follower);
if (players.isEmpty()) toRemove.add(player);
});
toRemove.forEach(followerMap::remove);
showDataPerRegionPerPlayer.forEach((region, playerPlayerTraceShowDataMap) -> {
toHide.forEach(player -> {
if (!playerPlayerTraceShowDataMap.containsKey(player)) return;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.hide(follower);
});
});
PlayerTraceShowData playerTraceShowData = playerPlayerTraceShowDataMap.get(follower);
if (playerTraceShowData == null) return;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.render(follower, playerTraceShowData);
});
});
}
/**
* Modifies the render for the given player, to only show tnts at the given time
* interval
*
* @param player
* @param from start of time interval
* @param to end of time interval
*/
public void renderAt(Player player, int from, int to) {
unfollow(player);
Region region = Region.getRegion(player.getLocation());
PlayerTraceShowData playerTraceShowData = showDataPerRegionPerPlayer
.computeIfAbsent(region, ignored -> new HashMap<>())
.computeIfAbsent(player, ignored -> new PlayerTraceShowData(BundleFilter.DEFAULT));
AtFlag atFlag = playerTraceShowData.getViewFlag(AtFlag.class);
if (atFlag == null) {
atFlag = new AtFlag(from, to);
playerTraceShowData.addViewFlag(atFlag);
} else {
atFlag.update(from, to);
}
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.render(player, playerTraceShowData);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
trace.render(follower, playerTraceShowData);
});
});
}
/**
* Toggles the isolated render for the given records and player
*
* @param player the player the trace is shown to
* @param records the record for which isolation is toggled
*/
public void isolate(Player player, TNTPoint... records) {
unfollow(player);
Region region = Region.getRegion(player.getLocation());
PlayerTraceShowData playerTraceShowData = showDataPerRegionPerPlayer
.computeIfAbsent(region, ignored -> new HashMap<>())
.computeIfAbsent(player, ignored -> new PlayerTraceShowData(BundleFilter.DEFAULT));
IsolateFlag isolateFlag;
if (playerTraceShowData.hasViewFlagOnly(IsolateFlag.class)) {
isolateFlag = playerTraceShowData.getViewFlag(IsolateFlag.class);
} else if (playerTraceShowData.hasNoViewFlags()) {
isolateFlag = new IsolateFlag();
playerTraceShowData.addViewFlag(isolateFlag);
} else {
playerTraceShowData = new PlayerTraceShowData(BundleFilter.DEFAULT);
isolateFlag = new IsolateFlag();
playerTraceShowData.addViewFlag(isolateFlag);
showDataPerRegionPerPlayer.get(region).put(player, playerTraceShowData);
}
for (TNTPoint record : records) {
isolateFlag.toggleId(record.getTntId());
}
PlayerTraceShowData finalPlayerTraceShowData = playerTraceShowData;
tracesByRegion.getOrDefault(region, Collections.emptyMap()).forEach((integer, trace) -> {
trace.render(player, finalPlayerTraceShowData);
followerMap.getOrDefault(player, Collections.emptySet()).forEach(follower -> {
trace.render(follower, finalPlayerTraceShowData);
});
});
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
unfollow(event.getPlayer());
new ArrayList<>(followerMap.getOrDefault(event.getPlayer(), Collections.emptySet())).forEach(this::unfollow);
showDataPerRegionPerPlayer.forEach((region, playerPlayerTraceShowDataMap) -> {
playerPlayerTraceShowDataMap.remove(event.getPlayer());
});
tracesByRegion.forEach((region, integerTraceMap) -> {
integerTraceMap.forEach((integer, trace) -> {
trace.hide(event.getPlayer());
});
});
}
}

Datei anzeigen

@ -0,0 +1,239 @@
/*
* 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.tracer;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.region.Region;
import de.steamwar.linkage.Linked;
import org.bukkit.block.Block;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import java.util.*;
@Linked
public class TraceRecorder implements Listener {
public static TraceRecorder instance;
{
instance = this;
}
/**
* Map for all traces being actively recorded
*/
private final Map<Region, TraceRecordingWrapper> activeTraces = new HashMap<>();
/**
* Map for all TNTs being traced, by region
*/
private final Map<Region, List<TNTPrimed>> trackedTNT = new HashMap<>();
/**
* Map from TNT to Region the TNT spawned in
*/
private final Map<TNTPrimed, Region> tntSpawnRegion = new HashMap<>();
/**
* Maps a tracked tnt entity to its entire recording history
*/
private final Map<TNTPrimed, List<TNTPoint>> historyMap = new HashMap<>();
/**
* Regions where auto-trace is enabled
*/
private final Set<Region> autoTraceRegions = new HashSet<>();
public TraceRecorder() {
BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
record();
checkForAutoTraceFinish();
}, 0, 1);
}
/**
* Toggles auto trace for the given region
*
* @param region
*/
public boolean toggleAutoTrace(Region region) {
if (!autoTraceRegions.remove(region)) {
autoTraceRegions.add(region);
return true;
} else {
return false;
}
}
/**
* Makes checks for whether auto traces finished
*/
public void checkForAutoTraceFinish() {
for (Region region : autoTraceRegions) {
if (autoTraceRegions.contains(region) && trackedTNT.getOrDefault(region, Collections.emptyList()).size() == 0) {
stopRecording(region);
}
}
}
/**
* Starts a recording at the given region
*
* @param region region to be recorded
*/
public int startRecording(Region region) {
if (activeTraces.containsKey(region)) {
return -1;
}
TraceRecordingWrapper wrappedTrace = new TraceRecordingWrapper(region);
activeTraces.put(region, wrappedTrace);
return TraceManager.instance.add(wrappedTrace.getTrace());
}
/**
* Stops the recording at the given region
*
* @param region region to stop recording
*/
public void stopRecording(Region region) {
TraceRecordingWrapper wrappedTrace = activeTraces.getOrDefault(region, null);
if (wrappedTrace == null) return;
wrappedTrace.finalizeRecording();
activeTraces.remove(region);
for (TNTPrimed tnt : trackedTNT.getOrDefault(region, Collections.emptyList())) {
historyMap.remove(tnt);
}
trackedTNT.put(region, new ArrayList<>());
}
/**
* Internal methode to record all tracked TNT Entities
*/
private void record() {
for (Region region : activeTraces.keySet()) {
TraceRecordingWrapper wrappedTrace = activeTraces.get(region);
for (TNTPrimed tnt : trackedTNT.getOrDefault(region, Collections.emptyList())) {
TNTPoint record = record(tnt, wrappedTrace, Collections.emptyList());
wrappedTrace.addRecord(record);
}
wrappedTrace.commitRecorded();
}
}
/**
* Internal methode to record exploded tnt
*
* @param tntPrimed tnt exploding
* @param wrappedTrace the trace to record the tnt for wrapped with metadata
* @param destroyedBlocks the blocks destoryed by the passed tnt
* @return the record creaded of the passed tnt
*/
private TNTPoint record(TNTPrimed tntPrimed, TraceRecordingWrapper wrappedTrace, List<Block> destroyedBlocks) {
List<TNTPoint> history = historyMap.getOrDefault(tntPrimed, new ArrayList<>());
int tntID;
if (history.size() == 0) {
historyMap.put(tntPrimed, history);
tntID = wrappedTrace.getNextOpenRecordIdAndIncrement();
} else {
tntID = history.get(0).getTntId();
}
boolean isExplosion = tntPrimed.getFuseTicks() == 0;
if (isExplosion) {
wrappedTrace.activateExplosionRecorded();
}
boolean afterFirstExplosion = wrappedTrace.isExplosionRecorded();
TNTPoint record = new TNTPoint(tntID, tntPrimed, isExplosion, afterFirstExplosion, TPSUtils.currentTick.get() - wrappedTrace.getStartTick(), history, destroyedBlocks);
history.add(record);
return record;
}
public boolean isAutoTraceEnabledInRegion(Region region) {
return autoTraceRegions.contains(region);
}
public boolean isTraceActiveInRegion(Region region) {
return activeTraces.containsKey(region);
}
public long getStartTimeOfTraceInRegion(Region region) {
TraceRecordingWrapper wrapper = activeTraces.get(region);
if (wrapper == null) return 0;
return wrapper.getStartTick();
}
/**
* Event for TNTs beeing spawn.
* Registers newly spawned TNT to be traced if reqired
*
* @param event
*/
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onTNTSpawn(EntitySpawnEvent event) {
if (!(event.getEntity() instanceof TNTPrimed)) return;
Region region = Region.getRegion(event.getLocation());
if (autoTraceRegions.contains(region) && !activeTraces.containsKey(region)) {
startRecording(region);
}
if (activeTraces.containsKey(region)) {
// Check whether set for tracking already exists. Creating it if necessary
if (!trackedTNT.containsKey(region)) {
trackedTNT.put(region, new ArrayList<>());
}
trackedTNT.get(region).add((TNTPrimed) event.getEntity());
tntSpawnRegion.put((TNTPrimed) event.getEntity(), region);
activeTraces.get(region).addRecord(record((TNTPrimed) event.getEntity(), activeTraces.get(region), Collections.emptyList()));
}
}
/**
* Event for TNTs exploding
* Unregisters TNTs from beeing traced on explode
*
* @param event
*/
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onTNTExplode(EntityExplodeEvent event) {
if (!(event.getEntity() instanceof TNTPrimed)) return;
Region region = tntSpawnRegion.getOrDefault((TNTPrimed) event.getEntity(), null);
if (region == null) return;
trackedTNT.get(region).remove((TNTPrimed) event.getEntity());
tntSpawnRegion.remove((TNTPrimed) event.getEntity());
activeTraces.get(region).addRecord(record((TNTPrimed) event.getEntity(), activeTraces.get(region), event.blockList()));
}
}

Datei anzeigen

@ -0,0 +1,98 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.region.Region;
import lombok.Getter;
import lombok.SneakyThrows;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.GZIPOutputStream;
public class TraceRecordingWrapper {
@Getter
private final long startTick;
private final List<TNTPoint> recordsToAdd;
private final List<TNTPoint> recordList;
private final ObjectOutputStream recordsOutputStream;
private int nextOpenRecordId = 0;
@Getter
private boolean explosionRecorded = false;
@Getter
private final Trace trace;
@SneakyThrows
public TraceRecordingWrapper(Region region) {
startTick = TPSUtils.currentRealTick.get();
recordsToAdd = new ArrayList<>();
recordList = new ArrayList<>();
trace = new Trace(region, recordList);
TraceManager.instance.add(trace);
File recordsSaveFile = new File(TraceManager.tracesFolder, trace.getUuid() + ".records");
recordsOutputStream = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream(recordsSaveFile)));
}
public int getNextOpenRecordIdAndIncrement() {
return nextOpenRecordId++;
}
public void activateExplosionRecorded() {
explosionRecorded = true;
}
public void addRecord(TNTPoint record) {
recordsToAdd.add(record);
}
public void commitRecorded() {
TraceManager.instance.showPartial(trace, recordsToAdd);
recordsToAdd.forEach(record -> {
try {
recordsOutputStream.writeObject(record);
recordsOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
}
});
recordList.addAll(recordsToAdd);
recordsToAdd.clear();
}
@SneakyThrows
protected void finalizeRecording() {
recordsOutputStream.flush();
recordsOutputStream.close();
if (trace.getRecords().isEmpty()) {
TraceManager.instance.remove(trace);
}
}
}

Datei anzeigen

@ -1,34 +1,33 @@
/*
* This file is a part of the SteamWar software.
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
* Copyright (C) 2024 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 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.
* 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/>.
* 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.tracer;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tracer.record.Recorder;
import de.steamwar.bausystem.features.tracer.show.Record;
import de.steamwar.bausystem.features.tracer.show.StoredRecords;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Collection;
@Linked
public class TraceScoreboardElement implements ScoreboardElement {
@ -45,17 +44,18 @@ public class TraceScoreboardElement implements ScoreboardElement {
@Override
public String get(Region region, Player p) {
String traceScore = Recorder.INSTANCE.get(region).scoreboard(p);
if (traceScore != null) {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + traceScore;
if (!Permission.BUILD.hasPermission(p)) return null;
if (TraceRecorder.instance.isTraceActiveInRegion(region)) {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + BauSystem.MESSAGE.parse("TRACE_RECORD", p) + " §8| §e" + (TPSUtils.currentRealTick.get() - TraceRecorder.instance.getStartTimeOfTraceInRegion(region)) + " §7" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE_TICKS", p);
} else if (TraceRecorder.instance.isAutoTraceEnabledInRegion(region)) {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + BauSystem.MESSAGE.parse("TRACE_IDLE_AUTO", p);
}
List<Record> records = StoredRecords.getRecords(region);
if (records.isEmpty()) {
Collection<Trace> traces = TraceManager.instance.get(region);
if (traces.isEmpty()) {
return null;
} else {
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + BauSystem.MESSAGE.parse("TRACE_HAS_TRACES", p);
}
if (records.stream().allMatch(record -> record.getTnt().isEmpty())) {
return null;
}
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + BauSystem.MESSAGE.parse("TRACE_HAS_TRACES", p);
}
}

Datei anzeigen

@ -1,101 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.features.tracer.record.*;
import de.steamwar.bausystem.linkage.specific.BauGuiItem;
import de.steamwar.bausystem.region.Region;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.Collections;
@Linked
public class TracerBauGuiItem extends BauGuiItem {
public TracerBauGuiItem() {
super(21);
}
@Override
public ItemStack getItem(Player player) {
Region region = Region.getRegion(player.getLocation());
return SWUtils.setCustomModelData(new SWItem(Material.OBSERVER, BauSystem.MESSAGE.parse("TRACE_GUI_ITEM_NAME", player), Arrays.asList(BauSystem.MESSAGE.parse("TRACE_GUI_ITEM_LORE", player, Recorder.INSTANCE.get(region).scoreboard(player))), false, clickType -> {
}), 1).getItemStack();
}
private static void open(Player p) {
Region region = Region.getRegion(p.getLocation());
TraceRecorder traceRecorder = Recorder.INSTANCE.get(region);
SWInventory inv = new SWInventory(p, 9, BauSystem.MESSAGE.parse("TRACE_GUI_NAME", p));
if (traceRecorder instanceof ActiveTracer) {
if (traceRecorder instanceof AutoTraceRecorder) {
inv.setItem(1, new SWItem(Material.GRAY_DYE, BauSystem.MESSAGE.parse("TRACE_GUI_TRACE_ACTIVE_AUTO", p)));
} else {
inv.setItem(1, new SWItem(Material.GREEN_DYE, BauSystem.MESSAGE.parse("TRACE_GUI_TRACE_ACTIVE", p), clickType -> {
p.performCommand("trace stop");
open(p);
}));
}
} else {
inv.setItem(1, new SWItem(Material.RED_DYE, BauSystem.MESSAGE.parse("TRACE_GUI_TRACE_INACTIVE", p), clickType -> {
p.performCommand("trace start");
open(p);
}));
}
if (traceRecorder instanceof AutoTraceRecorder) {
inv.setItem(3, new SWItem(Material.ENDER_EYE, BauSystem.MESSAGE.parse("TRACE_GUI_AUTO_TRACE_ACTIVE", p), clickType -> {
p.performCommand("trace auto");
open(p);
}));
} else {
inv.setItem(3, new SWItem(Material.FIREWORK_STAR, BauSystem.MESSAGE.parse("TRACE_GUI_AUTO_TRACE_INACTIVE", p), clickType -> {
p.performCommand("trace auto");
open(p);
}));
}
inv.setItem(7, new SWItem(Material.BARRIER, BauSystem.MESSAGE.parse("TRACE_GUI_DELETE", p), clickType -> {
p.performCommand("trace delete");
open(p);
}));
inv.open();
}
@Override
public boolean click(ClickType click, Player p) {
open(p);
return false;
}
@Override
public Permission permission() {
return Permission.BUILD;
}
}

Datei anzeigen

@ -1,23 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
public interface ActiveTracer {
}

Datei anzeigen

@ -1,38 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
public class AutoExplodeTraceRecorder extends AutoTraceRecorder {
@Override
protected String getInactivityMessage() {
return "TRACE_IDLE_AUTO_EXPLODE";
}
@Override
protected boolean shouldStartRecording(StartType startType) {
return startType == StartType.EXPLODE;
}
@Override
protected String getScriptState() {
return "IDLE_AUTO_EXPLODE";
}
}

Datei anzeigen

@ -1,38 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
public class AutoIgniteTraceRecorder extends AutoTraceRecorder implements ActiveTracer {
@Override
protected String getInactivityMessage() {
return "TRACE_IDLE_AUTO_IGNITE";
}
@Override
protected boolean shouldStartRecording(StartType startType) {
return startType == StartType.IGNITE;
}
@Override
protected String getScriptState() {
return "IDLE_AUTO_IGNITE";
}
}

Datei anzeigen

@ -1,154 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
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 lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public abstract class AutoTraceRecorder implements TraceRecorder {
protected boolean recording = false;
private long startTime = TPSUtils.currentRealTick.get();
private long lastExplosion = 0;
private final Map<TNTPrimed, Record.TNTRecord> recordMap = new HashMap<>();
private Record record;
private Region region;
private Supplier<Record> recordSupplier;
@Setter
@Getter
private TraceRecordAutoDeletion traceRecordAutoDeletion = TraceRecordAutoDeletion.NEVER;
private Record lastRecord;
private Record.TNTRecord getRecord(TNTPrimed tntPrimed) {
return recordMap.computeIfAbsent(tntPrimed, __ -> record.spawn());
}
protected abstract String getInactivityMessage();
protected abstract boolean shouldStartRecording(StartType startType);
protected void stoppedRecording() {
}
@Override
public final String scoreboard(Player player) {
if (recording) {
return BauSystem.MESSAGE.parse("TRACE_RECORD", player) + " §8| §e" + (TPSUtils.currentRealTick.get() - startTime) + " §7" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE_TICKS", player);
} else {
return BauSystem.MESSAGE.parse(getInactivityMessage(), player);
}
}
private void startRecording() {
if (lastRecord != null && traceRecordAutoDeletion.test(lastRecord)) {
StoredRecords.remove(region, lastRecord);
TraceShowManager.reshow(region);
}
lastExplosion = 0;
startTime = TPSUtils.currentRealTick.get();
record = recordSupplier.get();
recording = true;
}
@Override
public void init(Region region, Supplier<Record> recordSupplier) {
this.region = region;
this.recordSupplier = recordSupplier;
}
@Override
public void postClear() {
recordMap.clear();
record = recordSupplier.get();
}
@Override
public final void spawn(TNTPrimed tntPrimed) {
if (!recording && shouldStartRecording(StartType.IGNITE)) {
startRecording();
}
if (recording) {
getRecord(tntPrimed).source(tntPrimed);
}
}
@Override
public final void tick(TNTPrimed tntPrimed) {
if (recording) {
getRecord(tntPrimed).location(tntPrimed);
}
}
@Override
public final void explode(TNTPrimed tntPrimed, boolean inBuildRegion, boolean inTestblockRegion) {
if (!recording && shouldStartRecording(StartType.EXPLODE)) {
startRecording();
}
if (recording) {
Record.TNTRecord tntRecord = getRecord(tntPrimed);
if (inBuildRegion) tntRecord.setInBuildArea(true);
if (inTestblockRegion) tntRecord.setInTestblockArea(true);
tntRecord.explode(tntPrimed);
}
lastExplosion = 0;
}
@Override
public final void tick() {
lastExplosion++;
if (recording && lastExplosion > 80) {
recording = false;
recordMap.clear();
lastRecord = record;
record = null;
stoppedRecording();
}
}
protected enum StartType {
IGNITE,
EXPLODE
}
protected abstract String getScriptState();
@Override
public String scriptState() {
return recording ? "RECORDING" : getScriptState();
}
@Override
public long scriptTime() {
return TPSUtils.currentRealTick.get() - startTime;
}
}

Datei anzeigen

@ -1,160 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.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.region.Region;
import de.steamwar.bausystem.region.utils.RegionExtensionType;
import de.steamwar.bausystem.region.utils.RegionType;
import de.steamwar.linkage.Linked;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.entity.EntitySpawnEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
@Linked
public class Recorder implements Listener {
public static Recorder INSTANCE;
{
INSTANCE = this;
}
private static class DisabledTracerRecorder implements TraceRecorder {
@Override
public String scoreboard(Player player) {
return null;
}
@Override
public void spawn(TNTPrimed tntPrimed) {
}
@Override
public void tick(TNTPrimed tntPrimed) {
}
@Override
public void explode(TNTPrimed tntPrimed, boolean inBuildArea, boolean inTestblockRegion) {
}
@Override
public String scriptState() {
return "IDLE";
}
@Override
public long scriptTime() {
return 0;
}
}
private static final DisabledTracerRecorder DISABLED = new DisabledTracerRecorder();
private Map<Region, TraceRecorder> regionTraceRecorderMap = new HashMap<>();
private Map<TNTPrimed, Region> tntTraceRecorderMap = new HashMap<>();
private TraceRecorder get(TNTPrimed tntPrimed) {
return get(tntTraceRecorderMap.computeIfAbsent(tntPrimed, e -> Region.getRegion(e.getLocation())));
}
public boolean isDisabled(Region region) {
return !regionTraceRecorderMap.containsKey(region);
}
public TraceRecorder get(Region region) {
return regionTraceRecorderMap.getOrDefault(region, DISABLED);
}
public void set(Region region, TraceRecorder traceRecorder) {
regionTraceRecorderMap.put(region, traceRecorder);
traceRecorder.init(region, () -> {
Record record = new Record(region);
StoredRecords.add(region, record);
return record;
});
tntTraceRecorderMap.forEach((tntPrimed, rg) -> {
if (rg == region) {
traceRecorder.spawn(tntPrimed);
}
});
}
public void remove(Region region) {
regionTraceRecorderMap.remove(region);
}
public void postClear(Region region) {
get(region).postClear();
}
@EventHandler
public void onEntitySpawn(EntitySpawnEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof TNTPrimed)) {
return;
}
get((TNTPrimed) entity).spawn((TNTPrimed) entity);
}
{
BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
tick();
tntTraceRecorderMap.keySet()
.stream()
.filter(e -> !e.isValid())
.collect(Collectors.toList())
.forEach(tntTraceRecorderMap::remove);
new ArrayList<>(regionTraceRecorderMap.values()).forEach(TraceRecorder::tick);
}, 1, 1);
}
private void tick() {
TNTPrimedIterator.impl.iterator().forEach(tntPrimed -> {
get(tntPrimed).tick(tntPrimed);
});
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof TNTPrimed)) {
return;
}
TraceRecorder traceRecorder = get((TNTPrimed) entity);
Region region = tntTraceRecorderMap.get((TNTPrimed) entity);
boolean inBuildRegion = event.blockList().stream().anyMatch(block -> region.inRegion(block.getLocation(), RegionType.BUILD, RegionExtensionType.EXTENSION));
boolean inTestblockRegion = event.blockList().stream().anyMatch(block -> region.inRegion(block.getLocation(), RegionType.TESTBLOCK, RegionExtensionType.EXTENSION));
traceRecorder.explode((TNTPrimed) entity, inBuildRegion, inTestblockRegion);
tntTraceRecorderMap.remove(entity);
tick();
}
}

Datei anzeigen

@ -1,86 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.features.tracer.show.Record;
import de.steamwar.bausystem.region.Region;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class SimpleTraceRecorder implements TraceRecorder, ActiveTracer {
private final long startTime = TPSUtils.currentRealTick.get();
private final Map<TNTPrimed, Record.TNTRecord> recordMap = new HashMap<>();
private Record record;
@Override
public String scoreboard(Player player) {
return BauSystem.MESSAGE.parse("TRACE_RECORD", player) + " §8| §e" + (TPSUtils.currentRealTick.get() - startTime) + " §7" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE_TICKS", player);
}
@Override
public void init(Region region, Supplier<Record> recordSupplier) {
record = recordSupplier.get();
}
@Override
public void postClear() {
recordMap.clear();
}
private Record.TNTRecord getRecord(TNTPrimed tntPrimed) {
return recordMap.computeIfAbsent(tntPrimed, __ -> record.spawn());
}
@Override
public void spawn(TNTPrimed tntPrimed) {
getRecord(tntPrimed).source(tntPrimed);
}
@Override
public void tick(TNTPrimed tntPrimed) {
getRecord(tntPrimed).location(tntPrimed);
}
@Override
public void explode(TNTPrimed tntPrimed, boolean inBuildRegion, boolean inTestblockRegion) {
Record.TNTRecord tntRecord = getRecord(tntPrimed);
if (inBuildRegion) tntRecord.setInBuildArea(true);
if (inTestblockRegion) tntRecord.setInTestblockArea(true);
tntRecord.explode(tntPrimed);
recordMap.remove(tntPrimed);
}
@Override
public String scriptState() {
return "RECORD";
}
@Override
public long scriptTime() {
return TPSUtils.currentRealTick.get() - startTime;
}
}

Datei anzeigen

@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import de.steamwar.bausystem.region.Region;
public class SingleTraceRecorder extends AutoTraceRecorder {
private Region region;
public SingleTraceRecorder(Region region) {
this.region = region;
}
@Override
protected String getInactivityMessage() {
return "TRACE_IDLE_SINGLE";
}
@Override
protected boolean shouldStartRecording(StartType startType) {
return startType == StartType.IGNITE;
}
@Override
protected void stoppedRecording() {
Recorder.INSTANCE.remove(region);
}
@Override
protected String getScriptState() {
return "IDLE_SINGLE";
}
}

Datei anzeigen

@ -1,32 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
import org.bukkit.entity.TNTPrimed;
import java.util.stream.Stream;
public interface TNTPrimedIterator {
TNTPrimedIterator impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
Stream<TNTPrimed> iterator();
}

Datei anzeigen

@ -1,65 +0,0 @@
/*
* 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.tracer.record;
import de.steamwar.bausystem.features.tracer.show.Record;
public enum TraceRecordAutoDeletion {
ALWAYS {
@Override
public boolean test(Record record) {
return true;
}
},
NEVER {
@Override
public boolean test(Record record) {
return false;
}
},
NO_BUILD_DESTROY {
@Override
public boolean test(Record record) {
return record.getTnt().stream().noneMatch(Record.TNTRecord::isInBuildArea);
}
},
BUILD_DESTROY {
@Override
public boolean test(Record record) {
return record.getTnt().stream().anyMatch(Record.TNTRecord::isInBuildArea);
}
},
NO_TESTBLOCK_DESTROY {
@Override
public boolean test(Record record) {
return record.getTnt().stream().noneMatch(Record.TNTRecord::isInTestblockArea);
}
},
TESTBLOCK_DESTROY {
@Override
public boolean test(Record record) {
return record.getTnt().stream().anyMatch(Record.TNTRecord::isInTestblockArea);
}
},
;
public abstract boolean test(Record record);
}

Datei anzeigen

@ -1,44 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.record;
import de.steamwar.bausystem.features.tracer.show.Record;
import de.steamwar.bausystem.region.Region;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import java.util.function.Supplier;
public interface TraceRecorder {
String scoreboard(Player player);
default void init(Region region, Supplier<Record> recordSupplier) {
}
default void postClear() {
}
void spawn(TNTPrimed tntPrimed);
void tick(TNTPrimed tntPrimed);
void explode(TNTPrimed tntPrimed, boolean inBuildRegion, boolean inTestblockRegion);
default void tick() {
}
String scriptState();
long scriptTime();
}

Datei anzeigen

@ -0,0 +1,69 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import lombok.RequiredArgsConstructor;
import java.util.function.BiFunction;
/**
* A Comparator for determining whether two records should be bundled
*/
@RequiredArgsConstructor
public enum BundleFilter {
LOOSE((TNTPoint a, TNTPoint b) -> {
if (a.isExplosion() != b.isExplosion()) return false;
if (a.getLocation().distanceSquared(b.getLocation()) > BundleFilter.pixelSizeSquared * 8) return false;
if (a.getVelocity().distanceSquared(b.getVelocity()) > BundleFilter.pixelSizeSquared * 8) return false;
return true;
}),
DEFAULT((TNTPoint a, TNTPoint b) -> {
if (a.isExplosion() != b.isExplosion()) return false;
if (a.getTicksSinceStart() != b.getTicksSinceStart()) return null;
if (a.getLocation().distanceSquared(b.getLocation()) > BundleFilter.pixelSizeSquared) return false;
if (a.getVelocity().distanceSquared(b.getVelocity()) > BundleFilter.pixelSizeSquared) return false;
return true;
}),
RAW((TNTPoint a, TNTPoint b) -> {
if (a.isExplosion() != b.isExplosion()) return false;
if (!a.getLocation().equals(b.getLocation())) return false;
if (!a.getVelocity().equals(b.getVelocity())) return false;
if (a.getTicksSinceStart() != b.getTicksSinceStart()) return null;
return true;
}),
NONE((TNTPoint a, TNTPoint b) -> {
return null;
});
/**
* {@code null}: Bundling can be stopped from this point forward
* {@code false}: No bundling allowed
* {@code true}: Bundling should be applied
*/
public final BiFunction<TNTPoint, TNTPoint, Boolean> function;
private static final double pixelSize = 0.0625;
private static final double pixelSizeSquared = pixelSize * pixelSize;
}

Datei anzeigen

@ -0,0 +1,98 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering;
import lombok.Getter;
import lombok.Setter;
import java.util.*;
/**
* A holder for the view data of a player trace render
*/
public class PlayerTraceShowData {
/**
* The bundle filter applied by this class
*/
@Getter
@Setter
private BundleFilter bundleFilter;
/**
* A map for stating whether a flag is contained in this holder or not
*/
private final Map<Class<? extends ViewFlag>, ViewFlag> viewFlags = new HashMap<>();
public PlayerTraceShowData(BundleFilter bundleFilter, ViewFlag... viewFlags) {
this.bundleFilter = bundleFilter;
for (ViewFlag viewFlag : viewFlags) {
this.viewFlags.put(viewFlag.getClass(), viewFlag);
}
}
/**
* A methode that returns the flags that should be used by renders according
* to this holder. Especially handles inverse and required flags
*
* @return the flags that should be used in a render according to this holder
*/
public Set<ViewFlag> getEffectiveViewFlags() {
// Manage flags and required flags
Set<ViewFlag> flagList = new HashSet<>();
for (ViewFlag flag : viewFlags.values()) {
flagList.add(flag);
if (flag.required != null) {
flagList.addAll(Arrays.asList(flag.required));
}
}
// Manage inverse flags
ViewFlag.inverseFlags.forEach(viewFlag -> {
if (!flagList.remove(viewFlag)) {
flagList.add(viewFlag);
}
});
return flagList;
}
public boolean hasNoViewFlags() {
return viewFlags.isEmpty();
}
public boolean hasViewFlag(Class<? extends ViewFlag> clazz) {
return viewFlags.containsKey(clazz);
}
public boolean hasViewFlagOnly(Class<? extends ViewFlag> clazz) {
return viewFlags.containsKey(clazz) && viewFlags.size() == 1;
}
// TODO ?
@SuppressWarnings("unchecked")
public <T extends ViewFlag> T getViewFlag(Class<T> clazz) {
return (T) viewFlags.get(clazz);
}
public void addViewFlag(ViewFlag viewFlag) {
viewFlags.put(viewFlag.getClass(), viewFlag);
}
}

Datei anzeigen

@ -0,0 +1,95 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering;
import com.comphenix.tinyprotocol.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.bausystem.features.tracer.Trace;
import de.steamwar.bausystem.features.tracer.TraceManager;
import de.steamwar.core.Core;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.techhider.BlockIds;
import lombok.Getter;
import net.md_5.bungee.api.chat.ClickEvent;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.stream.Collectors;
/**
* Wrapper for the rendering of a record bundle
*/
public class TraceEntity extends REntity {
private static final Reflection.MethodInvoker addEntityMethod = Reflection.getMethod(REntityServer.class, "addEntity", REntity.class);
/**
* The records represented by this REntity
*/
@Getter
private final List<TNTPoint> records;
/**
* A string of all unique tnt records
*/
private final String uniqueTntIdsString;
private final Trace trace;
public TraceEntity(REntityServer server, Location location, boolean isExplosion, List<TNTPoint> records, Trace trace) {
super(server, EntityType.FALLING_BLOCK, location, BlockIds.impl.materialToId(isExplosion ? Material.RED_STAINED_GLASS : Material.TNT) >> (Core.getVersion() <= 12 ? 4 : 0));
setNoGravity(true);
this.records = records;
uniqueTntIdsString = records.stream().map(TNTPoint::getTntId).distinct().map(Object::toString).collect(Collectors.joining(" "));
this.trace = trace;
addEntityMethod.invoke(server, this);
}
/**
* Message for printing the data contained in this wrapper into player chat
*
* @param player the player the message should be printed for
*/
public void printIntoChat(Player player) {
TNTPoint representative = records.get(0);
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_HEADER", player);
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_FUSE_TIME", player, representative.getFuse());
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_X", player, representative.getLocation().getX() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_Y", player, representative.getLocation().getY() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_Z", player, representative.getLocation().getZ() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_X", player, representative.getVelocity().getX() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_Y", player, representative.getVelocity().getY() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_Z", player, representative.getVelocity().getZ() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_ISOLATE", player, BauSystem.MESSAGE.parse("TRACE_MESSAGE_CLICK_ISOLATE", player), new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/trace isolate " + TraceManager.instance.getId(trace) + " " + uniqueTntIdsString));
}
@Override
public boolean equals(Object object) {
if (!(object instanceof TraceEntity)) return false;
TraceEntity entity = (TraceEntity) object;
return records.get(0).equals(entity.getRecords().get(0));
}
}

Datei anzeigen

@ -0,0 +1,247 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* A settable flag that changes how a trace is rendered
*/
public abstract class ViewFlag {
/**
* Static registry of static flags
*/
public static final List<ViewFlag> flags = new ArrayList<>();
/**
* Inverse flags are used by trace render by default, as long as they are not explicitly added as argument
*/
public static final List<ViewFlag> inverseFlags = new ArrayList<>();
public static ViewFlag EXPLOSION = new ViewFlag(true, false, "explosion", "e") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(TNTPoint::isExplosion);
}
};
public static ViewFlag IGNITE = new ViewFlag(true, true, "ignite", "i") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(record -> record.isAfterFirstExplosion());
}
};
public static ViewFlag SOURCE = new ViewFlag(true, false, IGNITE, "source", "s") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(record -> record.getFuse() == 80);
}
};
public static ViewFlag BUILD_DESTROY_ONLY = new ViewFlag(true, false, "build-destroy-only") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(record -> record.getHistory().get(record.getHistory().size() - 1).isDestroyedBuildArea());
}
};
public static ViewFlag TESTBLOCK_DESTROY_ONLY = new ViewFlag(true, false, "testblock-destroy-only") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(record -> record.getHistory().get(record.getHistory().size() - 1).isDestroyedTestBlock());
}
};
public static ViewFlag MICROMOTION = new ViewFlag(true, false, "micromotion", "m") {
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> stream) {
List<TNTPoint> records = stream.collect(Collectors.toList());
;
Set<Integer> seen = new HashSet<>();
Set<TNTPoint> toRemove = new HashSet<>();
for (TNTPoint uniqueRecord : records) {
if (seen.contains(uniqueRecord.getTntId())) continue;
boolean hasMicromotion = false;
for (TNTPoint record : uniqueRecord.getHistory()) {
Vector velocity = record.getVelocity();
if (velocity.getY() == 0 && (Math.abs(velocity.getX()) < 0.001 || Math.abs(velocity.getZ()) < 0.001)) {
hasMicromotion = true;
break;
}
}
if (!hasMicromotion)
toRemove.add(uniqueRecord);
seen.add(uniqueRecord.getTntId());
}
for (TNTPoint record : toRemove) {
records.removeAll(record.getHistory());
}
return records.stream();
}
};
public static ViewFlag ADVANCED = new ViewFlag(true, false, "advanced", "a") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
TNTPoint current = entity.getRecords().get(0);
if (current.isExplosion()) continue;
TNTPoint next = current.getNext().orElse(null);
if (next == null) continue;
Location pos = current.getLocation().clone();
pos.setY(next.getLocation().getY());
if (pos.distanceSquared(current.getLocation()) >= 1.0 / 256.0) {
RFallingBlockEntity y = new RFallingBlockEntity(server, pos, Material.WHITE_STAINED_GLASS);
y.setNoGravity(true);
}
if (current.getVelocity().getX() >= current.getVelocity().getZ()) {
pos.setX(next.getLocation().getX());
} else {
pos.setZ(next.getLocation().getZ());
}
if (pos.distanceSquared(next.getLocation()) >= 1.0 / 256.0) {
RFallingBlockEntity second = new RFallingBlockEntity(server, pos, Material.WHITE_STAINED_GLASS);
second.setNoGravity(true);
}
}
}
};
public static ViewFlag COUNT = new ViewFlag(true, false, "count", "c") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
entity.setDisplayName(String.valueOf(entity.getRecords().size()));
}
}
};
public static ViewFlag FUSE = new ViewFlag(true, false, "fuse", "f") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
List<String> fuses = entity.getRecords()
.stream()
.map(TNTPoint::getFuse)
.distinct()
.sorted()
.map(i -> i + "")
.collect(Collectors.toList());
if (fuses.size() <= 5) {
entity.setDisplayName(String.join(",", fuses));
} else {
entity.setDisplayName(fuses.stream().limit(5).collect(Collectors.joining(",")) + ", +" + (fuses.size() - 5));
}
}
}
};
public static ViewFlag TIME = new ViewFlag(true, false, "time", "t") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
List<String> time = entity.getRecords()
.stream()
.map(TNTPoint::getTicksSinceStart)
.distinct()
.sorted()
.map(i -> i + "")
.collect(Collectors.toList());
if (time.size() <= 5) {
entity.setDisplayName(String.join(",", time));
} else {
entity.setDisplayName(time.stream().limit(5).collect(Collectors.joining(",")) + ", +" + (time.size() - 5));
}
}
}
};
/**
* Name of the flag
*/
public final String name;
/**
* Aliases of the flag
*/
public final String[] aliases;
/**
* A flag that is used whenever this flag is used
*/
public final ViewFlag[] required;
protected ViewFlag(boolean isStatic, boolean isInverse, String name, String... aliases) {
this(isStatic, isInverse, new ViewFlag[0], name, aliases);
}
protected ViewFlag(boolean isStatic, boolean isInverse, ViewFlag required, String name, String... aliases) {
this(isStatic, isInverse, new ViewFlag[]{required}, name, aliases);
}
protected ViewFlag(boolean isStatic, boolean isInverse, ViewFlag[] required, String name, String... aliases) {
this.name = name;
this.aliases = aliases;
if (isStatic) flags.add(this);
if (isInverse) inverseFlags.add(this);
this.required = required;
}
/**
* Filters the given records for a given condition
*
* @param records Records to be filtered
* @return Filtered records
*/
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records;
}
/**
* Modifies the trace rendering
*
* @param server the server the trace is rendered on
* @param entities the entities representing tnts
*/
public void modify(REntityServer server, List<TraceEntity> entities) {
}
}

Datei anzeigen

@ -0,0 +1,61 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering.dynamicflags;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import java.util.stream.Stream;
/**
* A view flag for rendering a trace only in a given time intervall
*/
public class AtFlag extends ViewFlag {
/**
* Start of the time interval
*/
private int start;
/**
* End of the time interval
*/
private int end;
public AtFlag(int start, int end) {
super(false, false, ViewFlag.IGNITE, null);
this.start = start;
this.end = end;
}
/**
* Update this flag to represent another time interval
*
* @param start new interval start
* @param end new interval end
*/
public void update(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
return records.filter(record -> record.getTicksSinceStart() >= start && record.getTicksSinceStart() <= end);
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 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.tracer.rendering.dynamicflags;
import de.steamwar.bausystem.features.tracer.TNTPoint;
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
/**
* A flag for rendering only the records of specific tnts
*/
public class IsolateFlag extends ViewFlag {
/**
* Tnt ids that will be isolated
*/
private final Set<Integer> tntToIsolate = new HashSet<>();
public IsolateFlag() {
super(false, false, ViewFlag.IGNITE, null);
}
/**
* Toggles the given id to be or not to be rendered
*
* @param id
*/
public void toggleId(int id) {
if (!tntToIsolate.remove(id)) {
tntToIsolate.add(id);
}
}
@Override
public Stream<TNTPoint> filter(Stream<TNTPoint> records) {
if (tntToIsolate.isEmpty()) return records;
return records.filter(record -> tntToIsolate.contains(record.getTntId()));
}
}

Datei anzeigen

@ -1,268 +0,0 @@
/*
* 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.tracer.show;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.shared.RoundedPosition;
import de.steamwar.bausystem.shared.ShowMode;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import net.md_5.bungee.api.chat.ClickEvent;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.stream.Stream;
public class EntityShowMode implements ShowMode<TNTPosition> {
private final int factor;
private final Player player;
private final ShowModeParameter showModeParameter;
private REntityServer entityServer;
private final Map<RoundedPosition, EntityStack> tntEntityMap = new HashMap<>();
private final Map<RoundedPosition, EntityStack> explodeEntityMap = new HashMap<>();
private final Map<RoundedPosition, EntityStack> updateEntityMap = new HashMap<>();
public EntityShowMode(Player player, ShowModeParameter showModeParameter, int factor) {
this.player = player;
this.showModeParameter = showModeParameter;
this.factor = factor;
}
@Override
public void show(TNTPosition position) {
if (entityServer == null) {
entityServer = new REntityServer();
entityServer.setCallback((player, rEntity, entityAction) -> {
if (entityAction != REntityServer.EntityAction.INTERACT) return;
TNTPosition tntPosition = Stream.concat(tntEntityMap.values().stream(), explodeEntityMap.values().stream())
.filter(entityStack -> entityStack.entity == rEntity)
.findFirst()
.map(entityStack -> entityStack.tntPosition)
.orElse(null);
if (tntPosition == null) return;
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_HEADER", player);
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_FUSE_TIME", player, tntPosition.getFuseTicks());
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_X", player, tntPosition.getLocation().getX() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_Y", player, tntPosition.getLocation().getY() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_POSITION_Z", player, tntPosition.getLocation().getZ() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_X", player, tntPosition.getVelocity().getX() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_Y", player, tntPosition.getVelocity().getY() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_VELOCITY_Z", player, tntPosition.getVelocity().getZ() + "");
BauSystem.MESSAGE.sendPrefixless("TNT_CLICK_ISOLATE", player, BauSystem.MESSAGE.parse("TRACE_MESSAGE_CLICK_ISOLATE", player), new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/trace isolate " + tntPosition.getRecord().getId()));
});
entityServer.addPlayer(player);
}
if (showModeParameter.isBuildDestroyOnly() && !position.getRecord().isInBuildArea()) {
return;
}
if (showModeParameter.isTestblockDestroyOnly() && !position.getRecord().isInTestblockArea()) {
return;
}
if (showModeParameter.isMicroMotion() && !position.getRecord().isHasMicroMotion()) {
return;
}
boolean exploded = position.getRecord().getPositions().stream().anyMatch(TNTPosition::isExploded);
boolean hideWater = !showModeParameter.isWater() && exploded && checkWater(position.getLocation());
if (showModeParameter.isExplodeOnly()) {
if (position.isExploded()) {
generatePositions(position, false, false);
}
if (!showModeParameter.isSourceOnly() && !showModeParameter.isMicroMotionLocation() && !hideWater) {
return;
}
}
if (showModeParameter.isSourceOnly()) {
if (position.isSource()) {
generatePositions(position, false, false);
}
if (!showModeParameter.isMicroMotionLocation() && !hideWater) {
return;
}
}
if (showModeParameter.isMicroMotionLocation()) {
if (position.isMicroMotion()) {
generatePositions(position, false, false);
}
if (!hideWater) {
return;
}
}
if (hideWater) {
if (position.isExploded()) {
for (TNTPosition pos : position.getRecord().getPositions()) {
generatePositions(pos, showModeParameter.isInterpolateY(), showModeParameter.isInterpolateXZ(), (positionType, vector) -> {
RoundedPosition roundedPosition = new RoundedPosition(vector, factor);
Map<RoundedPosition, EntityStack> map;
if (positionType == PositionType.TNT) {
map = tntEntityMap;
} else if (positionType == PositionType.EXPLODE) {
map = explodeEntityMap;
} else {
map = updateEntityMap;
}
map.computeIfPresent(roundedPosition, (roundedPosition1, entityStack) -> {
if (!entityStack.remove(pos.getRecord())) {
return null;
}
return entityStack;
});
});
}
}
return;
}
generatePositions(position, showModeParameter.isInterpolateY(), showModeParameter.isInterpolateXZ());
}
@Override
public void hide() {
tntEntityMap.clear();
explodeEntityMap.clear();
updateEntityMap.clear();
if (entityServer != null) {
entityServer.close();
entityServer = null;
}
}
private void generatePositions(TNTPosition position, boolean interpolateY, boolean interpolateXZ) {
generatePositions(position, interpolateY, interpolateXZ, (positionType, vector) -> {
RoundedPosition roundedPosition = new RoundedPosition(vector, factor);
EntityStack entityStack;
if (positionType == PositionType.TNT) {
entityStack = tntEntityMap.computeIfAbsent(roundedPosition, i -> new EntityStack(position, vector, positionType, position.getFuseTicks()));
} else if (positionType == PositionType.EXPLODE) {
entityStack = explodeEntityMap.computeIfAbsent(roundedPosition, i -> new EntityStack(position, vector, positionType, position.getFuseTicks()));
} else {
entityStack = updateEntityMap.computeIfAbsent(roundedPosition, i -> new EntityStack(position, vector, positionType, position.getFuseTicks()));
}
entityStack.add(position.getRecord());
});
}
private boolean checkWater(Vector position) {
return FlatteningWrapper.impl.inWater(player.getWorld(), position);
}
private REntity createEntity(Vector position, PositionType positionType) {
Material material;
if (positionType == PositionType.TNT) {
material = Material.TNT;
} else if (positionType == PositionType.EXPLODE) {
material = Material.RED_STAINED_GLASS;
} else {
material = Material.WHITE_STAINED_GLASS;
}
RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, position.toLocation(player.getWorld()), material);
entity.setNoGravity(true);
return entity;
}
private class EntityStack {
private final TNTPosition tntPosition;
private final Vector position;
private final PositionType positionType;
private final int fuseTicks;
private REntity entity;
private int count;
private List<Record.TNTRecord> records = new ArrayList<>();
public EntityStack(TNTPosition tntPosition, Vector position, PositionType positionType, int fuseTicks) {
this.tntPosition = tntPosition;
this.position = position;
this.positionType = positionType;
this.fuseTicks = fuseTicks;
}
public void add(Record.TNTRecord record) {
records.add(record);
if (entity == null) {
entity = createEntity(position, positionType);
}
count++;
if (showModeParameter.isFuse()) {
entity.setDisplayName(fuseTicks + "");
} else if (showModeParameter.isCount()) {
entity.setDisplayName(new HashSet<>(records).size() + "");
}
}
public boolean remove(Record.TNTRecord record) {
if (entity == null) return false;
records.remove(record);
count--;
if (count == 0) {
entity.die();
entity = null;
return false;
}
return true;
}
}
public static void generatePositions(TNTPosition position, boolean interpolateY, boolean interpolateXZ, BiConsumer<PositionType, Vector> positionCallback) {
positionCallback.accept(position.isExploded() ? PositionType.EXPLODE : PositionType.TNT, position.getLocation());
if (position.getPreviousLocation() == null) return;
if (interpolateY) {
Vector updatePointY = position.getPreviousLocation().clone().setY(position.getLocation().getY());
if (!position.getLocation().equals(updatePointY)) {
positionCallback.accept(PositionType.UPDATE, updatePointY);
}
}
if (interpolateXZ) {
Vector updatePointXZ = Math.abs(position.getUpdateVelocity().getX()) >= Math.abs(position.getUpdateVelocity().getZ())
? position.getLocation().clone().setZ(position.getPreviousLocation().getZ())
: position.getLocation().clone().setX(position.getPreviousLocation().getX());
if (!position.getLocation().equals(updatePointXZ)) {
positionCallback.accept(PositionType.UPDATE, updatePointXZ);
}
}
}
public enum PositionType {
TNT,
EXPLODE,
UPDATE
}
}

Datei anzeigen

@ -1,136 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.show;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.shared.ShowMode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.util.Vector;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RequiredArgsConstructor
public class Record {
@Getter
private final List<TNTRecord> tnt = new ArrayList<>();
private final Region region;
private final long startTicks = TPSUtils.currentTick.get();
public int size() {
return tnt.size();
}
public void showAll(ShowMode<TNTPosition> traceShowMode) {
tnt.forEach(tntRecord -> tntRecord.getPositions().forEach(traceShowMode::show));
}
public TNTRecord spawn() {
TNTRecord record = new TNTRecord(this, region);
tnt.add(record);
return record;
}
public void clear() {
tnt.clear();
}
private void checkMicroMotion(Vector explosionPosition) {
for (TNTRecord tntRecord : tnt) {
List<TNTPosition> positions = tntRecord.positions;
if (positions.isEmpty()) continue;
TNTPosition position = positions.get(positions.size() - 1);
if (position.isExploded()) continue;
if (position.getLocation().distanceSquared(explosionPosition) > 64) continue;
Vector velocity = position.getVelocity();
if (velocity.getY() == 0 && ((velocity.getX() != 0 && Math.abs(velocity.getX()) < 0.001) || (velocity.getZ() != 0 && Math.abs(velocity.getZ()) < 0.001))) {
if (!tntRecord.hasMicroMotion) {
positions.forEach(tntPosition -> TraceShowManager.show(region, tntPosition));
}
tntRecord.hasMicroMotion = true;
position.setMicroMotion(true);
}
}
}
public static class TNTRecord {
@Getter
private final UUID id = UUID.randomUUID();
private Record record;
@Getter
private final Region region;
@Getter
private final List<TNTPosition> positions = new ArrayList<>(82);
@Getter
@Setter
private boolean inBuildArea = false;
@Getter
@Setter
private boolean inTestblockArea = false;
@Getter
private boolean hasMicroMotion = false;
public TNTRecord(Record record, Region region) {
this.record = record;
this.region = region;
}
public void source(TNTPrimed tntPrimed) {
add(tntPrimed, true, false);
}
public void location(TNTPrimed tntPrimed) {
add(tntPrimed, false, false);
}
public void explode(TNTPrimed tntPrimed) {
add(tntPrimed, false, true);
record.checkMicroMotion(tntPrimed.getLocation().toVector());
}
private void add(TNTPrimed tntPrimed, boolean source, boolean exploded) {
TNTPosition position;
if (positions.isEmpty()) {
position = new TNTPosition(this, tntPrimed, TPSUtils.currentTick.get() - record.startTicks, null, tntPrimed.getVelocity(), 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, TPSUtils.currentTick.get() - record.startTicks, positions.get(positions.size() - 1).getLocation(), tntPrimed.getVelocity(), lastVelocity, source, exploded);
}
positions.add(position);
TraceShowManager.show(region, position);
}
}
}

Datei anzeigen

@ -1,85 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.show;
import lombok.Getter;
@Getter
public class ShowModeParameter {
private boolean water = false;
private boolean interpolateY = false;
private boolean interpolateXZ = false;
private boolean sourceOnly = false;
private boolean explodeOnly = false;
private boolean fuse = false;
private boolean count = false;
private boolean buildDestroyOnly = false;
private boolean testblockDestroyOnly = false;
private boolean microMotion = false;
private boolean microMotionLocation = false;
public void enableWater() {
this.water = true;
}
public void disableWater() {
this.water = false;
}
public void enableInterpolateY() {
this.interpolateY = true;
}
public void enableInterpolateXZ() {
this.interpolateXZ = true;
}
public void enableSourceOnly() {
this.sourceOnly = true;
}
public void enableExplodeOnly() {
this.explodeOnly = true;
}
public void enableFuse() {
this.fuse = true;
}
public void enableCount() {
this.count = true;
}
public void enableBuildDestroyOnly() {
this.buildDestroyOnly = true;
}
public void enableTestblockDestroyOnly() {
this.testblockDestroyOnly = true;
}
public void enableMicroMotion() {
this.microMotion = true;
}
public void enableMicroMotionLocation() {
this.microMotionLocation = true;
}
}

Datei anzeigen

@ -1,74 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.show;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Supplier;
public enum ShowModeParameterType {
WATER(ShowModeParameter::enableWater, Arrays.asList("-water"), "EXPLODE", "SOURCE", "BUILD_DESTROY_ONLY"),
INTERPOLATE_Y(ShowModeParameter::enableInterpolateY, Arrays.asList("-interpolatey", "-interpolate-y", "-interpolate_y", "-y"), "ADVANCED"),
INTERPOLATE_XZ(ShowModeParameter::enableInterpolateXZ, Arrays.asList("-interpolatex", "-interpolate-x", "-interpolate_x", "-x", "-interpolatez", "-interpolate-z", "-interpolate_z", "-z", "-interpolatexz", "-interpolate-xz", "-interpolate_xz", "-xz"), "ADVANCED"),
ADVANCED(showModeParameter -> {
showModeParameter.enableInterpolateY();
showModeParameter.enableInterpolateXZ();
}, Arrays.asList("-advanced", "-a"), "INTERPOLATE_Y", "INTERPOLATE_XZ"),
SOURCE(ShowModeParameter::enableSourceOnly, Arrays.asList("-source", "-sourceonly", "-ignite"), "FUSE", "ADVANCED", "INTERPOLATE_Y", "INTERPOLATE_XZ", "WATER"),
EXPLODE(ShowModeParameter::enableExplodeOnly, Arrays.asList("-explode", "-explodeonly"), "FUSE", "ADVANCED", "INTERPOLATE_Y", "INTERPOLATE_XZ", "WATER"),
FUSE(ShowModeParameter::enableFuse, Arrays.asList("-fuse", "-f"), "EXPLODE", "SOURCE", "COUNT"),
COUNT(ShowModeParameter::enableCount, Arrays.asList("-count", "-c"), "FUSE"),
BUILD_DESTROY_ONLY(ShowModeParameter::enableBuildDestroyOnly, Arrays.asList("-builddestroy", "-builddestoryonly"), "WATER", "TESTBLOCK_DESTROY_ONLY"),
TESTBLOCK_DESTROY_ONLY(ShowModeParameter::enableTestblockDestroyOnly, Arrays.asList("-testblockdestroy", "-testblockdestroyonly"), "WATER", "BUILD_DESTROY_ONLY"),
MICROMOTION(ShowModeParameter::enableMicroMotion, Arrays.asList("-micromotion", "-micro", "-m")),
MICROMOTION_LOCATION(ShowModeParameter::enableMicroMotionLocation, Arrays.asList("-micromotionloc", "-microloc", "-mloc", "-micromotionlocation", "-microlocation", "-mlocation")),
;
@Getter
private final Consumer<ShowModeParameter> showModeParameterConsumer;
@Getter
private List<String> tabCompletes;
@Getter
private final Supplier<ShowModeParameterType[]> removed;
private AtomicReference<ShowModeParameterType[]> cached = new AtomicReference<>();
ShowModeParameterType(Consumer<ShowModeParameter> showModeParameterConsumer, List<String> tabCompletes, String... removed) {
this.showModeParameterConsumer = showModeParameterConsumer;
this.tabCompletes = tabCompletes;
this.removed = () -> {
if (cached.get() == null) {
ShowModeParameterType[] showModeParameterTypes = new ShowModeParameterType[removed.length];
for (int i = 0; i < removed.length; i++) {
showModeParameterTypes[i] = ShowModeParameterType.valueOf(removed[i]);
}
cached.set(showModeParameterTypes);
return showModeParameterTypes;
}
return cached.get();
};
}
}

Datei anzeigen

@ -1,70 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.show;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.features.tracer.record.Recorder;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.shared.ShowMode;
import lombok.experimental.UtilityClass;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@UtilityClass
public class StoredRecords {
private static final Map<Region, List<Record>> records = new HashMap<>();
public static void add(Region region, Record record) {
records.computeIfAbsent(region, k -> new ArrayList<>()).add(record);
}
public static void remove(Region region, Record record) {
records.computeIfAbsent(region, k -> new ArrayList<>()).remove(record);
}
public static void clear(Region region) {
records.remove(region);
TraceShowManager.clear(region);
Recorder.INSTANCE.postClear(region);
}
public static List<Record> getRecords() {
return records.values().stream()
.flatMap(Collection::stream)
.collect(Collectors.toList());
}
public static List<Record> getRecords(Region region) {
return records.getOrDefault(region, Collections.emptyList());
}
static void show(Region region, Predicate<TNTPosition> traceShowFilter, ShowMode<TNTPosition> traceShowMode) {
records.getOrDefault(region, new ArrayList<>()).forEach(record -> {
record.getTnt().forEach(tntRecord -> {
tntRecord.getPositions().stream()
.filter(traceShowFilter)
.forEach(traceShowMode::show);
});
});
}
}

Datei anzeigen

@ -1,152 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 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.tracer.show;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tracer.TNTPosition;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.shared.ShowMode;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Predicate;
public class TraceShowManager implements Listener {
private TraceShowManager() {
}
private static final Map<Region, Map<Player, ShowMode<TNTPosition>>> showModes = new HashMap<>();
private static final Map<Region, Map<Player, Predicate<TNTPosition>>> showFilters = new HashMap<>();
public static void show(Player player, ShowMode<TNTPosition> traceShowMode) {
Region region = Region.getRegion(player.getLocation());
_hide(region, player, true);
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.computeIfAbsent(region, __ -> new HashMap<>());
regionalShowModes.put(player, traceShowMode);
StoredRecords.show(region, getShowFilter(player, region), traceShowMode);
}
public static void hide(Player player) {
Region region = Region.getRegion(player.getLocation());
_hide(region, player, true);
showFilters.getOrDefault(region, new HashMap<>()).remove(player);
}
public static void setShowFilter(Player player, Predicate<TNTPosition> showFilter) {
Region region = Region.getRegion(player.getLocation());
Map<Player, Predicate<TNTPosition>> regionShowFilters = showFilters.computeIfAbsent(region, __ -> new HashMap<>());
if (showFilter == null) {
regionShowFilters.remove(player);
} else {
regionShowFilters.put(player, showFilter);
}
_hide(region, player, false);
ShowMode<TNTPosition> showMode = showModes.computeIfAbsent(region, __ -> new HashMap<>()).computeIfAbsent(player, __ -> new EntityShowMode(player, new ShowModeParameter(), 16));
StoredRecords.show(region, getShowFilter(player, region), showMode);
}
public static void reshow(Region region) {
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.get(region);
if (regionalShowModes == null) {
return;
}
for (Map.Entry<Player, ShowMode<TNTPosition>> entry : regionalShowModes.entrySet()) {
entry.getValue().hide();
StoredRecords.show(region, getShowFilter(entry.getKey(), region), entry.getValue());
}
}
private static void _hide(Region region, Player player, boolean remove) {
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.get(region);
if (regionalShowModes == null) {
return;
}
ShowMode<TNTPosition> showMode;
if (remove) {
showMode = regionalShowModes.remove(player);
} else {
showMode = regionalShowModes.get(player);
}
if (showMode == null) {
return;
}
showMode.hide();
}
private static Predicate<TNTPosition> getShowFilter(Player player, Region region) {
return showFilters.getOrDefault(region, new HashMap<>()).getOrDefault(player, tntPosition -> true);
}
/* Only to be called by record */
static void show(Region region, TNTPosition tnt) {
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.get(region);
if (regionalShowModes == null) {
return;
}
regionalShowModes.forEach((player, tntPositionShowMode) -> {
if (getShowFilter(player, region).test(tnt)) {
tntPositionShowMode.show(tnt);
}
});
}
/* Only to be called by StoredRecords */
static void clear(Region region) {
Map<Player, ShowMode<TNTPosition>> regionalShowModes = showModes.get(region);
if (regionalShowModes == null) {
return;
}
regionalShowModes.values().forEach(ShowMode::hide);
}
/* Internal if player leaves*/
static {
Bukkit.getPluginManager().registerEvents(new TraceShowManager(), BauSystem.getInstance());
}
@EventHandler
public void onLeave(PlayerQuitEvent event) {
hideComplete(event.getPlayer());
}
@EventHandler
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
event.getNewSpectator().forEach(this::hideComplete);
}
private void hideComplete(Player player) {
showModes.forEach((region, playerShowModeMap) -> {
ShowMode<TNTPosition> showMode = playerShowModeMap.remove(player);
if (showMode != null) showMode.hide();
});
showFilters.forEach((region, playerPredicateMap) -> {
playerPredicateMap.remove(player);
});
}
}