package tc.oc.commons.bukkit.ticket; import java.time.Duration; import java.util.Collections; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; import com.google.common.cache.LoadingCache; import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.boss.BarColor; import org.bukkit.boss.BarStyle; import org.bukkit.boss.BossBar; 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.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.bukkit.users.OnlinePlayers; import tc.oc.api.docs.Arena; import tc.oc.api.docs.Game; import tc.oc.api.docs.Server; import tc.oc.api.docs.Ticket; import tc.oc.api.docs.virtual.MatchDoc; import tc.oc.api.games.ArenaStore; import tc.oc.api.games.GameStore; import tc.oc.api.games.TicketStore; import tc.oc.api.model.ModelDispatcher; import tc.oc.api.model.ModelListener; import tc.oc.api.servers.ServerStore; import tc.oc.commons.bukkit.bossbar.BossBarFactory; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.format.GameFormatter; import tc.oc.commons.bukkit.util.PlayerStates; import tc.oc.commons.core.plugin.PluginFacet; import tc.oc.commons.core.util.CacheUtils; import tc.oc.minecraft.api.scheduler.Tickable; /** * Displays the current state of {@link Game} queues to the {@link Player}s in each queue. */ @Singleton public class TicketDisplay implements ModelListener, Listener, PluginFacet, Tickable { private final BukkitUserStore userStore; private final OnlinePlayers players; private final PlayerStates playerStates; private final Audiences audiences; private final GameFormatter gameFormatter; private final Server localServer; private final ServerStore servers; private final TicketStore tickets; private final ArenaStore arenas; private final GameStore games; private final LoadingCache<Arena, BossBar> bars; @Inject TicketDisplay(BukkitUserStore userStore, OnlinePlayers players, PlayerStates playerStates, Audiences audiences, GameFormatter gameFormatter, Server localServer, BossBarFactory bossBarFactory, ServerStore servers, TicketStore tickets, ArenaStore arenas, GameStore games, ModelDispatcher modelDispatcher) { this.userStore = userStore; this.players = players; this.playerStates = playerStates; this.audiences = audiences; this.gameFormatter = gameFormatter; this.localServer = localServer; this.servers = servers; this.tickets = tickets; this.arenas = arenas; this.games = games; this.bars = CacheUtils.newCache(game -> bossBarFactory.createRenderedBossBar()); modelDispatcher.subscribe(this); } private void updateArena(Arena arena) { final Game game = games.byId(arena.game_id()); int minPlayers = 0; if(arena.next_server_id() != null) { minPlayers = servers.byId(arena.next_server_id()).min_players(); } final BaseComponent text; final double progress; if(minPlayers > 0 && arena.num_queued() < minPlayers) { text = gameFormatter.queued(game, minPlayers - arena.num_queued()); progress = (double) arena.num_queued() / (double) minPlayers; } else { text = gameFormatter.joining(game); progress = 1; } bars.getUnchecked(arena).update(text, progress, BarColor.YELLOW, BarStyle.SOLID, Collections.emptySet()); } @HandleModel public void arenaUpdated(@Nullable Arena before, @Nullable Arena after, Arena latest) { updateArena(latest); } @HandleModel public void ticketUpdated(@Nullable Ticket before, @Nullable Ticket after, Ticket latest) { final Arena arena = arenas.byId(latest.arena_id()); updateArena(arena); final Player player = userStore.find(latest.user()); if(player != null) { final BossBar bar = bars.getUnchecked(arena); if(after != null && after.server_id() == null) { bar.addPlayer(player); } else { bar.removePlayer(player); } } } @EventHandler(priority = EventPriority.MONITOR) public void onJoin(PlayerJoinEvent event) { final Ticket ticket = tickets.tryUser(userStore.getUser(event.getPlayer())); if(ticket != null && ticket.server_id() == null) { bars.getUnchecked(arenas.byId(ticket.arena_id())).addPlayer(event.getPlayer()); } } @EventHandler(priority = EventPriority.MONITOR) public void onQuit(PlayerQuitEvent event) { final Ticket ticket = tickets.tryUser(userStore.getUser(event.getPlayer())); if(ticket != null && ticket.server_id() == null) { bars.getUnchecked(arenas.byId(ticket.arena_id())).removePlayer(event.getPlayer()); } } @Override public Duration tickPeriod() { return Duration.ofSeconds(1); } @Override public void tick() { final MatchDoc match = localServer.current_match(); if(match != null && match.start() != null && !match.join_mid_match() && match.end() == null) { players.stream() .filter(playerStates::isObserving) .forEach(player -> { final Ticket ticket = tickets.tryUser(userStore.tryUser(player)); if(ticket != null) { final Game game = games.byId(arenas.byId(ticket.arena_id()).game_id()); if(game != null) { audiences.get(player).sendHotbarMessage(gameFormatter.replayMaybe(game)); } } }); } } }