Compare commits

..

13 Commits

Author SHA1 Message Date
184617e9c3 added EventRepository 2024-12-16 00:30:38 +01:00
696c4bc260 added bedrock displayname prefix 2024-12-09 22:04:30 +01:00
9004609c1b better synchronous call warning 2024-12-08 22:36:11 +01:00
ddedcea8ea added FeedbackRepository 2024-12-08 22:27:54 +01:00
318a30fe54 Merge branch 'refs/heads/master' into develop-apiUtil
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
2024-12-08 13:10:27 +01:00
4808d22f6a Merge remote-tracking branch 'origin/develop-apiUtil' into develop-apiUtil
# Conflicts:
#	src/main/java/eu/mhsl/craftattack/spawn/api/client/repositories/ReportRepository.java
#	src/main/java/eu/mhsl/craftattack/spawn/appliances/report/Report.java
2024-12-05 23:10:05 +01:00
6b2a323a9c implemented report repository 2024-12-05 23:02:15 +01:00
694ca0efba implemented reports request in Report.java 2024-12-05 22:16:15 +01:00
0276763a8d started implementing report repository 2024-12-05 18:58:55 +01:00
8811328571 implemented working WhitelistRepository 2024-12-05 12:46:51 +01:00
b3c43f1763 WIP: repositoryLoader and infrastructure 2024-12-04 23:34:12 +01:00
86677c942f implemented repository design pattern 2024-12-04 22:11:28 +01:00
31581fc643 added api util 2024-12-04 09:23:20 +01:00
27 changed files with 578 additions and 310 deletions

View File

