Merge branch 'master' into develop-bloodmoon
This commit is contained in:
@@ -2,7 +2,7 @@ dependencies {
|
||||
implementation project(':core')
|
||||
implementation project(':common')
|
||||
|
||||
compileOnly 'io.papermc.paper:paper-api:1.21.8-R0.1-SNAPSHOT'
|
||||
compileOnly 'io.papermc.paper:paper-api:1.21.10-R0.1-SNAPSHOT'
|
||||
compileOnly 'org.geysermc.floodgate:api:2.2.4-SNAPSHOT'
|
||||
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
|
||||
implementation 'com.sparkjava:spark-core:2.9.4'
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.api.repositories;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository;
|
||||
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class FeedbackRepository extends HttpRepository {
|
||||
@@ -15,14 +12,15 @@ public class FeedbackRepository extends HttpRepository {
|
||||
super(CraftAttackApi.getBaseUri(), new RequestModifier(null, CraftAttackApi::withAuthorizationHeader));
|
||||
}
|
||||
|
||||
public record Request(String event, List<UUID> users) {
|
||||
public record Request(String event, String title, List<UUID> users) {
|
||||
}
|
||||
|
||||
public ReqResp<Map<UUID, String>> createFeedbackUrls(Request data) {
|
||||
final Type responseType = new TypeToken<Map<UUID, String>>() {
|
||||
}.getType();
|
||||
ReqResp<Object> rawData = this.post("feedback", data, Object.class);
|
||||
// TODO: use convertToTypeToken from ReqResp
|
||||
return new ReqResp<>(rawData.status(), this.gson.fromJson(this.gson.toJson(rawData.data()), responseType));
|
||||
public record Response(List<Feedback> feedback) {
|
||||
public record Feedback(UUID uuid, String url) {
|
||||
}
|
||||
}
|
||||
|
||||
public ReqResp<Response> createFeedbackUrls(Request data) {
|
||||
return this.post("feedback", data, Response.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import eu.mhsl.craftattack.spawn.core.api.client.HttpRepository;
|
||||
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.spawn.common.api.CraftAttackApi;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class WhitelistRepository extends HttpRepository {
|
||||
@@ -16,17 +17,15 @@ public class WhitelistRepository extends HttpRepository {
|
||||
String username,
|
||||
String firstname,
|
||||
String lastname,
|
||||
Long banned_until,
|
||||
Long outlawed_until
|
||||
List<Strike> strikes
|
||||
) {
|
||||
public record Strike(long at, int weight) {
|
||||
}
|
||||
}
|
||||
|
||||
private record UserQuery(UUID uuid) {}
|
||||
|
||||
public ReqResp<UserData> getUserData(UUID userId) {
|
||||
return this.post(
|
||||
"player",
|
||||
new UserQuery(userId),
|
||||
return this.get(
|
||||
"users/%s".formatted(userId.toString()),
|
||||
UserData.class
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ public class Outlawed extends Appliance implements DisplayName.Prefixed {
|
||||
|
||||
void askForConfirmation(Player player) {
|
||||
Component confirmationMessage = switch(this.getLawStatus(player)) {
|
||||
case DISABLED -> Component.text("Wenn du Vogelfrei aktivierst, darfst du von allen Spielern grundlos angegriffen werden.");
|
||||
case VOLUNTARILY -> Component.text("Wenn du Vogelfrei deaktivierst, darfst du nicht mehr grundlos von Spielern angegriffen werden.");
|
||||
case DISABLED -> Component.text("Wenn du Vogelfrei aktivierst, darfst du von allen anderen vogelfreien Spielern grundlos angegriffen werden.");
|
||||
case VOLUNTARILY -> Component.text("Wenn du Vogelfrei deaktivierst, darfst du nicht mehr grundlos von anderen Spielern angegriffen werden.");
|
||||
case FORCED -> Component.text("Du darfst zurzeit deinen Vogelfreistatus nicht ändern, da dieser als Strafe auferlegt wurde!");
|
||||
};
|
||||
String command = String.format("/%s confirm", OutlawedCommand.commandName);
|
||||
@@ -130,7 +130,11 @@ public class Outlawed extends Appliance implements DisplayName.Prefixed {
|
||||
.append(Component.text("Es gelten die normalen Regeln!", NamedTextColor.GOLD));
|
||||
|
||||
case VOLUNTARILY, FORCED -> Component.text("Vogelfreistatus aktiv: ", NamedTextColor.RED)
|
||||
.append(Component.text("Du darfst von jedem angegriffen und getötet werden!", NamedTextColor.GOLD));
|
||||
.append(Component.text(
|
||||
"Du darfst von allen anderen vogelfreien Spielern angegriffen und getötet werden!" +
|
||||
"Wenn du getötet wirst, müssen andere Spieler deine Items nicht zurückerstatten!",
|
||||
NamedTextColor.GOLD
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
@@ -138,7 +142,9 @@ public class Outlawed extends Appliance implements DisplayName.Prefixed {
|
||||
public Component getNamePrefix(Player player) {
|
||||
if(this.isOutlawed(player)) {
|
||||
return Component.text("[☠]", NamedTextColor.RED)
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Vogelfreie Spieler dürfen ohne Grund angegriffen werden!")));
|
||||
.hoverEvent(HoverEvent.showText(Component.text(
|
||||
"Vogelfreie Spieler dürfen von anderen vogelfreien Spielern ohne Grund angegriffen werden!"
|
||||
)));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.metaGameplay.feedback;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.api.client.ReqResp;
|
||||
import eu.mhsl.craftattack.spawn.core.util.text.ComponentUtil;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.api.repositories.FeedbackRepository;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceCommand;
|
||||
@@ -9,7 +9,6 @@ import eu.mhsl.craftattack.spawn.core.api.HttpStatus;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentBuilder;
|
||||
import net.kyori.adventure.text.TextComponent;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.event.HoverEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.entity.Entity;
|
||||
@@ -18,32 +17,27 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
public class Feedback extends Appliance {
|
||||
public Feedback() {
|
||||
super("feedback");
|
||||
}
|
||||
|
||||
public void requestFeedback(String eventName, List<Player> receivers, @Nullable String question) {
|
||||
ReqResp<Map<UUID, String>> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls(
|
||||
new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList())
|
||||
public void requestFeedback(String eventName, String title, List<Player> receivers, @Nullable String question) {
|
||||
ReqResp<FeedbackRepository.Response> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls(
|
||||
new FeedbackRepository.Request(eventName, title, receivers.stream().map(Entity::getUniqueId).toList())
|
||||
);
|
||||
|
||||
System.out.println(response.toString());
|
||||
System.out.println(response.status());
|
||||
|
||||
if(response.status() != HttpStatus.CREATED) throw new RuntimeException();
|
||||
if(response.status() != HttpStatus.OK) throw new RuntimeException();
|
||||
|
||||
Component border = Component.text("-".repeat(40), NamedTextColor.GRAY);
|
||||
|
||||
receivers.forEach(player -> {
|
||||
String feedbackUrl = response.data().get(player.getUniqueId());
|
||||
if(feedbackUrl == null) {
|
||||
Main.logger().warning(String.format("FeedbackUrl not found for player '%s' from backend!", player.getUniqueId()));
|
||||
return;
|
||||
}
|
||||
String feedbackUrl = response.data().feedback().stream()
|
||||
.filter(feedback -> feedback.uuid().equals(player.getUniqueId()))
|
||||
.findFirst()
|
||||
.orElseThrow()
|
||||
.url();
|
||||
|
||||
ComponentBuilder<TextComponent, TextComponent.Builder> message = Component.text()
|
||||
.append(border)
|
||||
@@ -58,8 +52,7 @@ public class Feedback extends Appliance {
|
||||
|
||||
message
|
||||
.append(Component.text("Klicke hier und gib uns Feedback, damit wir dein Spielerlebnis verbessern können!", NamedTextColor.DARK_GREEN)
|
||||
.clickEvent(ClickEvent.openUrl(feedbackUrl)))
|
||||
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um Feedback zu geben.")))
|
||||
.hoverEvent(HoverEvent.showText(ComponentUtil.clickLink(feedbackUrl))))
|
||||
.appendNewline()
|
||||
.append(border);
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ class FeedbackCommand extends ApplianceCommand.PlayerChecked<Feedback> {
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().requestFeedback(
|
||||
"self-issued-ingame",
|
||||
"Dein Feedback an uns",
|
||||
List.of(this.getPlayer()),
|
||||
null
|
||||
)
|
||||
|
||||
@@ -20,6 +20,7 @@ class RequestFeedbackCommand extends ApplianceCommand<Feedback> {
|
||||
Main.instance(),
|
||||
() -> this.getAppliance().requestFeedback(
|
||||
"admin-issued-ingame",
|
||||
"Hilf uns dein Spielerlebnis zu verbessern!",
|
||||
new ArrayList<>(Bukkit.getOnlinePlayers()), String.join(" ", args)
|
||||
)
|
||||
);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.statistics;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.Main;
|
||||
import eu.mhsl.craftattack.spawn.core.api.server.HttpServer;
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Statistic;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class Statistics extends Appliance {
|
||||
record StatisticsResponse(List<PlayerStatistics> playerStatistics) {
|
||||
record PlayerStatistics(String playerName, String playerUuid, List<MaterialStatistic> statistics) {
|
||||
}
|
||||
}
|
||||
|
||||
record MaterialStatistic(String name, String material, int value) {
|
||||
}
|
||||
|
||||
record StatisticsRequest(List<MaterialStatistic> categories) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
||||
apiBuilder.post(
|
||||
"getStatistics",
|
||||
StatisticsRequest.class,
|
||||
(statistics, request) -> {
|
||||
Main.instance().getLogger().info("API requested statistics");
|
||||
List<StatisticsResponse.PlayerStatistics> statisticsList = Arrays.stream(Bukkit.getOfflinePlayers())
|
||||
.parallel()
|
||||
.map(player -> new StatisticsResponse.PlayerStatistics(
|
||||
player.getName(),
|
||||
player.getUniqueId().toString(),
|
||||
statistics.categories().stream()
|
||||
.map(category -> {
|
||||
String material = (category.material() == null || category.material().isBlank()) ? null : category.material();
|
||||
return new MaterialStatistic(category.name(), material, material == null
|
||||
? player.getStatistic(Statistic.valueOf(category.name()))
|
||||
: player.getStatistic(Statistic.valueOf(category.name()), Material.valueOf(material))
|
||||
);
|
||||
})
|
||||
.toList()
|
||||
))
|
||||
.toList();
|
||||
return new StatisticsResponse(statisticsList);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strikes;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.api.repositories.WhitelistRepository;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Strikes extends Appliance {
|
||||
public Strikes() {
|
||||
super("strike");
|
||||
}
|
||||
|
||||
private static final BanInfo falseInfo = new BanInfo(false, null, false);
|
||||
public record BanInfo(boolean isBanned, @Nullable Instant bannedUntil, boolean isPermanent) {
|
||||
}
|
||||
|
||||
private final Map<Integer, Duration> strikePunishmentMap = Map.of(
|
||||
1, Duration.ofHours(1),
|
||||
2, Duration.ofHours(24),
|
||||
3, Duration.ofDays(3),
|
||||
4, Duration.ofDays(7)
|
||||
);
|
||||
|
||||
|
||||
public BanInfo isBanned(List<WhitelistRepository.UserData.Strike> strikes) {
|
||||
if (strikes.isEmpty()) return falseInfo;
|
||||
|
||||
int strikeCount = strikes.stream().mapToInt(WhitelistRepository.UserData.Strike::weight).sum();
|
||||
if (strikeCount < 1) return falseInfo;
|
||||
if (strikeCount > this.strikePunishmentMap.size()) return new BanInfo(true, null, true);
|
||||
|
||||
Instant lastPunishment = strikes.stream()
|
||||
.map(strike -> Instant.ofEpochMilli(strike.at()))
|
||||
.max(Instant::compareTo)
|
||||
.orElse(Instant.EPOCH);
|
||||
|
||||
Duration duration = this.strikePunishmentMap.get(strikeCount);
|
||||
Instant bannedUntil = lastPunishment.plus(duration);
|
||||
|
||||
boolean stillBanned = bannedUntil.isAfter(Instant.now());
|
||||
|
||||
return new BanInfo(stillBanned, bannedUntil, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.whitelist;
|
||||
|
||||
import eu.mhsl.craftattack.spawn.core.appliance.ApplianceListener;
|
||||
import eu.mhsl.craftattack.spawn.core.event.StrikeCreatedEvent;
|
||||
import org.bukkit.event.EventHandler;
|
||||
|
||||
public class StrikeUpdateListener extends ApplianceListener<Whitelist> {
|
||||
@EventHandler
|
||||
public void onStrikeUpdate(StrikeCreatedEvent event) {
|
||||
this.getAppliance().profileUpdated(event.getStrike().playerToStrike());
|
||||
}
|
||||
}
|
||||
@@ -8,21 +8,18 @@ import eu.mhsl.craftattack.spawn.core.appliance.Appliance;
|
||||
import eu.mhsl.craftattack.spawn.core.api.HttpStatus;
|
||||
import eu.mhsl.craftattack.spawn.core.util.server.Floodgate;
|
||||
import eu.mhsl.craftattack.spawn.core.util.text.DisconnectInfo;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.gameplay.outlawed.Outlawed;
|
||||
import eu.mhsl.craftattack.spawn.craftattack.appliances.tooling.strikes.Strikes;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import javax.inject.Provider;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class Whitelist extends Appliance {
|
||||
@@ -47,7 +44,6 @@ public class Whitelist extends Appliance {
|
||||
player.getUniqueId()
|
||||
);
|
||||
}
|
||||
this.queryAppliance(Outlawed.class).updateForcedStatus(player, this.timestampRelevant(user.outlawed_until()));
|
||||
|
||||
String purePlayerName = Floodgate.isBedrock(player)
|
||||
? Floodgate.getBedrockPlayer(player).getUsername()
|
||||
@@ -67,23 +63,31 @@ public class Whitelist extends Appliance {
|
||||
Main.instance().getLogger().info(String.format("Running integrityCheck for %s", name));
|
||||
boolean overrideCheck = this.localConfig().getBoolean("overrideIntegrityCheck", false);
|
||||
WhitelistRepository.UserData user = overrideCheck
|
||||
? new WhitelistRepository.UserData(uuid, name, "", "", 0L, 0L)
|
||||
? new WhitelistRepository.UserData(uuid, name, "", "", List.of())
|
||||
: this.fetchUserData(uuid);
|
||||
|
||||
this.userData.put(uuid, user);
|
||||
Main.logger().info(String.format("got userdata %s", user.toString()));
|
||||
|
||||
if(this.timestampRelevant(user.banned_until())) {
|
||||
Instant bannedDate = new Date(user.banned_until() * 1000L)
|
||||
.toInstant()
|
||||
.plus(1, ChronoUnit.HOURS);
|
||||
Strikes.BanInfo banInfo = Main.instance().getAppliance(Strikes.class).isBanned(user.strikes());
|
||||
Main.logger().info(String.format("got baninfo %s", banInfo.toString()));
|
||||
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneOffset.UTC);
|
||||
DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneOffset.UTC);
|
||||
if (banInfo.isBanned()) {
|
||||
Provider<ZonedDateTime> zoned = () -> {
|
||||
Objects.requireNonNull(banInfo.bannedUntil(), "Cannot get zoned date time from null");
|
||||
return banInfo.bannedUntil().atZone(ZoneId.of("Europe/Berlin"));
|
||||
};
|
||||
|
||||
DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy");
|
||||
DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm");
|
||||
|
||||
String unbanText = banInfo.bannedUntil() != null
|
||||
? String.format("Dein Bann läuft am %s um %s ab!", dateFormat.format(zoned.get()), timeFormat.format(zoned.get()))
|
||||
: "Bandauer ist unbekannt.";
|
||||
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Du wurdest vom Server gebannt.",
|
||||
String.format("Dein Bann läuft am %s um %s ab!", dateFormat.format(bannedDate), timeFormat.format(bannedDate)),
|
||||
banInfo.isPermanent() ? "Dein Bann läuft nicht ab!" : unbanText,
|
||||
"Wende dich an einen Admin für weitere Informationen.",
|
||||
uuid
|
||||
);
|
||||
@@ -94,18 +98,13 @@ public class Whitelist extends Appliance {
|
||||
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
|
||||
throw new DisconnectInfo.Throwable(
|
||||
"Interner Serverfehler",
|
||||
"Deine Anmeldedaten konnten nicht abgerufen/ überprüft werden.",
|
||||
"Deine Anmeldedaten konnten nicht abgerufen/überprüft werden.",
|
||||
"Versuche es später erneut oder kontaktiere einen Admin!",
|
||||
uuid
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean timestampRelevant(Long timestamp) {
|
||||
if(timestamp == null) return false;
|
||||
return timestamp > System.currentTimeMillis() / 1000L;
|
||||
}
|
||||
|
||||
private WhitelistRepository.UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable {
|
||||
ReqResp<WhitelistRepository.UserData> response = this.queryRepository(WhitelistRepository.class).getUserData(uuid);
|
||||
|
||||
@@ -123,20 +122,24 @@ public class Whitelist extends Appliance {
|
||||
return response.data();
|
||||
}
|
||||
|
||||
public void profileUpdated(UUID uuid) {
|
||||
Main.instance().getLogger().info(String.format("API Triggered Profile update for %s", uuid));
|
||||
Player player = Bukkit.getPlayer(uuid);
|
||||
if(player != null) {
|
||||
try {
|
||||
this.fullIntegrityCheck(player);
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
e.getDisconnectScreen().applyKick(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
|
||||
record User(UUID user) {
|
||||
}
|
||||
apiBuilder.post("update", User.class, (user, request) -> {
|
||||
Main.instance().getLogger().info(String.format("API Triggered Profile update for %s", user.user));
|
||||
Player player = Bukkit.getPlayer(user.user);
|
||||
if(player != null) {
|
||||
try {
|
||||
this.fullIntegrityCheck(player);
|
||||
} catch(DisconnectInfo.Throwable e) {
|
||||
e.getDisconnectScreen().applyKick(player);
|
||||
}
|
||||
}
|
||||
this.profileUpdated(user.user);
|
||||
return HttpServer.nothing;
|
||||
});
|
||||
}
|
||||
@@ -145,7 +148,8 @@ public class Whitelist extends Appliance {
|
||||
@NotNull
|
||||
protected List<Listener> listeners() {
|
||||
return List.of(
|
||||
new PlayerJoinListener()
|
||||
new PlayerJoinListener(),
|
||||
new StrikeUpdateListener()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user