added antiGrief command
This commit is contained in:
@@ -2,7 +2,9 @@ package eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.core.util.NumberUtil;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.commands.AntiGriefCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.listener.*;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
@@ -79,17 +81,17 @@ public class AntiGrief extends Appliance {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AreaState {
|
||||
final UUID worldId;
|
||||
final int chunkX, chunkZ;
|
||||
public static final class AreaState {
|
||||
public final UUID worldId;
|
||||
public final int chunkX, chunkZ;
|
||||
|
||||
/** Rolling bucket scores for Spike Detection. */
|
||||
final NavigableMap<Long, Double> scores = new ConcurrentSkipListMap<>();
|
||||
public final NavigableMap<Long, Double> scores = new ConcurrentSkipListMap<>();
|
||||
/** Incidents per Bucket */
|
||||
final Map<Long, List<GriefIncident>> incidentsByBucket = new ConcurrentHashMap<>();
|
||||
public final Map<Long, List<GriefIncident>> incidentsByBucket = new ConcurrentHashMap<>();
|
||||
|
||||
volatile double ema = 0.0;
|
||||
volatile long lastAlertAt = 0L;
|
||||
public volatile double ema = 0.0;
|
||||
public volatile long lastAlertAt = 0L;
|
||||
|
||||
AreaState(UUID worldId, int chunkX, int chunkZ) {
|
||||
this.worldId = worldId;
|
||||
@@ -97,7 +99,7 @@ public class AntiGrief extends Appliance {
|
||||
this.chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
long getInhabitedTime() {
|
||||
public long getInhabitedTime() {
|
||||
return Objects.requireNonNull(Bukkit.getWorld(this.worldId))
|
||||
.getChunkAt(this.chunkX, this.chunkX).getInhabitedTime();
|
||||
}
|
||||
@@ -314,8 +316,18 @@ public class AntiGrief extends Appliance {
|
||||
);
|
||||
}
|
||||
|
||||
public Map<UUID, Set<GriefIncident>> getDirectGriefRegistry() {
|
||||
return this.directGriefRegistry;
|
||||
public @Nullable AreaState getInfoAtChunk(Chunk chunk) {
|
||||
long areaKey = this.packArea(chunk.getWorld().getUID(), chunk.getX(), chunk.getZ());
|
||||
return this.areas.get(areaKey);
|
||||
}
|
||||
|
||||
public List<AreaState> getHighesScoredChunks(int limit) {
|
||||
long nowBucket = this.bucketIdx(System.currentTimeMillis());
|
||||
|
||||
return this.areas.values().stream()
|
||||
.sorted(Comparator.comparingDouble((AreaState st) -> st.currentScore(nowBucket)).reversed())
|
||||
.limit(limit)
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -328,4 +340,11 @@ public class AntiGrief extends Appliance {
|
||||
new EntityRelatedGriefListener()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull List<ApplianceCommand<?>> commands() {
|
||||
return List.of(
|
||||
new AntiGriefCommand()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,63 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.commands;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.AntiGrief;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AntiGriefCommand extends ApplianceCommand.PlayerChecked<AntiGrief> {
|
||||
private final Gson prettyGson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
public AntiGriefCommand() {
|
||||
super("antiGrief");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
if(args.length != 1) throw new Error("One argument expected");
|
||||
switch(args[0]) {
|
||||
case "currentChunk": {
|
||||
AntiGrief.AreaState state = this.getAppliance().getInfoAtChunk(this.getPlayer().getChunk());
|
||||
if(state == null) throw new Error("The current chunk does not have a Score!");
|
||||
sender.sendMessage(this.areaStateDisplay(state));
|
||||
sender.sendMessage(String.format("ChunkLoaded: %ds", state.getInhabitedTime() / 1000));
|
||||
break;
|
||||
}
|
||||
case "topChunks": {
|
||||
List<AntiGrief.AreaState> states = this.getAppliance().getHighesScoredChunks(10);
|
||||
sender.sendMessage(Component.empty().append(
|
||||
states.stream().map(state -> this.areaStateDisplay(state).appendNewline()).toList()
|
||||
));
|
||||
break;
|
||||
}
|
||||
default: throw new Error("No such option!");
|
||||
}
|
||||
}
|
||||
|
||||
private Component areaStateDisplay(AntiGrief.AreaState state) {
|
||||
var object = Component.text("[\uD83D\uDCC2]", NamedTextColor.GRAY)
|
||||
.append(Component.text(" - ", NamedTextColor.GOLD))
|
||||
.hoverEvent(HoverEvent.showText(Component.text(this.prettyGson.toJson(state))));
|
||||
var location = Component.text(String.format("[%d,%d]", state.chunkX, state.chunkZ), NamedTextColor.YELLOW)
|
||||
.append(Component.text(" > ", NamedTextColor.DARK_GRAY));
|
||||
int incidentCount = state.incidentsByBucket.values().stream().map(List::size).mapToInt(Integer::intValue).sum();
|
||||
var total = Component.text(String.format("ema:%.2f, totalIncidents:%d", state.ema, incidentCount), NamedTextColor.GRAY);
|
||||
|
||||
return Component.empty().append(object, location, total);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
|
||||
if(args.length == 1) return List.of("currentChunk", "topChunks");
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -1,39 +0,0 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.commands;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.security.antiGrief.AntiGrief;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentBuilder;
|
||||
import net.kyori.adventure.text.ScopedComponent;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GriefOverviewCommand extends ApplianceCommand<AntiGrief> {
|
||||
public GriefOverviewCommand() {
|
||||
super("griefOverview");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception {
|
||||
sender.sendMessage(
|
||||
this.getAppliance().getDirectGriefRegistry().entrySet().stream()
|
||||
.map(griefEntry -> {
|
||||
List<TextComponent> entries = griefEntry.getValue().stream()
|
||||
.map(incident -> Component.text(incident.x()))
|
||||
.toList();
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> builder = Component.text()
|
||||
.append(Component.text(griefEntry.getKey().toString()))
|
||||
.append(Component.text(": "));
|
||||
entries.forEach(builder::append);
|
||||
|
||||
return builder.build();
|
||||
})
|
||||
.reduce(ScopedComponent::append)
|
||||
.orElseThrow()
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user