diff --git a/src/main/java/com/garbagemule/MobArena/ArenaMasterImpl.java b/src/main/java/com/garbagemule/MobArena/ArenaMasterImpl.java index e4a3873..4c0974f 100644 --- a/src/main/java/com/garbagemule/MobArena/ArenaMasterImpl.java +++ b/src/main/java/com/garbagemule/MobArena/ArenaMasterImpl.java @@ -674,6 +674,7 @@ public class ArenaMasterImpl implements ArenaMaster plugin.reloadConfig(); config = plugin.getConfig(); initialize(); + plugin.reloadSigns(); if (wasEnabled) setEnabled(true); } diff --git a/src/main/java/com/garbagemule/MobArena/MobArena.java b/src/main/java/com/garbagemule/MobArena/MobArena.java index 60f950d..813e413 100644 --- a/src/main/java/com/garbagemule/MobArena/MobArena.java +++ b/src/main/java/com/garbagemule/MobArena/MobArena.java @@ -8,6 +8,9 @@ import com.garbagemule.MobArena.listeners.MagicSpellsListener; import com.garbagemule.MobArena.metrics.ArenaCountChart; import com.garbagemule.MobArena.metrics.ClassCountChart; import com.garbagemule.MobArena.metrics.VaultChart; +import com.garbagemule.MobArena.signs.ArenaSign; +import com.garbagemule.MobArena.signs.SignBootstrap; +import com.garbagemule.MobArena.signs.SignListeners; import com.garbagemule.MobArena.things.ThingManager; import com.garbagemule.MobArena.util.VersionChecker; import com.garbagemule.MobArena.util.config.ConfigUtils; @@ -18,6 +21,7 @@ import org.bukkit.ChatColor; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.configuration.serialization.ConfigurationSerialization; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.RegisteredServiceProvider; @@ -52,6 +56,8 @@ public class MobArena extends JavaPlugin private Messenger messenger; private ThingManager thingman; + private SignListeners signListeners; + @Override public void onLoad() { thingman = new ThingManager(this); @@ -61,6 +67,7 @@ public class MobArena extends JavaPlugin // Initialize config-file configFile = new File(getDataFolder(), "config.yml"); config = new YamlConfiguration(); + ConfigurationSerialization.registerClass(ArenaSign.class); reloadConfig(); // Initialize global messenger @@ -88,6 +95,9 @@ public class MobArena extends JavaPlugin arenaMaster = new ArenaMasterImpl(this); arenaMaster.initialize(); + // Load signs after Messenger and ArenaMaster + reloadSigns(); + // Register event listeners registerListeners(); @@ -111,6 +121,7 @@ public class MobArena extends JavaPlugin } arenaMaster.resetArenaMap(); VersionChecker.shutdown(); + ConfigurationSerialization.unregisterClass(ArenaSign.class); getLogger().info("disabled."); } @@ -126,6 +137,16 @@ public class MobArena extends JavaPlugin @Override public void reloadConfig() { + // Make sure the data folder exists + File data = new File(getDataFolder(), "data"); + if (!data.exists()) { + boolean created = data.mkdir(); + if (!created) { + throw new IllegalStateException("Failed to create data folder!"); + } + getLogger().info("Created data folder."); + } + // Check if the config-file exists if (!configFile.exists()) { getLogger().info("No config-file found, creating default..."); @@ -162,6 +183,15 @@ public class MobArena extends JavaPlugin } } + void reloadSigns() { + if (signListeners != null) { + signListeners.unregister(); + } + SignBootstrap bootstrap = SignBootstrap.create(this); + signListeners = new SignListeners(); + signListeners.register(bootstrap); + } + @Override public void saveConfig() { try { diff --git a/src/main/java/com/garbagemule/MobArena/signs/ArenaSign.java b/src/main/java/com/garbagemule/MobArena/signs/ArenaSign.java new file mode 100644 index 0000000..783a189 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/ArenaSign.java @@ -0,0 +1,47 @@ +package com.garbagemule.MobArena.signs; + +import org.bukkit.Location; +import org.bukkit.configuration.serialization.ConfigurationSerializable; + +import java.util.HashMap; +import java.util.Map; + +public class ArenaSign implements ConfigurationSerializable { + + final Location location; + final String templateId; + final String arenaId; + final String type; + + ArenaSign(Location location, String templateId, String arenaId, String type) { + this.location = location; + this.templateId = templateId; + this.arenaId = arenaId; + this.type = type; + } + + @Override + public Map serialize() { + Map result = new HashMap<>(); + result.put("location", location); + result.put("templateId", templateId); + result.put("arenaId", arenaId); + result.put("type", type); + return result; + } + + @SuppressWarnings("WeakerAccess") + public static ArenaSign deserialize(Map map) { + try { + Location location = (Location) map.get("location"); + String templateId = (String) map.get("templateId"); + String arenaId = (String) map.get("arenaId"); + String type = (String) map.get("type"); + return new ArenaSign(location, templateId, arenaId, type); + } catch (ClassCastException e) { + String msg = "An arena sign in " + SignStore.FILENAME + " is invalid! You may have to delete the file."; + throw new IllegalStateException(msg); + } + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/HandlesSignClicks.java b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignClicks.java new file mode 100644 index 0000000..07c1d3b --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignClicks.java @@ -0,0 +1,52 @@ +package com.garbagemule.MobArena.signs; + +import org.bukkit.block.Block; +import org.bukkit.block.Sign; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerInteractEvent; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +class HandlesSignClicks implements Listener { + + private static final long COOLDOWN_TIME = 500; + + private final SignStore signStore; + private final InvokesSignAction invokesSignAction; + private final Map cooldowns; + + HandlesSignClicks(SignStore signStore, InvokesSignAction invokesSignAction) { + this.signStore = signStore; + this.invokesSignAction = invokesSignAction; + this.cooldowns = new HashMap<>(); + } + + @EventHandler(priority = EventPriority.MONITOR) + public void on(PlayerInteractEvent event) { + Block block = event.getClickedBlock(); + if (block == null) { + return; + } + if (!(block.getState() instanceof Sign)) { + return; + } + signStore.findByLocation(block.getLocation()) + .ifPresent(sign -> purgeAndInvoke(sign, event.getPlayer())); + } + + private void purgeAndInvoke(ArenaSign sign, Player player) { + long now = System.currentTimeMillis(); + cooldowns.values().removeIf(time -> time < now); + + cooldowns.computeIfAbsent(player.getUniqueId(), id -> { + invokesSignAction.invoke(sign, player); + return now + COOLDOWN_TIME; + }); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/HandlesSignCreation.java b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignCreation.java new file mode 100644 index 0000000..a878afa --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignCreation.java @@ -0,0 +1,65 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.Messenger; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.SignChangeEvent; + +import java.util.stream.IntStream; + +class HandlesSignCreation implements Listener { + + private final StoresNewSign storesNewSign; + private final RendersTemplateById rendersTemplate; + private final Messenger messenger; + + HandlesSignCreation( + StoresNewSign storesNewSign, + RendersTemplateById rendersTemplate, + Messenger messenger + ) { + this.storesNewSign = storesNewSign; + this.rendersTemplate = rendersTemplate; + this.messenger = messenger; + } + + @EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true) + public void on(SignChangeEvent event) { + if (!trim(event, 0).equalsIgnoreCase("[MA]")) { + return; + } + + Location location = event.getBlock().getLocation(); + String arenaId = trim(event, 1); + String signType = trim(event, 2).toLowerCase(); + String templateId = trim(event, 3).toLowerCase(); + + if (templateId.isEmpty()) { + templateId = signType; + } + + Player player = event.getPlayer(); + try { + storesNewSign.store(location, arenaId, templateId, signType); + messenger.tell(player, "New " + signType + " sign created for arena " + arenaId); + + String[] lines = rendersTemplate.render(templateId, arenaId); + IntStream.range(0, 4) + .forEach(i -> event.setLine(i, lines[i])); + } catch (IllegalArgumentException e) { + messenger.tell(player, e.getMessage()); + } + } + + private String trim(SignChangeEvent event, int index) { + String line = event.getLine(index); + if (line == null) { + return ""; + } + return line.trim(); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/HandlesSignDestruction.java b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignDestruction.java new file mode 100644 index 0000000..348869b --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/HandlesSignDestruction.java @@ -0,0 +1,33 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.Messenger; +import org.bukkit.Location; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.block.BlockBreakEvent; + +class HandlesSignDestruction implements Listener { + + private final RemovesSignAtLocation removesSignAtLocation; + private final Messenger messenger; + + HandlesSignDestruction( + RemovesSignAtLocation removesSignAtLocation, + Messenger messenger + ) { + this.removesSignAtLocation = removesSignAtLocation; + this.messenger = messenger; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(BlockBreakEvent event) { + Location location = event.getBlock().getLocation(); + removesSignAtLocation.remove(location) + .ifPresent(sign -> messenger.tell( + event.getPlayer(), + "Removed " + sign.type + " sign for arena " + sign.arenaId + )); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/InvokesSignAction.java b/src/main/java/com/garbagemule/MobArena/signs/InvokesSignAction.java new file mode 100644 index 0000000..e6c89c5 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/InvokesSignAction.java @@ -0,0 +1,50 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.Messenger; +import com.garbagemule.MobArena.Msg; +import com.garbagemule.MobArena.framework.Arena; +import com.garbagemule.MobArena.framework.ArenaMaster; +import org.bukkit.entity.Player; + +import java.util.function.Consumer; + +class InvokesSignAction { + + private final ArenaMaster arenaMaster; + private final Messenger messenger; + + InvokesSignAction(ArenaMaster arenaMaster, Messenger messenger) { + this.arenaMaster = arenaMaster; + this.messenger = messenger; + } + + void invoke(ArenaSign sign, Player player) { + if (sign.type.equals("join")) { + withArena(sign, player, arena -> { + if (arena.canJoin(player)) { + // Join message is sent in playerJoin + arena.playerJoin(player, player.getLocation()); + } + }); + } else if (sign.type.equals("leave")) { + withArena(sign, player, arena -> { + if (arena.inArena(player) || arena.inLobby(player) || arena.inSpec(player)) { + // Leave message is not sent in playerLeave + if (arena.playerLeave(player)) { + arena.getMessenger().tell(player, Msg.LEAVE_PLAYER_LEFT); + } + } + }); + } + } + + private void withArena(ArenaSign sign, Player player, Consumer action) { + Arena arena = arenaMaster.getArenaWithName(sign.arenaId); + if (arena == null) { + messenger.tell(player, "Arena " + sign.arenaId + " not found"); + return; + } + action.accept(arena); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/LoadsSignStore.java b/src/main/java/com/garbagemule/MobArena/signs/LoadsSignStore.java new file mode 100644 index 0000000..6074094 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/LoadsSignStore.java @@ -0,0 +1,46 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +class LoadsSignStore { + + private final MobArena plugin; + + LoadsSignStore(MobArena plugin) { + this.plugin = plugin; + } + + SignStore load() { + YamlConfiguration yaml = new YamlConfiguration(); + try { + File data = new File(plugin.getDataFolder(), "data"); + yaml.load(new File(data, SignStore.FILENAME)); + } catch (FileNotFoundException e) { + return new SignStore(Collections.emptyList()); + } catch (InvalidConfigurationException e) { + String msg = SignStore.FILENAME + " is invalid! You may have to delete it."; + throw new IllegalStateException(msg, e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + List signs = yaml.getList("signs").stream() + .filter(raw -> raw instanceof ArenaSign) + .map(raw -> (ArenaSign) raw) + .collect(Collectors.toList()); + + plugin.getLogger().info("Loaded " + signs.size() + " arena signs."); + + return new SignStore(signs); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/LoadsTemplateStore.java b/src/main/java/com/garbagemule/MobArena/signs/LoadsTemplateStore.java new file mode 100644 index 0000000..b2c0c9a --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/LoadsTemplateStore.java @@ -0,0 +1,106 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class LoadsTemplateStore { + + private final MobArena plugin; + + LoadsTemplateStore(MobArena plugin) { + this.plugin = plugin; + } + + TemplateStore read() { + YamlConfiguration yaml = new YamlConfiguration(); + try { + File file = new File(plugin.getDataFolder(), TemplateStore.FILENAME); + if (!file.exists()) { + plugin.getLogger().info(TemplateStore.FILENAME + " not found, creating default..."); + plugin.saveResource(TemplateStore.FILENAME, false); + } + yaml.load(file); + } catch (InvalidConfigurationException e) { + throw new IllegalStateException(TemplateStore.FILENAME + " is invalid!", e); + } catch (FileNotFoundException e) { + throw new IllegalStateException(TemplateStore.FILENAME + " is missing!", e); + } catch (IOException e) { + throw new IllegalStateException(e); + } + + Map map = new HashMap<>(); + for (String key : yaml.getKeys(false)) { + validateTemplateNode(key, yaml); + String templateId = stripStateSuffix(key); + map.computeIfAbsent(templateId, id -> loadTemplate(id, yaml)); + } + + plugin.getLogger().info("Loaded " + map.size() + " sign templates."); + + return new TemplateStore(map); + } + + private void validateTemplateNode(String key, YamlConfiguration yaml) { + List list = yaml.getList(key); + if (list == null) { + String msg = "Template " + key + " in " + TemplateStore.FILENAME + " is not a list!"; + throw new IllegalStateException(msg); + } + list.forEach(element -> { + if (!(element instanceof String)) { + String msg = "Template " + key + " in " + TemplateStore.FILENAME + " is not a valid list of strings!"; + throw new IllegalStateException(msg); + } + }); + } + + private String stripStateSuffix(String id) { + if (hasStateSuffix(id)) { + return id.split("-", -1)[0]; + } + return id; + } + + private boolean hasStateSuffix(String id) { + return id.endsWith("-idle") + || id.endsWith("-joining") + || id.endsWith("-running"); + } + + private Template loadTemplate(String id, YamlConfiguration yaml) { + String[] base = getLines(yaml, id); + String[] idle = getLines(yaml, id + "-idle"); + String[] joining = getLines(yaml, id + "-joining"); + String[] running = getLines(yaml, id + "-running"); + + return new Template.Builder(id) + .withBase(base) + .withIdle(idle) + .withJoining(joining) + .withRunning(running) + .build(); + } + + private String[] getLines(YamlConfiguration config, String id) { + List list = config.getStringList(id); + if (list.isEmpty()) { + return null; + } + while (list.size() < 4) { + list.add(""); + } + if (list.size() > 4) { + list = list.subList(0, 4); + } + return list.toArray(new String[0]); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/RedrawsArenaSigns.java b/src/main/java/com/garbagemule/MobArena/signs/RedrawsArenaSigns.java new file mode 100644 index 0000000..3c6856d --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/RedrawsArenaSigns.java @@ -0,0 +1,61 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.ChatColor; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +class RedrawsArenaSigns { + + private final SignStore signStore; + private final TemplateStore templateStore; + private final RendersTemplate rendersTemplate; + private final SetsLines setsSignLines; + + RedrawsArenaSigns( + SignStore signStore, + TemplateStore templateStore, + RendersTemplate rendersTemplate, + SetsLines setsSignLines + ) { + this.signStore = signStore; + this.templateStore = templateStore; + this.rendersTemplate = rendersTemplate; + this.setsSignLines = setsSignLines; + } + + void redraw(Arena arena) { + List signs = signStore.findByArenaId(arena.configName()); + + Map rendered = signs.stream() + .map(sign -> sign.templateId) + .distinct() + .collect(Collectors.toMap( + templateId -> templateId, + templateId -> render(templateId, arena) + )); + + signs.forEach(sign -> setsSignLines.set( + sign.location, + rendered.get(sign.templateId) + )); + } + + private String[] render(String templateId, Arena arena) { + return templateStore.findById(templateId) + .map(template -> rendersTemplate.render(template, arena)) + .orElseGet(() -> notFound(templateId)); + } + + private static String[] notFound(String templateId) { + return new String[]{ + String.join(ChatColor.MAGIC + "b" + ChatColor.RESET, "BROKEN".split("")), + "Template", + ChatColor.BOLD + templateId, + "not found! :(" + }; + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/RedrawsSignsOnUpdates.java b/src/main/java/com/garbagemule/MobArena/signs/RedrawsSignsOnUpdates.java new file mode 100644 index 0000000..054de17 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/RedrawsSignsOnUpdates.java @@ -0,0 +1,65 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; +import com.garbagemule.MobArena.events.*; +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.scheduler.BukkitScheduler; + +class RedrawsSignsOnUpdates implements Listener { + + private final RedrawsArenaSigns redrawsArenaSigns; + private final BukkitScheduler scheduler; + private final MobArena plugin; + + RedrawsSignsOnUpdates( + RedrawsArenaSigns redrawsArenaSigns, + MobArena plugin + ) { + this.redrawsArenaSigns = redrawsArenaSigns; + this.scheduler = plugin.getServer().getScheduler(); + this.plugin = plugin; + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaPlayerJoinEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaPlayerLeaveEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaPlayerReadyEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaStartEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(NewWaveEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaPlayerDeathEvent event) { + handle(event.getArena()); + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void on(ArenaEndEvent event) { + handle(event.getArena()); + } + + private void handle(Arena arena) { + scheduler.runTask(plugin, () -> redrawsArenaSigns.redraw(arena)); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/RemovesSignAtLocation.java b/src/main/java/com/garbagemule/MobArena/signs/RemovesSignAtLocation.java new file mode 100644 index 0000000..4bf9c92 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/RemovesSignAtLocation.java @@ -0,0 +1,26 @@ +package com.garbagemule.MobArena.signs; + +import org.bukkit.Location; + +import java.util.Optional; + +class RemovesSignAtLocation { + + private final SignStore signStore; + private final SavesSignStore savesSignStore; + + RemovesSignAtLocation( + SignStore signStore, + SavesSignStore savesSignStore + ) { + this.signStore = signStore; + this.savesSignStore = savesSignStore; + } + + Optional remove(Location location) { + Optional sign = signStore.remove(location); + sign.ifPresent(s -> savesSignStore.save(signStore)); + return sign; + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/RendersTemplate.java b/src/main/java/com/garbagemule/MobArena/signs/RendersTemplate.java new file mode 100644 index 0000000..48f7188 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/RendersTemplate.java @@ -0,0 +1,76 @@ +package com.garbagemule.MobArena.signs; + +import static java.lang.String.valueOf; + +import com.garbagemule.MobArena.framework.Arena; +import org.bukkit.ChatColor; + +class RendersTemplate { + + String[] render(Template template, Arena arena) { + String[] lines = getTemplateByState(template, arena); + + String[] result = new String[lines.length]; + for (int i = 0; i < lines.length; i++) { + String rendered = render(lines[i], arena); + result[i] = truncate(rendered); + } + return result; + } + + private String[] getTemplateByState(Template template, Arena arena) { + if (arena.isRunning()) { + return template.running; + } + if (arena.getPlayersInLobby().size() > 0) { + return template.joining; + } + return template.idle; + } + + private String render(String line, Arena arena) { + String result = generic(line, arena); + if (arena.isRunning()) { + result = running(result, arena); + } else { + result = joining(result, arena); + } + return ChatColor.translateAlternateColorCodes('&', result); + } + + private String generic(String line, Arena arena) { + return line + .replace("", arena.configName()) + .replace("", valueOf(arena.getMinPlayers())) + .replace("", valueOf(arena.getMaxPlayers())); + } + + private String running(String line, Arena arena) { + return line + .replace("", valueOf(arena.getPlayerCount())) + .replace("", valueOf(arena.getPlayersInArena().size())) + .replace("", valueOf(arena.getPlayerCount() - arena.getPlayersInArena().size())) + .replace("", valueOf(arena.getWaveManager().getWaveNumber())) + .replace("", valueOf(arena.getWaveManager().getFinalWave())) + .replace("", "-") + .replace("", "-"); + } + + private String joining(String line, Arena arena) { + return line + .replace("", valueOf(arena.getPlayersInLobby().size())) + .replace("", valueOf(arena.getPlayersInLobby().size())) + .replace("", "-") + .replace("", "-") + .replace("", valueOf(arena.getPlayersInLobby().size())) + .replace("", valueOf(arena.getReadyPlayersInLobby().size())); + } + + private String truncate(String rendered) { + if (rendered.length() <= 15) { + return rendered; + } + return rendered.substring(0, 15); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/RendersTemplateById.java b/src/main/java/com/garbagemule/MobArena/signs/RendersTemplateById.java new file mode 100644 index 0000000..f6a405e --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/RendersTemplateById.java @@ -0,0 +1,36 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.framework.Arena; +import com.garbagemule.MobArena.framework.ArenaMaster; + +class RendersTemplateById { + + private final ArenaMaster arenaMaster; + private final TemplateStore templateStore; + private final RendersTemplate rendersTemplate; + + RendersTemplateById( + ArenaMaster arenaMaster, + TemplateStore templateStore, + RendersTemplate rendersTemplate + ) { + this.arenaMaster = arenaMaster; + this.templateStore = templateStore; + this.rendersTemplate = rendersTemplate; + } + + String[] render(String templateId, String arenaId) { + Template template = templateStore.findById(templateId) + .orElseThrow(() -> new IllegalArgumentException( + "Template " + templateId + " not found" + )); + + Arena arena = arenaMaster.getArenaWithName(arenaId); + if (arena == null) { + throw new IllegalStateException("Arena " + arenaId + " not found"); + } + + return rendersTemplate.render(template, arena); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/SavesSignStore.java b/src/main/java/com/garbagemule/MobArena/signs/SavesSignStore.java new file mode 100644 index 0000000..5aa2a0a --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/SavesSignStore.java @@ -0,0 +1,32 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +class SavesSignStore { + + private final MobArena plugin; + + SavesSignStore(MobArena plugin) { + this.plugin = plugin; + } + + void save(SignStore signStore) { + YamlConfiguration yaml = new YamlConfiguration(); + List values = new ArrayList<>(signStore.findAll()); + yaml.set("signs", values); + try { + File data = new File(plugin.getDataFolder(), "data"); + yaml.options().header("MobArena Sign Store\n\nPlease DON'T edit this file by hand!\n"); + yaml.save(new File(data, SignStore.FILENAME)); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/SetsLines.java b/src/main/java/com/garbagemule/MobArena/signs/SetsLines.java new file mode 100644 index 0000000..24a8784 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/SetsLines.java @@ -0,0 +1,22 @@ +package com.garbagemule.MobArena.signs; + +import org.bukkit.Location; +import org.bukkit.block.BlockState; +import org.bukkit.block.Sign; + +class SetsLines { + + void set(Location location, String[] lines) { + BlockState state = location.getBlock().getState(); + if (!(state instanceof Sign)) { + return; + } + + Sign sign = (Sign) state; + for (int i = 0; i < lines.length; i++) { + sign.setLine(i, lines[i]); + } + sign.update(); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/SignBootstrap.java b/src/main/java/com/garbagemule/MobArena/signs/SignBootstrap.java new file mode 100644 index 0000000..e05abb0 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/SignBootstrap.java @@ -0,0 +1,130 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; + +@SuppressWarnings("WeakerAccess") +public class SignBootstrap { + + private final MobArena plugin; + private final SignStore signStore; + private final TemplateStore templateStore; + + private InvokesSignAction invokesSignAction; + private RedrawsArenaSigns redrawsArenaSigns; + private RemovesSignAtLocation removesSignAtLocation; + private RendersTemplate rendersTemplate; + private RendersTemplateById rendersTemplateById; + private SetsLines setsLines; + private StoresNewSign storesNewSign; + private SavesSignStore savesSignStore; + + private SignBootstrap( + MobArena plugin, + SignStore signStore, + TemplateStore templateStore + ) { + this.plugin = plugin; + this.signStore = signStore; + this.templateStore = templateStore; + } + + MobArena getPlugin() { + return plugin; + } + + SignStore getSignStore() { + return signStore; + } + + TemplateStore getTemplateStore() { + return templateStore; + } + + InvokesSignAction getInvokesSignAction() { + if (invokesSignAction == null) { + invokesSignAction = new InvokesSignAction( + plugin.getArenaMaster(), + plugin.getGlobalMessenger() + ); + } + return invokesSignAction; + } + + RedrawsArenaSigns getRedrawsArenaSigns() { + if (redrawsArenaSigns == null) { + redrawsArenaSigns = new RedrawsArenaSigns( + getSignStore(), + getTemplateStore(), + getRendersTemplate(), + getSetsLines() + ); + } + return redrawsArenaSigns; + } + + RemovesSignAtLocation getRemovesSignAtLocation() { + if (removesSignAtLocation == null) { + removesSignAtLocation = new RemovesSignAtLocation( + getSignStore(), + getSavesSignStore() + ); + } + return removesSignAtLocation; + } + + RendersTemplate getRendersTemplate() { + if (rendersTemplate == null) { + rendersTemplate = new RendersTemplate(); + } + return rendersTemplate; + } + + RendersTemplateById getRendersTemplateById() { + if (rendersTemplateById == null) { + rendersTemplateById = new RendersTemplateById( + plugin.getArenaMaster(), + getTemplateStore(), + getRendersTemplate() + ); + } + return rendersTemplateById; + } + + SetsLines getSetsLines() { + if (setsLines == null) { + setsLines = new SetsLines(); + } + return setsLines; + } + + StoresNewSign getStoresNewSign() { + if (storesNewSign == null) { + storesNewSign = new StoresNewSign( + plugin.getArenaMaster(), + getTemplateStore(), + getSignStore(), + getSavesSignStore() + ); + } + return storesNewSign; + } + + SavesSignStore getSavesSignStore() { + if (savesSignStore == null) { + savesSignStore = new SavesSignStore(plugin); + } + return savesSignStore; + } + + public static SignBootstrap create(MobArena plugin) { + TemplateStore templateStore = new LoadsTemplateStore(plugin).read(); + SignStore signStore = new LoadsSignStore(plugin).load(); + + SignBootstrap bootstrap = new SignBootstrap(plugin, signStore, templateStore); + + plugin.getArenaMaster().getArenas() + .forEach(bootstrap.getRedrawsArenaSigns()::redraw); + + return bootstrap; + } +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/SignListeners.java b/src/main/java/com/garbagemule/MobArena/signs/SignListeners.java new file mode 100644 index 0000000..b1276db --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/SignListeners.java @@ -0,0 +1,66 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.MobArena; +import org.bukkit.event.HandlerList; +import org.bukkit.event.Listener; + +import java.util.ArrayList; +import java.util.List; + +public class SignListeners { + + private final List listeners; + + public SignListeners() { + this.listeners = new ArrayList<>(); + } + + public void register(SignBootstrap bootstrap) { + listeners.add(clicks(bootstrap)); + listeners.add(creation(bootstrap)); + listeners.add(destruction(bootstrap)); + listeners.add(redraw(bootstrap)); + + listeners.forEach(listener -> register(listener, bootstrap)); + } + + private HandlesSignClicks clicks(SignBootstrap bootstrap) { + return new HandlesSignClicks( + bootstrap.getSignStore(), + bootstrap.getInvokesSignAction() + ); + } + + private HandlesSignCreation creation(SignBootstrap bootstrap) { + return new HandlesSignCreation( + bootstrap.getStoresNewSign(), + bootstrap.getRendersTemplateById(), + bootstrap.getPlugin().getGlobalMessenger() + ); + } + + private HandlesSignDestruction destruction(SignBootstrap bootstrap) { + return new HandlesSignDestruction( + bootstrap.getRemovesSignAtLocation(), + bootstrap.getPlugin().getGlobalMessenger() + ); + } + + private RedrawsSignsOnUpdates redraw(SignBootstrap bootstrap) { + return new RedrawsSignsOnUpdates( + bootstrap.getRedrawsArenaSigns(), + bootstrap.getPlugin() + ); + } + + private void register(Listener listener, SignBootstrap bootstrap) { + MobArena plugin = bootstrap.getPlugin(); + plugin.getServer().getPluginManager().registerEvents(listener, plugin); + } + + public void unregister() { + listeners.forEach(HandlerList::unregisterAll); + listeners.clear(); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/SignStore.java b/src/main/java/com/garbagemule/MobArena/signs/SignStore.java new file mode 100644 index 0000000..e244783 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/SignStore.java @@ -0,0 +1,49 @@ +package com.garbagemule.MobArena.signs; + +import org.bukkit.Location; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +class SignStore { + + static final String FILENAME = "signs.data"; + + private final Map signs; + + SignStore(List signs) { + this.signs = signs.stream() + .collect(Collectors.toMap( + sign -> sign.location, + sign -> sign + )); + } + + void store(ArenaSign sign) { + signs.put(sign.location, sign); + } + + Optional remove(Location location) { + ArenaSign sign = signs.remove(location); + return Optional.ofNullable(sign); + } + + Collection findAll() { + return signs.values(); + } + + Optional findByLocation(Location location) { + ArenaSign sign = signs.get(location); + return Optional.ofNullable(sign); + } + + List findByArenaId(String arenaId) { + return signs.values().stream() + .filter(sign -> sign.arenaId.equals(arenaId)) + .collect(Collectors.toList()); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/StoresNewSign.java b/src/main/java/com/garbagemule/MobArena/signs/StoresNewSign.java new file mode 100644 index 0000000..ebf9443 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/StoresNewSign.java @@ -0,0 +1,56 @@ +package com.garbagemule.MobArena.signs; + +import com.garbagemule.MobArena.framework.Arena; +import com.garbagemule.MobArena.framework.ArenaMaster; +import org.bukkit.Location; + +class StoresNewSign { + + private final ArenaMaster arenaMaster; + private final TemplateStore templateStore; + private final SignStore signStore; + private final SavesSignStore savesSignStore; + + StoresNewSign( + ArenaMaster arenaMaster, + TemplateStore templateStore, + SignStore signStore, + SavesSignStore savesSignStore + ) { + this.arenaMaster = arenaMaster; + this.templateStore = templateStore; + this.signStore = signStore; + this.savesSignStore = savesSignStore; + } + + void store( + Location location, + String arenaId, + String templateId, + String signType + ) { + Arena arena = arenaMaster.getArenaWithName(arenaId); + if (arena == null) { + throw new IllegalArgumentException("Arena " + arenaId + " not found"); + } + + templateStore.findById(templateId) + .orElseThrow(() -> new IllegalArgumentException( + "Template " + templateId + " not found" + )); + + switch (signType) { + case "info": + case "join": + case "leave": + break; + default: + throw new IllegalArgumentException("Invalid sign type: " + signType); + } + + signStore.store(new ArenaSign(location, templateId, arenaId, signType)); + + savesSignStore.save(signStore); + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/Template.java b/src/main/java/com/garbagemule/MobArena/signs/Template.java new file mode 100644 index 0000000..3d88767 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/Template.java @@ -0,0 +1,78 @@ +package com.garbagemule.MobArena.signs; + +class Template { + + final String[] idle; + final String[] joining; + final String[] running; + + private Template(String[] idle, String[] joining, String[] running) { + this.idle = idle; + this.joining = joining; + this.running = running; + } + + static class Builder { + + private String id; + private String[] base; + private String[] idle; + private String[] joining; + private String[] running; + + Builder(String id) { + this.id = id; + } + + Builder withBase(String[] lines) { + this.base = lines; + return this; + } + + Builder withIdle(String[] lines) { + this.idle = lines; + return this; + } + + Builder withJoining(String[] lines) { + this.joining = lines; + return this; + } + + Builder withRunning(String[] lines) { + this.running = lines; + return this; + } + + Template build() { + if (base == null) { + if (idle == null) { + missing("idle"); + } + if (joining == null) { + missing("joining"); + } + if (running == null) { + missing("running"); + } + } + if (idle == null) { + idle = base; + } + if (joining == null) { + joining = base; + } + if (running == null) { + running = base; + } + return new Template(idle, joining, running); + } + + private void missing(String state) { + String msg = "Missing either base template '" + id + "' or state template '" + id + "-" + state + "'"; + throw new IllegalArgumentException(msg); + } + + } + +} diff --git a/src/main/java/com/garbagemule/MobArena/signs/TemplateStore.java b/src/main/java/com/garbagemule/MobArena/signs/TemplateStore.java new file mode 100644 index 0000000..c18b626 --- /dev/null +++ b/src/main/java/com/garbagemule/MobArena/signs/TemplateStore.java @@ -0,0 +1,21 @@ +package com.garbagemule.MobArena.signs; + +import java.util.Map; +import java.util.Optional; + +class TemplateStore { + + static final String FILENAME = "signs.yml"; + + private final Map templates; + + TemplateStore(Map templates) { + this.templates = templates; + } + + Optional