finalized strikesystem

This commit is contained in:
2025-06-22 14:20:45 +02:00
parent 15ac47b314
commit 3590a5d278
5 changed files with 158 additions and 61 deletions

View File

@ -14,13 +14,12 @@ import java.lang.reflect.Type;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.HashMap; import java.time.Instant;
import java.util.Map; import java.util.*;
import java.util.UUID;
public class CordLeak { public class CordLeak {
private static final Path saveFile = Paths.get(Main.instance().getDataFolder().getAbsolutePath(), "cordleak.json"); private static final Path saveFile = Paths.get(Main.instance().getDataFolder().getAbsolutePath(), "cordleak.json");
private static final Map<UUID, Location> locations = new HashMap<>(); private static final Map<UUID, LeakInfo> leaks = new HashMap<>();
static { static {
load(); load();
@ -36,18 +35,66 @@ public class CordLeak {
} }
} }
public record LeakInfo(Location location, Instant leakedAt) {}
private record LeakInfoDto(LocationDto location, long leakedAt) {
static LeakInfoDto from(LeakInfo info) {
return new LeakInfoDto(LocationDto.from(info.location), info.leakedAt.toEpochMilli());
}
LeakInfo toLeakInfo() {
return new LeakInfo(this.location.toLocation(), Instant.ofEpochMilli(this.leakedAt));
}
}
public static void update() { public static void update() {
IteratorUtil.onlinePlayers(player -> locations.put(player.getUniqueId(), player.getLocation())); IteratorUtil.onlinePlayers(player -> {
UUID uuid = player.getUniqueId();
Location loc = player.getLocation();
leaks.compute(uuid, (key, existing) -> {
// Falls bereits geleakt, nicht aktualisieren
if (existing != null && !existing.leakedAt.equals(Instant.EPOCH)) {
return existing;
}
return new LeakInfo(loc, existing != null ? existing.leakedAt : Instant.EPOCH);
});
});
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), CordLeak::save); Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), CordLeak::save);
} }
public static void markAsLeaked(UUID uuid) {
LeakInfo existing = leaks.get(uuid);
if (existing == null) return;
leaks.put(uuid, new LeakInfo(existing.location, Instant.now()));
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), CordLeak::save);
}
public static LeakInfo getLastKnownLocation(UUID uuid) {
return leaks.get(uuid);
}
public static Map<UUID, LeakInfo> getAllLeaked() {
Map<UUID, LeakInfo> result = new HashMap<>();
Instant cutoff = Instant.now().minusSeconds(3 * 24 * 60 * 60); // 3 Tage in Sekunden
for (Map.Entry<UUID, LeakInfo> entry : leaks.entrySet()) {
LeakInfo info = entry.getValue();
if (info.leakedAt.isAfter(cutoff)) {
result.put(entry.getKey(), info);
}
}
return result;
}
private static void load() { private static void load() {
if (!Files.exists(saveFile)) return; if (!Files.exists(saveFile)) return;
try (Reader reader = Files.newBufferedReader(saveFile)) { try (Reader reader = Files.newBufferedReader(saveFile)) {
Type type = new TypeToken<Map<String, LocationDto>>() {}.getType(); Type type = new TypeToken<Map<String, LeakInfoDto>>() {}.getType();
Map<String, LocationDto> raw = new Gson().fromJson(reader, type); Map<String, LeakInfoDto> raw = new Gson().fromJson(reader, type);
for (Map.Entry<String, LocationDto> entry : raw.entrySet()) { for (Map.Entry<String, LeakInfoDto> entry : raw.entrySet()) {
locations.put(UUID.fromString(entry.getKey()), entry.getValue().toLocation()); leaks.put(UUID.fromString(entry.getKey()), entry.getValue().toLeakInfo());
} }
} catch (IOException e) { } catch (IOException e) {
Main.logger().warning("Failed to load CordLeak data: " + e.getMessage()); Main.logger().warning("Failed to load CordLeak data: " + e.getMessage());
@ -57,9 +104,9 @@ public class CordLeak {
private static void save() { private static void save() {
try { try {
Files.createDirectories(saveFile.getParent()); Files.createDirectories(saveFile.getParent());
Map<String, LocationDto> raw = new HashMap<>(); Map<String, LeakInfoDto> raw = new HashMap<>();
for (Map.Entry<UUID, Location> entry : locations.entrySet()) { for (Map.Entry<UUID, LeakInfo> entry : leaks.entrySet()) {
raw.put(entry.getKey().toString(), LocationDto.from(entry.getValue())); raw.put(entry.getKey().toString(), LeakInfoDto.from(entry.getValue()));
} }
try (Writer writer = Files.newBufferedWriter(saveFile)) { try (Writer writer = Files.newBufferedWriter(saveFile)) {
new Gson().toJson(raw, writer); new Gson().toJson(raw, writer);
@ -68,8 +115,4 @@ public class CordLeak {
Main.logger().warning("Failed to save CordLeak data: " + e.getMessage()); Main.logger().warning("Failed to save CordLeak data: " + e.getMessage());
} }
} }
public static Location getLastKnownLocation(UUID uuid) {
return locations.get(uuid);
}
} }

View File

@ -23,6 +23,7 @@ public class InvClear {
} }
public static void add(UUID player) { public static void add(UUID player) {
Main.logger().info(String.format("Cannot clear inv because Player is offline. Remembering %s", player));
players.add(player); players.add(player);
Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), InvClear::save); Bukkit.getScheduler().runTaskAsynchronously(Main.instance(), InvClear::save);
} }

View File

@ -1,6 +1,8 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.strike; package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.strike;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
@ -8,5 +10,16 @@ public class OnStrikeActionListener extends ApplianceListener<Strike> {
@EventHandler @EventHandler
public void onJoin(PlayerJoinEvent event) { public void onJoin(PlayerJoinEvent event) {
this.getAppliance().checkJoin(event.getPlayer()); this.getAppliance().checkJoin(event.getPlayer());
var leaks = CordLeak.getAllLeaked();
var text = Component.text();
if(!leaks.isEmpty()) {
text.append(Component.text("Geleakte Koordinaten:", NamedTextColor.AQUA));
leaks.forEach((uuid, leakInfo) -> {
this.getAppliance().appendCordLeak(text, uuid);
});
event.getPlayer().sendMessage(text);
}
} }
} }

View File

@ -19,6 +19,8 @@ import org.bukkit.entity.Player;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -32,9 +34,6 @@ public class Strike extends Appliance {
); );
} }
public void leakCoordinates() {
}
record StrikeInfo(List<UUID> users, int totalWeight) { record StrikeInfo(List<UUID> users, int totalWeight) {
} }
@Override @Override
@ -47,8 +46,31 @@ public class Strike extends Appliance {
this.processUpdate(data); this.processUpdate(data);
return HttpServer.nothing; return HttpServer.nothing;
}); });
apiBuilder.get("cords", req -> {
Map<UUID, CordLeak.LeakInfo> raw = CordLeak.getAllLeaked();
Map<String, LeakInfoDto> dto = new HashMap<>();
for (Map.Entry<UUID, CordLeak.LeakInfo> entry : raw.entrySet()) {
dto.put(entry.getKey().toString(), LeakInfoDto.from(entry.getKey(), entry.getValue()));
}
return dto;
});
} }
record LocationDto(String world, double x, double y, double z, float yaw, float pitch) {
static LocationDto from(Location loc) {
return new LocationDto(loc.getWorld().getName(), loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
}
}
record LeakInfoDto(String name, LocationDto location, long leakedAt) {
static LeakInfoDto from(UUID uuid, CordLeak.LeakInfo info) {
String name = Bukkit.getOfflinePlayer(uuid).getName();
return new LeakInfoDto(name, LocationDto.from(info.location()), info.leakedAt().toEpochMilli());
}
}
private void processUpdate(StrikeInfo data) { private void processUpdate(StrikeInfo data) {
try { try {
VaroTeam select = null; VaroTeam select = null;
@ -59,46 +81,33 @@ public class Strike extends Appliance {
Objects.requireNonNull(select); Objects.requireNonNull(select);
VaroTeam team = select; VaroTeam team = select;
switch(data.totalWeight) { if(data.totalWeight < 3) {
case 0 -> team.members.forEach(member -> { team.members.forEach(member -> {
Main.logger().info(String.format("Unbanning player %s because there are now 0 Strikes!", member.player.getName())); Main.logger().info(String.format("Unbanning player %s because there are less than 3 Strikes!", member.player.getName()));
Bukkit.getBanList(BanListType.PROFILE).pardon(member.player.getPlayerProfile()); Bukkit.getBanList(BanListType.PROFILE).pardon(member.player.getPlayerProfile());
}); });
}
switch(data.totalWeight) {
case 1 -> { case 1 -> {
Main.logger().info(String.format("Cord leak for Team %s", team.name)); Main.logger().info(String.format("Cord leak for Team %s", team.name));
ComponentBuilder<TextComponent, TextComponent.Builder> text = Component.text()
.append( ComponentBuilder<TextComponent, TextComponent.Builder> text = Component.text();
Component.text( IteratorUtil.onlinePlayers(player -> player.sendMessage(Component.text(
String.format("Das Team %s hat einen Strike erhalten. Daher werden folgende Koordinaten des Teams veröffentlicht:", team.name), String.format("Das Team %s hat einen Strike erhalten. Daher werden folgende Koordinaten des Teams veröffentlicht:", team.name),
NamedTextColor.RED NamedTextColor.RED
) )));
);
data.users.forEach(uuid -> { data.users.forEach(uuid -> {
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid); CordLeak.markAsLeaked(uuid);
Location cords = CordLeak.getLastKnownLocation(uuid); this.appendCordLeak(text, uuid);
if(cords == null) return;
text.appendNewline();
text.append(Component.text(Objects.requireNonNull(player.getName()), NamedTextColor.DARK_AQUA));
text.append(Component.text(": ", NamedTextColor.GRAY));
text.append(Component.text(
String.format(
"(%s) %d, %d, %d",
cords.getWorld().getName(),
cords.getBlockX(),
cords.getBlockY(),
cords.getBlockZ()
),
NamedTextColor.AQUA)
);
}); });
IteratorUtil.onlinePlayers(player -> player.sendMessage(text)); IteratorUtil.onlinePlayers(p -> p.sendMessage(text));
} }
case 2 -> team.members.forEach(member -> { case 2 -> team.members.forEach(member -> {
Main.logger().info(String.format("Clearing Inventory of %s", member.player.getName()));
Optional<Player> player = Optional.ofNullable(Bukkit.getPlayer(member.player.getUniqueId())); Optional<Player> player = Optional.ofNullable(Bukkit.getPlayer(member.player.getUniqueId()));
player.ifPresentOrElse( player.ifPresentOrElse(
p -> p.getInventory().clear(), p -> p.getInventory().clear(),
@ -110,9 +119,9 @@ public class Strike extends Appliance {
team.members.forEach(member -> { team.members.forEach(member -> {
Main.logger().info(String.format("Banning player %s because of third Strike!", member.player.getName())); Main.logger().info(String.format("Banning player %s because of third Strike!", member.player.getName()));
Bukkit.getBanList(BanListType.PROFILE) Bukkit.getBanList(BanListType.PROFILE)
.addBan(member.player.getPlayerProfile(), "projektausschluiss", (Date) null, null); .addBan(member.player.getPlayerProfile(), "projektausschluss", (Date) null, null);
}); });
team.getOnlinePlayers().forEach(Player::kick); Bukkit.getScheduler().runTask(Main.instance(), () -> team.getOnlinePlayers().forEach(Player::kick));
} }
} }
} catch(Exception e) { } catch(Exception e) {
@ -121,6 +130,31 @@ public class Strike extends Appliance {
} }
} }
public void appendCordLeak(ComponentBuilder<TextComponent, TextComponent.Builder> text, UUID playerToLeak) {
OfflinePlayer player = Bukkit.getOfflinePlayer(playerToLeak);
CordLeak.LeakInfo cords = CordLeak.getLastKnownLocation(playerToLeak);
if(cords == null) return;
String timestamp = DateTimeFormatter.ofPattern("dd.MM HH:mm")
.withZone(ZoneId.of("Europe/Berlin"))
.format(cords.leakedAt());
text.appendNewline();
text.append(Component.text(Objects.requireNonNull(player.getName()), NamedTextColor.DARK_AQUA));
text.append(Component.text(": ", NamedTextColor.GRAY));
text.append(Component.text(
String.format(
"(%s) %d, %d, %d - Uhrzeit: %s",
cords.location().getWorld().getName(),
cords.location().getBlockX(),
cords.location().getBlockY(),
cords.location().getBlockZ(),
timestamp
),
NamedTextColor.AQUA)
);
}
public void checkJoin(@NotNull Player player) { public void checkJoin(@NotNull Player player) {
if(InvClear.contains(player.getUniqueId())) { if(InvClear.contains(player.getUniqueId())) {
player.getInventory().clear(); player.getInventory().clear();

View File

@ -5,6 +5,7 @@ import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp; import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener; import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo; import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.playTimer.PlayTimer;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
@ -46,19 +47,24 @@ class ConnectivityChangeListener extends ApplianceListener<Teams> {
VaroTeam team = Main.instance().getAppliance(Teams.class).getTeamFromPlayer(event.getPlayer().getUniqueId()); VaroTeam team = Main.instance().getAppliance(Teams.class).getTeamFromPlayer(event.getPlayer().getUniqueId());
Objects.requireNonNull(team, "Team not found for player " + event.getPlayer().getUniqueId()); Objects.requireNonNull(team, "Team not found for player " + event.getPlayer().getUniqueId());
Main.logger().info(String.format("Team %s got a Strike, because they %s left early!", team.name, event.getPlayer().getName()));
VaroReportRepository.StrikeCreationInfo report = new VaroReportRepository.StrikeCreationInfo( if(Main.instance().getAppliance(PlayTimer.class).isEnabled()) {
null, Main.logger().info(String.format("Team %s got a Strike, because they %s left early!", team.name, event.getPlayer().getName()));
event.getPlayer().getUniqueId(),
"early left", VaroReportRepository.StrikeCreationInfo report = new VaroReportRepository.StrikeCreationInfo(
"player left the server too early", null,
null, event.getPlayer().getUniqueId(),
null, "early left",
1 "player left the server too early",
); null,
ReqResp<Void> response = Main.instance().getRepositoryLoader().getRepository(VaroReportRepository.class).createStrike(report); null,
Main.logger().info(String.format("Autostrike response for Team %s: %s", team.name, response)); 1
);
ReqResp<Void> response = Main.instance().getRepositoryLoader().getRepository(VaroReportRepository.class).createStrike(report);
Main.logger().info(String.format("Autostrike response for Team %s: %s", team.name, response));
} else {
Main.logger().info("Allow ealry leave because PlayTimer is not active!");
}
this.getAppliance().enforceTeamLeave(event.getPlayer()); this.getAppliance().enforceTeamLeave(event.getPlayer());
} }