This commit is contained in:
@@ -10,14 +10,16 @@ import org.joml.Vector3f;
|
||||
import us.tss3.blockparty.BlockPartyPlugin;
|
||||
import us.tss3.blockparty.config.ArenaConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* Owns an optional {@link BlockDisplay} entity that visually mirrors an arena's currently
|
||||
* selected target color. Purely cosmetic: fully optional (global toggle + per-arena
|
||||
* toggle/location), and every method safely no-ops when disabled, unset, or the arena's
|
||||
* world isn't loaded.
|
||||
* Owns any number of named, optional {@link BlockDisplay} entities that visually mirror an
|
||||
* arena's currently selected target color. Purely cosmetic: fully optional (global toggle +
|
||||
* per-billboard toggle/location), and every method safely no-ops when disabled, unset, or the
|
||||
* arena's world isn't loaded.
|
||||
*/
|
||||
public class BillboardManager {
|
||||
|
||||
@@ -25,75 +27,96 @@ public class BillboardManager {
|
||||
|
||||
private final BlockPartyPlugin plugin;
|
||||
private final ArenaConfig config;
|
||||
private UUID entityId;
|
||||
/** billboard name -> live display entity id, only present while spawned. */
|
||||
private final Map<String, UUID> entityIds = new HashMap<>();
|
||||
|
||||
public BillboardManager(BlockPartyPlugin plugin, ArenaConfig config) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
private boolean isActive() {
|
||||
return plugin.getConfigManager().isBillboardEnabled() && config.hasBillboard()
|
||||
&& config.getBillboard().getWorld() != null;
|
||||
private boolean globallyActive() {
|
||||
return plugin.getConfigManager().isBillboardEnabled() && config.hasAnyBillboards();
|
||||
}
|
||||
|
||||
/** Shows (spawning the display entity if needed) the given material as the current target. */
|
||||
/** Shows the given material as the current target on every enabled, valid billboard. */
|
||||
public void show(Material material) {
|
||||
if (!isActive()) {
|
||||
if (!globallyActive()) {
|
||||
return;
|
||||
}
|
||||
for (String name : config.getBillboards().keySet()) {
|
||||
showOne(name, material);
|
||||
}
|
||||
}
|
||||
|
||||
private void showOne(String name, Material material) {
|
||||
Location loc = config.getBillboard(name);
|
||||
if (loc == null || loc.getWorld() == null || !config.isBillboardEnabled(name)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
BlockDisplay display = resolve();
|
||||
BlockDisplay display = resolve(name);
|
||||
if (display == null) {
|
||||
display = spawn();
|
||||
display = spawn(name, loc);
|
||||
}
|
||||
if (display != null) {
|
||||
display.setBlock(material.createBlockData());
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to update billboard for arena '" + config.getName() + "'", ex);
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to update billboard '" + name
|
||||
+ "' for arena '" + config.getName() + "'", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/** Blanks the display (e.g. between matches) without destroying the entity. */
|
||||
/** Blanks every spawned display (e.g. between matches) without destroying the entities. */
|
||||
public void clear() {
|
||||
BlockDisplay display = resolve();
|
||||
if (display != null) {
|
||||
display.setBlock(Material.AIR.createBlockData());
|
||||
for (String name : entityIds.keySet().toArray(new String[0])) {
|
||||
BlockDisplay display = resolve(name);
|
||||
if (display != null) {
|
||||
display.setBlock(Material.AIR.createBlockData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fully removes the display entity. Safe to call even if nothing was ever spawned. */
|
||||
/** Fully removes every display entity. Safe to call even if nothing was ever spawned. */
|
||||
public void despawn() {
|
||||
BlockDisplay display = resolve();
|
||||
for (String name : entityIds.keySet().toArray(new String[0])) {
|
||||
BlockDisplay display = resolve(name);
|
||||
if (display != null) {
|
||||
display.remove();
|
||||
}
|
||||
entityIds.remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
/** Removes a single named billboard's live entity (e.g. after deletion or relocation). */
|
||||
public void despawnOne(String name) {
|
||||
BlockDisplay display = resolve(name);
|
||||
if (display != null) {
|
||||
display.remove();
|
||||
}
|
||||
entityId = null;
|
||||
entityIds.remove(name);
|
||||
}
|
||||
|
||||
/** Forces the entity to be re-created at its (possibly updated) configured location next time it's shown. */
|
||||
public void invalidate() {
|
||||
despawn();
|
||||
/** Forces a specific billboard to be re-created at its (possibly updated) location next show(). */
|
||||
public void invalidate(String name) {
|
||||
despawnOne(name);
|
||||
}
|
||||
|
||||
private BlockDisplay resolve() {
|
||||
if (entityId == null) {
|
||||
private BlockDisplay resolve(String name) {
|
||||
UUID id = entityIds.get(name);
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
Entity entity = plugin.getServer().getEntity(entityId);
|
||||
Entity entity = plugin.getServer().getEntity(id);
|
||||
if (entity instanceof BlockDisplay display && !display.isDead()) {
|
||||
return display;
|
||||
}
|
||||
entityId = null;
|
||||
entityIds.remove(name);
|
||||
return null;
|
||||
}
|
||||
|
||||
private BlockDisplay spawn() {
|
||||
Location loc = config.getBillboard();
|
||||
if (loc == null || loc.getWorld() == null) {
|
||||
return null;
|
||||
}
|
||||
private BlockDisplay spawn(String name, Location loc) {
|
||||
BlockDisplay display = loc.getWorld().spawn(loc, BlockDisplay.class, bd -> {
|
||||
bd.setPersistent(false);
|
||||
bd.setGlowing(true);
|
||||
@@ -103,7 +126,7 @@ public class BillboardManager {
|
||||
new Vector3f(1.5f, 1.5f, 1.5f),
|
||||
NO_ROTATION));
|
||||
});
|
||||
entityId = display.getUniqueId();
|
||||
entityIds.put(name, display.getUniqueId());
|
||||
return display;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
case "setspectator" -> setSpectator(sender, args);
|
||||
case "setbillboard" -> setBillboard(sender, args);
|
||||
case "delbillboard" -> delBillboard(sender, args);
|
||||
case "billboards" -> listBillboards(sender, args);
|
||||
case "pos1" -> pos1(sender, args);
|
||||
case "pos2" -> pos2(sender, args);
|
||||
case "setfloor" -> setFloor(sender, args);
|
||||
@@ -78,8 +79,9 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
"/blockparty setlobby <arena>",
|
||||
"/blockparty setspawn <arena>",
|
||||
"/blockparty setspectator <arena>",
|
||||
"/blockparty setbillboard <arena>",
|
||||
"/blockparty delbillboard <arena>",
|
||||
"/blockparty setbillboard <arena> [name]",
|
||||
"/blockparty delbillboard <arena> [name]",
|
||||
"/blockparty billboards <arena>",
|
||||
"/blockparty pos1 <arena>",
|
||||
"/blockparty pos2 <arena>",
|
||||
"/blockparty setfloor <arena>",
|
||||
@@ -198,25 +200,56 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
private void setBillboard(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
|
||||
String billboardName = args.length > 2 ? args[2] : "default";
|
||||
withArena(sender, args, arena -> {
|
||||
arena.getConfig().setBillboard(((Player) sender).getLocation());
|
||||
arena.getConfig().setBillboardEnabled(true);
|
||||
arena.getBillboardManager().invalidate();
|
||||
arena.getConfig().setBillboard(billboardName, ((Player) sender).getLocation());
|
||||
arena.getConfig().setBillboardEnabled(billboardName, true);
|
||||
arena.getBillboardManager().invalidate(billboardName);
|
||||
plugin.getArenaManager().save(arena);
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-set", Map.of("arena", arena.getConfig().getName())));
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-set",
|
||||
Map.of("arena", arena.getConfig().getName(), "name", billboardName)));
|
||||
});
|
||||
}
|
||||
|
||||
private void delBillboard(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup")) return;
|
||||
String billboardName = args.length > 2 ? args[2] : "default";
|
||||
withArena(sender, args, arena -> {
|
||||
arena.getBillboardManager().despawn();
|
||||
arena.getConfig().setBillboard(null);
|
||||
if (!arena.getConfig().hasBillboard(billboardName)) {
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-unknown",
|
||||
Map.of("arena", arena.getConfig().getName(), "name", billboardName)));
|
||||
return;
|
||||
}
|
||||
arena.getBillboardManager().despawnOne(billboardName);
|
||||
arena.getConfig().removeBillboard(billboardName);
|
||||
plugin.getArenaManager().save(arena);
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-removed", Map.of("arena", arena.getConfig().getName())));
|
||||
sender.sendMessage(plugin.getMessages().get("admin.billboard-removed",
|
||||
Map.of("arena", arena.getConfig().getName(), "name", billboardName)));
|
||||
});
|
||||
}
|
||||
|
||||
private void listBillboards(CommandSender sender, String[] args) {
|
||||
String name = argOrCurrentArenaless(args);
|
||||
if (name == null) {
|
||||
sender.sendMessage("Usage: /blockparty billboards <arena>");
|
||||
return;
|
||||
}
|
||||
var opt = plugin.getArenaManager().get(name);
|
||||
if (opt.isEmpty()) {
|
||||
sender.sendMessage(plugin.getMessages().get("errors.unknown-arena", Map.of("arena", name)));
|
||||
return;
|
||||
}
|
||||
ArenaConfig cfg = opt.get().getConfig();
|
||||
sender.sendMessage("§6=== Billboards: " + cfg.getName() + " ===");
|
||||
if (cfg.getBillboards().isEmpty()) {
|
||||
sender.sendMessage("§7(none set)");
|
||||
return;
|
||||
}
|
||||
for (String bbName : cfg.getBillboards().keySet()) {
|
||||
sender.sendMessage("§7- §f" + bbName + " §7(enabled: " + cfg.isBillboardEnabled(bbName) + ")");
|
||||
}
|
||||
}
|
||||
|
||||
private void pos1(CommandSender sender, String[] args) {
|
||||
if (!requireAdmin(sender, "blockparty.admin.setup") || !requirePlayer(sender)) return;
|
||||
withArena(sender, args, arena -> {
|
||||
@@ -335,7 +368,7 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
sender.sendMessage("§7Lobby set: §f" + (cfg.getLobby() != null));
|
||||
sender.sendMessage("§7Spawn set: §f" + (cfg.getSpawn() != null));
|
||||
sender.sendMessage("§7Spectator set: §f" + (cfg.getSpectator() != null));
|
||||
sender.sendMessage("§7Billboard set: §f" + (cfg.getBillboard() != null) + (cfg.getBillboard() != null ? " (enabled: " + cfg.isBillboardEnabled() + ")" : ""));
|
||||
sender.sendMessage("§7Billboards: §f" + cfg.getBillboards().size() + " (see /bp billboards " + cfg.getName() + ")");
|
||||
sender.sendMessage("§7Floor region set: §f" + cfg.hasFloorRegion());
|
||||
sender.sendMessage("§7Floor generated: §f" + cfg.hasGeneratedLayout());
|
||||
sender.sendMessage("§7Floor materials: §f" + cfg.getFloorMaterials().size());
|
||||
@@ -453,7 +486,7 @@ public class BlockPartyCommand implements CommandExecutor, TabCompleter {
|
||||
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
|
||||
if (args.length == 1) {
|
||||
return List.of("help", "join", "leave", "arenas", "stats", "create", "delete", "enable", "disable",
|
||||
"setlobby", "setspawn", "setspectator", "setbillboard", "delbillboard", "pos1", "pos2", "setfloor", "generate", "info", "list", "reload")
|
||||
"setlobby", "setspawn", "setspectator", "setbillboard", "delbillboard", "billboards", "pos1", "pos2", "setfloor", "generate", "info", "list", "reload")
|
||||
.stream().filter(s -> s.startsWith(args[0].toLowerCase())).collect(Collectors.toList());
|
||||
}
|
||||
if (args.length == 2) {
|
||||
|
||||
@@ -5,7 +5,12 @@ import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/** Mutable POJO describing a single arena's configuration, mirrors arenas/<name>.yml. */
|
||||
public class ArenaConfig {
|
||||
@@ -15,8 +20,10 @@ public class ArenaConfig {
|
||||
private Location lobby;
|
||||
private Location spawn;
|
||||
private Location spectator;
|
||||
private Location billboard;
|
||||
private boolean billboardEnabled = true;
|
||||
/** Named billboards: displays that mirror the current round's target color. Any number
|
||||
* may be set (e.g. one per side of the arena), each independently enabled/disabled. */
|
||||
private final Map<String, Location> billboards = new LinkedHashMap<>();
|
||||
private final Set<String> disabledBillboards = new HashSet<>();
|
||||
private int[] pos1; // x,y,z block coords
|
||||
private int[] pos2;
|
||||
|
||||
@@ -86,24 +93,42 @@ public class ArenaConfig {
|
||||
this.spectator = spectator;
|
||||
}
|
||||
|
||||
public Location getBillboard() {
|
||||
return billboard;
|
||||
/** All configured billboards by name, in insertion order. */
|
||||
public Map<String, Location> getBillboards() {
|
||||
return Collections.unmodifiableMap(billboards);
|
||||
}
|
||||
|
||||
public void setBillboard(Location billboard) {
|
||||
this.billboard = billboard;
|
||||
public Location getBillboard(String name) {
|
||||
return billboards.get(name);
|
||||
}
|
||||
|
||||
public boolean isBillboardEnabled() {
|
||||
return billboardEnabled;
|
||||
public void setBillboard(String name, Location location) {
|
||||
billboards.put(name, location);
|
||||
}
|
||||
|
||||
public void setBillboardEnabled(boolean billboardEnabled) {
|
||||
this.billboardEnabled = billboardEnabled;
|
||||
public void removeBillboard(String name) {
|
||||
billboards.remove(name);
|
||||
disabledBillboards.remove(name);
|
||||
}
|
||||
|
||||
public boolean hasBillboard() {
|
||||
return billboard != null && billboardEnabled;
|
||||
public boolean hasBillboard(String name) {
|
||||
return billboards.containsKey(name);
|
||||
}
|
||||
|
||||
public boolean hasAnyBillboards() {
|
||||
return !billboards.isEmpty();
|
||||
}
|
||||
|
||||
public boolean isBillboardEnabled(String name) {
|
||||
return !disabledBillboards.contains(name);
|
||||
}
|
||||
|
||||
public void setBillboardEnabled(String name, boolean enabled) {
|
||||
if (enabled) {
|
||||
disabledBillboards.remove(name);
|
||||
} else {
|
||||
disabledBillboards.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
public int[] getPos1() {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package us.tss3.blockparty.config;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import us.tss3.blockparty.logic.ArenaConfigValidator;
|
||||
@@ -57,8 +59,21 @@ public class ArenaConfigLoader {
|
||||
cfg.setLobby(LocationUtil.fromSection(yml.getConfigurationSection("lobby")));
|
||||
cfg.setSpawn(LocationUtil.fromSection(yml.getConfigurationSection("spawn")));
|
||||
cfg.setSpectator(LocationUtil.fromSection(yml.getConfigurationSection("spectator")));
|
||||
cfg.setBillboard(LocationUtil.fromSection(yml.getConfigurationSection("billboard")));
|
||||
cfg.setBillboardEnabled(yml.getBoolean("billboard-enabled", true));
|
||||
ConfigurationSection billboardsSection = yml.getConfigurationSection("billboards");
|
||||
if (billboardsSection != null) {
|
||||
for (String bbName : billboardsSection.getKeys(false)) {
|
||||
ConfigurationSection bbSection = billboardsSection.getConfigurationSection(bbName);
|
||||
if (bbSection == null) {
|
||||
continue;
|
||||
}
|
||||
Location loc = LocationUtil.fromSection(bbSection.getConfigurationSection("location"));
|
||||
if (loc == null) {
|
||||
continue;
|
||||
}
|
||||
cfg.setBillboard(bbName, loc);
|
||||
cfg.setBillboardEnabled(bbName, bbSection.getBoolean("enabled", true));
|
||||
}
|
||||
}
|
||||
if (yml.contains("pos1")) {
|
||||
cfg.setPos1(new int[]{yml.getInt("pos1.x"), yml.getInt("pos1.y"), yml.getInt("pos1.z")});
|
||||
}
|
||||
@@ -108,10 +123,14 @@ public class ArenaConfigLoader {
|
||||
if (cfg.getSpectator() != null) {
|
||||
LocationUtil.toSection(yml.createSection("spectator"), cfg.getSpectator());
|
||||
}
|
||||
if (cfg.getBillboard() != null) {
|
||||
LocationUtil.toSection(yml.createSection("billboard"), cfg.getBillboard());
|
||||
if (!cfg.getBillboards().isEmpty()) {
|
||||
ConfigurationSection billboardsSection = yml.createSection("billboards");
|
||||
for (var entry : cfg.getBillboards().entrySet()) {
|
||||
ConfigurationSection bbSection = billboardsSection.createSection(entry.getKey());
|
||||
LocationUtil.toSection(bbSection.createSection("location"), entry.getValue());
|
||||
bbSection.set("enabled", cfg.isBillboardEnabled(entry.getKey()));
|
||||
}
|
||||
}
|
||||
yml.set("billboard-enabled", cfg.isBillboardEnabled());
|
||||
if (cfg.getPos1() != null) {
|
||||
yml.set("pos1.x", cfg.getPos1()[0]);
|
||||
yml.set("pos1.y", cfg.getPos1()[1]);
|
||||
|
||||
Reference in New Issue
Block a user