diff --git a/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java b/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java index ad5790a0..2d2773b4 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/BauSystem.java @@ -95,6 +95,16 @@ public class BauSystem extends JavaPlugin implements Listener { } return true; }); + SWCommandUtils.addValidator("supervisor", (commandSender, object, messageSender) -> { + if (commandSender instanceof Player) { + if (Permission.SUPERVISOR.hasPermission((Player) commandSender)) { + return true; + } + messageSender.send("NO_PERMISSION"); + return false; + } + return true; + }); LinkageUtils.link(); RamUsage.init(); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/Permission.java b/BauSystem_Main/src/de/steamwar/bausystem/Permission.java index 8e51d97d..2d642ac1 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/Permission.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/Permission.java @@ -20,11 +20,16 @@ package de.steamwar.bausystem; import de.steamwar.bausystem.config.BauServer; +import de.steamwar.bausystem.features.world.BauMemberUpdate; +import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.sql.BauweltMember; import de.steamwar.sql.SteamwarUser; import lombok.AllArgsConstructor; +import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import java.util.HashSet; +import java.util.Set; import java.util.function.Predicate; @AllArgsConstructor @@ -32,10 +37,17 @@ public enum Permission { OWNER(bauweltMember -> false), SUPERVISOR(bauweltMember -> { - return bauweltMember.isWorld(); + return bauweltMember.isSupervisor(); }), BUILD(bauweltMember -> { - return bauweltMember.isWorldEdit() || SUPERVISOR.permissionPredicate.test(bauweltMember); + if (isTempOnlySpectator(bauweltMember)) return false; + return bauweltMember.isBuild() || SUPERVISOR.permissionPredicate.test(bauweltMember); + }), + /** + * Only used for {@link BauMemberUpdate} + */ + REAL_SPECTATOR(bauweltMember -> { + return !bauweltMember.isBuild() && !bauweltMember.isSupervisor(); }), SPECTATOR(bauweltMember -> { return !BUILD.permissionPredicate.test(bauweltMember); @@ -44,6 +56,28 @@ public enum Permission { return true; }); + private static final Set TEMP_ONLY_SPECTATOR = new HashSet<>(); + + private static boolean isTempOnlySpectator(BauweltMember bauweltMember) { + return TEMP_ONLY_SPECTATOR.contains(bauweltMember.getMemberID()); + } + + public static boolean isTempOnlySpectator(Player player) { + return TEMP_ONLY_SPECTATOR.contains(SteamwarUser.get(player.getUniqueId()).getId()); + } + + public static void forceOnlySpectator(Player player) { + TEMP_ONLY_SPECTATOR.add(SteamwarUser.get(player.getUniqueId()).getId()); + BauMemberUpdate.baumemberUpdate(); + } + + /** + * Only used by {@link BauMemberUpdate} + */ + public static void removeForceOnlySpectator(Player player) { + TEMP_ONLY_SPECTATOR.remove(SteamwarUser.get(player.getUniqueId()).getId()); + } + private final Predicate permissionPredicate; public boolean hasPermission(BauweltMember bauweltMember) { @@ -53,10 +87,10 @@ public enum Permission { public boolean hasPermission(Player member) { if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) { - return this != SPECTATOR; + return this != SPECTATOR && this != REAL_SPECTATOR; } BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId()); - if (bauweltMember == null) return this == SPECTATOR; + if (bauweltMember == null) return this == SPECTATOR || this == REAL_SPECTATOR; return permissionPredicate.test(bauweltMember); } } \ No newline at end of file diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java deleted file mode 100644 index 69ee7af8..00000000 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/BauCommand.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is a part of the SteamWar software. - * - * Copyright (C) 2021 SteamWar.de-Serverteam - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package de.steamwar.bausystem.features.bau; - -import de.steamwar.command.SWCommand; -import de.steamwar.linkage.Linked; -import de.steamwar.linkage.LinkedInstance; -import org.bukkit.entity.Player; - -@Linked -public class BauCommand extends SWCommand { - - @LinkedInstance - public InfoCommand infoCommand; - - public BauCommand() { - super("bau", "b", "gs"); - } - - @Register(value = "info", description = "BAU_COMMAND_HELP_INFO") - public void infoCommand(Player p) { - infoCommand.sendBauInfo(p); - } -} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java new file mode 100644 index 00000000..7bef91df --- /dev/null +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/ForceSpectatorCommand.java @@ -0,0 +1,73 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package de.steamwar.bausystem.features.bau; + +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.command.PreviousArguments; +import de.steamwar.command.SWCommand; +import de.steamwar.command.TypeMapper; +import de.steamwar.linkage.Linked; +import de.steamwar.techhider.TechHider; +import org.bukkit.Bukkit; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Collection; +import java.util.stream.Collectors; + +@Linked +public class ForceSpectatorCommand extends SWCommand { + + public ForceSpectatorCommand() { + super("forcespectator"); + } + + @Register + public void forceSpectator(@Validator("supervisor") Player player, @Mapper("builder") Player other) { + Permission.forceOnlySpectator(other); + } + + @Mapper("builder") + public TypeMapper spectatorMapper() { + return new TypeMapper<>() { + @Override + public Player map(CommandSender commandSender, String[] previousArguments, String s) { + Player player = Bukkit.getPlayer(s); + if (player == null) { + return null; + } + if (Permission.BUILD.hasPermission(player) && !Permission.SUPERVISOR.hasPermission(player)) { + return player; + } + return null; + } + + @Override + public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { + return Bukkit.getOnlinePlayers().stream() + .filter(Permission.BUILD::hasPermission) + .filter(player -> !Permission.SUPERVISOR.hasPermission(player)) + .map(Player::getName) + .collect(Collectors.toList()); + } + }; + } +} diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java index 9f53f563..19d8aab5 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/bau/InfoCommand.java @@ -27,10 +27,6 @@ public class InfoCommand extends SWCommand { @Register(description = "BAU_INFO_COMMAND_HELP") public void genericCommand(Player p) { - sendBauInfo(p); - } - - public void sendBauInfo(Player p) { BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.get(bauServer.getOwnerID()).getUserName()); Region region = Region.getRegion(p.getLocation()); for (Flag flag : Flag.getFlags()) { diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java b/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java index 731b583a..612cf8cb 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java @@ -20,16 +20,22 @@ package de.steamwar.bausystem.features.techhider; import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; +import de.steamwar.bausystem.features.world.SpectatorListener; import de.steamwar.bausystem.features.xray.XrayCommand; import de.steamwar.bausystem.region.Region; import de.steamwar.bausystem.utils.ScoreboardElement; +import de.steamwar.command.PreviousArguments; import de.steamwar.command.SWCommand; +import de.steamwar.command.TypeMapper; import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; import de.steamwar.linkage.LinkedInstance; import de.steamwar.techhider.TechHider; import net.md_5.bungee.api.ChatMessageType; +import org.bukkit.Bukkit; import org.bukkit.Material; +import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; @@ -104,6 +110,33 @@ public class TechHiderCommand extends SWCommand implements Listener, ScoreboardE }); } + @Register + public void toggleHider(@Validator("supervisor") Player player, @Mapper("spectator") Player other) { + SpectatorListener.toggleNoTechHider(other); + } + + @Mapper("spectator") + public TypeMapper spectatorMapper() { + return new TypeMapper<>() { + @Override + public Player map(CommandSender commandSender, String[] previousArguments, String s) { + Player player = Bukkit.getPlayer(s); + if (player != null && Permission.REAL_SPECTATOR.hasPermission(player)) { + return player; + } + return null; + } + + @Override + public Collection tabCompletes(CommandSender sender, PreviousArguments previousArguments, String s) { + return Bukkit.getOnlinePlayers().stream() + .filter(Permission.REAL_SPECTATOR::hasPermission) + .map(Player::getName) + .collect(Collectors.toList()); + } + }; + } + @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { hidden.values().forEach(set -> set.remove(event.getPlayer())); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java index fe90be02..576b4fa1 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/BauMemberUpdate.java @@ -19,6 +19,8 @@ package de.steamwar.bausystem.features.world; +import de.steamwar.bausystem.BauSystem; +import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; import de.steamwar.command.SWCommand; import de.steamwar.linkage.Linked; @@ -26,15 +28,93 @@ import de.steamwar.network.packets.PacketHandler; import de.steamwar.network.packets.server.BaumemberUpdatePacket; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.PlayerDeathEvent; +import org.bukkit.event.player.PlayerItemConsumeEvent; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.event.player.PlayerRespawnEvent; +import org.bukkit.potion.PotionEffect; +import org.bukkit.potion.PotionEffectType; + +import java.util.HashSet; +import java.util.Set; @Linked -public class BauMemberUpdate extends PacketHandler { +public class BauMemberUpdate extends PacketHandler implements Listener { + + private static final Set SPECTATORS = new HashSet<>(); @Handler public void baumemberUpdate(BaumemberUpdatePacket baumemberUpdatePacket) { - Bukkit.getPluginManager().callEvent(new BauMemberUpdateEvent()); + baumemberUpdate(); } - + + public static void baumemberUpdate() { + Set newSpectator = new HashSet<>(); + Set newBuilder = new HashSet<>(); + Bukkit.getOnlinePlayers().forEach(player -> { + if (Permission.REAL_SPECTATOR.hasPermission(player)) { + if (!SPECTATORS.contains(player)) { + SPECTATORS.add(player); + Permission.removeForceOnlySpectator(player); + newSpectator.add(player); + player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, -1, 1, false,false, false)); + } + } else { + if (Permission.SUPERVISOR.hasPermission(player)) { + Permission.removeForceOnlySpectator(player); + } + if (SPECTATORS.contains(player)) { + SPECTATORS.remove(player); + newBuilder.add(player); + player.removePotionEffect(PotionEffectType.GLOWING); + } + } + }); + Bukkit.getPluginManager().callEvent(new BauMemberUpdateEvent(newSpectator, newBuilder)); + } + + @EventHandler + public void onPlayerJoin(PlayerJoinEvent event) { + if (Permission.SPECTATOR.hasPermission(event.getPlayer())) { + SPECTATORS.add(event.getPlayer()); + event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, -1, 1, false,false, false)); + } + } + + @EventHandler + public void onPlayerQuit(PlayerQuitEvent event) { + SPECTATORS.remove(event.getPlayer()); + } + + @EventHandler + public void onPlayerDeath(PlayerDeathEvent event) { + event.setDeathMessage(null); + if (SPECTATORS.contains(event.getEntity())) { + event.getDrops().clear(); + } + } + + @EventHandler + public void onPlayerRespawn(PlayerRespawnEvent event) { + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + if (SPECTATORS.contains(event.getPlayer())) { + event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, -1, 1, false, false, false)); + } + }, 1); + } + + @EventHandler + public void onPlayerItemConsume(PlayerItemConsumeEvent event) { + Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> { + if (SPECTATORS.contains(event.getPlayer())) { + event.getPlayer().addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, -1, 1, false,false, false)); + } + }, 1); + } + @Linked public static class TestCommand extends SWCommand { // TODO: Remove before merge diff --git a/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java b/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java index 163bec3c..68bb465a 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/features/world/SpectatorListener.java @@ -22,8 +22,12 @@ package de.steamwar.bausystem.features.world; import de.steamwar.bausystem.BauSystem; import de.steamwar.bausystem.Permission; import de.steamwar.bausystem.utils.BauMemberUpdateEvent; +import de.steamwar.core.CraftbukkitWrapper; import de.steamwar.linkage.Linked; +import de.steamwar.techhider.TechHider; import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -35,9 +39,41 @@ import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.entity.EntityPickupItemEvent; import org.bukkit.event.player.*; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + @Linked public class SpectatorListener implements Listener { + private static final Set NO_TECHHIDER = new HashSet<>(); + + static { + TechHider techHider = new TechHider((player, i, i1) -> { + return Permission.BUILD.hasPermission(player) || Permission.isTempOnlySpectator(player) || NO_TECHHIDER.contains(player); + }, Material.END_STONE, new HashSet<>(Arrays.asList(Material.REDSTONE_WIRE)), new HashSet<>()); + techHider.enable(); + } + + public static void toggleNoTechHider(Player player) { + if (NO_TECHHIDER.contains(player)) { + NO_TECHHIDER.remove(player); + } else { + NO_TECHHIDER.add(player); + } + resendChunks(player); + } + + private static void resendChunks(Player player) { + int distance = player.getClientViewDistance(); + Location location = player.getLocation(); + for (int x = (int) Math.floor(location.getX() / 16.0) - distance; x <= (int) Math.ceil(location.getX() / 16.0) + distance; x++) { + for (int z = (int) Math.floor(location.getZ() / 16.0) - distance; z <= (int) Math.ceil(location.getZ() / 16.0) + distance; z++) { + CraftbukkitWrapper.impl.sendChunk(player, x, z); + } + } + } + private boolean anySupervisorOnline(Player player) { return Bukkit.getOnlinePlayers().stream().filter(p -> p != player).anyMatch(Permission.SUPERVISOR::hasPermission); } @@ -60,11 +96,19 @@ public class SpectatorListener implements Listener { Bukkit.getOnlinePlayers().forEach(player -> { player.kickPlayer(""); }); + return; } + + event.getChanged().forEach(player -> { + NO_TECHHIDER.remove(player); + if (Permission.isTempOnlySpectator(player)) return; + resendChunks(player); + }); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { + NO_TECHHIDER.remove(event.getPlayer()); if (!anySupervisorOnline(event.getPlayer())) { Bukkit.getOnlinePlayers().forEach(player -> { player.kickPlayer(""); diff --git a/BauSystem_Main/src/de/steamwar/bausystem/utils/BauMemberUpdateEvent.java b/BauSystem_Main/src/de/steamwar/bausystem/utils/BauMemberUpdateEvent.java index 4d26dc6f..77f2ba3e 100644 --- a/BauSystem_Main/src/de/steamwar/bausystem/utils/BauMemberUpdateEvent.java +++ b/BauSystem_Main/src/de/steamwar/bausystem/utils/BauMemberUpdateEvent.java @@ -19,11 +19,35 @@ package de.steamwar.bausystem.utils; +import lombok.Getter; +import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + public class BauMemberUpdateEvent extends Event { + @Getter + private final Set changed; + + @Getter + private final Set newSpectator; + + @Getter + private final Set newBuilder; + + public BauMemberUpdateEvent(Set newSpectator, Set newBuilder) { + this.newSpectator = Collections.unmodifiableSet(newSpectator); + this.newBuilder = Collections.unmodifiableSet(newBuilder); + Set changed = new HashSet<>(); + changed.addAll(newSpectator); + changed.addAll(newBuilder); + this.changed = Collections.unmodifiableSet(changed); + } + private static final HandlerList handlers = new HandlerList(); public HandlerList getHandlers() {