diff --git a/build.gradle b/build.gradle index 146bf4c..bd6b27c 100644 --- a/build.gradle +++ b/build.gradle @@ -56,5 +56,5 @@ tasks.register('copyJarToServer', Exec) { dependsOn shadowJar mustRunAfter shadowJar - commandLine 'scp', 'build/libs/spawn-1.0-all.jar', 'root@10.20.6.1:/root/server/plugins' + commandLine 'scp', 'build/libs/spawn-1.0-all.jar', 'root@10.20.6.1:/home/minecraft/server/plugins' } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/Main.java b/src/main/java/eu/mhsl/craftattack/spawn/Main.java index 3a03a1f..1558e88 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/Main.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/Main.java @@ -1,10 +1,12 @@ package eu.mhsl.craftattack.spawn; +import eu.mhsl.craftattack.spawn.aggregates.displayName.DisplayName; import eu.mhsl.craftattack.spawn.api.HttpServer; import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; import eu.mhsl.craftattack.spawn.appliances.kick.Kick; import eu.mhsl.craftattack.spawn.appliances.chatMessages.ChatMessages; +import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; import eu.mhsl.craftattack.spawn.appliances.panicBan.PanicBan; import eu.mhsl.craftattack.spawn.appliances.projectStart.ProjectStart; import eu.mhsl.craftattack.spawn.appliances.debug.Debug; @@ -51,8 +53,11 @@ public final class Main extends JavaPlugin { new Restart(), new Kick(), new PanicBan(), + new Outlawed(), + new DisplayName(), new Debug() ); + Bukkit.getLogger().info("Loading appliances..."); appliances.forEach(appliance -> { Bukkit.getLogger().info("Enabling " + appliance.getClass().getSimpleName()); @@ -81,7 +86,11 @@ public final class Main extends JavaPlugin { } public T getAppliance(Class clazz) { - return this.appliances.stream().filter(clazz::isInstance).map(clazz::cast).findFirst().orElseThrow(); + return this.appliances.stream() + .filter(clazz::isInstance) + .map(clazz::cast) + .findFirst() + .orElseThrow(() -> new RuntimeException(String.format("Appliance %s not loaded or instantiated!", clazz))); } @SuppressWarnings("unchecked") diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java b/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java new file mode 100644 index 0000000..368d0ed --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/aggregate/Aggregate.java @@ -0,0 +1,10 @@ +package eu.mhsl.craftattack.spawn.aggregate; + +import eu.mhsl.craftattack.spawn.Main; +import eu.mhsl.craftattack.spawn.appliance.Appliance; + +public abstract class Aggregate extends Appliance { + public T queryAppliance(Class clazz) { + return Main.instance().getAppliance(clazz); + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java b/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java new file mode 100644 index 0000000..a87260e --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayName.java @@ -0,0 +1,53 @@ +package eu.mhsl.craftattack.spawn.aggregates.displayName; + +import eu.mhsl.craftattack.spawn.aggregate.Aggregate; +import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker; +import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarkerListener; +import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.ComponentBuilder; +import net.kyori.adventure.text.TextComponent; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.Supplier; + +public class DisplayName extends Aggregate { + public void update(Player player) { + TextColor playerColor = queryAppliance(AdminMarker.class).getPlayerColor(player); + List> prefixes = List.of( + () -> queryAppliance(Outlawed.class).getNamePrefix(player) + ); + + ComponentBuilder playerName = Component.text(); + prefixes.forEach(supplier -> { + Component prefix = supplier.get(); + if(prefix == null) return; + playerName.append(prefix).append(Component.text(" ")); + }); + + playerName.append(Component.text(player.getName(), playerColor)); + + setGlobal(player, playerName.build()); + } + + private void setGlobal(Player player, Component component) { + try { + player.customName(component); + 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 + protected @NotNull List eventHandlers() { + return List.of(new AdminMarkerListener()); + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java b/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java new file mode 100644 index 0000000..b27ccd4 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/aggregates/displayName/DisplayNameUpdateListener.java @@ -0,0 +1,13 @@ +package eu.mhsl.craftattack.spawn.aggregates.displayName; + +import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerJoinEvent; + +public class DisplayNameUpdateListener extends ApplianceListener { + @EventHandler(priority = EventPriority.LOWEST) + public void onJoin(PlayerJoinEvent event) { + getAppliance().update(event.getPlayer()); + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java b/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java index 607f3c4..36bd3a6 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/api/HttpServer.java @@ -51,7 +51,8 @@ public class HttpServer { public void post(String path, Class clazz, RequestProvider onCall) { Spark.post(this.buildRoute(path), (req, resp) -> { - TRequest parsed = new Gson().fromJson(req.body(), clazz); + Main.instance().getLogger().info(req.body()); + TRequest parsed = HttpServer.this.gson.fromJson(req.body(), clazz); return this.process(() -> onCall.apply(parsed, req)); }); } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarker.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarker.java index 8235f1d..0f5552d 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarker.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarker.java @@ -1,12 +1,19 @@ package eu.mhsl.craftattack.spawn.appliances.adminMarker; import eu.mhsl.craftattack.spawn.appliance.Appliance; +import net.kyori.adventure.text.format.TextColor; +import org.bukkit.Color; +import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.jetbrains.annotations.NotNull; import java.util.List; public class AdminMarker extends Appliance { + public TextColor getPlayerColor(Player player) { + if (player.hasPermission("chatcolor")) return TextColor.color(Color.AQUA.asRGB()); // TODO read permission from config + return TextColor.color(Color.WHITE.asRGB()); + } @Override protected @NotNull List eventHandlers() { diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarkerListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarkerListener.java index d76120d..b3683bb 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarkerListener.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/adminMarker/AdminMarkerListener.java @@ -7,17 +7,12 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerJoinEvent; -import java.util.Optional; - - public class AdminMarkerListener extends ApplianceListener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { Player p = event.getPlayer(); - p.customName(Optional.ofNullable(p.customName()).orElse(p.displayName()).color(getPlayerColor(p))); - p.displayName(p.displayName().color(getPlayerColor(p))); - p.playerListName(p.playerListName().color(getPlayerColor(p))); + } private TextColor getPlayerColor(Player player) { diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java index 0a9cdcb..e921a4e 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/event/Event.java @@ -22,6 +22,7 @@ import org.bukkit.entity.Player; import org.bukkit.entity.Villager; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -30,10 +31,7 @@ import java.net.URISyntaxException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; public class Event extends Appliance { public DisplayVillager.ConfigBound villager; @@ -42,8 +40,8 @@ public class Event extends Appliance { private final HttpClient eventServerClient = HttpClient.newHttpClient(); private final List pendingRewards = new ArrayList<>(); - record RewardConfiguration(String material, Map rewards) {} - record Reward(UUID playerUuid, Material material, int amount) {} + record RewardConfiguration(String memorialMaterial, String memorialTitle, String memorialLore, List memorials, String material, Map rewards) {} + record Reward(UUID playerUuid, ItemStack itemStack) {} public Event() { super("event"); @@ -87,6 +85,7 @@ public class Event extends Appliance { } 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); @@ -95,9 +94,13 @@ public class Event extends Appliance { .POST(HttpRequest.BodyPublishers.ofString(new Gson().toJson(request))) .build(); - HttpResponse rawResponse = eventServerClient.send(queueRoomRequest, HttpResponse.BodyHandlers.discarding()); - if(rawResponse.statusCode() != 200) { - p.sendMessage(Component.text("Fehler beim beitreten: " + rawResponse.statusCode(), NamedTextColor.RED)); + record Response(String error) {} + HttpResponse 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)); return; } @@ -116,24 +119,36 @@ public class Event extends Appliance { private void rewardPlayers(RewardConfiguration rewardConfiguration) { rewardConfiguration.rewards.forEach((uuid, amount) -> { - Reward reward = new Reward(uuid, Material.matchMaterial(rewardConfiguration.material), amount); - if(reward.material == null) throw new RuntimeException("Material not found"); + Reward reward = new Reward(uuid, new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.material)), amount)); if(Bukkit.getPlayer(uuid) == null) { pendingRewards.add(reward); return; } - giveReward(reward); }); + + for (UUID uuid : rewardConfiguration.memorials) { + ItemStack memorialItem = new ItemStack(Objects.requireNonNull(Material.matchMaterial(rewardConfiguration.memorialMaterial))); + ItemMeta meta = memorialItem.getItemMeta(); + meta.displayName(Component.text(rewardConfiguration.memorialTitle, NamedTextColor.GOLD)); + meta.lore(List.of(Component.text(rewardConfiguration.memorialLore, NamedTextColor.AQUA))); + memorialItem.setItemMeta(meta); + Reward memorial = new Reward(uuid, memorialItem); + + if (Bukkit.getPlayer(uuid) == null) { + pendingRewards.add(memorial); + continue; + } + giveReward(memorial); + } } private void giveReward(Reward reward) { Player player = Bukkit.getPlayer(reward.playerUuid); if(player == null) throw new RuntimeException("Cannot reward offline playerUuid!"); - ItemStack rewardStack = new ItemStack(reward.material, reward.amount); - Map remaining = player.getInventory().addItem(rewardStack); + Map remaining = player.getInventory().addItem(reward.itemStack); Bukkit.getScheduler().runTask( Main.instance(), () -> remaining.values().forEach(remainingStack -> player.getWorld().dropItem(player.getLocation(), remainingStack)) diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawForcedException.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawForcedException.java new file mode 100644 index 0000000..d3fb430 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawForcedException.java @@ -0,0 +1,7 @@ +package eu.mhsl.craftattack.spawn.appliances.outlawed; + +public class OutlawForcedException extends Exception { + public OutlawForcedException(String message) { + super(message); + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java new file mode 100644 index 0000000..2e55f90 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/Outlawed.java @@ -0,0 +1,117 @@ +package eu.mhsl.craftattack.spawn.appliances.outlawed; + +import eu.mhsl.craftattack.spawn.Main; +import eu.mhsl.craftattack.spawn.aggregates.displayName.DisplayName; +import eu.mhsl.craftattack.spawn.appliance.Appliance; +import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; +import eu.mhsl.craftattack.spawn.appliances.whitelist.Whitelist; +import eu.mhsl.craftattack.spawn.config.Configuration; +import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +public class Outlawed extends Appliance { + public enum Status { + DISABLED, + VOLUNTARILY, + FORCED + } + + private final Map playerStatusMap = new WeakHashMap<>(); + private final String voluntarilyEntry = "voluntarily"; + public Outlawed() { + super("outlawed"); + Bukkit.getScheduler().runTaskTimerAsynchronously( + Main.instance(), + () -> { + playerStatusMap.forEach((player, status) -> { + if(status != Status.FORCED) return; + try { + Main.instance().getAppliance(Whitelist.class).integrityCheck(player); + } catch (DisconnectInfo.Throwable e) { + Bukkit.getScheduler().runTask(Main.instance(), () -> e.getDisconnectScreen().applyKick(player)); + } + }); + }, + 20*60, + 20*60*5 + ); + } + + public void switchLawStatus(Player player) throws OutlawForcedException { + if(getLawStatus(player).equals(Status.FORCED)) { + throw new OutlawForcedException("Dein Vogelfreistatus wurde als Strafe auferlegt und kann daher nicht verändert werden."); + } + + setLawStatus(player, isOutlawed(player) ? Status.DISABLED : Status.VOLUNTARILY); + } + + public void updateForcedStatus(Player player, boolean forced) { + setLawStatus(player, forced ? Status.FORCED : getLawStatus(player) == Status.FORCED ? Status.DISABLED : getLawStatus(player)); + } + + public Status getLawStatus(Player player) { + Status status = playerStatusMap.computeIfAbsent(player, p -> { + if(localConfig().getStringList(voluntarilyEntry).contains(p.getUniqueId().toString())) return Status.VOLUNTARILY; + return Status.DISABLED; + }); + return status; + } + + public void setLawStatus(Player player, Status status) { + playerStatusMap.put(player, status); + Main.instance().getAppliance(DisplayName.class).update(player); + + List newList = localConfig().getStringList(voluntarilyEntry); + if(status.equals(Status.VOLUNTARILY)) { + newList.add(player.getUniqueId().toString()); + } else { + newList.remove(player.getUniqueId().toString()); + } + localConfig().set(voluntarilyEntry, newList.stream().distinct().toList()); + Configuration.saveChanges(); + + } + + public boolean isOutlawed(Player player) { + return getLawStatus(player) != Status.DISABLED; + } + + public Component getStatusDescription(Status status) { + return switch (status) { + case DISABLED -> Component.text("Vogelfreistatus inaktiv: ", NamedTextColor.GREEN) + .append(Component.text("Es gelten die Standart 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)); + }; + } + + public Component getNamePrefix(Player player) { + if(isOutlawed(player)) { + return Component.text("[☠]", NamedTextColor.RED) + .hoverEvent(HoverEvent.showText(Component.text("Vogelfreie Spieler dürfen ohne Grund angegriffen werden!"))); + } + + return null; + } + + @Override + protected @NotNull List> commands() { + return List.of(new OutlawedCommand()); + } + + @Override + protected @NotNull List eventHandlers() { + return List.of(new OutlawedReminderListener()); + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedCommand.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedCommand.java new file mode 100644 index 0000000..e1c394d --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedCommand.java @@ -0,0 +1,27 @@ +package eu.mhsl.craftattack.spawn.appliances.outlawed; + +import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; + +public class OutlawedCommand extends ApplianceCommand.PlayerChecked { + public OutlawedCommand() { + super("vogelfrei"); + } + + @Override + protected void execute(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) throws Exception { + try { + getAppliance().switchLawStatus(getPlayer()); + sender.sendMessage( + getAppliance() + .getStatusDescription(getAppliance().getLawStatus(getPlayer())) + ); + } catch (OutlawForcedException e) { + sender.sendMessage(Component.text(e.getMessage(), NamedTextColor.RED)); + } + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedReminderListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedReminderListener.java new file mode 100644 index 0000000..f5d522e --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/outlawed/OutlawedReminderListener.java @@ -0,0 +1,14 @@ +package eu.mhsl.craftattack.spawn.appliances.outlawed; + +import eu.mhsl.craftattack.spawn.appliance.ApplianceListener; +import org.bukkit.event.EventHandler; +import org.bukkit.event.player.PlayerJoinEvent; + +public class OutlawedReminderListener extends ApplianceListener { + @EventHandler + public void onJoin(PlayerJoinEvent e) { + if(getAppliance().isOutlawed(e.getPlayer())) { + e.getPlayer().sendMessage(getAppliance().getStatusDescription(getAppliance().getLawStatus(e.getPlayer()))); + } + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java index 1da05ff..2440ba6 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/projectStart/ProjectStart.java @@ -7,6 +7,7 @@ import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartCom import eu.mhsl.craftattack.spawn.appliances.projectStart.command.ProjectStartResetCommand; import eu.mhsl.craftattack.spawn.appliances.projectStart.listener.PlayerInvincibleListener; import eu.mhsl.craftattack.spawn.config.Configuration; +import eu.mhsl.craftattack.spawn.util.entity.PlayerUtils; import eu.mhsl.craftattack.spawn.util.world.BlockCycle; import eu.mhsl.craftattack.spawn.util.text.ComponentUtil; import eu.mhsl.craftattack.spawn.util.text.Countdown; @@ -21,13 +22,21 @@ import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Map; +import java.util.Objects; import static java.util.Map.entry; import static org.bukkit.Sound.MUSIC_DISC_PIGSTEP; public class ProjectStart extends Appliance { private final int startMusicAt = 137; - private final Location glassLocation = new Location(Bukkit.getWorld("world"), -224, 67, -368); + private final World startWorld = Bukkit.getWorld("world"); + private final Location glassLocation = new Location(startWorld, -224, 67, -368); + private final List netherFireLocations = List.of( + new Location(startWorld, -211, 68, -354), + new Location(startWorld, -209, 68, -356), + new Location(startWorld, -207, 68, -354), + new Location(startWorld, -209, 68, -352) + ); private final Countdown countdown = new Countdown( localConfig().getInt("countdown"), @@ -102,6 +111,9 @@ public class ProjectStart extends Appliance { IteratorUtil.worlds(World::getWorldBorder, worldBorder -> worldBorder.setSize(worldBorder.getMaxSize())); IteratorUtil.worlds(world -> IteratorUtil.setGameRules(gameRulesAfterStart, false)); + IteratorUtil.worlds(world -> world.setFullTime(0)); + + netherFireLocations.forEach(location -> Objects.requireNonNull(startWorld).getBlockAt(location).setType(Material.FIRE)); Bukkit.getOnlinePlayers().forEach(player -> { player.setFoodLevel(20); @@ -114,6 +126,9 @@ public class ProjectStart extends Appliance { player.playSound(Sound.sound(org.bukkit.Sound.ITEM_GOAT_HORN_SOUND_5, Sound.Source.MASTER, 500f, 1f)); player.sendMessage(Component.text("Viel Spaß bei CraftAttack!", NamedTextColor.GREEN)); + + player.setStatistic(Statistic.TIME_SINCE_REST, 0); + PlayerUtils.resetStatistics(player); }); Bukkit.getServer().advancementIterator().forEachRemaining( @@ -140,6 +155,7 @@ public class ProjectStart extends Appliance { IteratorUtil.worlds(world -> world, world -> IteratorUtil.setGameRules(gameRulesAfterStart, true)); + blockCycle.reset(); } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java index 2744493..32acae4 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/PlayerJoinListener.java @@ -9,7 +9,7 @@ public class PlayerJoinListener extends ApplianceListener { @EventHandler public void preLoginEvent(PlayerLoginEvent preLoginEvent) { try { - getAppliance().login(preLoginEvent); + getAppliance().integrityCheck(preLoginEvent.getPlayer()); } catch (DisconnectInfo.Throwable e) { preLoginEvent.disallow( PlayerLoginEvent.Result.KICK_WHITELIST, diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java index fbb7dd9..f17f143 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/whitelist/Whitelist.java @@ -2,12 +2,16 @@ 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.appliance.Appliance; +import eu.mhsl.craftattack.spawn.appliances.outlawed.Outlawed; 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.bukkit.event.player.PlayerLoginEvent; +import org.eclipse.jetty.websocket.server.WebSocketHandler; import org.jetbrains.annotations.NotNull; import java.io.IOException; @@ -16,13 +20,15 @@ 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.UUID; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.util.*; import java.util.logging.Level; public class Whitelist extends Appliance { - private record UserData(UUID uuid, String username, String firstname, String lastname) {} + 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"))); @@ -30,23 +36,42 @@ public class Whitelist extends Appliance { super("whitelist"); } - public void login(PlayerLoginEvent login) throws DisconnectInfo.Throwable { + public void integrityCheck(Player player) throws DisconnectInfo.Throwable { try { - UserData data = this.fetchUserData(login.getPlayer().getUniqueId()); + Main.instance().getLogger().info(String.format("Running integrityCheck for %s", player.getName())); + UserData user = this.fetchUserData(player.getUniqueId()); - String purePlayerName; - if(Floodgate.isBedrock(login.getPlayer())) { - purePlayerName = Floodgate.getBedrockPlayer(login.getPlayer()).getUsername(); - } else { - purePlayerName = login.getPlayer().getName(); + if(timestampRelevant(user.banned_until)) { + Instant bannedDate = new Date(user.banned_until * 1000L) + .toInstant() + .plus(1, ChronoUnit.HOURS); + + DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("dd.MM.yyyy").withZone(ZoneOffset.UTC); + DateTimeFormatter timeFormat = DateTimeFormatter.ofPattern("HH:mm").withZone(ZoneOffset.UTC); + + 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)), + "Wende dich an einen Admin für weitere Informationen.", + player.getUniqueId() + ); } - if(!data.username.equalsIgnoreCase(purePlayerName)) + Main.instance().getAppliance(Outlawed.class).updateForcedStatus(player, timestampRelevant(user.outlawed_until)); + + String purePlayerName; + if(Floodgate.isBedrock(player)) { + purePlayerName = Floodgate.getBedrockPlayer(player).getUsername(); + } else { + purePlayerName = player.getName(); + } + + if(!user.username.trim().equalsIgnoreCase(purePlayerName)) throw new DisconnectInfo.Throwable( "Nutzername geändert", - String.format("Der Name '%s' stimmt nicht mit '%s' überein.", data.username, login.getPlayer().getName()), + String.format("Der Name '%s' stimmt nicht mit '%s' überein.", user.username, player.getName()), "Bitte kontaktiere einen Admin, um Deine Anmeldedaten zu aktualisieren!", - login.getPlayer().getUniqueId() + player.getUniqueId() ); } catch (DisconnectInfo.Throwable e) { @@ -55,13 +80,18 @@ public class Whitelist extends Appliance { Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage); throw new DisconnectInfo.Throwable( "Interner Serverfehler", - "Deine Zugangsdaten konnten nicht abgerufen werden.", + "Deine Anmeldedaten konnten nicht abgerufen/ überprüft werden.", "Versuche es später erneut oder kontaktiere einen Admin!", - login.getPlayer().getUniqueId() + player.getUniqueId() ); } } + private boolean timestampRelevant(Long timestamp) { + if(timestamp == null) return false; + return timestamp > System.currentTimeMillis() / 1000L; + } + private UserData fetchUserData(UUID uuid) throws DisconnectInfo.Throwable { URIBuilder uriBuilder = new URIBuilder(apiEndpoint); uriBuilder.addParameter("uuid", uuid.toString()); @@ -76,7 +106,7 @@ public class Whitelist extends Appliance { HttpResponse httpResponse = client.send(httpRequest, HttpResponse.BodyHandlers.ofString()); - if(httpResponse.statusCode() == 400) + if(httpResponse.statusCode() == 404) throw new DisconnectInfo.Throwable( "Nicht angemeldet", "Du bist derzeit nicht als Teilnehmer des CraftAttack-Projektes registriert!", @@ -91,6 +121,23 @@ public class Whitelist extends Appliance { } } + @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.integrityCheck(player); + } catch (DisconnectInfo.Throwable e) { + e.getDisconnectScreen().applyKick(player); + } + } + return HttpServer.nothing; + }); + } + @Override protected @NotNull List eventHandlers() { return List.of( diff --git a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java index 0d6bef5..5443817 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/appliances/worldmuseum/WorldMuseum.java @@ -3,6 +3,7 @@ package eu.mhsl.craftattack.spawn.appliances.worldmuseum; import eu.mhsl.craftattack.spawn.appliance.Appliance; import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand; import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager; +import eu.mhsl.craftattack.spawn.util.server.Floodgate; import eu.mhsl.craftattack.spawn.util.server.PluginMessage; import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder; import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener; @@ -12,6 +13,8 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.*; import org.bukkit.event.Listener; +import org.geysermc.cumulus.form.SimpleForm; +import org.geysermc.cumulus.form.util.FormBuilder; import org.jetbrains.annotations.NotNull; import java.util.List; @@ -39,6 +42,19 @@ public class WorldMuseum extends Appliance { } public void handleVillagerInteraction(Player player) { + if(Floodgate.isBedrock(player)) { + Floodgate.runBedrockOnly(player, floodgatePlayer -> { + floodgatePlayer.sendForm( + SimpleForm.builder() + .title("Nicht unterstützt") + .content("Bedrock-Spieler werden derzeit für das Weltenmuseum aus Kompatiblitätsgründen nicht zugelassen! Tut uns Leid.") + .button("Ok") + .build() + ); + }); + return; + } + Bukkit.getLogger().info("Sending" + player.getName() + " to WorldMuseum"); PluginMessage.connect(player, localConfig().getString("connect-server-name")); } diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/entity/PlayerUtils.java b/src/main/java/eu/mhsl/craftattack/spawn/util/entity/PlayerUtils.java new file mode 100644 index 0000000..6f1c826 --- /dev/null +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/entity/PlayerUtils.java @@ -0,0 +1,32 @@ +package eu.mhsl.craftattack.spawn.util.entity; + +import org.bukkit.Material; +import org.bukkit.Statistic; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.Player; + +public class PlayerUtils { + public static void resetStatistics(Player player) { + for(Statistic statistic : Statistic.values()) { + for(Material material : Material.values()) { + try { + player.setStatistic(statistic, material, 0); + } catch (IllegalArgumentException e) { + break; + } + } + + for(EntityType entityType : EntityType.values()) { + try { + player.setStatistic(statistic, entityType, 0); + } catch (IllegalArgumentException e) { + break; + } + } + + try{ + player.setStatistic(statistic, 0); + } catch (IllegalArgumentException ignored){} + } + } +} diff --git a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java index d41f9e1..eac5385 100644 --- a/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java +++ b/src/main/java/eu/mhsl/craftattack/spawn/util/text/DisconnectInfo.java @@ -1,14 +1,16 @@ package eu.mhsl.craftattack.spawn.util.text; +import eu.mhsl.craftattack.spawn.Main; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.UUID; public record DisconnectInfo(String error, String description, String help, UUID user) { public void applyKick(Player player) { - player.kick(this.getComponent()); + Bukkit.getScheduler().runTask(Main.instance(), () -> player.kick(this.getComponent())); } public Component getComponent() { return Component.text() diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index e3883f3..9b5866b 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -33,4 +33,5 @@ commands: scheduleRestart: cancelRestart: kick: - panicBan: \ No newline at end of file + panicBan: + vogelfrei: \ No newline at end of file