generic tweaks

started implementation of FightDetector
This commit is contained in:
Elias Müller 2025-06-09 13:52:39 +02:00
parent a4289d5ac9
commit b1f188dece
12 changed files with 158 additions and 44 deletions

View File

@ -92,7 +92,12 @@ public abstract class HttpRepository extends Repository {
private <TResponse> ReqResp<TResponse> execute(HttpRequest request, Class<TResponse> clazz) { private <TResponse> ReqResp<TResponse> execute(HttpRequest request, Class<TResponse> clazz) {
ReqResp<String> rawResponse = this.sendHttp(request); ReqResp<String> rawResponse = this.sendHttp(request);
Main.logger().info(String.format("HTTP-Repository fired %s, response: %s", request, rawResponse)); Main.logger().info(String.format(
"HTTP-Repository fired %s, sending: %s, response: %s",
request,
request.bodyPublisher().orElse(HttpRequest.BodyPublishers.ofString("none")),
rawResponse
));
return new ReqResp<>(rawResponse.status(), this.gson.fromJson(rawResponse.data(), clazz)); return new ReqResp<>(rawResponse.status(), this.gson.fromJson(rawResponse.data(), clazz));
} }

View File

@ -1,6 +1,7 @@
package eu.mhsl.craftattack.spawn.core.api.client; package eu.mhsl.craftattack.spawn.core.api.client;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@ -12,7 +13,9 @@ public abstract class Repository {
public Repository(URI basePath) { public Repository(URI basePath) {
this.basePath = basePath; this.basePath = basePath;
this.gson = new Gson(); this.gson = new GsonBuilder()
.serializeNulls()
.create();
} }
protected void validateThread(String commandName) { protected void validateThread(String commandName) {

View File

@ -28,7 +28,6 @@ public class TeamRepository extends HttpRepository {
public ReqResp<List<Team>> getTeams() { public ReqResp<List<Team>> getTeams() {
var resp = this.get("team", Object.class); var resp = this.get("team", Object.class);
System.out.println(resp.toString());
return resp return resp
.convertToTypeToken(new TypeToken<List<Team>>() {}.getType()) .convertToTypeToken(new TypeToken<List<Team>>() {}.getType())
.cast(); .cast();

View File

@ -0,0 +1,19 @@
package eu.mhsl.craftattack.spawn.varo.api.repositories;
import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.varo.api.VaroApi;
import org.jetbrains.annotations.Nullable;
import java.util.UUID;
public class VaroPlayerRepository extends HttpRepository {
public VaroPlayerRepository() {
super(VaroApi.getBaseUri(), new RequestModifier(null, VaroApi::authorizationHeader));
}
public record VaroDeath(UUID user, @Nullable UUID killer, String message) {}
public ReqResp<Void> registerDeath(VaroDeath death) {
return this.post("player/death", death, Void.class);
}
}

View File

@ -0,0 +1,36 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.fightDetector;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.VaroTeam;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class FightDetector extends Appliance {
public static final Long FIGHT_TIMEOUT = 60 * 1000L;
public final Map<VaroTeam, Long> fights = new HashMap<>();
public boolean isInFight(VaroTeam team) {
Long lastFightTime = this.fights.get(team);
if(lastFightTime == null) return false;
return (System.currentTimeMillis() - lastFightTime <= FIGHT_TIMEOUT);
}
public void setInFight(Player player) {
VaroTeam team = this.queryAppliance(Teams.class).getTeamFromPlayer(player.getUniqueId());
this.fights.put(team, System.currentTimeMillis());
}
@Override
protected @NotNull List<Listener> listeners() {
return List.of(
new FightOnDistanceListener(),
new FightOnInteractionListener()
);
}
}

View File

@ -0,0 +1,7 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.fightDetector;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
class FightOnDistanceListener extends ApplianceListener<FightDetector> {
//TODO implementation
}

View File

@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.fightDetector;
import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.VaroTeam;
import io.papermc.paper.event.player.PrePlayerAttackEntityEvent;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
class FightOnInteractionListener extends ApplianceListener<FightDetector> {
@EventHandler
public void onAttack(PrePlayerAttackEntityEvent event) {
if(!event.willAttack()) return;
if(event.getAttacked() instanceof Player attackedPlayer) {
Teams teamsAppliance = Main.instance().getAppliance(Teams.class);
VaroTeam attacker = teamsAppliance.getTeamFromPlayer(event.getPlayer().getUniqueId());
VaroTeam attacked = teamsAppliance.getTeamFromPlayer(event.getAttacked().getUniqueId());
if(attacked == null) return;
if(attacker.equals(attacked)) return;
this.getAppliance().setInFight(event.getPlayer());
this.getAppliance().setInFight(attackedPlayer);
}
}
}

View File

@ -2,7 +2,7 @@ package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.joinProtection;
import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams; import eu.mhsl.craftattack.spawn.core.util.IteratorUtil;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.util.Ticks; import net.kyori.adventure.util.Ticks;
@ -32,10 +32,11 @@ public class JoinProtection extends Appliance {
PotionEffect blindness = new PotionEffect(PotionEffectType.DARKNESS, Ticks.TICKS_PER_SECOND * 3, 1); PotionEffect blindness = new PotionEffect(PotionEffectType.DARKNESS, Ticks.TICKS_PER_SECOND * 3, 1);
player.addPotionEffects(List.of(resistance, blindness)); player.addPotionEffects(List.of(resistance, blindness));
Bukkit.getScheduler().runTaskLater( Bukkit.getScheduler().runTaskTimer(
Main.instance(), Main.instance(),
() -> this.protectedPlayers.remove(player.getUniqueId()), this::notifyPlayers,
Ticks.TICKS_PER_SECOND * resistanceDuration Ticks.TICKS_PER_SECOND,
Ticks.TICKS_PER_SECOND
); );
} }
@ -43,18 +44,36 @@ public class JoinProtection extends Appliance {
return this.protectedPlayers.get(player.getUniqueId()); return this.protectedPlayers.get(player.getUniqueId());
} }
public void cancelEvent(Player player, Cancellable event) { public boolean isNotProtected(Player player) {
var teamCountdown = Main.instance().getAppliance(Teams.class).getTeamJoinCountdown(player); Options options = this.protectedPlayers.get(player.getUniqueId());
if(teamCountdown.isFree(resistanceDuration)) return; if(options == null) return true;
event.setCancelled(true); return options.joinTime <= System.currentTimeMillis() - (resistanceDuration * 1000L);
}
int secondsLeft = Math.abs((int) ((System.currentTimeMillis() - teamCountdown.timestampSince()) / 1000) - resistanceDuration); public void cancelEvent(Player player, Cancellable event) {
player.sendActionBar( if(this.isNotProtected(player)) return;
Component.text( event.setCancelled(true);
String.format("Du bist in %d Sekunden angreifbar", secondsLeft), }
NamedTextColor.RED
) public void notifyPlayers() {
); IteratorUtil.onlinePlayers(player -> {
Options options = this.protectedPlayers.get(player.getUniqueId());
if(options == null) return;
if(this.isNotProtected(player)) {
this.protectedPlayers.remove(player.getUniqueId());
}
int secondsLeft = Math.abs((int) ((System.currentTimeMillis() - options.joinTime) / 1000) - resistanceDuration);
player.sendActionBar(
Component.text(
secondsLeft > 0
? String.format("Du bist in %d Sekunden angreifbar!", secondsLeft)
: "Du jetzt angreifbar!",
NamedTextColor.RED
)
);
});
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams; package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams;
import eu.mhsl.craftattack.spawn.core.Main;
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 org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
@ -27,6 +28,12 @@ class ConnectivityChangeListener extends ApplianceListener<Teams> {
@EventHandler @EventHandler
public void onLeave(PlayerQuitEvent event) { public void onLeave(PlayerQuitEvent event) {
if(event.getReason().equals(PlayerQuitEvent.QuitReason.KICKED)) {
Main.logger().info(String.format(
"Player %s left the Server. The 'teamLeave' enforcement was skipped, since the QuitReqson is 'kicked'",
event.getPlayer().getName()
));
}
this.getAppliance().enforceTeamLeave(event.getPlayer()); this.getAppliance().enforceTeamLeave(event.getPlayer());
} }
} }

View File

@ -44,7 +44,7 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
.anyMatch(varoTeam -> varoTeam.hasMember(playerId) && !Objects.requireNonNull(varoTeam.getMemberById(playerId)).isDead); .anyMatch(varoTeam -> varoTeam.hasMember(playerId) && !Objects.requireNonNull(varoTeam.getMemberById(playerId)).isDead);
} }
VaroTeam getTeamFromPlayer(UUID playerId) { public VaroTeam getTeamFromPlayer(UUID playerId) {
return this.teams.stream() return this.teams.stream()
.filter(varoTeam -> varoTeam.hasMember(playerId)) .filter(varoTeam -> varoTeam.hasMember(playerId))
.findFirst() .findFirst()
@ -52,8 +52,6 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
} }
public void enforceTeamJoin(Player joinedPlayer) { public void enforceTeamJoin(Player joinedPlayer) {
this.getTeamFromPlayer(joinedPlayer.getUniqueId()).joinCountdown = new VaroTeam.JoinCountdown();
DisconnectInfo disconnectInfo = new DisconnectInfo( DisconnectInfo disconnectInfo = new DisconnectInfo(
"Teampartner nicht beigetreten", "Teampartner nicht beigetreten",
"Deine Verbindung wurde getrennt, da dein Teampartner keine Verbindung zum Server hergestellt hat!", "Deine Verbindung wurde getrennt, da dein Teampartner keine Verbindung zum Server hergestellt hat!",
@ -84,15 +82,7 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
leftPlayer.getUniqueId() leftPlayer.getUniqueId()
); );
VaroTeam team = this.getTeamFromPlayer(leftPlayer.getUniqueId()); VaroTeam team = this.getTeamFromPlayer(leftPlayer.getUniqueId());
Bukkit.getScheduler().scheduleSyncDelayedTask( team.kickTeam(disconnectInfo);
Main.instance(),
() -> team.kickTeam(disconnectInfo),
Ticks.TICKS_PER_SECOND
);
}
public VaroTeam.JoinCountdown getTeamJoinCountdown(Player player) {
return this.getTeamFromPlayer(player.getUniqueId()).joinCountdown;
} }
@Override @Override

View File

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.UUID; import java.util.UUID;
class VaroTeam { public class VaroTeam {
public static class Member { public static class Member {
public final OfflinePlayer player; public final OfflinePlayer player;
public boolean isDead; public boolean isDead;
@ -20,23 +20,11 @@ class VaroTeam {
} }
} }
public record JoinCountdown(long timestampSince) {
public JoinCountdown() {
this(System.currentTimeMillis());
}
public boolean isFree(int countdownSeconds) {
return this.timestampSince < System.currentTimeMillis() - (countdownSeconds * 1000L);
}
}
public final List<Member> members; public final List<Member> members;
public final UUID teamUuid; public final UUID teamUuid;
public final String name; public final String name;
public final String color; public final String color;
public JoinCountdown joinCountdown;
public VaroTeam(List<Member> members, String name, String color) { public VaroTeam(List<Member> members, String name, String color) {
this.teamUuid = UUID.randomUUID(); this.teamUuid = UUID.randomUUID();
this.members = members; this.members = members;

View File

@ -1,11 +1,15 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.varoDeath; package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.varoDeath;
import eu.mhsl.craftattack.spawn.core.Main; import eu.mhsl.craftattack.spawn.core.Main;
import eu.mhsl.craftattack.spawn.core.api.HttpStatus;
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.core.appliance.Appliance; import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo; import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.varo.api.repositories.VaroPlayerRepository;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams; import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player; 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;
@ -13,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
public class VaroDeath extends Appliance { public class VaroDeath extends Appliance {
public void registerPlayerDeath(Player player, @Nullable Component component) { public void registerPlayerDeath(Player player, @Nullable Component component) {
@ -24,7 +29,16 @@ public class VaroDeath extends Appliance {
player.getUniqueId() player.getUniqueId()
); );
//TODO send player death to backend var death = new VaroPlayerRepository.VaroDeath(
player.getUniqueId(),
Optional.ofNullable(player.getKiller())
.map(Entity::getUniqueId)
.orElse(null),
deathMessage
);
ReqResp<Void> response = this.queryRepository(VaroPlayerRepository.class).registerDeath(death);
if(response.status() != HttpStatus.OK) Main.logger().warning(String.format("Failed to send death: %s", response));
disconnectInfo.applyKick(player); disconnectInfo.applyKick(player);
Main.instance().getAppliance(Teams.class).refreshTeamList(); Main.instance().getAppliance(Teams.class).refreshTeamList();