Merge pull request 'Trace Refactor' (#233) from TracerGUI into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #233 Reviewed-by: YoyoNow <jwsteam@nidido.de>
Dieser Commit ist enthalten in:
Commit
aeb5ebfc12
8
.gitignore
vendored
8
.gitignore
vendored
@ -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
|
||||
|
||||
#linkage
|
||||
LinkageUtils.java
|
@ -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());
|
||||
}
|
||||
}
|
@ -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
@ -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");
|
||||
|
||||
|
@ -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 -> {
|
||||
|
@ -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()));
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -196,4 +196,9 @@ public class CreatingLaufState implements LaufbauState {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LaufbauState getNextState() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -45,4 +45,6 @@ public interface LaufbauState {
|
||||
}
|
||||
return LocalTime.ofNanoOfDay(eta).format(DateTimeFormatter.ofPattern(BauSystem.MESSAGE.parse("TIME", p)));
|
||||
}
|
||||
|
||||
LaufbauState getNextState();
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@ -19,97 +19,110 @@
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
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 {
|
||||
TNTPosition tntPosition = tntPositionList.remove(0);
|
||||
createCuboid(tntPosition);
|
||||
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 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));
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
227
BauSystem_Main/src/de/steamwar/bausystem/features/tracer/TNTPoint.java
Normale Datei
227
BauSystem_Main/src/de/steamwar/bausystem/features/tracer/TNTPoint.java
Normale Datei
@ -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;
|
||||
}
|
||||
}
|
@ -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() +
|
||||
'}';
|
||||
}
|
||||
}
|
339
BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java
Normale Datei
339
BauSystem_Main/src/de/steamwar/bausystem/features/tracer/Trace.java
Normale Datei
@ -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() +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@ -20,25 +20,24 @@
|
||||
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 = {"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_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"}, 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"}, 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) {
|
||||
if (to < from) {
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_TO_SMALLER", p);
|
||||
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);
|
||||
}
|
||||
|
||||
// /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();
|
||||
}
|
||||
for (ShowModeParameterType showModeParameterType : showModeParameterTypes) {
|
||||
if (showModeParameterType == ShowModeParameterType.WATER && region.getWaterLevel() != 0) {
|
||||
showModeParameter.disableWater();
|
||||
@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 {
|
||||
showModeParameterType.getShowModeParameterConsumer().accept(showModeParameter);
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_AUTO_STOP", player);
|
||||
}
|
||||
}
|
||||
TraceShowManager.show(p, showModeType.showModeBiFunction.apply(p, showModeParameter));
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW", 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 = {"hide"}, description = "TRACE_COMMAND_HELP_HIDE")
|
||||
public void hideCommand(@Validator Player p) {
|
||||
TraceShowManager.hide(p);
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_HIDE", p);
|
||||
@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 = {"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);
|
||||
@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);
|
||||
}
|
||||
|
||||
@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;
|
||||
@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", player);
|
||||
return;
|
||||
}
|
||||
showInternal(player, from, to, bundleFilter, flags);
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_SHOW_FROM_TO", player, from, to);
|
||||
}
|
||||
|
||||
@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());
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
Map<String, ShowModeParameterType> showModeParameterTypesMap = new HashMap<>();
|
||||
showModeParameterTypeListMap.forEach((k, v) -> v.forEach(s -> showModeParameterTypesMap.put(s, k)));
|
||||
@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);
|
||||
}
|
||||
|
||||
return new TypeMapper<ShowModeParameterType>() {
|
||||
@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;
|
||||
}
|
||||
TraceManager.instance.follow(player, toFollow);
|
||||
BauSystem.MESSAGE.send("TRACE_MESSAGE_FOLLOW", player, toFollow.getName());
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
Set<ShowModeParameterType> removed = showModeParameterTypeSet.stream()
|
||||
.map(ShowModeParameterType::getRemoved)
|
||||
.map(Supplier::get)
|
||||
.flatMap(Arrays::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<String> tabCompletes = new ArrayList<>();
|
||||
for (Map.Entry<ShowModeParameterType, List<String>> entry : showModeParameterTypeListMap.entrySet()) {
|
||||
if (removed.contains(entry.getKey()) || showModeParameterTypeSet.contains(entry.getKey())) {
|
||||
continue;
|
||||
public Collection<String> tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) {
|
||||
return TraceManager.instance.getAllIds().stream()
|
||||
.map(Object::toString)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
tabCompletes.addAll(entry.getValue());
|
||||
};
|
||||
}
|
||||
return tabCompletes;
|
||||
|
||||
@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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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());
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 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
|
||||
@ -20,15 +20,14 @@
|
||||
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;
|
||||
}
|
||||
if (records.stream().allMatch(record -> record.getTnt().isEmpty())) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return "§e" + BauSystem.MESSAGE.parse("SCOREBOARD_TRACE", p) + "§8: " + BauSystem.MESSAGE.parse("TRACE_HAS_TRACES", p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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);
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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()));
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
};
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
In neuem Issue referenzieren
Einen Benutzer sperren