This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package com.lexian.ambientaudio.service;
|
||||
|
||||
import com.lexian.ambientaudio.model.AudioZone;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** Selects audible zones and starts/stops client sounds without replaying a zone while it remains selected. */
|
||||
public final class PlaybackService {
|
||||
private final JavaPlugin plugin; private final ZoneIndex index = new ZoneIndex();
|
||||
private final Map<UUID, Map<String, ActiveSound>> active = new HashMap<>();
|
||||
private boolean blending; private long leaveGraceTicks; private int taskId = -1;
|
||||
|
||||
public PlaybackService(JavaPlugin plugin) { this.plugin = plugin; }
|
||||
public void configure(Collection<AudioZone> zones, int intervalTicks, long leaveGraceTicks, boolean blending) {
|
||||
stopAll(); index.rebuild(zones); this.blending = blending; this.leaveGraceTicks = leaveGraceTicks;
|
||||
if (taskId != -1) Bukkit.getScheduler().cancelTask(taskId);
|
||||
taskId = Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, this::tick, intervalTicks, intervalTicks);
|
||||
}
|
||||
public void shutdown() { if (taskId != -1) Bukkit.getScheduler().cancelTask(taskId); stopAll(); }
|
||||
private void tick() { Bukkit.getOnlinePlayers().forEach(this::update); }
|
||||
private void update(Player player) {
|
||||
long now = Bukkit.getCurrentTick();
|
||||
List<AudioZone> audible = index.candidates(player.getLocation()).stream().filter(zone -> zone.isAudible(player.getLocation())).sorted(Comparator.comparingInt(AudioZone::priority).reversed()).toList();
|
||||
if (!blending && !audible.isEmpty()) audible = List.of(audible.getFirst());
|
||||
Set<String> desired = audible.stream().map(AudioZone::normalizedName).collect(Collectors.toSet());
|
||||
Map<String, ActiveSound> current = active.computeIfAbsent(player.getUniqueId(), unused -> new HashMap<>());
|
||||
for (AudioZone zone : audible) if (!current.containsKey(zone.normalizedName())) {
|
||||
player.playSound(zone.originLocation(player.getWorld()), zone.sound(), zone.category(), zone.volume(), zone.pitch());
|
||||
current.put(zone.normalizedName(), new ActiveSound(zone, Long.MAX_VALUE));
|
||||
}
|
||||
for (Iterator<Map.Entry<String, ActiveSound>> it = current.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry<String, ActiveSound> entry = it.next(); ActiveSound sound = entry.getValue();
|
||||
if (desired.contains(entry.getKey())) { entry.setValue(new ActiveSound(sound.zone(), Long.MAX_VALUE)); continue; }
|
||||
if (sound.stopAtTick() == Long.MAX_VALUE) { entry.setValue(new ActiveSound(sound.zone(), now + leaveGraceTicks)); continue; }
|
||||
if (now >= sound.stopAtTick()) { player.stopSound(sound.zone().sound(), sound.zone().category()); it.remove(); }
|
||||
}
|
||||
if (current.isEmpty()) active.remove(player.getUniqueId());
|
||||
}
|
||||
/** Removes disconnected-player state; the client no longer needs a stop packet. */
|
||||
public void forget(UUID playerId) { active.remove(playerId); }
|
||||
/** Stops all sounds started by this plugin. */
|
||||
public void stopAll() {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
Map<String, ActiveSound> sounds = active.get(player.getUniqueId()); if (sounds != null) sounds.values().forEach(sound -> player.stopSound(sound.zone().sound(), sound.zone().category()));
|
||||
} active.clear();
|
||||
}
|
||||
private record ActiveSound(AudioZone zone, long stopAtTick) { }
|
||||
}
|
||||
Reference in New Issue
Block a user