diff --git a/src/main/java/me/trouper/clonedupecore/server/Manager.java b/src/main/java/me/trouper/clonedupecore/server/Manager.java index 431fccb..17002db 100644 --- a/src/main/java/me/trouper/clonedupecore/server/Manager.java +++ b/src/main/java/me/trouper/clonedupecore/server/Manager.java @@ -40,6 +40,7 @@ public class Manager { trimManager.register(new NetheriteAnimation()); trimManager.register(new QuartzAnimation()); trimManager.register(new RedstoneAnimation()); + trimManager.register(); trimManager.startTicking(); } diff --git a/src/main/java/me/trouper/clonedupecore/server/commands/AdminCommand.java b/src/main/java/me/trouper/clonedupecore/server/commands/AdminCommand.java index 4f9505e..26345fb 100644 --- a/src/main/java/me/trouper/clonedupecore/server/commands/AdminCommand.java +++ b/src/main/java/me/trouper/clonedupecore/server/commands/AdminCommand.java @@ -74,16 +74,12 @@ public class AdminCommand implements QuickCommand, Data { ) ) ).then( - b.arg("update") - ).then( - b.arg("reload") - ).then( - b.arg("cleanup") + b.arg("update","reload","cleanup") ); } private void handleUpdate(CommandSender sender, Args args) { - infoAny(sender,"Checking for an updated..."); + infoAny(sender,"Checking for an update..."); Bukkit.getScheduler().runTask(main.getPlugin(),()->{ if (AutoUpdater.checkUpdate(main.getPlugin(),main.getCommon())) { successAny(sender,"Updated plugin has been downloaded to {0}.","plugins/update"); @@ -108,7 +104,7 @@ public class AdminCommand implements QuickCommand, Data { } } } - if (count > 0) successAny(sender,"Successfully removed {0} temporary entities."); + if (count > 0) successAny(sender,"Successfully removed {0} temporary entities.",count); else errorAny(sender,"Could not find any temporary entities."); }); } diff --git a/src/main/java/me/trouper/clonedupecore/server/events/FreezeEvents.java b/src/main/java/me/trouper/clonedupecore/server/events/FreezeEvents.java index c642eb3..4aba5d9 100644 --- a/src/main/java/me/trouper/clonedupecore/server/events/FreezeEvents.java +++ b/src/main/java/me/trouper/clonedupecore/server/events/FreezeEvents.java @@ -17,6 +17,7 @@ public class FreezeEvents implements QuickListener { public void onMove(PlayerMoveEvent e) { Freeze.FrozenPlayer frozen = Freeze.getFrozen().get(e.getPlayer().getUniqueId()); if (frozen == null) return; + if (frozen.movementAllowed()) return; if (!e.getFrom().toVector().equals(e.getTo().toVector())) { e.setTo(e.getFrom()); @@ -37,6 +38,7 @@ public class FreezeEvents implements QuickListener { public void onCommand(PlayerCommandPreprocessEvent e) { Freeze.FrozenPlayer frozen = Freeze.getFrozen().get(e.getPlayer().getUniqueId()); if (frozen == null) return; + if (frozen.commandsAllowed()) return; String baseCmd = e.getMessage().replace("/", "").split(" ")[0].toLowerCase(); if (!Data.data.getConfig().allowedFreezeCommands.contains(baseCmd)) { @@ -50,6 +52,7 @@ public class FreezeEvents implements QuickListener { if (!(e.getEntity() instanceof Player p)) return; Freeze.FrozenPlayer frozen = Freeze.getFrozen().get(p.getUniqueId()); if (frozen == null) return; + if (frozen.damageAllowed()) return; e.setCancelled(true); if (frozen.getOnMove() != null) frozen.getOnMove().accept(p); @@ -60,6 +63,7 @@ public class FreezeEvents implements QuickListener { if (!(e.getEntity() instanceof Player p)) return; Freeze.FrozenPlayer frozen = Freeze.getFrozen().get(p.getUniqueId()); if (frozen == null) return; + if (frozen.damageAllowed()) return; e.setCancelled(true); if (frozen.getOnMove() != null) frozen.getOnMove().accept(p); diff --git a/src/main/java/me/trouper/clonedupecore/server/punishment/Freeze.java b/src/main/java/me/trouper/clonedupecore/server/punishment/Freeze.java index 15a6857..bdd513d 100644 --- a/src/main/java/me/trouper/clonedupecore/server/punishment/Freeze.java +++ b/src/main/java/me/trouper/clonedupecore/server/punishment/Freeze.java @@ -48,6 +48,9 @@ public class Freeze { private final Location backLocation; private final Consumer onQuit; private final Consumer onMove; + private boolean allowMovement = false; + private boolean allowDamage = false; + private boolean allowCommands = false; public FrozenPlayer(UUID uuid, Location backLocation, Consumer onQuit, Consumer onMove) { this.uuid = uuid; @@ -73,5 +76,29 @@ public class Freeze { if (player == null) return; player.teleport(backLocation); } + + public void setAllowDamage(boolean allowDamage) { + this.allowDamage = allowDamage; + } + + public void setAllowMovement(boolean allowMovement) { + this.allowMovement = allowMovement; + } + + public boolean commandsAllowed() { + return allowCommands; + } + + public void setAllowCommands(boolean allowCommands) { + this.allowCommands = allowCommands; + } + + public boolean damageAllowed() { + return allowDamage; + } + + public boolean movementAllowed() { + return allowMovement; + } } } diff --git a/src/main/java/me/trouper/clonedupecore/server/scripts/JailHeadWand.java b/src/main/java/me/trouper/clonedupecore/server/scripts/JailHeadWand.java index 5d6bd52..351f999 100644 --- a/src/main/java/me/trouper/clonedupecore/server/scripts/JailHeadWand.java +++ b/src/main/java/me/trouper/clonedupecore/server/scripts/JailHeadWand.java @@ -4,45 +4,96 @@ import me.trouper.alias.server.events.QuickListener; import me.trouper.alias.server.systems.AbstractWand; import me.trouper.alias.server.systems.world.Snapshot; import me.trouper.alias.utils.ItemBuilder; +import me.trouper.alias.utils.TargetingUtils; +import me.trouper.clonedupecore.server.punishment.Freeze; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; -import org.bukkit.util.BoundingBox; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; import java.util.UUID; public class JailHeadWand extends AbstractWand implements QuickListener { public JailHeadWand() { super("clonedupe.jailwand", ItemBuilder.of(Material.PLAYER_HEAD) + .createCustomHead("http://textures.minecraft.net/texture/a63b87ee6a55f2bf0135b26bd96ec279eded175f948c036d88a60725e127371c","Port-a-Jail") + .loreComponent(Component.text("Right click to use on a player!", NamedTextColor.GRAY).decoration(TextDecoration.ITALIC,false)) .build()); } + private final Map jailed = new HashMap<>(); + @Override protected void onRightClick(Player player) { + Optional optional = TargetingUtils.livingClosestAngle(player.getEyeLocation(),player.getEyeLocation().getDirection(),10,0.5); + if (optional.isEmpty() || !(optional.get() instanceof Player target)) return; + if (jailed.containsKey(target.getUniqueId())) { + Freeze.thawPlayer(target.getUniqueId()); + jailed.remove(target.getUniqueId()).revertCage(); + return; + } + Freeze.quickFreeze(target); + Freeze.FrozenPlayer fp = new Freeze.FrozenPlayer(target.getUniqueId(),target.getLocation().clone(),null,null); + fp.setAllowMovement(true); + Freeze.freezePlayer(fp); + Jail jail = new Jail(target); + jail.createCage(); + jailed.put(target.getUniqueId(),jail); } private class Jail { private final UUID prisoner; - private final List changedBlocks = new ArrayList<>(); + private final Map snapshots = new HashMap<>(); public Jail(Player prisoner) { this.prisoner = prisoner.getUniqueId(); - Location loc = prisoner.getLocation(); + } + + public void createCage() { + Player p = Bukkit.getPlayer(prisoner); + if (p == null) return; + + Location loc = p.getLocation().clone().subtract(0,1,0); final int x = loc.getBlockX(); final int y = loc.getBlockY(); final int z = loc.getBlockZ(); - for (int dx = x - 1; dx < x + 1; dx++) { - for (int dy = y - 1; dy < y + 2; dy++) { - for (int dz = z - 1; dz < z + 1; dz++) { - Location pointer = loc.clone(); - pointer.setX(dx); - pointer.setY(dy); - pointer.setZ(dz); + + for (int dx = -1; dx <= 1; dx++) { + for (int dy = 0; dy <= 3; dy++) { + for (int dz = -1; dz <= 1; dz++) { + int bx = x + dx; + int by = y + dy; + int bz = z + dz; + + Location blockLoc = new Location(loc.getWorld(), bx, by, bz); + Block block = blockLoc.getBlock(); + snapshots.put(block, new Snapshot(block)); + + if (dy == 0 || dy == 3) { + block.setType(Material.IRON_BLOCK); + } else if (dy == 1 || dy == 2) { + if (Math.abs(dx) == 1 || Math.abs(dz) == 1) { + block.setType(Material.IRON_BARS); + } else { + block.setType(Material.AIR); + } + } } } } } + + + public void revertCage() { + snapshots.forEach((block,snapshot) -> snapshot.restore(block)); + } } } diff --git a/src/main/java/me/trouper/clonedupecore/server/trims/TrimManager.java b/src/main/java/me/trouper/clonedupecore/server/trims/TrimManager.java index ff42576..5f3f9a8 100644 --- a/src/main/java/me/trouper/clonedupecore/server/trims/TrimManager.java +++ b/src/main/java/me/trouper/clonedupecore/server/trims/TrimManager.java @@ -1,111 +1,105 @@ package me.trouper.clonedupecore.server.trims; -import me.trouper.alias.server.Main; import me.trouper.alias.server.events.QuickListener; +import me.trouper.alias.server.events.Registrar; +import me.trouper.alias.server.systems.Verbose; import me.trouper.clonedupecore.data.Data; -import me.trouper.clonedupecore.utils.ArmorUtils; import org.bukkit.Bukkit; import org.bukkit.Location; -import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.player.PlayerQuitEvent; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ArmorMeta; -import org.bukkit.inventory.meta.ItemMeta; -import org.bukkit.inventory.meta.trim.ArmorTrim; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; +@Registrar(exclude = true) public class TrimManager implements QuickListener, Data { - private final Map animations = new EnumMap<>(ValidMaterial.class); - private final Map activePlayers = new HashMap<>(); + private final Set allAnimations = new HashSet<>(); + private final Map trackerMap = new HashMap<>(); + private final ConcurrentHashMap lastAnimationMap = new ConcurrentHashMap<>(); + private final Set isStationary = new HashSet<>(); public void register(MaterialAnimation animation) { - animations.put(animation.getMaterial(), animation); - } - - public void tickPlayer(Player player) { - UUID uuid = player.getUniqueId(); - if (shouldHide(player)) { - safeRemove(player); - return; - } - - ValidMaterial currentMaterial = getActiveMaterial(player); - ActiveTrim active = activePlayers.get(uuid); - Location currentLocation = player.getLocation(); - - if (currentMaterial == null) { - safeRemove(player); - return; - } - - if (active != null && active.material == currentMaterial) { - active.ticks++; - - if (active.lastCheckedLocation != null && active.lastCheckedLocation.getWorld().equals(currentLocation.getWorld()) && currentLocation.distanceSquared(active.lastCheckedLocation) > 0.001) { - active.lastMoveTimestamp = System.currentTimeMillis(); - } - - active.lastCheckedLocation = currentLocation; - } else { - active = new ActiveTrim(currentMaterial, currentLocation); - safeRemove(player); - activePlayers.put(uuid, active); - } - - MaterialAnimation animation = animations.get(currentMaterial); - if (animation != null) { - long loopTime = active.ticks % animation.getLoopDuration(); - long timeSinceLastMove = System.currentTimeMillis() - active.lastMoveTimestamp; - - if (timeSinceLastMove < 1000) { - animation.onRemove(player); - animation.tickMoving(player, getViewers(player), loopTime); - } else { - animation.tickStationary(player, getViewers(player), loopTime); - } - } - } - - public void safeRemove(OfflinePlayer player) { - UUID id = player.getUniqueId(); - if (activePlayers.containsKey(id)) animations.get(activePlayers.get(id).material).onRemove(player); - activePlayers.remove(id); + allAnimations.add(animation); } public void startTicking() { - Bukkit.getScheduler().runTaskTimer(main.getPlugin(), () -> Bukkit.getOnlinePlayers().forEach(this::tickPlayer),0,1); + AtomicInteger globalTime = new AtomicInteger(0); + Bukkit.getScheduler().runTaskTimer(main.getPlugin(), () -> { + Bukkit.getOnlinePlayers().forEach(player->{ + if (!player.isOnline()) return; + tickPlayer(player, globalTime.get()); + }); + globalTime.getAndIncrement(); + }, 0, 1); } - private ValidMaterial getActiveMaterial(Player player) { - ItemStack[] armor = player.getInventory().getArmorContents(); - if (armor.length != 4) return null; - - ValidMaterial found = null; - for (ItemStack piece : armor) { - if (!ArmorUtils.isArmor(piece)) return null; - - ItemMeta meta = piece.getItemMeta(); - if (!(meta instanceof ArmorMeta armorMeta)) return null; - if (!armorMeta.hasTrim()) return null; - - ArmorTrim trim = armorMeta.getTrim(); - if (trim == null) return null; - - ValidMaterial vm = ValidMaterial.validate(trim.getMaterial()); - if (vm == null) return null; - - if (found == null) { - found = vm; - } else if (found != vm) { - return null; - } + public void tickPlayer(Player player, int globalTime) { + UUID playerId = player.getUniqueId(); + MaterialAnimation activeAnimation = getAnimation(player); + if (!player.isOnline()) return; + if (activeAnimation == null && lastAnimationMap.containsKey(playerId)) { + MaterialAnimation lastAnimation = lastAnimationMap.remove(playerId); + Verbose.send("Player has no active animation, removing and stopping {0}, their last active animation.", lastAnimation.getMaterial().name()); + lastAnimation.onRemove(player); + return; } - return found; + + if (activeAnimation == null) { + return; + } else if (lastAnimationMap.containsKey(playerId) && !lastAnimationMap.get(playerId).equals(activeAnimation)) { + lastAnimationMap.put(playerId,activeAnimation); + } else if (!lastAnimationMap.containsKey(playerId)) { + lastAnimationMap.put(playerId,activeAnimation); + } + + MovementTracker tracker = trackerMap.computeIfAbsent(playerId,k -> new MovementTracker(player.getLocation())); + long loopTime = globalTime % activeAnimation.getLoopDuration(); + + if (tracker.isStationary) { + isStationary.add(playerId); + activeAnimation.tickStationary(player,getViewers(player),loopTime); + } else { + if (isStationary.remove(playerId)) activeAnimation.onRemove(player); + activeAnimation.tickMoving(player,getViewers(player),loopTime); + } + + tracker.tick(player.getLocation()); + } + + public static class MovementTracker { + private Location lastLocation; + private long lastMovedTime; + private boolean isStationary; + + public MovementTracker(Location currentLocation) { + lastMovedTime = System.currentTimeMillis(); + isStationary = true; + lastLocation = currentLocation; + } + + public void tick(Location currentLocation) { + long now = System.currentTimeMillis(); + if (lastLocation.distanceSquared(currentLocation) > 0.01) { + lastMovedTime = now; + } + isStationary = (now - lastMovedTime) >= 1000; + + lastLocation = currentLocation; + } + } + + public MaterialAnimation getAnimation(Player player) { + ValidMaterial playerMaterial = ValidMaterial.getActiveMaterial(player); + if (playerMaterial == null) return null; + for (MaterialAnimation animation : allAnimations) { + if (animation.getMaterial().equals(playerMaterial)) return animation; + } + return null; } private boolean shouldHide(Player player) { @@ -113,26 +107,26 @@ public class TrimManager implements QuickListener, Data { } private Set getViewers(Player player) { - if (shouldHide(player)) return new HashSet<>(); - return Bukkit.getOnlinePlayers().stream().filter(viewer-> !getStorage().disabledGlobalParticles.contains(viewer.getUniqueId().toString())).collect(Collectors.toSet()); - } - - private static class ActiveTrim { - final ValidMaterial material; - long ticks; - long lastMoveTimestamp; - Location lastCheckedLocation; - - ActiveTrim(ValidMaterial material, Location initialLocation) { - this.material = material; - this.ticks = 0; - this.lastMoveTimestamp = System.currentTimeMillis(); - this.lastCheckedLocation = initialLocation; + if (shouldHide(player)) { + return Collections.emptySet(); } + + return Bukkit.getOnlinePlayers().stream() + .filter(viewer -> !getStorage().disabledGlobalParticles.contains(viewer.getUniqueId().toString())) + .collect(Collectors.toSet()); } @EventHandler - public void onLeave(PlayerQuitEvent e) { - safeRemove(e.getPlayer()); + public void onQuit(PlayerQuitEvent e) { + Player player = e.getPlayer(); + UUID playerId = player.getUniqueId(); + Verbose.send("{0} has quit!",player.getName()); + if (lastAnimationMap.containsKey(playerId)) { + MaterialAnimation lastAnimation = lastAnimationMap.get(playerId); + Verbose.send("Last animation for {0} is {1}.",player.getName(),lastAnimation.getMaterial().name()); + lastAnimation.onRemove(player); + } else { + Verbose.send("{0} had no active animations.",player.getName()); + } } -} +} \ No newline at end of file diff --git a/src/main/java/me/trouper/clonedupecore/server/trims/ValidMaterial.java b/src/main/java/me/trouper/clonedupecore/server/trims/ValidMaterial.java index ea8b1df..894e3df 100644 --- a/src/main/java/me/trouper/clonedupecore/server/trims/ValidMaterial.java +++ b/src/main/java/me/trouper/clonedupecore/server/trims/ValidMaterial.java @@ -1,5 +1,11 @@ package me.trouper.clonedupecore.server.trims; +import me.trouper.clonedupecore.utils.ArmorUtils; +import org.bukkit.entity.Player; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ArmorMeta; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.trim.ArmorTrim; import org.bukkit.inventory.meta.trim.TrimMaterial; public enum ValidMaterial { @@ -36,4 +42,34 @@ public enum ValidMaterial { name = name.toUpperCase(); return ValidMaterial.valueOf(name).getCanonical(); } + + public static ValidMaterial getActiveMaterial(Player player) { + ItemStack[] armor = player.getInventory().getArmorContents(); + if (armor.length != 4) return null; + + ValidMaterial trimMaterial = null; + + for (ItemStack piece : armor) { + if (!ArmorUtils.isArmor(piece)) return null; + + ItemMeta meta = piece.getItemMeta(); + if (!(meta instanceof ArmorMeta armorMeta) || !armorMeta.hasTrim()) { + return null; + } + + ArmorTrim trim = armorMeta.getTrim(); + if (trim == null) return null; + + ValidMaterial currentPieceMaterial = ValidMaterial.validate(trim.getMaterial()); + if (currentPieceMaterial == null) return null; + + if (trimMaterial == null) { + trimMaterial = currentPieceMaterial; + } else if (trimMaterial != currentPieceMaterial) { + return null; + } + } + + return trimMaterial; + } } diff --git a/src/main/java/me/trouper/clonedupecore/server/trims/animations/NetheriteAnimation.java b/src/main/java/me/trouper/clonedupecore/server/trims/animations/NetheriteAnimation.java index 1b99b81..ee9f75d 100644 --- a/src/main/java/me/trouper/clonedupecore/server/trims/animations/NetheriteAnimation.java +++ b/src/main/java/me/trouper/clonedupecore/server/trims/animations/NetheriteAnimation.java @@ -1,5 +1,6 @@ package me.trouper.clonedupecore.server.trims.animations; +import me.trouper.alias.server.systems.Verbose; import me.trouper.alias.server.systems.visual.DisplayUtils; import me.trouper.alias.server.systems.visual.ParticleUtils; import me.trouper.clonedupecore.server.trims.MaterialAnimation; @@ -43,6 +44,7 @@ public class NetheriteAnimation extends MaterialAnimation { double angle = (loopTime / (double) LOOP_DURATION) * 2 * Math.PI; List displays = activeDisplays.computeIfAbsent(playerId, id -> { + Verbose.send("{0} needed new BlockDisplays",player.getName()); List list = new ArrayList<>(); for (int i = 0; i < 4; i++) { Location initialLoc = player.getLocation().add(0, 1, 0); @@ -132,13 +134,9 @@ public class NetheriteAnimation extends MaterialAnimation { @Override public void onRemove(OfflinePlayer player) { - List displays = activeDisplays.remove(player.getUniqueId()); - if (displays != null) { - for (BlockDisplay display : displays) { - if (!display.isDead()) { - display.remove(); - } - } - } + List displays = activeDisplays.get(player.getUniqueId()); + if (displays == null || displays.isEmpty()) return; + displays.forEach(BlockDisplay::remove); + activeDisplays.remove(player.getUniqueId()); } }