From b290d113de1a83bdd83341cac1ae8795dee313b7 Mon Sep 17 00:00:00 2001 From: Pablete1234 Date: Thu, 8 Apr 2021 10:41:51 +0200 Subject: [PATCH] Rewrite legacy flag beams Signed-off-by: Pablete1234 --- .../pgm/flag/LegacyFlagBeamMatchModule.java | 237 +++++++----------- .../java/tc/oc/pgm/util/nms/NMSHacks.java | 11 +- 2 files changed, 104 insertions(+), 144 deletions(-) diff --git a/core/src/main/java/tc/oc/pgm/flag/LegacyFlagBeamMatchModule.java b/core/src/main/java/tc/oc/pgm/flag/LegacyFlagBeamMatchModule.java index b6190d3f91..2a64b5ee9d 100644 --- a/core/src/main/java/tc/oc/pgm/flag/LegacyFlagBeamMatchModule.java +++ b/core/src/main/java/tc/oc/pgm/flag/LegacyFlagBeamMatchModule.java @@ -3,18 +3,19 @@ import static java.util.stream.IntStream.range; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import java.util.Collection; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.concurrent.Future; +import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; import org.bukkit.Location; import org.bukkit.Material; -import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; @@ -25,13 +26,11 @@ import tc.oc.pgm.api.match.Match; import tc.oc.pgm.api.match.MatchModule; import tc.oc.pgm.api.match.MatchScope; -import tc.oc.pgm.api.match.event.MatchStartEvent; import tc.oc.pgm.api.match.factory.MatchModuleFactory; import tc.oc.pgm.api.module.exception.ModuleLoadException; import tc.oc.pgm.api.player.MatchPlayer; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.events.PlayerJoinMatchEvent; -import tc.oc.pgm.events.PlayerJoinPartyEvent; import tc.oc.pgm.events.PlayerLeaveMatchEvent; import tc.oc.pgm.flag.event.FlagStateChangeEvent; import tc.oc.pgm.flag.state.Carried; @@ -57,171 +56,100 @@ public LegacyFlagBeamMatchModule createMatchModule(Match match) throws ModuleLoa private static final int UPDATE_DELAY = 0; private static final int UPDATE_FREQUENCY = 50; - private UpdateTask task; private final Match match; - private final Map> beams; + private final Map beams; public LegacyFlagBeamMatchModule(Match match) { this.match = match; this.beams = new HashMap<>(); + FlagMatchModule module = match.needModule(FlagMatchModule.class); + module.getFlags().forEach(f -> beams.put(f, new Beam(f))); } - protected Stream flags() { - FlagMatchModule module = match.getModule(FlagMatchModule.class); - return module == null ? Stream.empty() : module.getFlags().stream(); - } - - protected void retrackFlag(Flag flag) { - match.getPlayers().forEach(p -> retrackFlag(flag, p)); - } - - protected void retrackFlag(Flag flag, MatchPlayer player) { - untrackFlag(flag, player); - trackFlag(flag, player); - } - - protected void trackFlag(Flag flag) { - match.getPlayers().forEach(p -> trackFlag(flag, p)); - } - - protected void trackFlag(Flag flag, MatchPlayer player) { - Map flags = beams.containsKey(player) ? beams.get(player) : new HashMap<>(); - if (flags.containsKey(flag) // beam duplication check - || !flag.getDefinition().showBeam() // considers the flag definition's flag beam setting. - || (!player.isLegacy() // version greater than 1.7 & - && !PGM.get() - .getConfiguration() - .useLegacyFlagBeams())) { // we shouldn't show to >1.7 players - return; - } - - flags.put(flag, new Beam(flag, player)); - - beams.put(player, flags); - } - - protected void untrackFlag(Flag flag) { - match.getPlayers().forEach(p -> untrackFlag(flag, p)); - } - - protected void untrackFlag(Flag flag, MatchPlayer player) { - if (beams.containsKey(player)) { - Beam beam = beams.get(player).get(flag); - if (beam != null) { - beams.get(player).remove(flag).hide(); - } - } + private Stream beams() { + return beams.values().stream(); } @Override public void enable() { - flags().filter(flag -> flag.getState() instanceof Spawned).forEach(this::trackFlag); - this.task = new UpdateTask(); + match + .getExecutor(MatchScope.RUNNING) + .scheduleAtFixedRate( + () -> beams.values().forEach(Beam::update), + UPDATE_DELAY, + UPDATE_FREQUENCY, + TimeUnit.MILLISECONDS); } @Override public void unload() { - flags().forEach(this::untrackFlag); + beams.values().forEach(Beam::hide); beams.clear(); - this.task.stop(); } - // retrackFlags when players join a party so players are able to see the wool beams - // player join match event doesn't work at times. - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPlayerJoinParty(PlayerJoinPartyEvent event) { - flags() - .filter(flag -> flag.getState() instanceof Spawned) - .forEach(flag -> retrackFlag(flag, event.getPlayer())); - } - - // retrackFlags when players switch worlds - // player join match event doesn't work at times. @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onPlayerChangedWorld(PlayerChangedWorldEvent event) { - try { - flags() - .filter(flag -> flag.getState() instanceof Spawned) - .forEach(flag -> retrackFlag(flag, match.getParticipant(event.getPlayer()))); - } catch (NullPointerException e) { - /* ignore */ - } - } + MatchPlayer player = match.getPlayer(event.getPlayer()); + if (player == null) return; - // retrackFlags when match starts to ensure all players can see flag beams - // player join match event doesn't work at times. - @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onMatchStart(MatchStartEvent event) { - flags().filter(flag -> flag.getState() instanceof Spawned).forEach(flag -> retrackFlag(flag)); + if (event.getWorld() == match.getWorld()) beams().forEach(beam -> beam.show(player)); + else beams().forEach(beam -> beam.hide(player)); } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) - public void onPlayerJoinMatch(PlayerJoinMatchEvent event) { - flags() - .filter(flag -> flag.getState() instanceof Spawned) - .forEach(flag -> trackFlag(flag, event.getPlayer())); + public void onPlayerJoin(PlayerJoinMatchEvent event) { + beams().forEach(beam -> beam.show(event.getPlayer())); } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onPlayerLeaveMatch(PlayerLeaveMatchEvent event) { - flags() - .filter(flag -> flag.getState() instanceof Spawned) - .forEach(flag -> untrackFlag(flag, event.getPlayer())); + beams().forEach(beam -> beam.hide(event.getPlayer())); } @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) public void onFlagStateChange(FlagStateChangeEvent event) { - Flag flag = event.getFlag(); - untrackFlag(flag); - if (event.getNewState() instanceof Spawned) { - trackFlag(flag); - } - } - - private class UpdateTask implements Runnable { + Beam beam = beams.get(event.getFlag()); - private final Future task; + boolean wasSpawned = event.getOldState() instanceof Spawned, + isSpawned = event.getNewState() instanceof Spawned; - private UpdateTask() { - this.task = - match - .getExecutor(MatchScope.RUNNING) - .scheduleAtFixedRate(this, UPDATE_DELAY, UPDATE_FREQUENCY, TimeUnit.MILLISECONDS); - } + if (wasSpawned && !isSpawned) beam.hide(); + else if (!wasSpawned && isSpawned) beam.show(); - public void stop() { - this.task.cancel(true); - } + // Hide beam for carrier + if (event.getNewState() instanceof Carried) + beam.hide(((Carried) event.getNewState()).getCarrier()); - @Override - public void run() { - ImmutableList.copyOf(beams.values()).forEach(flags -> flags.forEach((f, b) -> b.update())); - } + // Show beam to old carrier + if (event.getOldState() instanceof Carried) + beam.show(((Carried) event.getOldState()).getCarrier()); } class Beam { - final Flag flag; - final Player bukkit; - final List segments; + private final Flag flag; + private final NMSHacks.FakeEntity base, legacyBase; + private final List segments; + + private final Set viewers = new HashSet<>(); - Beam(Flag flag, MatchPlayer player) { + Beam(Flag flag) { this.flag = flag; - this.bukkit = player.getBukkit(); + + ItemStack wool = new ItemBuilder().material(Material.WOOL).color(flag.getDyeColor()).build(); + this.base = new NMSHacks.FakeArmorStand(match.getWorld(), wool); + this.legacyBase = new NMSHacks.FakeWitherSkull(match.getWorld()); this.segments = range(0, 64) // ~100 blocks is the height which the particles appear to be reasonably // visible (similar amount to amount closest to the flag), we limit this to 64 blocks // to reduce load on the client - .mapToObj(i -> new NMSHacks.FakeArmorStand(match.getWorld())) + .mapToObj(i -> new NMSHacks.FakeArmorStand(match.getWorld(), wool)) .collect(Collectors.toList()); - if (player.isLegacy()) segments.set(0, new NMSHacks.FakeWitherSkull(match.getWorld())); - show(); } - Optional carrier() { - return Optional.ofNullable( - flag.getState() instanceof Carried - ? ((Carried) flag.getState()).getCarrier().getBukkit() - : null); + Optional carrier() { + return flag.getState() instanceof Carried + ? Optional.of(((Carried) flag.getState()).getCarrier()) + : Optional.empty(); } Optional location() { @@ -235,36 +163,59 @@ Optional location() { return Optional.of(location); } - ItemStack wool() { - return new ItemBuilder().material(Material.WOOL).color(flag.getDyeColor()).build(); + private NMSHacks.FakeEntity base(MatchPlayer player) { + return player.isLegacy() ? legacyBase : base; + } + + public void show() { + match.getPlayers().forEach(this::show); } - void show() { - if (carrier().map(carrier -> carrier.equals(bukkit)).orElse(false)) return; - segments.forEach( - segment -> { - location().ifPresent(l -> segment.spawn(bukkit, l.clone())); - segment.wear(bukkit, 4, wool()); // Head slot - }); + public void show(MatchPlayer player) { + if (!flag.getDefinition().showBeam()) return; + if (!player.isLegacy() && !PGM.get().getConfiguration().useLegacyFlagBeams()) return; + if (!(flag.getState() instanceof Spawned)) return; + + if (carrier().map(player::equals).orElse(false) || !viewers.add(player)) return; + + Player bukkit = player.getBukkit(); + spawn(bukkit, base(player)); + segments.forEach(segment -> spawn(bukkit, segment)); range(1, segments.size()) .forEachOrdered(i -> segments.get(i - 1).ride(bukkit, segments.get(i).entity())); - update(); + base(player).ride(bukkit, segments.get(0).entity()); + + update(player); } - void update() { - Location loc = carrier().map(Entity::getLocation).orElseGet(() -> location().orElse(null)); - if (loc != null) { - loc = loc.clone().add(0, 2.75, 0); - loc.setPitch(0f); - loc.setYaw(0f); - segments.get(0).teleport(bukkit, loc); - } + private void spawn(Player player, NMSHacks.FakeEntity entity) { + location().ifPresent(l -> entity.spawn(player, l)); } - void hide() { - for (int i = segments.size() - 1; i >= 0; i--) { - segments.get(i).destroy(bukkit); - } + public void update() { + viewers.forEach(this::update); + } + + public void update(MatchPlayer player) { + Location loc = + carrier().map(c -> c.getBukkit().getLocation()).orElseGet(() -> location().orElse(null)); + if (loc == null) return; + loc = loc.clone().add(0, 2.75, 0); + loc.setPitch(0f); + loc.setYaw(0f); + base(player).teleport(player.getBukkit(), loc); + } + + public void hide() { + ImmutableSet.copyOf(viewers).forEach(this::hide); + viewers.clear(); + } + + private void hide(MatchPlayer player) { + if (!viewers.remove(player)) return; + Player bukkit = player.getBukkit(); + for (int i = segments.size() - 1; i >= 0; i--) segments.get(i).destroy(bukkit); + base(player).destroy(bukkit); } } } diff --git a/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java b/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java index eceddc1437..844fe3b793 100644 --- a/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java +++ b/util/src/main/java/tc/oc/pgm/util/nms/NMSHacks.java @@ -602,8 +602,11 @@ protected Packet spawnPacket() { class FakeArmorStand extends FakeLivingEntity { - public FakeArmorStand(World world) { + private final ItemStack head; + + public FakeArmorStand(World world, ItemStack head) { super(new EntityArmorStand(((CraftWorld) world).getHandle())); + this.head = head; entity.setInvisible(true); NBTTagCompound tag = entity.getNBTTag(); @@ -617,6 +620,12 @@ public FakeArmorStand(World world) { tag.setBoolean("NoAI", true); entity.f(tag); } + + @Override + public void spawn(Player viewer, Location location, Vector velocity) { + super.spawn(viewer, location, velocity); + if (head != null) wear(viewer, 4, head); + } } class FakeWitherSkull extends FakeEntityImpl {