package tc.oc.pgm.match; import java.util.HashSet; import java.util.Set; import java.util.UUID; import java.util.stream.Stream; import javax.inject.Inject; import com.google.common.collect.ImmutableSet; import org.bukkit.entity.Entity; import org.bukkit.event.EntityAction; import org.bukkit.event.Event; import org.bukkit.event.Listener; import org.bukkit.event.PlayerAction; import org.bukkit.event.entity.EntityEvent; import org.bukkit.event.player.PlayerEvent; import tc.oc.commons.bukkit.event.UserEvent; import tc.oc.commons.bukkit.event.targeted.TargetedEventRouter; import tc.oc.commons.bukkit.event.targeted.TargetedEventRouterBinder; import tc.oc.commons.bukkit.inject.BukkitFacetContext; import tc.oc.commons.core.inject.HybridManifest; import tc.oc.commons.core.stream.Collectors; import tc.oc.commons.core.util.Optionals; import tc.oc.commons.core.util.Streams; import tc.oc.pgm.events.MatchPlayerEvent; import tc.oc.pgm.events.MatchUserEvent; public class MatchPlayerEventRouter implements TargetedEventRouter<Object> { public static class Manifest extends HybridManifest { private static final Set<Class<?>> EVENT_TYPES = ImmutableSet.of( EntityAction.class, EntityEvent.class, PlayerAction.class, PlayerEvent.class, UserEvent.class, MatchUserEvent.class, MatchPlayerEvent.class ); @Override protected void configure() { bindAndExpose(MatchPlayerEventRouter.class); // Only register with the event types we can actually do something with. // We still want to get errors if we try to target an untargetable event. final TargetedEventRouterBinder routers = new TargetedEventRouterBinder(publicBinder()); EVENT_TYPES.forEach(event -> routers.bindEvent(event).to(MatchPlayerEventRouter.class)); } } @Inject MatchFinder finder; @Override public Stream<Listener> listeners(Object event) { return contexts(event).flatMap(BukkitFacetContext::listeners); } protected Stream<? extends BukkitFacetContext<?>> contexts(Object event) { // Try to get some online players from the event, either directly // through MatchPlayerEvent, or indirectly through entities. final Set<MatchPlayer> players; if(event instanceof MatchPlayerEvent) { players = ((MatchPlayerEvent) event).players().collect(Collectors.toImmutableSet()); } else { final Set<Entity> entities = new HashSet<>(); if(event instanceof EntityAction) entities.add(((EntityAction) event).getActor()); if(event instanceof EntityEvent) entities.add(((EntityEvent) event).getEntity()); if(event instanceof PlayerAction) entities.add(((PlayerAction) event).getActor()); if(event instanceof PlayerEvent) entities.add(((PlayerEvent) event).getPlayer()); players = entities.stream() .flatMap(entity -> Streams.ofNullable(finder.getPlayer(entity))) .collect(Collectors.toImmutableSet()); } // If we have one or more MatchPlayers, return them along with their user contexts if(!players.isEmpty()) { return Stream.concat( players.stream(), players.stream().map(player -> player.userContext) ); } // If we couldn't derive any online players from the event, try for offline player UUIDs final Set<UUID> uuids; if(event instanceof MatchUserEvent) { uuids = ((MatchUserEvent) event).users().collect(Collectors.toImmutableSet()); } else if(event instanceof UserEvent) { uuids = ImmutableSet.of(((UserEvent) event).getUser().uuid()); } else { return Stream.empty(); } // Restrict to a specific match, if possible final Stream<Match> matches = finder.match((Event) event) .map(Stream::of) .orElseGet(() -> finder.currentMatches().stream()); // Search the selected matches for both users and players // with the selected UUIDs. return matches.flatMap( match -> uuids.stream().flatMap( uuid -> Stream.concat( Optionals.stream(match.player(uuid)), Optionals.stream(match.userContext(uuid)) ) ) ); } }