@ -1,6 +1,7 @@
package eu.mhsl.craftattack.spawn;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.api.client.RepositoryLoader;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.Bukkit;
@ -19,6 +20,7 @@ public final class Main extends JavaPlugin {
private static Logger logger;
private List<Appliance> appliances;
private RepositoryLoader repositoryLoader;
@Override
public void onEnable() {
@ -37,7 +39,11 @@ public final class Main extends JavaPlugin {
Configuration.readConfig();
List<String> disabledAppliances = Configuration.pluginConfig.getStringList("disabledAppliances");
Main.logger.info("Loading appliances...");
Main.logger().info("Loading Repositories...");
this.repositoryLoader = new RepositoryLoader();
Main.logger().info(String.format("Loaded %d repositories!", this.repositoryLoader.getRepositories().size()));
Main.logger().info("Loading appliances...");
Reflections reflections = new Reflections(this.getClass().getPackageName());
Set<Class<? extends Appliance>> applianceClasses = reflections.getSubTypesOf(Appliance.class);
@ -98,6 +104,10 @@ public final class Main extends JavaPlugin {
return appliances;
}
public RepositoryLoader getRepositoryLoader() {
return this.repositoryLoader;
}
public static Main instance() {
return instance;
}

View File

@ -0,0 +1,85 @@
package eu.mhsl.craftattack.spawn.api.client;
import eu.mhsl.craftattack.spawn.Main;
import org.apache.http.client.utils.URIBuilder;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.function.Consumer;
@RepositoryLoader.IgnoreRepository
public abstract class HttpRepository extends Repository {
private final Consumer<URIBuilder> baseUriBuilder;
public HttpRepository(URI basePath) {
this(basePath, null);
}
public HttpRepository(URI basePath, @Nullable Consumer<URIBuilder> baseUriBuilder) {
super(basePath);
this.baseUriBuilder = baseUriBuilder == null
? uriBuilder -> {}
: baseUriBuilder;
}
protected <TInput, TOutput> ReqResp<TOutput> post(String command, TInput data, Class<TOutput> outputType) {
return this.post(command, parameters -> {}, data, outputType);
}
protected <TInput, TOutput> ReqResp<TOutput> post(String command, Consumer<URIBuilder> parameters, TInput data, Class<TOutput> outputType) {
HttpRequest request = this.getRequestBuilder(this.getUri(command, parameters))
.POST(HttpRequest.BodyPublishers.ofString(this.gson.toJson(data)))
.build();
return this.execute(request, outputType);
}
protected <TOutput> ReqResp<TOutput> get(String command, Class<TOutput> outputType) {
return this.get(command, parameters -> {}, outputType);
}
protected <TOutput> ReqResp<TOutput> get(String command, Consumer<URIBuilder> parameters, Class<TOutput> outputType) {
HttpRequest request = this.getRequestBuilder(this.getUri(command, parameters))
.GET()
.build();
return this.execute(request, outputType);
}
private URI getUri(String command, Consumer<URIBuilder> parameters) {
try {
URIBuilder builder = new URIBuilder(this.basePath + "/" + command);
this.baseUriBuilder.accept(builder);
parameters.accept(builder);
return builder.build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
private HttpRequest.Builder getRequestBuilder(URI endpoint) {
return HttpRequest.newBuilder()
.uri(endpoint)
.header("User-Agent", Main.instance().getServer().getBukkitVersion())
.header("Content-Type", "application/json");
}
private <TResponse> ReqResp<TResponse> execute(HttpRequest request, Class<TResponse> clazz) {
ReqResp<String> rawResponse = this.sendHttp(request);
return new ReqResp<>(rawResponse.status(), this.gson.fromJson(rawResponse.data(), clazz));
}
private ReqResp<String> sendHttp(HttpRequest request) {
try(HttpClient client = HttpClient.newHttpClient()) {
this.validateThread(request.uri().getPath());
HttpResponse<String> httpResponse = client.send(request, HttpResponse.BodyHandlers.ofString());
return new ReqResp<>(httpResponse.statusCode(), httpResponse.body());
} catch(IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,27 @@
package eu.mhsl.craftattack.spawn.api.client;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import org.bukkit.Bukkit;
import java.net.URI;
public abstract class Repository {
protected URI basePath;
protected Gson gson;
public Repository(URI basePath) {
this.basePath = basePath;
this.gson = new Gson();
}
protected void validateThread(String commandName) {
if(!Bukkit.isPrimaryThread()) return;
Main.logger().warning(String.format(
"Repository '%s' was called synchronously with command '%s'!",
this.getClass().getSimpleName(),
commandName
));
}
}

View File

@ -0,0 +1,47 @@
package eu.mhsl.craftattack.spawn.api.client;
import org.apache.commons.lang3.NotImplementedException;
import org.reflections.Reflections;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Set;
public class RepositoryLoader {
private final List<Repository> repositories;
@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreRepository {
}
public RepositoryLoader() {
Reflections reflections = new Reflections(this.getClass().getPackageName());
Set<Class<? extends Repository>> repositories = reflections.getSubTypesOf(Repository.class);
this.repositories = repositories.stream()
.filter(repository -> !repository.isAnnotationPresent(IgnoreRepository.class))
.map(repository -> {
try {
return (Repository) repository.getDeclaredConstructor().newInstance();
} catch(InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
})
.toList();
}
public <T> T getRepository(Class<T> clazz) {
//noinspection unchecked
return this.repositories.stream()
.filter(clazz::isInstance)
.map(repository -> (T) repository)
.findFirst()
.orElseThrow(() -> new NotImplementedException(String.format("Repository '%s' not found!", clazz.getSimpleName())));
}
public List<Repository> getRepositories() {
return repositories;
}
}

View File

@ -0,0 +1,4 @@
package eu.mhsl.craftattack.spawn.api.client;
public record ReqResp<TData>(int status, TData data) {
}

View File

@ -0,0 +1,29 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.EventApiUtil;
import java.util.UUID;
public class EventRepository extends HttpRepository {
public EventRepository() {
super(EventApiUtil.getBaseUri());
}
public record CreatedRoom(UUID uuid) {
}
public record QueueRoom(UUID player, UUID room) {
public record Response(String error) {
}
}
public ReqResp<CreatedRoom> createSession() {
return this.post("room", null, CreatedRoom.class);
}
public ReqResp<QueueRoom.Response> queueRoom(QueueRoom request) {
return this.post("queueRoom", request, QueueRoom.Response.class);
}
}

View File

@ -0,0 +1,25 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import com.google.common.reflect.TypeToken;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class FeedbackRepository extends HttpRepository {
public FeedbackRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
public record Request(String event, 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);
return new ReqResp<>(rawData.status(), this.gson.fromJson(this.gson.toJson(rawData.data()), responseType));
}
}

View File

@ -0,0 +1,57 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.UUID;
public class ReportRepository extends HttpRepository {
public ReportRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
public record ReportCreationInfo(@NotNull UUID reporter, @Nullable UUID reported, String reason) {
}
public record ReportUrl(@NotNull String url) {
}
public record PlayerReports(
List<Report> from_self,
Object to_self
) {
public record Report(
@Nullable Reporter reported,
@NotNull String subject,
boolean draft,
@NotNull String status,
@NotNull String url
) {
public record Reporter(
@NotNull String username,
@NotNull String uuid
) {
}
}
}
public ReqResp<PlayerReports> queryReports(UUID player) {
return this.get(
"report",
(parameters) -> parameters.addParameter("uuid", player.toString()),
PlayerReports.class
);
}
public ReqResp<ReportUrl> createReport(ReportCreationInfo data) {
return this.post(
"report",
data,
ReportUrl.class
);
}
}

View File

@ -0,0 +1,31 @@
package eu.mhsl.craftattack.spawn.api.client.repositories;
import eu.mhsl.craftattack.spawn.api.client.HttpRepository;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.util.api.WebsiteApiUtil;
import java.util.UUID;
public class WhitelistRepository extends HttpRepository {
public WhitelistRepository() {
super(WebsiteApiUtil.getBaseUri(), WebsiteApiUtil::withAuthorizationSecret);
}
public record UserData(
UUID uuid,
String username,
String firstname,
String lastname,
Long banned_until,
Long outlawed_until
) {
}
public ReqResp<UserData> getUserData(UUID userId) {
return this.get(
"user",
parameters -> parameters.addParameter("uuid", userId.toString()),
UserData.class
);
}
}

View File

@ -1,4 +1,4 @@
package eu.mhsl.craftattack.spawn.api;
package eu.mhsl.craftattack.spawn.api.server;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;

View File

@ -1,7 +1,8 @@
package eu.mhsl.craftattack.spawn.appliance;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.api.client.Repository;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
@ -94,10 +95,14 @@ public abstract class Appliance {
listeners.forEach(HandlerList::unregisterAll);
}
protected static <T extends Appliance> T queryAppliance(Class<T> clazz) {
protected <T extends Appliance> T queryAppliance(Class<T> clazz) {
return Main.instance().getAppliance(clazz);
}
protected <T extends Repository> T queryRepository(Class<T> clazz) {
return Main.instance().getRepositoryLoader().getRepository(clazz);
}
private void setCommandExecutor(JavaPlugin plugin, String name, ApplianceCommand<?> executor) {
PluginCommand command = plugin.getCommand(name);
if(command != null && executor != null) {

View File

@ -15,7 +15,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class AfkTag extends Appliance implements DisplayName.DisplayNamed {
public class AfkTag extends Appliance implements DisplayName.Prefixed {
private final HashMap<UUID, Long> afkTimings = new HashMap<>();
private static final int updateIntervalSeconds = 30;
private static final int afkWhenMillis = 3 * 60 * 1000;

View File

@ -8,9 +8,13 @@ import eu.mhsl.craftattack.spawn.appliances.afkTag.AfkTag;
import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed;
import eu.mhsl.craftattack.spawn.appliances.sleepTag.SleepTag;
import eu.mhsl.craftattack.spawn.appliances.yearRank.YearRank;
import eu.mhsl.craftattack.spawn.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.util.text.ComponentUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
@ -22,7 +26,7 @@ import java.util.function.Supplier;
import java.util.logging.Level;
public class DisplayName extends Appliance {
public interface DisplayNamed {
public interface Prefixed {
@Nullable Component getNamePrefix(Player player);
}
@ -39,10 +43,22 @@ public class DisplayName extends Appliance {
prefixes.forEach(supplier -> {
Component prefix = supplier.get();
if(prefix == null) return;
playerName.append(prefix).append(
Component.text(" ").hoverEvent(Component.empty().asHoverEvent()));
playerName
.append(prefix)
.append(ComponentUtil.clearedSpace());
});
if(Floodgate.isBedrock(player)) {
playerName
.append(
Component.text("\uD83C\uDFAE", NamedTextColor.GRAY)
.hoverEvent(HoverEvent.showText(Component.text(
String.format("%s spielt die Minecraft: Bedrock Edition", player.getName())
)))
)
.append(ComponentUtil.clearedSpace());
}
playerName.append(Component.text(player.getName(), playerColor));
setGlobal(player, playerName.build());
@ -54,12 +70,10 @@ public class DisplayName extends Appliance {
player.displayName(component);
player.playerListName(component);
} catch(Exception e) {
//TODO this throws often exceptions, but still works, don't know why
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
}
}
@Override
@NotNull
protected List<Listener> listeners() {

View File

@ -1,8 +1,9 @@
package eu.mhsl.craftattack.spawn.appliances.event;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.EventRepository;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.customAdvancements.Advancements;
@ -10,6 +11,7 @@ import eu.mhsl.craftattack.spawn.appliances.customAdvancements.CustomAdvancement
import eu.mhsl.craftattack.spawn.appliances.event.command.*;
import eu.mhsl.craftattack.spawn.appliances.event.listener.ApplyPendingRewardsListener;
import eu.mhsl.craftattack.spawn.util.IteratorUtil;
import eu.mhsl.craftattack.spawn.util.api.HttpStatus;
import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager;
import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder;
import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener;
@ -27,12 +29,6 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.*;
public class Event extends Appliance {
@ -57,7 +53,6 @@ public class Event extends Appliance {
private boolean isOpen = false;
private AdvertisementStatus advertiseStatus = AdvertisementStatus.BEFORE;
private UUID roomId;
private final HttpClient eventServerClient = HttpClient.newHttpClient();
private final List<Reward> pendingRewards = new ArrayList<>();
record RewardConfiguration(String memorialMaterial, String memorialTitle, String memorialLore, List<UUID> memorials,
@ -74,85 +69,62 @@ public class Event extends Appliance {
@Override
public void onEnable() {
this.villager = new DisplayVillager.ConfigBound(
localConfig(),
this.localConfig(),
villager -> {
villager.customName(Component.text("Events", NamedTextColor.GOLD));
villager.setProfession(Villager.Profession.LIBRARIAN);
villager.setVillagerType(Villager.Type.SNOW);
}
);
this.isOpen = localConfig().getBoolean("enabled", false);
if(this.isOpen) this.roomId = UUID.fromString(localConfig().getString("roomId", ""));
this.isOpen = this.localConfig().getBoolean("enabled", false);
if(this.isOpen) this.roomId = UUID.fromString(this.localConfig().getString("roomId", ""));
}
public void openEvent() throws URISyntaxException, IOException, InterruptedException {
if(isOpen) throw new ApplianceCommand.Error("Es läuft derzeit bereits ein Event!");
HttpRequest createRoomRequest = HttpRequest.newBuilder()
.uri(new URI(localConfig().getString("api") + "/room"))
.POST(HttpRequest.BodyPublishers.noBody())
.build();
public void openEvent() {
if(this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit bereits ein Event!");
HttpResponse<String> rawResponse = eventServerClient.send(createRoomRequest, HttpResponse.BodyHandlers.ofString());
if(rawResponse.statusCode() != 200)
throw new ApplianceCommand.Error("Event-Server meldet Fehler: " + rawResponse.statusCode());
ReqResp<EventRepository.CreatedRoom> sessionResponse = this.queryRepository(EventRepository.class).createSession();
record Response(UUID uuid) {
}
Response response = new Gson().fromJson(rawResponse.body(), Response.class);
if(sessionResponse.status() != HttpStatus.OK)
throw new ApplianceCommand.Error("Event-Server meldet Fehler: " + sessionResponse.status());
isOpen = true;
roomId = response.uuid;
this.isOpen = true;
this.roomId = sessionResponse.data().uuid();
}
public void joinEvent(Player p) {
if(!isOpen) {
if(!this.isOpen) {
p.sendMessage(Component.text("Zurzeit ist kein Event geöffnet.", NamedTextColor.RED));
return;
}
if(!p.hasPermission("admin") && advertiseStatus == AdvertisementStatus.BEFORE) {
if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.BEFORE) {
p.sendMessage(Component.text("Die Event befinden sich noch in der Vorbereitung.", NamedTextColor.RED));
return;
}
if(!p.hasPermission("admin") && advertiseStatus == AdvertisementStatus.DONE) {
if(!p.hasPermission("admin") && this.advertiseStatus == AdvertisementStatus.DONE) {
p.sendMessage(Component.text("Die Events laufen bereits. Ein nachträgliches Beitreten ist nicht möglich.", NamedTextColor.RED));
return;
}
try {
Main.instance().getLogger().info("Verbinde mit eventserver: " + p.getName());
p.sendMessage(Component.text("Authentifiziere...", NamedTextColor.GREEN));
record Request(UUID player, UUID room) {
}
Request request = new Request(p.getUniqueId(), this.roomId);
HttpRequest queueRoomRequest = HttpRequest.newBuilder()
.uri(new URI(localConfig().getString("api") + "/queueRoom"))
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(request)))
.build();
ReqResp<EventRepository.QueueRoom.Response> queueRespone = this.queryRepository(EventRepository.class)
.queueRoom(new EventRepository.QueueRoom(p.getUniqueId(), this.roomId));
record Response(String error) {
}
HttpResponse<String> rawResponse = eventServerClient.send(queueRoomRequest, HttpResponse.BodyHandlers.ofString());
Main.instance().getLogger().info("Response: " + rawResponse.body());
Response response = new Gson().fromJson(rawResponse.body(), Response.class);
if(rawResponse.statusCode() != 200 || response.error != null) {
p.sendMessage(Component.text("Fehler beim Betreten: " + response.error, NamedTextColor.RED));
if(queueRespone.status() != HttpStatus.OK || queueRespone.data().error() != null) {
p.sendMessage(Component.text("Fehler beim Betreten: " + queueRespone.data().error(), NamedTextColor.RED));
return;
}
p.sendMessage(Component.text("Betrete...", NamedTextColor.GREEN));
PluginMessage.connect(p, localConfig().getString("connect-server-name"));
} catch(URISyntaxException | IOException | InterruptedException e) {
throw new RuntimeException(e);
}
PluginMessage.connect(p, this.localConfig().getString("connect-server-name"));
}
public void endEvent() {
if(!isOpen) throw new ApplianceCommand.Error("Es läuft derzeit kein Event!");
isOpen = false;
if(!this.isOpen) throw new ApplianceCommand.Error("Es läuft derzeit kein Event!");
this.isOpen = false;
}
private void rewardPlayers(RewardConfiguration rewardConfiguration) {
@ -160,10 +132,10 @@ public class Event extends Appliance {
Reward reward = new Reward(uuid, new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.material)), amount));
if(Bukkit.getPlayer(uuid) == null) {
pendingRewards.add(reward);
this.pendingRewards.add(reward);
return;
}
giveReward(reward);
this.giveReward(reward);
});
rewardConfiguration.memorials.forEach(uuid -> {
@ -177,10 +149,10 @@ public class Event extends Appliance {
Main.instance().getAppliance(CustomAdvancements.class).grantAdvancement(Advancements.participateEvent, uuid);
if(Bukkit.getPlayer(uuid) == null) {
pendingRewards.add(memorial);
this.pendingRewards.add(memorial);
return;
}
giveReward(memorial);
this.giveReward(memorial);
});
rewardConfiguration.rewards.keySet().stream()
@ -208,7 +180,7 @@ public class Event extends Appliance {
}
public void advertise() {
advertiseCountdown.cancelIfRunning();
this.advertiseCountdown.cancelIfRunning();
this.advertiseStatus = AdvertisementStatus.ADVERTISED;
IteratorUtil.onlinePlayers(player -> player.sendMessage(
Component.text()
@ -219,7 +191,7 @@ public class Event extends Appliance {
.append(Component.text(", um dem Event beizutreten!")).appendNewline()
.append(Component.text("-".repeat(10), NamedTextColor.GRAY)).appendNewline()
));
advertiseCountdown.start();
this.advertiseCountdown.start();
}
@Override
@ -247,7 +219,7 @@ public class Event extends Appliance {
protected List<Listener> listeners() {
return List.of(
new ApplyPendingRewardsListener(),
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> joinEvent(playerInteractAtEntityEvent.getPlayer())),
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> this.joinEvent(playerInteractAtEntityEvent.getPlayer())),
new DismissInventoryOpenFromHolder(this.villager.getUniqueId())
);
}

View File

@ -1,12 +1,13 @@
package eu.mhsl.craftattack.spawn.appliances.feedback;
import com.google.common.reflect.TypeToken;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.FeedbackRepository;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.feedback.commands.FeedbackCommand;
import eu.mhsl.craftattack.spawn.appliances.feedback.commands.RequestFeedbackCommand;
import eu.mhsl.craftattack.spawn.util.api.HttpStatus;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentBuilder;
import net.kyori.adventure.text.TextComponent;
@ -18,33 +19,27 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.*;
public class Feedback extends Appliance {
private final URI apiEndpoint;
public Feedback() {
super("feedback");
this.apiEndpoint = URI.create(Objects.requireNonNull(localConfig().getString("api")));
}
public void requestFeedback(String eventName, List<Player> receivers, @Nullable String question) {
Map<UUID, String> feedbackUrls = createPersonalizedUrls(
new Request(eventName, receivers.stream().map(Entity::getUniqueId).toList())
ReqResp<Map<UUID, String>> response = this.queryRepository(FeedbackRepository.class).createFeedbackUrls(
new FeedbackRepository.Request(eventName, receivers.stream().map(Entity::getUniqueId).toList())
);
System.out.println(feedbackUrls.toString());
System.out.println(response.toString());
System.out.println(response.status());
if(response.status() != HttpStatus.CREATED) throw new RuntimeException();
Component border = Component.text("-".repeat(40), NamedTextColor.GRAY);
receivers.forEach(player -> {
String feedbackUrl = feedbackUrls.get(player.getUniqueId());
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;
@ -72,23 +67,6 @@ public class Feedback extends Appliance {
});
}
private record Request(String event, List<UUID> users) {}
private final Type responseType = new TypeToken<Map<UUID, String>>(){}.getType();
private Map<UUID, String> createPersonalizedUrls(Request data) {
try(HttpClient client = HttpClient.newHttpClient()) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(this.apiEndpoint)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(data)))
.build();
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
return new Gson().fromJson(httpResponse.body(), responseType);
} catch(IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(

View File

@ -17,7 +17,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.*;
public class Outlawed extends Appliance implements DisplayName.DisplayNamed {
public class Outlawed extends Appliance implements DisplayName.Prefixed {
public final int timeoutInMs = 1000 * 60 * 60 * 6;
private final Map<UUID, Long> timeouts = new HashMap<>();

View File

@ -1,7 +1,8 @@
package eu.mhsl.craftattack.spawn.appliances.report;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.ReportRepository;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import net.kyori.adventure.text.Component;
@ -10,23 +11,14 @@ 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.apache.http.client.utils.URIBuilder;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
public class Report extends Appliance {
public static Component helpText() {
@ -39,158 +31,51 @@ public class Report extends Appliance {
.build();
}
private final URI apiEndpoint;
public Report() {
super("report");
this.apiEndpoint = URI.create(Objects.requireNonNull(localConfig().getString("api")));
}
private record Request(@NotNull UUID reporter, @Nullable UUID reported, String reason) {
}
private record ReportResponse(@NotNull String url) {
}
private record ReportsResponse(List<ReportInfo> from_self, Object to_self) {
}
private record ReportInfo(Reporter reported, @NotNull String subject, boolean draft, @NotNull String status, @NotNull String url) {
}
private record Reporter(@NotNull String username, @NotNull String uuid) {
}
public void reportToUnknown(@NotNull Player issuer) {
Request request = new Request(issuer.getUniqueId(), null, "");
this.issueReport(issuer, request);
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(issuer.getUniqueId(), null, "");
this.createReport(issuer, request);
}
public void reportToKnown(@NotNull Player issuer, @NotNull String targetUsername, @Nullable String reason) {
OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(targetUsername);
if(issuer.getUniqueId().equals(offlinePlayer.getUniqueId())) {
issuer.sendMessage(
Component.text("Du kannst dich nicht selbst reporten.").color(NamedTextColor.RED)
);
issuer.sendMessage(Component.text("Du kannst dich nicht selbst reporten.", NamedTextColor.RED));
return;
}
Request request = new Request(issuer.getUniqueId(), offlinePlayer.getUniqueId(), Optional.ofNullable(reason).orElse(""));
this.issueReport(issuer, request);
}
public void requestReports(Player issuer) {
URIBuilder uriBuilder = new URIBuilder(this.apiEndpoint);
uriBuilder.addParameter("uuid", issuer.getUniqueId().toString());
try(HttpClient client = HttpClient.newHttpClient()) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(uriBuilder.build())
.header("Content-Type", "application/json")
.GET()
.build();
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
this.printReports(issuer, httpResponse);
} catch (IOException | InterruptedException | URISyntaxException e) {
issuer.sendMessage(
Component.text("Internal server description: " + e.getMessage()).color(NamedTextColor.RED)
ReportRepository.ReportCreationInfo request = new ReportRepository.ReportCreationInfo(
issuer.getUniqueId(),
offlinePlayer.getUniqueId(),
Optional.ofNullable(reason).orElse("")
);
throw new RuntimeException(e);
}
this.createReport(issuer, request);
}
private void issueReport(Player issuer, Request reportRequest) {
try(HttpClient client = HttpClient.newHttpClient()) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(this.apiEndpoint)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(reportRequest)))
.build();
private void createReport(Player issuer, ReportRepository.ReportCreationInfo reportRequest) {
ReqResp<ReportRepository.ReportUrl> createdReport = this.queryRepository(ReportRepository.class)
.createReport(reportRequest);
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
this.printResultMessage(issuer, httpResponse);
} catch(IOException | InterruptedException e) {
issuer.sendMessage(
Component.text("Internal server error: " + e.getMessage()).color(NamedTextColor.RED)
);
throw new RuntimeException(e);
}
}
private void printReports(Player issuer, HttpResponse<String> httpResponse) {
if(httpResponse.statusCode() != 200) {
Main.logger().warning("Failed to request Reports: " + httpResponse.statusCode());
issuer.sendMessage(
Component.text()
.append(Component.text("Interner Serverfehler beim abfragen der Reports.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
);
return;
}
List<ReportInfo> reports = new Gson().fromJson(httpResponse.body(), ReportsResponse.class).from_self;
reports.removeIf(reportInfo -> reportInfo.draft);
if(reports.isEmpty()) {
issuer.sendMessage(
Component.text()
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY))
);
return;
}
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text();
component.append(
Component.newline()
.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD))
.appendNewline()
);
reports.forEach(reportInfo -> {
component.append(
Component.text()
.append(Component.text(" - ", NamedTextColor.WHITE))
.append(Component.text(reportInfo.reported.username, NamedTextColor.WHITE))
.append(Component.text(String.format(": %s", reportInfo.subject), NamedTextColor.GRAY))
.clickEvent(ClickEvent.openUrl(reportInfo.url))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD)))
);
component.appendNewline();
});
issuer.sendMessage(component.build());
}
private void printResultMessage(Player issuer, HttpResponse<String> httpResponse) {
switch(httpResponse.statusCode()) {
switch(createdReport.status()) {
case 201:
ReportResponse createdReport = new Gson().fromJson(httpResponse.body(), ReportResponse.class);
issuer.sendMessage(
Component.text()
.append(Component.text("\\/".repeat(20), NamedTextColor.DARK_GRAY))
.appendNewline()
.appendNewline()
.append(Component.text("!!! Report hier fertigstellen !!!", NamedTextColor.GOLD))
.append(Component.text("⚠ Der Report muss über den folgenden Link fertiggestellt werden!", NamedTextColor.GOLD))
.appendNewline()
.appendNewline()
.append(
Component
.text(" > Hier klicken < ", NamedTextColor.GREEN)
.hoverEvent(HoverEvent.showText(Component.text(createdReport.url).color(NamedTextColor.GREEN)))
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.url))
.text(createdReport.data().url(), NamedTextColor.GRAY) // URL mit Weltkugel-Emoji
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.data().url()))
)
.appendNewline()
.appendNewline()
.append(
Component
.text(createdReport.url, NamedTextColor.GRAY)
.clickEvent(ClickEvent.clickEvent(ClickEvent.Action.OPEN_URL, createdReport.url))
)
.appendNewline()
.append(Component.text("Ohne das Fertigstellen des Reports wird dieser nicht bearbeitet!", NamedTextColor.DARK_RED))
.appendNewline()
.append(Component.text("/\\".repeat(20), NamedTextColor.DARK_GRAY))
);
@ -209,7 +94,7 @@ public class Report extends Appliance {
case 401:
default:
Main.logger().warning("Failed to request Report: " + httpResponse.statusCode());
Main.logger().warning("Failed to request Report: " + createdReport.status());
issuer.sendMessage(
Component.text()
.append(Component.text("Interner Serverfehler beim anlegen des Reports.", NamedTextColor.RED))
@ -220,6 +105,61 @@ public class Report extends Appliance {
}
}
public void queryReports(Player issuer) {
ReqResp<ReportRepository.PlayerReports> userReports = this.queryRepository(ReportRepository.class)
.queryReports(issuer.getUniqueId());
if(userReports.status() != 200) {
Main.logger().warning("Failed to request Reports: " + userReports.status());
issuer.sendMessage(
Component.text()
.append(Component.text("Interner Serverfehler beim abfragen der Reports.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Bitte melde dich bei einem Admin!", NamedTextColor.RED))
);
return;
}
List<ReportRepository.PlayerReports.Report> reports = userReports
.data()
.from_self()
.stream()
.filter(report -> !report.draft())
.toList()
.reversed();
if(reports.isEmpty()) {
issuer.sendMessage(
Component.text()
.append(Component.text("Du hast noch niemanden reportet.", NamedTextColor.RED))
.appendNewline()
.append(Component.text("Um jemanden zu melden, nutze /report", NamedTextColor.GRAY))
);
return;
}
ComponentBuilder<TextComponent, TextComponent.Builder> component = Component.text()
.append(Component.newline())
.append(Component.text("Von dir erstellte Reports: ", NamedTextColor.GOLD))
.appendNewline();
reports.forEach(report -> {
component
.append(Component.text(" - ", NamedTextColor.WHITE))
.append(
report.reported() != null
? Component.text(report.reported().username(), NamedTextColor.WHITE)
: Component.text("Unbekannt", NamedTextColor.YELLOW)
)
.append(Component.text(String.format(": %s", report.subject()), NamedTextColor.GRAY))
.clickEvent(ClickEvent.openUrl(report.url()))
.hoverEvent(HoverEvent.showText(Component.text("Klicke, um den Report einzusehen.", NamedTextColor.GOLD)));
component.appendNewline();
});
issuer.sendMessage(component.build());
}
@Override
@NotNull
protected List<ApplianceCommand<?>> commands() {

View File

@ -14,6 +14,6 @@ public class ReportsCommand extends ApplianceCommand.PlayerChecked<Report> {
@Override
protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) {
sender.sendMessage(ComponentUtil.pleaseWait());
getAppliance().requestReports(getPlayer());
getAppliance().queryReports(getPlayer());
}
}

View File

@ -1,5 +1,6 @@
package eu.mhsl.craftattack.spawn.appliances.settings;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.settings.datatypes.Setting;
@ -36,7 +37,7 @@ public class Settings extends Appliance {
public static Settings instance() {
if(settingsInstance != null) return settingsInstance;
Settings.settingsInstance = queryAppliance(Settings.class);
Settings.settingsInstance = Main.instance().getAppliance(Settings.class);
return settingsInstance;
}

View File

@ -17,7 +17,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SleepTag extends Appliance implements DisplayName.DisplayNamed {
public class SleepTag extends Appliance implements DisplayName.Prefixed {
private final Set<Player> sleepingPlayers = new HashSet<>();
@Override

View File

@ -1,41 +1,29 @@
package eu.mhsl.craftattack.spawn.appliances.whitelist;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.api.client.ReqResp;
import eu.mhsl.craftattack.spawn.api.client.repositories.WhitelistRepository;
import eu.mhsl.craftattack.spawn.api.server.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed;
import eu.mhsl.craftattack.spawn.util.api.HttpStatus;
import eu.mhsl.craftattack.spawn.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
import org.apache.http.client.utils.URIBuilder;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
public class Whitelist extends Appliance {
private record UserData(UUID uuid, String username, String firstname, String lastname, Long banned_until,
Long outlawed_until) {
}
private final URI apiEndpoint = URI.create(Objects.requireNonNull(localConfig().getString("api")));
public Whitelist() {
super("whitelist");
}
@ -44,12 +32,12 @@ public class Whitelist extends Appliance {
try {
Main.instance().getLogger().info(String.format("Running integrityCheck for %s", player.getName()));
boolean overrideCheck = localConfig().getBoolean("overrideIntegrityCheck", false);
UserData user = overrideCheck
? new UserData(player.getUniqueId(), player.getName(), "", "", 0L, 0L)
WhitelistRepository.UserData user = overrideCheck
? new WhitelistRepository.UserData(player.getUniqueId(), player.getName(), "", "", 0L, 0L)
: this.fetchUserData(player.getUniqueId());
if(timestampRelevant(user.banned_until)) {
Instant bannedDate = new Date(user.banned_until * 1000L)
if (timestampRelevant(user.banned_until())) {
Instant bannedDate = new Date(user.banned_until() * 1000L)
.toInstant()
.plus(1, ChronoUnit.HOURS);
@ -64,16 +52,16 @@ public class Whitelist extends Appliance {
);
}
queryAppliance(Outlawed.class).updateForcedStatus(player, timestampRelevant(user.outlawed_until));
queryAppliance(Outlawed.class).updateForcedStatus(player, timestampRelevant(user.outlawed_until()));
String purePlayerName = Floodgate.isBedrock(player)
? Floodgate.getBedrockPlayer(player).getUsername()
: player.getName();
if(!user.username.trim().equalsIgnoreCase(purePlayerName))
if (!user.username().trim().equalsIgnoreCase(purePlayerName))
throw new DisconnectInfo.Throwable(
"Nutzername geändert",
String.format("Der Name '%s' stimmt nicht mit '%s' überein.", user.username, player.getName()),
String.format("Der Name '%s' stimmt nicht mit '%s' überein.", user.username(), player.getName()),
"Bitte kontaktiere einen Admin, um Deine Anmeldedaten zu aktualisieren!",
player.getUniqueId()
);
@ -96,20 +84,10 @@ public class Whitelist extends Appliance {
return timestamp > System.currentTimeMillis() / 1000L;
}
private UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable {
URIBuilder uriBuilder = new URIBuilder(apiEndpoint);
uriBuilder.addParameter("uuid", uuid.toString());
private WhitelistRepository.UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable {
ReqResp<WhitelistRepository.UserData> response = this.queryRepository(WhitelistRepository.class).getUserData(uuid);
try(HttpClient client = HttpClient.newHttpClient()) {
HttpRequest httpRequest = HttpRequest.newBuilder()
.uri(uriBuilder.build())
.header("Content-Type", "application/json")
.GET()
.build();
HttpResponse<String> httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
if(httpResponse.statusCode() == 404)
if(response.status() == HttpStatus.NOT_FOUND)
throw new DisconnectInfo.Throwable(
"Nicht angemeldet",
"Du bist derzeit nicht als Teilnehmer des CraftAttack-Projektes registriert!",
@ -117,11 +95,7 @@ public class Whitelist extends Appliance {
uuid
);
return new Gson().fromJson(httpResponse.body(), UserData.class);
} catch(IOException | InterruptedException | URISyntaxException e) {
throw new RuntimeException(e);
}
return response.data();
}
@Override

View File

@ -16,7 +16,7 @@ import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
public class YearRank extends Appliance implements DisplayName.DisplayNamed {
public class YearRank extends Appliance implements DisplayName.Prefixed {
record CraftAttackYear(String name) {}
private final Map<UUID, List<CraftAttackYear>> rankMap = new HashMap<>();

View File

@ -0,0 +1,23 @@
package eu.mhsl.craftattack.spawn.util.api;
import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.configuration.ConfigurationSection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
public class EventApiUtil {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("event"));
public final static String basePath = apiConfig.getString("api");
public static URI getBaseUri() {
Objects.requireNonNull(basePath);
try {
return new URI(basePath);
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
}

View File

@ -0,0 +1,14 @@
package eu.mhsl.craftattack.spawn.util.api;
public class HttpStatus {
public static final int OK = 200;
public static final int CREATED = 201;
public static final int ACCEPTED = 202;
public static final int NO_CONTENT = 204;
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int INTERNAL_SERVER_ERROR = 500;
public static final int SERVICE_UNAVAILABLE = 503;
}

View File

@ -0,0 +1,28 @@
package eu.mhsl.craftattack.spawn.util.api;
import eu.mhsl.craftattack.spawn.config.Configuration;
import org.apache.http.client.utils.URIBuilder;
import org.bukkit.configuration.ConfigurationSection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
public class WebsiteApiUtil {
private final static ConfigurationSection apiConfig = Objects.requireNonNull(Configuration.cfg.getConfigurationSection("api"));
public final static String basePath = apiConfig.getString("baseurl");
public final static String apiSecret = apiConfig.getString("secret");
public static URI getBaseUri() {
Objects.requireNonNull(basePath);
try {
return new URI(basePath);
} catch(URISyntaxException e) {
throw new RuntimeException(e);
}
}
public static void withAuthorizationSecret(URIBuilder builder) {
builder.addParameter("secret", apiSecret);
}
}

View File

@ -22,6 +22,10 @@ public class ComponentUtil {
return Component.text("Bitte warte einen Augenblick...", NamedTextColor.GRAY);
}
public static Component clearedSpace() {
return Component.text(" ").hoverEvent(Component.empty().asHoverEvent());
}
public static TextComponent appendWithNewline(Component a, Component b) {
return Component.text().append(a.appendNewline().append(b)).build();
}

View File

@ -2,6 +2,10 @@ plugin:
disabledAppliances:
- NameOfApplianceClass
api:
secret: secret
baseurl: https://mhsl.eu/craftattack/api/
worldMuseum:
uuid:
connect-server-name: worldmuseum
@ -24,9 +28,6 @@ countdown:
worldborder-before: 37
worldborder-after: 0
report:
api: https://mhsl.eu/craftattack/api/report
event:
api: http://10.20.6.5:8080/
connect-server-name: event
@ -50,7 +51,6 @@ playerLimit:
whitelist:
overrideIntegrityCheck: false
api: https://mhsl.eu/craftattack/api/user
tablist:
interface: eth0