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) {
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));
}

View File

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

View File

@ -28,7 +28,6 @@ public class TeamRepository extends HttpRepository {
public ReqResp<List<Team>> getTeams() {
var resp = this.get("team", Object.class);
System.out.println(resp.toString());
return resp
.convertToTypeToken(new TypeToken<List<Team>>() {}.getType())
.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.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.format.NamedTextColor;
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);
player.addPotionEffects(List.of(resistance, blindness));
Bukkit.getScheduler().runTaskLater(
Bukkit.getScheduler().runTaskTimer(
Main.instance(),
() -> this.protectedPlayers.remove(player.getUniqueId()),
Ticks.TICKS_PER_SECOND * resistanceDuration
this::notifyPlayers,
Ticks.TICKS_PER_SECOND,
Ticks.TICKS_PER_SECOND
);
}
@ -43,18 +44,36 @@ public class JoinProtection extends Appliance {
return this.protectedPlayers.get(player.getUniqueId());
}
public void cancelEvent(Player player, Cancellable event) {
var teamCountdown = Main.instance().getAppliance(Teams.class).getTeamJoinCountdown(player);
if(teamCountdown.isFree(resistanceDuration)) return;
event.setCancelled(true);
public boolean isNotProtected(Player player) {
Options options = this.protectedPlayers.get(player.getUniqueId());
if(options == null) return true;
return options.joinTime <= System.currentTimeMillis() - (resistanceDuration * 1000L);
}
int secondsLeft = Math.abs((int) ((System.currentTimeMillis() - teamCountdown.timestampSince()) / 1000) - resistanceDuration);
player.sendActionBar(
Component.text(
String.format("Du bist in %d Sekunden angreifbar", secondsLeft),
NamedTextColor.RED
)
);
public void cancelEvent(Player player, Cancellable event) {
if(this.isNotProtected(player)) return;
event.setCancelled(true);
}
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

View File

@ -1,5 +1,6 @@
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.util.text.DisconnectInfo;
import org.bukkit.event.EventHandler;
@ -27,6 +28,12 @@ class ConnectivityChangeListener extends ApplianceListener<Teams> {
@EventHandler
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());
}
}

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);
}
VaroTeam getTeamFromPlayer(UUID playerId) {
public VaroTeam getTeamFromPlayer(UUID playerId) {
return this.teams.stream()
.filter(varoTeam -> varoTeam.hasMember(playerId))
.findFirst()
@ -52,8 +52,6 @@ public class Teams extends Appliance implements DisplayName.Prefixed, DisplayNam
}
public void enforceTeamJoin(Player joinedPlayer) {
this.getTeamFromPlayer(joinedPlayer.getUniqueId()).joinCountdown = new VaroTeam.JoinCountdown();
DisconnectInfo disconnectInfo = new DisconnectInfo(
"Teampartner nicht beigetreten",
"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()
);
VaroTeam team = this.getTeamFromPlayer(leftPlayer.getUniqueId());
Bukkit.getScheduler().scheduleSyncDelayedTask(
Main.instance(),
() -> team.kickTeam(disconnectInfo),
Ticks.TICKS_PER_SECOND
);
}
public VaroTeam.JoinCountdown getTeamJoinCountdown(Player player) {
return this.getTeamFromPlayer(player.getUniqueId()).joinCountdown;
team.kickTeam(disconnectInfo);
}
@Override

View File

@ -9,7 +9,7 @@ import java.util.List;
import java.util.Objects;
import java.util.UUID;
class VaroTeam {
public class VaroTeam {
public static class Member {
public final OfflinePlayer player;
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 UUID teamUuid;
public final String name;
public final String color;
public JoinCountdown joinCountdown;
public VaroTeam(List<Member> members, String name, String color) {
this.teamUuid = UUID.randomUUID();
this.members = members;

View File

@ -1,11 +1,15 @@
package eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.varoDeath;
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.util.text.DisconnectInfo;
import eu.mhsl.craftattack.spawn.varo.api.repositories.VaroPlayerRepository;
import eu.mhsl.craftattack.spawn.varo.appliances.metaGameplay.teams.Teams;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
@ -13,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public class VaroDeath extends Appliance {
public void registerPlayerDeath(Player player, @Nullable Component component) {
@ -24,7 +29,16 @@ public class VaroDeath extends Appliance {
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);
Main.instance().getAppliance(Teams.class).refreshTeamList();