From 44481e4b63edadedd6ded9c68dbffc182a9c1c72 Mon Sep 17 00:00:00 2001 From: Ashcon Partovi Date: Mon, 21 May 2018 19:44:20 -0700 Subject: [PATCH] Allow cross-server chat messages --- .../src/main/java/tc/oc/api/ApiManifest.java | 2 + .../tc/oc/api/chat/ChatModelManifest.java | 16 ++ .../src/main/java/tc/oc/api/docs/Chat.java | 7 + .../java/tc/oc/api/docs/virtual/ChatDoc.java | 52 +++++ .../tc/oc/api/docs/virtual/ServerDoc.java | 1 + .../java/tc/oc/api/docs/virtual/UserDoc.java | 7 +- .../oc/api/reports/ReportSearchRequest.java | 22 +- .../java/tc/oc/api/servers/ServerStore.java | 28 +++ .../main/java/tc/oc/api/util/Permissions.java | 67 ++++-- .../servers/LocalServerDocument.java | 5 + .../minecraft/users/LocalUserDocument.java | 7 + .../java/tc/oc/api/ocn/OCNModelsManifest.java | 5 + Commons/bukkit/pom.xml | 12 -- .../commons/bukkit/CommonsBukkitManifest.java | 7 +- .../bukkit/broadcast/BroadcastSender.java | 171 +++++++++++++++ .../bukkit/broadcast/BroadcastSettings.java | 2 +- .../commons/bukkit/channels/AdminChannel.java | 131 ------------ .../bukkit/channels/AdminChatManifest.java | 18 -- .../oc/commons/bukkit/channels/Channel.java | 28 +++ .../bukkit/channels/ChannelChatEvent.java | 60 ++++++ .../bukkit/channels/ChannelCommands.java | 99 +++++++++ .../bukkit/channels/ChannelManifest.java | 26 +++ .../bukkit/channels/ChannelRouter.java | 82 ++++++++ .../bukkit/channels/PermissibleChannel.java | 25 +++ .../bukkit/channels/SimpleChannel.java | 79 +++++++ .../bukkit/channels/admin/AdminChannel.java | 75 +++++++ .../bukkit/channels/server/ServerChannel.java | 43 ++++ .../oc/commons/bukkit/chat/ChatAnnouncer.java | 98 +++++++++ .../oc/commons/bukkit/chat/ChatCreator.java | 91 ++++++++ .../oc/commons/bukkit/chat/ChatManifest.java | 20 ++ .../tc/oc/commons/bukkit/chat/Paginator.java | 2 +- .../commons/bukkit/commands/CommandUtils.java | 39 ++++ .../commons/bukkit/commands/UserCommands.java | 12 +- .../commons/bukkit/flairs/FlairRenderer.java | 2 + .../commons/bukkit/nick/ConsoleIdentity.java | 5 + .../tc/oc/commons/bukkit/nick/Identity.java | 5 + .../oc/commons/bukkit/nick/IdentityImpl.java | 5 + .../commons/bukkit/nick/UsernameRenderer.java | 2 +- .../bukkit/punishment/PunishmentCreator.java | 7 +- .../bukkit/report/ReportAnnouncer.java | 36 +--- .../commons/bukkit/report/ReportCommands.java | 7 +- .../bukkit/report/ReportConfiguration.java | 9 - .../bukkit/teleport/TeleportListener.java | 2 +- .../bukkit/whisper/WhisperFormatter.java | 10 +- Commons/bukkit/src/main/resources/config.yml | 2 - Commons/bukkit/src/main/resources/plugin.yml | 2 +- .../i18n/templates/commons/Commons.properties | 11 + .../i18n/templates/pgm/PGMMessages.properties | 1 + .../projectares/PAMessages.properties | 3 + PGM/src/main/java/tc/oc/pgm/PGM.java | 2 - .../java/tc/oc/pgm/PGMModulesManifest.java | 2 + .../tc/oc/pgm/antigrief/DefuseListener.java | 34 +-- .../tc/oc/pgm/channels/ChannelCommands.java | 44 ---- .../tc/oc/pgm/channels/ChannelManifest.java | 12 ++ .../oc/pgm/channels/ChannelMatchModule.java | 198 +++--------------- .../oc/pgm/channels/FilteredPartyChannel.java | 21 -- .../java/tc/oc/pgm/channels/PartyChannel.java | 67 +++++- .../pgm/channels/UnfilteredPartyChannel.java | 21 -- .../java/tc/oc/pgm/commands/MapCommands.java | 4 +- PGM/src/main/java/tc/oc/pgm/ffa/Tribute.java | 5 +- .../main/java/tc/oc/pgm/freeze/Freeze.java | 32 +-- .../java/tc/oc/pgm/goals/TouchableGoal.java | 4 +- .../pgm/listeners/BlockTransformListener.java | 2 +- .../pgm/mapratings/MapRatingsMatchModule.java | 2 +- PGM/src/main/java/tc/oc/pgm/match/Match.java | 4 +- .../java/tc/oc/pgm/match/MatchAudiences.java | 48 ++--- .../main/java/tc/oc/pgm/match/MatchImpl.java | 34 +-- .../java/tc/oc/pgm/match/MatchPlayer.java | 13 +- .../tc/oc/pgm/match/MatchPlayerFinder.java | 8 + .../tc/oc/pgm/match/MatchPlayerState.java | 9 +- .../tc/oc/pgm/match/MultiPlayerParty.java | 13 +- PGM/src/main/java/tc/oc/pgm/match/Party.java | 5 +- .../oc/pgm/module/MatchModulesManifest.java | 2 - .../mutation/command/MutationCommands.java | 2 +- .../oc/pgm/mutation/types/EntityMutation.java | 2 +- .../pgm/rotation/DynamicRotationListener.java | 2 +- .../java/tc/oc/pgm/start/HuddleCountdown.java | 12 +- .../java/tc/oc/pgm/teams/TeamMatchModule.java | 2 +- .../java/tc/oc/pgm/wool/WoolMatchModule.java | 4 +- PGM/src/main/resources/plugin.yml | 3 - Tourney/src/net/anxuiz/tourney/Tourney.java | 2 +- .../anxuiz/tourney/listener/TeamListener.java | 5 - .../net/anxuiz/tourney/vote/VoteContext.java | 4 +- .../tc/oc/commons/bukkit/chat/Audiences.java | 183 +++++++++++++++- .../commons/bukkit/chat/BukkitAudiences.java | 40 ---- .../bukkit/chat/CommandSenderAudience.java | 50 ----- .../commons/bukkit/chat/ConsoleAudience.java | 26 --- .../bukkit/chat/LegacyPlayerAudience.java | 39 ---- .../commons/bukkit/chat/PlayerAudience.java | 40 ---- .../bukkit/inject/BukkitServerManifest.java | 6 +- .../listeners/PlayerMovementListener.java | 8 +- .../permissions/BukkitPermissionRegistry.java | 4 +- .../permissions/PermissionRegistry.java | 29 ++- .../tc/oc/commons/bungee/chat/Audiences.java | 86 +++++++- .../commons/bungee/chat/BungeeAudiences.java | 20 -- .../commons/bungee/chat/ConsoleAudience.java | 18 -- .../commons/bungee/chat/PlayerAudience.java | 49 ----- .../bungee/inject/BungeeServerManifest.java | 5 +- .../commons/core/chat/AbstractAudience.java | 17 -- .../commons/core/chat/AbstractAudiences.java | 35 ++++ .../core/chat/AbstractConsoleAudience.java | 25 --- .../core/chat/AbstractMultiAudience.java | 50 ----- .../tc/oc/commons/core/chat/Audience.java | 40 ++-- .../tc/oc/commons/core/chat/Audiences.java | 26 ++- .../oc/commons/core/chat/ConsoleAudience.java | 35 ++++ .../commons/core/chat/ForwardingAudience.java | 29 ++- .../commons/core/chat/MinecraftAudiences.java | 40 ---- .../oc/commons/core/chat/MultiAudience.java | 55 ++++- .../tc/oc/commons/core/chat/NullAudience.java | 39 +--- 109 files changed, 1894 insertions(+), 1172 deletions(-) create mode 100644 API/api/src/main/java/tc/oc/api/chat/ChatModelManifest.java create mode 100644 API/api/src/main/java/tc/oc/api/docs/Chat.java create mode 100644 API/api/src/main/java/tc/oc/api/docs/virtual/ChatDoc.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSender.java delete mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChannel.java delete mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChatManifest.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/Channel.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelChatEvent.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelCommands.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelManifest.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelRouter.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/PermissibleChannel.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/SimpleChannel.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/admin/AdminChannel.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/server/ServerChannel.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatAnnouncer.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatCreator.java create mode 100644 Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatManifest.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/channels/ChannelCommands.java create mode 100644 PGM/src/main/java/tc/oc/pgm/channels/ChannelManifest.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/channels/FilteredPartyChannel.java delete mode 100644 PGM/src/main/java/tc/oc/pgm/channels/UnfilteredPartyChannel.java delete mode 100644 Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/BukkitAudiences.java delete mode 100644 Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/CommandSenderAudience.java delete mode 100644 Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ConsoleAudience.java delete mode 100644 Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/LegacyPlayerAudience.java delete mode 100644 Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/PlayerAudience.java delete mode 100644 Util/bungee/src/main/java/tc/oc/commons/bungee/chat/BungeeAudiences.java delete mode 100644 Util/bungee/src/main/java/tc/oc/commons/bungee/chat/ConsoleAudience.java delete mode 100644 Util/bungee/src/main/java/tc/oc/commons/bungee/chat/PlayerAudience.java delete mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudience.java create mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudiences.java delete mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/AbstractConsoleAudience.java delete mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/AbstractMultiAudience.java create mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/ConsoleAudience.java delete mode 100644 Util/core/src/main/java/tc/oc/commons/core/chat/MinecraftAudiences.java diff --git a/API/api/src/main/java/tc/oc/api/ApiManifest.java b/API/api/src/main/java/tc/oc/api/ApiManifest.java index 1e3fb01..3d2b3d1 100644 --- a/API/api/src/main/java/tc/oc/api/ApiManifest.java +++ b/API/api/src/main/java/tc/oc/api/ApiManifest.java @@ -1,5 +1,6 @@ package tc.oc.api; +import tc.oc.api.chat.ChatModelManifest; import tc.oc.api.document.DocumentsManifest; import tc.oc.api.engagement.EngagementModelManifest; import tc.oc.api.friendships.FriendshipModelManifest; @@ -46,5 +47,6 @@ public final class ApiManifest extends HybridManifest { install(new TrophyModelManifest()); install(new TournamentModelManifest()); install(new FriendshipModelManifest()); + install(new ChatModelManifest()); } } diff --git a/API/api/src/main/java/tc/oc/api/chat/ChatModelManifest.java b/API/api/src/main/java/tc/oc/api/chat/ChatModelManifest.java new file mode 100644 index 0000000..977ab62 --- /dev/null +++ b/API/api/src/main/java/tc/oc/api/chat/ChatModelManifest.java @@ -0,0 +1,16 @@ +package tc.oc.api.chat; + +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.api.model.ModelBinders; +import tc.oc.commons.core.inject.HybridManifest; + +public class ChatModelManifest extends HybridManifest implements ModelBinders { + + @Override + protected void configure() { + bindModel(Chat.class, ChatDoc.Partial.class, model -> { + model.bindDefaultService().to(model.nullService()); + }); + } +} diff --git a/API/api/src/main/java/tc/oc/api/docs/Chat.java b/API/api/src/main/java/tc/oc/api/docs/Chat.java new file mode 100644 index 0000000..0356ea5 --- /dev/null +++ b/API/api/src/main/java/tc/oc/api/docs/Chat.java @@ -0,0 +1,7 @@ +package tc.oc.api.docs; + +import tc.oc.api.annotations.Serialize; +import tc.oc.api.docs.virtual.ChatDoc; + +@Serialize +public interface Chat extends ChatDoc.Complete {} diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/ChatDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/ChatDoc.java new file mode 100644 index 0000000..aac2c7c --- /dev/null +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/ChatDoc.java @@ -0,0 +1,52 @@ +package tc.oc.api.docs.virtual; + +import tc.oc.api.annotations.Serialize; +import tc.oc.api.docs.PlayerId; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.time.Instant; + +public interface ChatDoc { + interface Partial extends PartialModel {} + + @Serialize + interface Base extends Model, Partial { + @Nonnull String message(); + @Nonnull String server_id(); + @Nullable String match_id(); + @Nonnull Type type(); + @Nonnull Instant sent_at(); + @Nullable Broadcast broadcast(); + } + + @Serialize + interface Broadcast extends Partial { + @Nonnull Destination destination(); + @Nullable String id(); + } + + @Serialize + interface Creation extends Base { + @Nullable String sender_id(); + } + + @Serialize + interface Complete extends Base { + @Nullable PlayerId sender(); + } + + enum Type { + TEAM(true), SERVER(true), ADMIN(false), BROADCAST(false); + + public boolean batchUpdate; + + Type(boolean batchUpdate) { + this.batchUpdate = batchUpdate; + } + } + + enum Destination { + SERVER, FAMILY, GAME, NETWORK, GLOBAL + } +} diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java index e5c3d48..9348274 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/ServerDoc.java @@ -125,6 +125,7 @@ public interface ServerDoc { @Nullable String resource_pack_url(); @Nullable String resource_pack_sha1(); boolean resource_pack_fast_update(); + @Nullable String cross_server_profile(); } @Serialize diff --git a/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java b/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java index 831a029..760c457 100644 --- a/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java +++ b/API/api/src/main/java/tc/oc/api/docs/virtual/UserDoc.java @@ -43,6 +43,11 @@ public interface UserDoc { List trophy_ids(); } + @Serialize + interface Channel extends Partial { + @Nonnull ChatDoc.Type chat_channel(); + } + interface License { @Serialize @@ -79,7 +84,7 @@ public interface UserDoc { * Stuff we get from the API on login, and keep around for plugins to use */ @Serialize - interface Login extends Identity, Locale, Trophies, DefaultServer, FriendTokens, DeathScreen, License.Complete { + interface Login extends Identity, Locale, Trophies, DefaultServer, FriendTokens, DeathScreen, License.Complete, Channel { int raindrops(); int maptokens(); int mutationtokens(); diff --git a/API/api/src/main/java/tc/oc/api/reports/ReportSearchRequest.java b/API/api/src/main/java/tc/oc/api/reports/ReportSearchRequest.java index 1066c14..22aee40 100644 --- a/API/api/src/main/java/tc/oc/api/reports/ReportSearchRequest.java +++ b/API/api/src/main/java/tc/oc/api/reports/ReportSearchRequest.java @@ -1,6 +1,6 @@ package tc.oc.api.reports; -import java.util.Collection; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import tc.oc.api.annotations.Serialize; @@ -15,39 +15,33 @@ import static com.google.common.base.Preconditions.checkState; public class ReportSearchRequest extends FindRequest { @Serialize private final @Nullable String server_id; - @Serialize private final @Nullable Collection family_ids; @Serialize private final @Nullable String user_id; + @Serialize private final boolean cross_server; private final int page, perPage; - private ReportSearchRequest(String server_id, Collection family_ids, String user_id, int page, int perPage) { + private ReportSearchRequest(String server_id, String user_id, boolean cross_server, int page, int perPage) { checkArgument(page > 0); checkArgument(perPage > 0); this.server_id = server_id; - this.family_ids = family_ids; this.user_id = user_id; + this.cross_server = cross_server; this.page = page; this.perPage = perPage; } public static ReportSearchRequest create(int page, int perPage) { - return new ReportSearchRequest(null, null, null, page, perPage); + return new ReportSearchRequest(null, null, false, page, perPage); } - public ReportSearchRequest forServer(ServerDoc.Identity server) { - checkState(server_id == null); - return new ReportSearchRequest(server._id(), null, null, page, perPage); - } - - public ReportSearchRequest forFamilies(Collection familyIds) { - checkState(family_ids == null); - return new ReportSearchRequest(null, familyIds, null, page, perPage); + public ReportSearchRequest forServer(ServerDoc.Identity server, boolean cross_server) { + return new ReportSearchRequest(server._id(), null, cross_server, page, perPage); } public ReportSearchRequest forPlayer(PlayerId playerId) { checkState(user_id == null); - return new ReportSearchRequest(server_id, family_ids, playerId._id(), page, perPage); + return new ReportSearchRequest(server_id, playerId._id(), true, page, perPage); } @Override diff --git a/API/api/src/main/java/tc/oc/api/servers/ServerStore.java b/API/api/src/main/java/tc/oc/api/servers/ServerStore.java index 3105800..cc0700a 100644 --- a/API/api/src/main/java/tc/oc/api/servers/ServerStore.java +++ b/API/api/src/main/java/tc/oc/api/servers/ServerStore.java @@ -23,8 +23,11 @@ import static com.google.common.base.Preconditions.checkArgument; @Singleton public class ServerStore extends ModelStore { + private final SetMultimap byName = HashMultimap.create(); private final Map byBungeeName = new HashMap<>(); private final SetMultimap byRole = HashMultimap.create(); + private final SetMultimap byNetwork = HashMultimap.create(); + private final SetMultimap byFamily = HashMultimap.create(); private final SetMultimap byArenaId = HashMultimap.create(); @Override @@ -32,6 +35,18 @@ public class ServerStore extends ModelStore { return new ServerSearchRequest(); } + public ImmutableSet byName(String name) { + return ImmutableSet.copyOf(byName.get(name)); + } + + public ImmutableSet byNetwork(ServerDoc.Network network) { + return ImmutableSet.copyOf(byNetwork.get(network)); + } + + public ImmutableSet byFamily(String family) { + return ImmutableSet.copyOf(byFamily.get(family)); + } + public @Nullable Server tryBungeeName(String name) { checkArgument(!"default".equals(name), "Cannot lookup lobbies by bungee_name"); return byBungeeName.get(name); @@ -59,10 +74,20 @@ public class ServerStore extends ModelStore { return playerCount; } + public boolean canCommunicate(String serverIdA, String serverIdB) { + if(serverIdA.equals(serverIdB)) return true; + String profileA = byId(serverIdA).cross_server_profile(); + String profileB = byId(serverIdB).cross_server_profile(); + return profileA != null && profileB != null && profileA.equalsIgnoreCase(profileB); + } + @Override protected void unindex(Server doc) { super.unindex(doc); + byName.remove(doc.name(), doc); byRole.remove(doc.role(), doc); + if(doc.network() != null) byNetwork.remove(doc.network(), doc); + if(doc.family() != null) byFamily.remove(doc.family(), doc); if(doc.arena_id() != null) byArenaId.remove(doc.arena_id(), doc); if(doc.bungee_name() != null) byBungeeName.remove(doc.bungee_name()); } @@ -70,7 +95,10 @@ public class ServerStore extends ModelStore { @Override protected void reindex(Server doc) { super.reindex(doc); + byName.put(doc.name(), doc); byRole.put(doc.role(), doc); + if(doc.network() != null) byNetwork.put(doc.network(), doc); + if(doc.family() != null) byFamily.put(doc.family(), doc); if(doc.arena_id() != null) byArenaId.put(doc.arena_id(), doc); if(doc.bungee_name() != null) byBungeeName.put(doc.bungee_name(), doc); } diff --git a/API/api/src/main/java/tc/oc/api/util/Permissions.java b/API/api/src/main/java/tc/oc/api/util/Permissions.java index 3b2fd0e..6f88c65 100644 --- a/API/api/src/main/java/tc/oc/api/util/Permissions.java +++ b/API/api/src/main/java/tc/oc/api/util/Permissions.java @@ -1,22 +1,25 @@ package tc.oc.api.util; +import com.google.common.collect.Lists; +import tc.oc.minecraft.api.command.CommandSender; + import java.util.Collection; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.function.Function; -public final class Permissions { +public interface Permissions { - private Permissions() {} - - public static final String CONSOLE = "ocn.console"; - public static final String LOGIN = "ocn.login"; - public static final String STAFF = "projectares.staff"; - public static final String OBSERVER = "ocn.observer"; - public static final String PARTICIPANT = "ocn.participant"; - public static final String MAPMAKER = "ocn.mapmaker"; - public static final String DEVELOPER = "ocn.developer"; - public static final String MAPDEV = "pgm.mapdev"; - public static final String MAPERRORS = "pgm.maperrors"; + String CONSOLE = "ocn.console"; + String LOGIN = "ocn.login"; + String STAFF = "projectares.staff"; + String OBSERVER = "ocn.observer"; + String PARTICIPANT = "ocn.participant"; + String MAPMAKER = "ocn.mapmaker"; + String DEVELOPER = "ocn.developer"; + String MAPDEV = "pgm.mapdev"; + String MAPERRORS = "pgm.maperrors"; /** * Merge the given by-realm permissions into a single set of permissions using the given (ordered) realms @@ -24,7 +27,7 @@ public final class Permissions { * @param permsByRealm Permissions, grouped by realm * @return Effective permissions */ - public static Map mergePermissions(Collection realms, Map> permsByRealm) { + static Map mergePermissions(Collection realms, Map> permsByRealm) { Map effectivePerms = new HashMap<>(); for(String realm : realms) { Map perms = permsByRealm.get(realm); @@ -34,4 +37,42 @@ public final class Permissions { } return effectivePerms; } + + /** + * Get a list of enums a {@link CommandSender} has permission to use. + * + * This is useful for enums that correspond to an action. Instead of granting permission + * to a user for each node, they have access to any enum below the highest ordinal node. + * + * enum Trig { + * SOH, CAH, TOA + * } + * + * So if a sender has explicit permission to ocn.foo.cah, the sender has implicit + * permission to use Trig.SOH and Trig.CAH. + * + * @param sender The command sender. + * @param enumClass The class of the enum to get the values from. + * @return List of {@link E}s that the {@param sender} is allowed to use, ascending order based on {@link E#ordinal()}. + */ + static List enumPermissions(CommandSender sender, String base, Class enumClass) { + final List enums = Lists.newArrayList(enumClass.getEnumConstants()); + final Function normalizer = value -> base + "." + value.name().toLowerCase().replaceAll("_", "-"); + final int max = enums.stream() + .filter(value -> sender.hasPermission(normalizer.apply(value))) + .map(E::ordinal) + .max(Integer::compare) + .orElse(-1); + return enums.subList(0, max + 1); + } + + /** + * Get whether a {@link CommandSender} has permission to use a selected {@link E}. + * + * @see #enumPermissions(CommandSender, String, Class) + * @return Whether the {@param sender} has permission. + */ + static boolean hasPermissionForEnum(CommandSender sender, String base, E selected) { + return enumPermissions(sender, base, (Class) selected.getClass()).contains(selected); + } } diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java index b82a9b2..a012984 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/servers/LocalServerDocument.java @@ -223,6 +223,11 @@ public class LocalServerDocument extends StartupServerDocument implements Server return false; } + @Override + public String cross_server_profile() { + return null; + } + @Override public Map fake_usernames() { return Collections.emptyMap(); diff --git a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java index fdbd8e0..931804b 100644 --- a/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java +++ b/API/minecraft/src/main/java/tc/oc/api/minecraft/users/LocalUserDocument.java @@ -7,12 +7,14 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.collect.ImmutableMap; import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.SimplePlayerId; import tc.oc.api.docs.User; +import tc.oc.api.docs.virtual.ChatDoc; import tc.oc.api.docs.virtual.UserDoc; import tc.oc.api.minecraft.servers.DefaultPermissions; @@ -161,4 +163,9 @@ public class LocalUserDocument extends SimplePlayerId implements User { public String death_screen() { return null; } + + @Override + public ChatDoc.Type chat_channel() { + return ChatDoc.Type.TEAM; + } } diff --git a/API/ocn/src/main/java/tc/oc/api/ocn/OCNModelsManifest.java b/API/ocn/src/main/java/tc/oc/api/ocn/OCNModelsManifest.java index 6523c71..dc4f754 100644 --- a/API/ocn/src/main/java/tc/oc/api/ocn/OCNModelsManifest.java +++ b/API/ocn/src/main/java/tc/oc/api/ocn/OCNModelsManifest.java @@ -1,6 +1,7 @@ package tc.oc.api.ocn; import tc.oc.api.docs.Arena; +import tc.oc.api.docs.Chat; import tc.oc.api.docs.Death; import tc.oc.api.docs.Game; import tc.oc.api.docs.Objective; @@ -8,6 +9,7 @@ import tc.oc.api.docs.Participation; import tc.oc.api.docs.Punishment; import tc.oc.api.docs.Report; import tc.oc.api.docs.Trophy; +import tc.oc.api.docs.virtual.ChatDoc; import tc.oc.api.docs.virtual.DeathDoc; import tc.oc.api.docs.virtual.MatchDoc; import tc.oc.api.docs.virtual.PunishmentDoc; @@ -47,6 +49,9 @@ public class OCNModelsManifest extends HybridManifest implements ModelBinders { bindModel(Punishment.class, PunishmentDoc.Partial.class, model -> { model.bindService().to(model.httpService()); }); + bindModel(Chat.class, ChatDoc.Partial.class, model -> { + model.bindService().to(model.httpService()); + }); bindModel(MatchDoc.class, model -> { model.bindService().to(model.httpService()); }); diff --git a/Commons/bukkit/pom.xml b/Commons/bukkit/pom.xml index e8f8f5b..3897d76 100644 --- a/Commons/bukkit/pom.xml +++ b/Commons/bukkit/pom.xml @@ -41,18 +41,6 @@ ${project.version} - - com.github.rmsy.Channels - Channels - 1.9-SNAPSHOT - - - org.bukkit - bukkit - - - - me.anxuiz bukkit-settings diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java index a2c2ef2..6ec2b04 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/CommonsBukkitManifest.java @@ -12,7 +12,8 @@ import tc.oc.bukkit.analytics.BukkitPlayerReporter; import tc.oc.bukkit.analytics.LatencyReporter; import tc.oc.bukkit.analytics.TickReporter; import tc.oc.commons.bukkit.broadcast.BroadcastManifest; -import tc.oc.commons.bukkit.channels.AdminChatManifest; +import tc.oc.commons.bukkit.channels.ChannelManifest; +import tc.oc.commons.bukkit.chat.ChatManifest; import tc.oc.commons.bukkit.chat.ComponentRenderContext; import tc.oc.commons.bukkit.chat.ComponentRendererRegistry; import tc.oc.commons.bukkit.chat.ComponentRenderers; @@ -62,7 +63,6 @@ import tc.oc.commons.bukkit.respack.ResourcePackManager; import tc.oc.commons.bukkit.restart.RestartCommands; import tc.oc.commons.bukkit.sessions.SessionListener; import tc.oc.commons.bukkit.settings.SettingManifest; -import tc.oc.commons.bukkit.stats.StatsCommands; import tc.oc.commons.bukkit.stats.StatsManifest; import tc.oc.commons.bukkit.suspend.SuspendListener; import tc.oc.commons.bukkit.tablist.PlayerTabEntry; @@ -104,7 +104,8 @@ public final class CommonsBukkitManifest extends HybridManifest { install(new SettingManifest()); install(new WhisperManifest()); install(new JoinMessageManifest()); - install(new AdminChatManifest()); + install(new ChatManifest()); + install(new ChannelManifest()); install(new BroadcastManifest()); install(new LocalizationManifest()); install(new NavigatorManifest()); diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSender.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSender.java new file mode 100644 index 0000000..4412685 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSender.java @@ -0,0 +1,171 @@ +package tc.oc.commons.bukkit.broadcast; + +import com.google.common.collect.Lists; +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissionsException; +import com.sk89q.minecraft.util.commands.SuggestionContext; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import tc.oc.api.bukkit.users.BukkitUserStore; +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.Game; +import tc.oc.api.docs.Server; +import tc.oc.api.docs.User; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.api.games.GameStore; +import tc.oc.api.servers.ServerStore; +import tc.oc.commons.bukkit.chat.Audiences; +import tc.oc.commons.bukkit.chat.BukkitSound; +import tc.oc.commons.bukkit.chat.ChatCreator; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.bukkit.chat.WarningComponent; +import tc.oc.commons.bukkit.commands.CommandUtils; +import tc.oc.commons.bukkit.nick.IdentityProvider; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.commands.Commands; +import tc.oc.commons.core.formatting.StringUtils; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static tc.oc.api.util.Permissions.hasPermissionForEnum; +import static tc.oc.commons.bukkit.commands.CommandUtils.newCommandException; +import static tc.oc.commons.bukkit.commands.CommandUtils.tryEnum; + +/** + * Allows {@link User}s to broadcast {@link Chat} messages across multiple servers. + */ +@Singleton +public class BroadcastSender implements Commands { + + private final static String PERMISSION = "ocn.broadcast"; + + private final Server server; + private final ServerStore serverStore; + private final GameStore gameStore; + private final ChatCreator chatCreator; + private final Audiences audiences; + private final BukkitUserStore userStore; + private final IdentityProvider identityProvider; + + @Inject BroadcastSender(Server server, ServerStore serverStore, GameStore gameStore, ChatCreator chatCreator, Audiences audiences, BukkitUserStore userStore, IdentityProvider identityProvider) { + this.server = server; + this.serverStore = serverStore; + this.gameStore = gameStore; + this.chatCreator = chatCreator; + this.audiences = audiences; + this.userStore = userStore; + this.identityProvider = identityProvider; + } + + private Set destinations(@Nullable ChatDoc.Destination type) { + Stream options = Stream.empty(); + if(type != null) { + switch(type) { + case SERVER: + options = serverStore.all().map(Server::name); + break; + case FAMILY: + options = serverStore.all().map(Server::family); + break; + case GAME: + options = gameStore.all().map(Game::name); + break; + case NETWORK: + options = serverStore.all().map(Server::network).map(Enum::name); + break; + } + } + return options.map(String::toLowerCase).collect(Collectors.toSet()); + } + + @Command( + aliases = { "broadcast", "b" }, + desc = "Broadcast a message to players across the network.", + usage = " [message...]", + min = 1 + ) + public List broadcast(final CommandContext args, final CommandSender sender) throws CommandException { + SuggestionContext suggest = args.getSuggestionContext(); + ChatDoc.Destination type = tryEnum(args.getString(0, ""), ChatDoc.Destination.class); + Set destinations = destinations(type); + String message = ""; + String destination = ""; + + if(suggest != null) { + switch(suggest.getIndex()) { + case 0: + return CommandUtils.completeEnum(args.getString(0), ChatDoc.Destination.class); + case 1: + if(type != null && type != ChatDoc.Destination.GLOBAL) { + return StringUtils.complete(args.getString(1), destinations); + } + } + } + + if(type == null) { + type = ChatDoc.Destination.SERVER; + destination = server._id(); + message = args.getRemainingString(0); + } else if(args.argsLength() >= 2) { + if(type == ChatDoc.Destination.GLOBAL) { + destination = null; + message = args.getRemainingString(1); + } else if(args.argsLength() >= 3) { + destination = args.getString(1); + message = args.getRemainingString(2); + if(!destinations.contains(destination)) { + throw newCommandException(sender, new WarningComponent("command.error.invalidOption", destination, destinations)); + } + } + } else { + CommandUtils.notEnoughArguments(sender); + } + + if(hasPermissionForEnum(sender, PERMISSION, type)) { + chatCreator.broadcast( + sender instanceof Player ? userStore.tryUser((Player) sender) : null, + message, + type, + destination + ); + } else { + throw new CommandPermissionsException(); + } + return null; + } + + public void show(Chat chat) { + final Audience audience = audiences.all(); + audience.playSound(new BukkitSound(Sound.ENTITY_ENDERDRAGON_HURT, 1, 1)); + audience.sendMessage( + new Component( + Lists.newArrayList( + new Component("["), + new TranslatableComponent("broadcast.prefix"), + new Component("] "), + new Component(chat.message()) + ), ChatColor.RED + ).hoverEvent( + HoverEvent.Action.SHOW_TEXT, + new TranslatableComponent( + "tip.sentBy", + new PlayerComponent(identityProvider.currentOrConsoleIdentity(chat.sender())) + ) + ) + ); + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSettings.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSettings.java index 1480e5a..2d9729a 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSettings.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/broadcast/BroadcastSettings.java @@ -59,7 +59,6 @@ public class BroadcastSettings { break; case NEWS: - case ALERT: setting = NEWS; break; @@ -72,6 +71,7 @@ public class BroadcastSettings { setting = RANDOM; break; + case ALERT: default: return true; } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChannel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChannel.java deleted file mode 100644 index d1ada45..0000000 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChannel.java +++ /dev/null @@ -1,131 +0,0 @@ -package tc.oc.commons.bukkit.channels; - -import java.util.stream.Stream; -import javax.inject.Inject; -import javax.inject.Singleton; - -import com.github.rmsy.channels.Channel; -import com.github.rmsy.channels.ChannelsPlugin; -import com.github.rmsy.channels.PlayerManager; -import com.github.rmsy.channels.event.ChannelMessageEvent; -import com.github.rmsy.channels.impl.SimpleChannel; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissions; -import com.sk89q.minecraft.util.commands.CommandPermissionsException; -import com.sk89q.minecraft.util.commands.CommandUsageException; -import com.sk89q.minecraft.util.commands.Console; -import me.anxuiz.settings.Setting; -import me.anxuiz.settings.SettingBuilder; -import me.anxuiz.settings.types.BooleanType; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; -import tc.oc.api.bukkit.users.OnlinePlayers; -import tc.oc.commons.bukkit.settings.SettingManagerProvider; -import tc.oc.commons.bukkit.util.ItemCreator; -import tc.oc.commons.core.commands.Commands; - -@Singleton -public class AdminChannel extends SimpleChannel implements Commands { - - static final Setting SETTING = new SettingBuilder() - .name("AdminChat").alias("ac") - .summary("Show confidential staff info") - .type(new BooleanType()) - .defaultValue(true).get(); - - public static final String PERM_NODE = "chat.admin"; - public static final String PERM_SEND = PERM_NODE + ".send"; - public static final String PERM_RECEIVE = PERM_NODE + ".receive"; - - public static final String PREFIX = ChatColor.WHITE + "[" + ChatColor.GOLD + "A" + ChatColor.WHITE + "]"; - public static final String BROADCAST_FORMAT = PREFIX + " {2}"; - public static final String FORMAT = PREFIX + " {1}" + ChatColor.WHITE + ": {2}"; - - private final ConsoleCommandSender console; - private final OnlinePlayers players; - private final SettingManagerProvider settings; - - @Inject AdminChannel(ConsoleCommandSender console, OnlinePlayers players, SettingManagerProvider settings) { - super(FORMAT, BROADCAST_FORMAT, new Permission(PERM_RECEIVE, PermissionDefault.OP)); - this.players = players; - this.settings = settings; - this.console = console; - } - - @Command(aliases = "a", - desc = "Sends a message to the staff channel (or sets the staff channel to your default channel).", - max = -1, - min = 0, - anyFlags = true, - usage = "[message...]") - @Console - @CommandPermissions({PERM_SEND, PERM_RECEIVE}) - public void onAdminChatCommand(final CommandContext arguments, final CommandSender sender) throws CommandException { - if(arguments.argsLength() == 0) { - if (sender.hasPermission(PERM_RECEIVE)) { - if (sender instanceof Player) { - Player player = (Player) sender; - PlayerManager playerManager = ChannelsPlugin.get().getPlayerManager(); - Channel oldChannel = playerManager.getMembershipChannel(player); - playerManager.setMembershipChannel(player, this); - if (!oldChannel.equals(this)) { - sender.sendMessage(org.bukkit.ChatColor.YELLOW + "Changed default channel to administrator chat"); - } else { - throw new CommandException("Administrator chat is already your default channel"); - } - } else { - throw new CommandUsageException("You must provide a message.", "/a "); - } - } else { - throw new CommandPermissionsException(); - } - } else if (sender.hasPermission(PERM_SEND)) { - Player sendingPlayer = null; - if (sender instanceof Player) { - sendingPlayer = (Player) sender; - } - this.sendMessage(arguments.getJoinedStrings(0), sendingPlayer); - if (!sender.hasPermission(PERM_RECEIVE)) { - sender.sendMessage(org.bukkit.ChatColor.YELLOW + "Message sent"); - } - } else { - throw new CommandPermissionsException(); - } - } - - @Override - public void sendMessageToViewer(Player sender, CommandSender viewer, String sanitizedMessage, ChannelMessageEvent event) { - if(viewer != null && !isEnabled(viewer)) { - return; - } - - super.sendMessageToViewer(sender, viewer, sanitizedMessage, event); - } - - public boolean isEnabled(Player viewer) { - return (boolean) settings.getManager(viewer) - .getValue(SETTING); - } - - public boolean isEnabled(CommandSender viewer) { - return !(viewer instanceof Player) || isEnabled((Player) viewer); - } - - public boolean isVisible(CommandSender viewer) { - return viewer.hasPermission(getListeningPermission()) && - isEnabled(viewer); - } - - public Stream viewers() { - return Stream.concat(Stream.of(console), - players.all().stream()) - .filter(this::isVisible); - } -} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChatManifest.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChatManifest.java deleted file mode 100644 index 1aa2569..0000000 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/AdminChatManifest.java +++ /dev/null @@ -1,18 +0,0 @@ -package tc.oc.commons.bukkit.channels; - -import tc.oc.commons.bukkit.settings.SettingBinder; -import tc.oc.commons.core.inject.HybridManifest; -import tc.oc.commons.core.plugin.PluginFacetBinder; - -public class AdminChatManifest extends HybridManifest { - @Override - protected void configure() { - new SettingBinder(publicBinder()) - .addBinding().toInstance(AdminChannel.SETTING); - - new PluginFacetBinder(binder()) - .register(AdminChannel.class); - - expose(AdminChannel.class); - } -} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/Channel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/Channel.java new file mode 100644 index 0000000..eb301ed --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/Channel.java @@ -0,0 +1,28 @@ +package tc.oc.commons.bukkit.channels; + +import org.bukkit.command.CommandSender; +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.PlayerId; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.core.chat.Audience; + +import javax.annotation.Nullable; + +/** + * An {@link Audience} that sends {@link Chat} messages to the API. + */ +public interface Channel extends Audience { + + ChatDoc.Type type(); + + void chat(CommandSender sender, String message); + + void chat(@Nullable PlayerId playerId, String message); + + void show(Chat message); + + boolean sendable(CommandSender sender); + + boolean viewable(CommandSender sender); + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelChatEvent.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelChatEvent.java new file mode 100644 index 0000000..845e5b0 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelChatEvent.java @@ -0,0 +1,60 @@ +package tc.oc.commons.bukkit.channels; + +import org.bukkit.event.Cancellable; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import tc.oc.api.docs.PlayerId; + +import javax.annotation.Nullable; + +/** + * Called when a command sender chats in a local {@link Channel}. + * If cancelled, the message will not be seen by users or reported to the API. + */ +public class ChannelChatEvent extends Event implements Cancellable { + private final static HandlerList handlers = new HandlerList(); + + private final Channel channel; + private final PlayerId sender; + private final String message; + private boolean cancelled; + + public ChannelChatEvent(Channel channel, PlayerId sender, String message) { + this.channel = channel; + this.sender = sender; + this.message = message; + } + + public Channel channel() { + return channel; + } + + public @Nullable + PlayerId sender() { + return sender; + } + + public String message() { + return message; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelCommands.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelCommands.java new file mode 100644 index 0000000..718eab4 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelCommands.java @@ -0,0 +1,99 @@ +package tc.oc.commons.bukkit.channels; + +import com.sk89q.minecraft.util.commands.Command; +import com.sk89q.minecraft.util.commands.CommandContext; +import com.sk89q.minecraft.util.commands.CommandException; +import com.sk89q.minecraft.util.commands.CommandPermissionsException; +import net.md_5.bungee.api.chat.TranslatableComponent; +import org.bukkit.command.CommandSender; +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.AsyncPlayerChatEvent; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.bukkit.chat.Audiences; +import tc.oc.commons.bukkit.commands.CommandUtils; +import tc.oc.commons.bukkit.util.SyncPlayerExecutorFactory; +import tc.oc.commons.core.commands.Commands; +import tc.oc.commons.core.commands.TranslatableCommandException; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class ChannelCommands implements Commands, Listener { + + private final SyncPlayerExecutorFactory syncPlayerExecutorFactory; + private final ChannelRouter channelRouter; + private final Audiences audiences; + + @Inject ChannelCommands(SyncPlayerExecutorFactory syncPlayerExecutorFactory, ChannelRouter channelRouter, Audiences audiences) { + this.syncPlayerExecutorFactory = syncPlayerExecutorFactory; + this.channelRouter = channelRouter; + this.audiences = audiences; + } + + @Command( + aliases = "a", + desc = "Send a message to the staff channel.", + usage = "[message...]" + ) + public void admin(final CommandContext args, final CommandSender sender) throws CommandException { + onChatCommand(ChatDoc.Type.ADMIN, args, sender); + } + + @Command( + aliases = "g", + desc = "Send a message to everyone on the local server.", + usage = "[message...]" + ) + public void server(final CommandContext args, final CommandSender sender) throws CommandException { + onChatCommand(ChatDoc.Type.SERVER, args, sender); + } + + @Command( + aliases = "t", + desc = "Send a message to your teammates.", + usage = "[message...]" + ) + public void chat(final CommandContext args, final CommandSender sender) throws CommandException { + onChatCommand(ChatDoc.Type.TEAM, args, sender); + } + + public void onChatCommand(ChatDoc.Type type, CommandContext args, CommandSender sender) throws CommandException { + final String typeName = type.name().toLowerCase(); + final Channel channel = channelRouter.getChannel(sender, type) + .orElseThrow(() -> new TranslatableCommandException("channels.unavailable", typeName)); + if(channel.sendable(sender)) { + if(args.argsLength() == 0) { + final Player player = CommandUtils.senderToPlayer(sender); + if(channel.equals(channelRouter.getDefaultChannel(player))) { + throw new TranslatableCommandException("channels.default.alreadySet", typeName); + } else { + channelRouter.setDefaultChannel(player, channel.type()); + audiences.get(player).sendMessage(new TranslatableComponent("channels.default.set", typeName)); + } + } else { + channel.chat(sender, args.getRemainingString(0)); + } + } else { + throw new CommandPermissionsException(); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) + public void onChat(AsyncPlayerChatEvent event) { + event.setCancelled(true); + syncPlayerExecutorFactory.queued(event.getPlayer()).execute(player -> { + Channel channel = channelRouter.getDefaultChannel(player); + if(!channel.sendable(player)) { + // If player cannot chat in their preferred channel, + // assume they can send to the default channel. + channel = channelRouter.getDefaultChannel(); + } + channel.chat(player, event.getMessage()); + }); + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelManifest.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelManifest.java new file mode 100644 index 0000000..d10191a --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelManifest.java @@ -0,0 +1,26 @@ +package tc.oc.commons.bukkit.channels; + +import tc.oc.commons.bukkit.channels.admin.AdminChannel; +import tc.oc.commons.bukkit.channels.server.ServerChannel; +import tc.oc.commons.bukkit.settings.SettingBinder; +import tc.oc.commons.core.inject.HybridManifest; +import tc.oc.commons.core.plugin.PluginFacetBinder; + +public class ChannelManifest extends HybridManifest { + @Override + protected void configure() { + + bindAndExpose(ChannelRouter.class); + expose(ChannelCommands.class); + expose(AdminChannel.class); + expose(ServerChannel.class); + + final PluginFacetBinder facets = new PluginFacetBinder(binder()); + facets.register(ChannelCommands.class); + facets.register(AdminChannel.class); + facets.register(ServerChannel.class); + + final SettingBinder settings = new SettingBinder(publicBinder()); + settings.addBinding().toInstance(AdminChannel.SETTING); + } +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelRouter.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelRouter.java new file mode 100644 index 0000000..1f8c56f --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/ChannelRouter.java @@ -0,0 +1,82 @@ +package tc.oc.commons.bukkit.channels; + +import com.google.common.util.concurrent.ListenableFuture; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import tc.oc.api.bukkit.users.BukkitUserStore; +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.User; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.api.docs.virtual.UserDoc; +import tc.oc.api.users.UserService; +import tc.oc.commons.bukkit.channels.admin.AdminChannel; +import tc.oc.commons.bukkit.channels.server.ServerChannel; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; +import java.util.function.Function; + +/** + * Get the {@link Channel} based on the {@link ChatDoc.Type}. + */ +@Singleton +public class ChannelRouter { + + private final BukkitUserStore userStore; + private final UserService userService; + private final ServerChannel serverChannel; + private final AdminChannel adminChannel; + private Function teamChannelFunction; + + @Inject ChannelRouter(BukkitUserStore userStore, UserService userService, ServerChannel serverChannel, AdminChannel adminChannel) { + this.userStore = userStore; + this.userService = userService; + this.serverChannel = serverChannel; + this.adminChannel = adminChannel; + setTeamChannelFunction(null); + } + + public Optional getChannel(ChatDoc.Type type) { + return getChannel(null, type); + } + + public Optional getChannel(Chat chat) { + return getChannel(userStore.find(chat.sender()), chat.type()); + } + + public Optional getChannel(@Nullable CommandSender sender, ChatDoc.Type type) { + Channel channel = null; + if(type == ChatDoc.Type.SERVER) { + channel = serverChannel; + } else if(type == ChatDoc.Type.ADMIN) { + channel = adminChannel; + } else if(sender != null && sender instanceof Player && type == ChatDoc.Type.TEAM) { + channel = teamChannelFunction.apply((Player) sender); + } + return Optional.ofNullable(channel); + } + + public Channel getDefaultChannel() { + return serverChannel; + } + + public Channel getDefaultChannel(Player player) { + return getChannel(player, userStore.getUser(player).chat_channel()).orElse(getDefaultChannel()); + } + + public ListenableFuture setDefaultChannel(Player player, ChatDoc.Type type) { + return userService.update(userStore.playerId(player), new UserDoc.Channel() { + @Override + public ChatDoc.Type chat_channel() { + return type; + } + }); + } + + public void setTeamChannelFunction(@Nullable Function function) { + teamChannelFunction = function != null ? function : sender -> serverChannel; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/PermissibleChannel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/PermissibleChannel.java new file mode 100644 index 0000000..8a301db --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/PermissibleChannel.java @@ -0,0 +1,25 @@ +package tc.oc.commons.bukkit.channels; + +import org.bukkit.command.CommandSender; +import org.bukkit.permissions.Permission; +import tc.oc.commons.core.chat.Audience; + +/** + * An {@link Audience} with membership access based off of a {@link Permission} node. + */ +public interface PermissibleChannel extends Channel { + + Permission permission(); + + @Override + default boolean sendable(CommandSender sender) { + return sender.hasPermission(permission()); + } + + @Override + default boolean viewable(CommandSender sender) { + return sender.hasPermission(permission()); + } + +} + diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/SimpleChannel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/SimpleChannel.java new file mode 100644 index 0000000..2aec4cf --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/SimpleChannel.java @@ -0,0 +1,79 @@ +package tc.oc.commons.bukkit.channels; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import net.md_5.bungee.api.chat.BaseComponent; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventBus; +import tc.oc.api.bukkit.users.BukkitUserStore; +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.PlayerId; +import tc.oc.commons.bukkit.chat.Audiences; +import tc.oc.commons.bukkit.chat.ChatCreator; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.bukkit.nick.IdentityProvider; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.MultiAudience; +import tc.oc.commons.core.plugin.PluginFacet; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +public abstract class SimpleChannel implements MultiAudience, Channel, PluginFacet { + + @Inject protected Audiences audiences; + @Inject protected EventBus eventBus; + @Inject protected BukkitUserStore userStore; + @Inject protected ChatCreator chatCreator; + @Inject protected IdentityProvider identityProvider; + + // Chat messages are sent to the local server before API verification. + // This prevents downtime or lockup from stopping all local server chat. + protected Cache chatCache = CacheBuilder.newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(); + + public abstract BaseComponent prefix(); + + public abstract BaseComponent format(PlayerComponent player, String message); + + @Override + public void sendMessage(BaseComponent message) { + MultiAudience.super.sendMessage(new Component(prefix()).extra(message)); + } + + @Override + public Stream audiences() { + return Stream.of(audiences.filter(this::viewable)); + } + + @Override + public void chat(@Nullable PlayerId playerId, String message) { + final ChannelChatEvent event = new ChannelChatEvent(this, playerId, message); + eventBus.callEvent(event); + if(!event.isCancelled()) { + chatCreator.chat(playerId, event.message(), type(), this::show); + } + } + + @Override + public void show(Chat chat) { + if(chatCache.getIfPresent(chat._id()) == null) { + chatCache.put(chat._id(), true); + sendMessage(format( + new PlayerComponent(identityProvider.currentOrConsoleIdentity(chat.sender())), + chat.message() + )); + } + } + + @Override + public void chat(CommandSender sender, String message) { + chat(sender instanceof Player ? userStore.tryUser((Player) sender) : null, message); + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/admin/AdminChannel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/admin/AdminChannel.java new file mode 100644 index 0000000..c4d279d --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/admin/AdminChannel.java @@ -0,0 +1,75 @@ +package tc.oc.commons.bukkit.channels.admin; + +import me.anxuiz.settings.Setting; +import me.anxuiz.settings.SettingBuilder; +import me.anxuiz.settings.types.BooleanType; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.bukkit.channels.PermissibleChannel; +import tc.oc.commons.bukkit.channels.SimpleChannel; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.bukkit.permissions.PermissionRegistry; +import tc.oc.commons.bukkit.settings.SettingManagerProvider; +import tc.oc.commons.core.chat.Component; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class AdminChannel extends SimpleChannel implements PermissibleChannel { + + public final static Permission PERMISSION = new Permission("ocn.chat.admin", PermissionDefault.OP); + public final static Setting SETTING = new SettingBuilder() + .name("AdminChat") + .alias("ac") + .summary("Show confidential staff chat") + .type(new BooleanType()) + .defaultValue(true) + .get(); + + private final SettingManagerProvider settings; + private final PermissionRegistry permissions; + + @Inject AdminChannel(PermissionRegistry permissions, SettingManagerProvider settings) { + this.settings = settings; + this.permissions = permissions; + } + + @Override + public void enable() { + permissions.register(PERMISSION); + } + + @Override + public Permission permission() { + return PERMISSION; + } + + @Override + public boolean viewable(CommandSender sender) { + return !(sender instanceof Player) || + (PermissibleChannel.super.viewable(sender) && + settings.getManager((Player) sender).getValue(SETTING, Boolean.class, true)); + } + + @Override + public BaseComponent prefix() { + return new Component().extra("[").extra(new Component("A", ChatColor.GOLD)).extra("] "); + } + + @Override + public BaseComponent format(PlayerComponent player, String message) { + return new Component(player).extra(": ").extra(message); + } + + @Override + public ChatDoc.Type type() { + return ChatDoc.Type.ADMIN; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/server/ServerChannel.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/server/ServerChannel.java new file mode 100644 index 0000000..bb6c7ed --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/channels/server/ServerChannel.java @@ -0,0 +1,43 @@ +package tc.oc.commons.bukkit.channels.server; + +import net.md_5.bungee.api.chat.BaseComponent; +import org.bukkit.command.CommandSender; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.bukkit.channels.SimpleChannel; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.core.chat.Component; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class ServerChannel extends SimpleChannel { + + @Inject ServerChannel() {} + + @Override + public BaseComponent prefix() { + return new Component(); + } + + @Override + public BaseComponent format(PlayerComponent player, String message) { + return new Component().extra("<").extra(player).extra(">: ").extra(message); + } + + @Override + public ChatDoc.Type type() { + return ChatDoc.Type.SERVER; + } + + @Override + public boolean sendable(CommandSender sender) { + return true; + } + + @Override + public boolean viewable(CommandSender sender) { + return true; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatAnnouncer.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatAnnouncer.java new file mode 100644 index 0000000..1df3191 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatAnnouncer.java @@ -0,0 +1,98 @@ +package tc.oc.commons.bukkit.chat; + +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.Server; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.api.message.MessageListener; +import tc.oc.api.message.types.ModelUpdate; +import tc.oc.api.queue.PrimaryQueue; +import tc.oc.api.servers.ServerStore; +import tc.oc.commons.bukkit.broadcast.BroadcastSender; +import tc.oc.commons.bukkit.channels.Channel; +import tc.oc.commons.bukkit.channels.ChannelRouter; +import tc.oc.commons.core.plugin.PluginFacet; +import tc.oc.minecraft.scheduler.MainThreadExecutor; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; + +@Singleton +public class ChatAnnouncer implements PluginFacet, MessageListener { + + private final Server server; + private final ServerStore serverStore; + private final PrimaryQueue primaryQueue; + private final MainThreadExecutor executor; + private final ChannelRouter channelRouter; + private final BroadcastSender broadcaster; + + @Inject ChatAnnouncer(Server server, ServerStore serverStore, PrimaryQueue primaryQueue, MainThreadExecutor executor, ChannelRouter channelRouter, BroadcastSender broadcaster) { + this.server = server; + this.primaryQueue = primaryQueue; + this.executor = executor; + this.channelRouter = channelRouter; + this.broadcaster = broadcaster; + this.serverStore = serverStore; + } + + @Override + public void enable() { + primaryQueue.bind(ModelUpdate.class); + primaryQueue.subscribe(this, executor); + } + + @Override + public void disable() { + primaryQueue.unsubscribe(this); + } + + @MessageListener.HandleMessage + public void onChat(ModelUpdate message) { + final Chat chat = message.document(); + if(shouldAnnounce(chat)) { + final ChatDoc.Type type = chat.type(); + final Optional channel = channelRouter.getChannel(chat); + if(channel.isPresent()) { + channel.get().show(chat); + } else if(type == ChatDoc.Type.BROADCAST) { + broadcaster.show(chat); + } + } + } + + public boolean shouldAnnounce(Chat chat) { + final Server origin = serverStore.byId(chat.server_id()); + final boolean local = server.equals(origin); + final boolean remote = serverStore.canCommunicate(server._id(), origin._id()); + switch(chat.type()) { + case SERVER: + case TEAM: + return local; + case ADMIN: + return local || remote; + case BROADCAST: + return shouldAnnounce(chat.broadcast()); + } + return false; + } + + public boolean shouldAnnounce(ChatDoc.Broadcast broadcast) { + final String destination = broadcast.id(); + switch(broadcast.destination()) { + case SERVER: + return server._id().equalsIgnoreCase(destination); + case FAMILY: + return server.family().equalsIgnoreCase(destination); + case GAME: + final String game = server.game_id(); + return game == null || game.equalsIgnoreCase(destination); + case NETWORK: + return server.network().name().equalsIgnoreCase(destination); + case GLOBAL: + return true; + } + return false; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatCreator.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatCreator.java new file mode 100644 index 0000000..bbbdc17 --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatCreator.java @@ -0,0 +1,91 @@ +package tc.oc.commons.bukkit.chat; + +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import tc.oc.api.docs.Chat; +import tc.oc.api.docs.PlayerId; +import tc.oc.api.docs.Server; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.api.docs.virtual.MatchDoc; +import tc.oc.api.model.BatchUpdater; +import tc.oc.api.model.BatchUpdaterFactory; +import tc.oc.api.model.IdFactory; +import tc.oc.api.model.ModelService; +import tc.oc.commons.core.plugin.PluginFacet; +import tc.oc.minecraft.api.event.Listener; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.time.Duration; +import java.time.Instant; +import java.util.function.Consumer; + +import static java.util.Optional.ofNullable; + +@Singleton +public class ChatCreator implements PluginFacet, Listener { + + private final IdFactory idFactory; + private final ModelService chatService; + private final BatchUpdater chatBatchUpdater; + private final Server server; + + @Inject ChatCreator(IdFactory idFactory, ModelService chatService, BatchUpdaterFactory chatBatchUpdaterFactory, Server server) { + this.idFactory = idFactory; + this.chatService = chatService; + this.chatBatchUpdater = chatBatchUpdaterFactory.createBatchUpdater(Duration.ofMinutes(1)); + this.server = server; + } + + public ListenableFuture chat(@Nullable PlayerId sender, String message, ChatDoc.Type type, Consumer callback) { + return send(sender, message, type, null, callback); + } + + public ListenableFuture broadcast(@Nullable PlayerId sender, String message, ChatDoc.Destination destination, String destination_id) { + return send(sender, message, ChatDoc.Type.BROADCAST, + new ChatDoc.Broadcast() { + public ChatDoc.Destination destination() { return destination; } + public String id() { return destination_id; } + }, null + ); + } + + protected ListenableFuture send(@Nullable PlayerId sender, String message, ChatDoc.Type type, @Nullable ChatDoc.Broadcast broadcast, @Nullable Consumer callback) { + final Instant time = Instant.now(); + final String id = idFactory.newId(); + ChatDoc.Creation chat = new ChatDoc.Creation() { + public String _id() { return id; } + public String sender_id() { return sender != null ? sender._id() : null; } + public String message() { return message; } + public String server_id() { return server._id(); } + public String match_id() { return ofNullable(server.current_match()).map(MatchDoc::_id).orElse(null); } + public ChatDoc.Type type() { return type; } + public Instant sent_at() { return time; } + public ChatDoc.Broadcast broadcast() { return broadcast; } + }; + // Some chats are only consumed by the local server, + // so those messages can have delayed reporting to the API. + if(type.batchUpdate) { + if(callback != null) callback.accept(mock(sender, chat)); + chatBatchUpdater.update(chat); + return Futures.immediateFuture(null); + } else { + return chatService.update(chat); + } + } + + private Chat mock(@Nullable PlayerId sender, ChatDoc.Creation chat) { + return new Chat() { + public PlayerId sender() { return sender; } + public String _id() { return chat._id(); } + public String message() { return chat.message(); } + public String server_id() { return chat.server_id(); } + public String match_id() { return chat.match_id(); } + public ChatDoc.Type type() { return chat.type(); } + public Instant sent_at() { return chat.sent_at(); } + public ChatDoc.Broadcast broadcast() { return chat.broadcast(); } + }; + } + +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatManifest.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatManifest.java new file mode 100644 index 0000000..e6e5e1d --- /dev/null +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ChatManifest.java @@ -0,0 +1,20 @@ +package tc.oc.commons.bukkit.chat; + +import tc.oc.commons.bukkit.broadcast.BroadcastSender; +import tc.oc.commons.core.inject.HybridManifest; +import tc.oc.commons.core.plugin.PluginFacetBinder; + +public class ChatManifest extends HybridManifest { + @Override + protected void configure() { + + expose(ChatCreator.class); + expose(ChatAnnouncer.class); + expose(BroadcastSender.class); + + final PluginFacetBinder facets = new PluginFacetBinder(binder()); + facets.register(ChatCreator.class); + facets.register(ChatAnnouncer.class); + facets.register(BroadcastSender.class); + } +} diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Paginator.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Paginator.java index 051d879..f5f6294 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Paginator.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Paginator.java @@ -52,7 +52,7 @@ public class Paginator { } public void display(CommandSender sender, Collection results, int page) { - display(BukkitAudiences.getAudience(sender), results, page); + display(Audiences.Deprecated.get(sender), results, page); } public void display(Audience audience, Collection results, int page) { diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java index 10de6e3..0d433b4 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/CommandUtils.java @@ -2,14 +2,20 @@ package tc.oc.commons.bukkit.commands; import java.time.Duration; import java.time.format.DateTimeParseException; +import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.annotation.Nullable; import com.sk89q.minecraft.util.commands.CommandContext; import com.sk89q.minecraft.util.commands.CommandException; import com.sk89q.minecraft.util.commands.CommandPermissionsException; import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; import net.md_5.bungee.api.chat.TranslatableComponent; import org.bukkit.Bukkit; import org.bukkit.ChatColor; @@ -20,9 +26,11 @@ import org.bukkit.permissions.Permission; import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.Server; import tc.oc.commons.bukkit.chat.ComponentRenderers; +import tc.oc.commons.bukkit.chat.ListComponent; import tc.oc.commons.bukkit.localization.Translations; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.commands.TranslatableCommandException; +import tc.oc.commons.core.formatting.StringUtils; import tc.oc.commons.core.util.TimeUtils; public abstract class CommandUtils { @@ -218,4 +226,35 @@ public abstract class CommandUtils { public static void notEnoughArguments(CommandSender sender) throws CommandException { throw new CommandException(Translations.get().t("command.error.notEnoughArguments", sender)); } + + public static Map enumChoices(Class enumClass) { + return Stream.of(enumClass.getEnumConstants()) + .collect(Collectors.toMap(e -> e.name().toLowerCase().replaceAll("_", "-"), Function.identity())); + } + + public static List enumChoicesList(Class enumClass) { + return new ArrayList<>(enumChoices(enumClass).keySet()); + } + + public static @Nullable E tryEnum(String text, Class enumClass) { + return StringUtils.bestFuzzyMatch(text, enumChoices(enumClass), 0.8); + } + + public static E tryEnum(String text, Class enumClass, E def) { + final E option = tryEnum(text, enumClass); + return option == null ? def : option; + } + + public static E getEnum(String text, Class enumClass) throws CommandException { + final E option = tryEnum(text, enumClass); + if(option != null) { + return option; + } else { + throw new TranslatableCommandException("command.error.invalidOption", text, new ListComponent(enumChoicesList(enumClass), TextComponent::new)); + } + } + + public static List completeEnum(String prefix, Class enumClass) { + return StringUtils.complete(prefix, enumChoicesList(enumClass)); + } } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/UserCommands.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/UserCommands.java index e3db63c..45f162c 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/UserCommands.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/commands/UserCommands.java @@ -17,13 +17,13 @@ import tc.oc.api.docs.User; import tc.oc.api.friendships.FriendshipRequest; import tc.oc.api.friendships.FriendshipService; import tc.oc.api.minecraft.MinecraftService; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.Links; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; import tc.oc.commons.core.util.Lazy; import tc.oc.minecraft.scheduler.SyncExecutor; import tc.oc.api.sessions.SessionService; -import tc.oc.commons.bukkit.chat.BukkitAudiences; import tc.oc.commons.bukkit.chat.ComponentPaginator; import tc.oc.commons.bukkit.chat.ComponentRenderers; import tc.oc.commons.bukkit.chat.HeaderComponent; @@ -47,8 +47,9 @@ public class UserCommands implements Commands { private final UserFinder userFinder; private final IdentityProvider identityProvider; private final UserFormatter userFormatter; + private final Audiences audiences; - @Inject UserCommands(MinecraftService minecraftService, SyncExecutor syncExecutor, SessionService sessionService, FriendshipService friendshipService, UserFinder userFinder, IdentityProvider identityProvider, UserFormatter userFormatter) { + @Inject UserCommands(MinecraftService minecraftService, SyncExecutor syncExecutor, SessionService sessionService, FriendshipService friendshipService, UserFinder userFinder, IdentityProvider identityProvider, UserFormatter userFormatter, Audiences audiences) { this.minecraftService = minecraftService; this.syncExecutor = syncExecutor; this.sessionService = sessionService; @@ -56,6 +57,7 @@ public class UserCommands implements Commands { this.userFinder = userFinder; this.identityProvider = identityProvider; this.userFormatter = userFormatter; + this.audiences = audiences; } @Command( @@ -113,7 +115,7 @@ public class UserCommands implements Commands { @CommandPermissions("ocn.friend.request") public void friend(final CommandContext args, final CommandSender sender) throws CommandException { User friender = userFinder.getLocalUser(CommandUtils.senderToPlayer(sender)); - Audience audience = BukkitAudiences.getAudience(sender); + Audience audience = audiences.get(sender); syncExecutor.callback( userFinder.findUser(sender, args, 0), response -> { @@ -163,7 +165,7 @@ public class UserCommands implements Commands { @CommandPermissions("ocn.friend.request") public void unfriend(final CommandContext args, final CommandSender sender) throws CommandException { User friender = userFinder.getLocalUser(CommandUtils.senderToPlayer(sender)); - Audience audience = BukkitAudiences.getAudience(sender); + Audience audience = audiences.get(sender); syncExecutor.callback( userFinder.findUser(sender, args, 0), response -> { @@ -204,7 +206,7 @@ public class UserCommands implements Commands { syncExecutor.callback( sessionService.staff(minecraftService.getLocalServer().network(), identityProvider.revealAll(sender)), CommandFutureCallback.onSuccess(sender, args, result -> { - final Audience audience = BukkitAudiences.getAudience(sender); + final Audience audience = audiences.get(sender); if(result.documents().isEmpty()) { audience.sendMessage(new TranslatableComponent("command.staff.noStaffOnline")); return; diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/flairs/FlairRenderer.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/flairs/FlairRenderer.java index a83b821..d03ec42 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/flairs/FlairRenderer.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/flairs/FlairRenderer.java @@ -8,6 +8,7 @@ import javax.inject.Inject; import javax.inject.Singleton; import com.google.common.collect.ImmutableSet; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.chat.BaseComponent; import tc.oc.api.bukkit.users.BukkitUserStore; import tc.oc.api.docs.virtual.UserDoc; @@ -37,6 +38,7 @@ public class FlairRenderer implements PartialNameRenderer { @Override public String getLegacyName(Identity identity, NameType type) { if(!(type.style.contains(NameFlag.FLAIR) && type.reveal)) return ""; + if(identity.isConsole()) return ChatColor.GOLD + "❖"; return getFlairs(identity).reduce("", String::concat); } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/ConsoleIdentity.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/ConsoleIdentity.java index 880cddb..acdfa90 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/ConsoleIdentity.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/ConsoleIdentity.java @@ -64,6 +64,11 @@ public class ConsoleIdentity implements Identity { return true; } + @Override + public boolean isConsole() { + return true; + } + @Override public String getName(CommandSender viewer) { return NAME; diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/Identity.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/Identity.java index ab31210..070fba7 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/Identity.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/Identity.java @@ -60,6 +60,11 @@ public interface Identity { */ boolean isCurrent(); + /** + * Does the identity belong to a console? + */ + boolean isConsole(); + // Viewer-relative properties /** diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/IdentityImpl.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/IdentityImpl.java index 482088c..6274b62 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/IdentityImpl.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/IdentityImpl.java @@ -71,6 +71,11 @@ public class IdentityImpl implements Identity { return player != null && equals(identityProvider.currentIdentity(player)); } + @Override + public boolean isConsole() { + return false; + } + @Override public boolean isDead(CommandSender viewer) { if(!isOnline(viewer)) return false; diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/UsernameRenderer.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/UsernameRenderer.java index d1f5951..bf7fc34 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/UsernameRenderer.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/nick/UsernameRenderer.java @@ -84,7 +84,7 @@ public class UsernameRenderer implements PartialNameRenderer { rendered.setColor(getColor(identity, type)); } - if(type.style.contains(NameFlag.TELEPORT)) { + if(!identity.isConsole() && type.style.contains(NameFlag.TELEPORT)) { Component dupe = rendered.duplicate(); rendered.clickEvent(makeRemoteTeleportClickEvent(identity, identity.getNickname() != null && !type.reveal)); rendered.hoverEvent(HoverEvent.Action.SHOW_TEXT, new TranslatableComponent("tip.teleportTo", dupe)); diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/punishment/PunishmentCreator.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/punishment/PunishmentCreator.java index 855e7a2..1090286 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/punishment/PunishmentCreator.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/punishment/PunishmentCreator.java @@ -14,25 +14,22 @@ import tc.oc.api.docs.virtual.MatchDoc; import tc.oc.api.docs.virtual.PunishmentDoc; import tc.oc.api.model.IdFactory; import tc.oc.api.model.ModelService; -import tc.oc.commons.bukkit.report.ReportConfiguration; @Singleton public class PunishmentCreator { - private final ReportConfiguration config; private final ModelService punishmentService; private final IdFactory idFactory; private final Server localServer; - @Inject PunishmentCreator(ReportConfiguration config, ModelService punishmentService, IdFactory idFactory, Server localServer) { - this.config = config; + @Inject PunishmentCreator(ModelService punishmentService, IdFactory idFactory, Server localServer) { this.punishmentService = punishmentService; this.idFactory = idFactory; this.localServer = localServer; } public boolean offRecord() { - return !config.crossServer(); + return localServer.cross_server_profile() == null; } public ListenableFuture create(@Nullable PlayerId punisher, PlayerId punished, String reason, @Nullable PunishmentDoc.Type type, @Nullable Duration duration, boolean silent, boolean auto, boolean offrecord) { diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportAnnouncer.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportAnnouncer.java index 5cdefba..9fc82d1 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportAnnouncer.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportAnnouncer.java @@ -1,25 +1,22 @@ package tc.oc.commons.bukkit.report; -import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; import me.anxuiz.settings.Setting; import me.anxuiz.settings.SettingBuilder; import me.anxuiz.settings.types.BooleanType; -import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Sound; -import org.bukkit.entity.Player; import tc.oc.api.docs.Report; import tc.oc.api.docs.Server; import tc.oc.api.message.MessageListener; import tc.oc.api.message.MessageQueue; import tc.oc.api.message.types.ModelUpdate; -import tc.oc.commons.bukkit.channels.AdminChannel; +import tc.oc.api.servers.ServerStore; +import tc.oc.commons.bukkit.channels.admin.AdminChannel; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.BukkitSound; import tc.oc.commons.bukkit.settings.SettingManagerProvider; -import tc.oc.commons.core.chat.Audience; import tc.oc.commons.core.plugin.PluginFacet; import tc.oc.minecraft.scheduler.MainThreadExecutor; @@ -39,24 +36,17 @@ public class ReportAnnouncer implements PluginFacet, MessageListener { private final MessageQueue primaryQueue; private final MainThreadExecutor executor; private final Server localServer; - private final AdminChannel adminChannel; + private final ServerStore serverStore; private final Audiences audiences; - private final SettingManagerProvider settings; - @Inject ReportAnnouncer(ReportConfiguration config, ReportFormatter reportFormatter, MessageQueue primaryQueue, MainThreadExecutor executor, Server localServer, AdminChannel adminChannel, Audiences audiences, SettingManagerProvider settings) { + @Inject ReportAnnouncer(ReportConfiguration config, ReportFormatter reportFormatter, MessageQueue primaryQueue, MainThreadExecutor executor, Server localServer, ServerStore serverStore, Audiences audiences) { this.config = config; this.reportFormatter = reportFormatter; this.primaryQueue = primaryQueue; this.executor = executor; this.localServer = localServer; - this.adminChannel = adminChannel; + this.serverStore = serverStore; this.audiences = audiences; - this.settings = settings; - } - - @Override - public boolean isActive() { - return config.crossServer(); } @Override @@ -72,19 +62,9 @@ public class ReportAnnouncer implements PluginFacet, MessageListener { @HandleMessage public void broadcast(ModelUpdate message) { - if(localServer._id().equals(message.document().server_id()) || - (config.crossServer() && config.families().contains(message.document().family()))) { - - final List formatted = reportFormatter.format(message.document(), true, false); - adminChannel.viewers() - .filter(viewer -> viewer.hasPermission(ReportPermissions.RECEIVE)) - .forEach(viewer -> { - Audience audience = audiences.get(viewer); - audience.sendMessages(formatted); - if (viewer instanceof Player && (boolean)settings.getManager((Player)viewer).getValue(SOUND_SETTING)) { - audience.playSound(REPORT_SOUND); - } - }); + if(serverStore.canCommunicate(localServer._id(), message.document().server_id())) { + audiences.permission(ReportPermissions.RECEIVE) + .sendMessages(reportFormatter.format(message.document(), true, false)); } } } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportCommands.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportCommands.java index 520ee3b..b94431e 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportCommands.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportCommands.java @@ -161,11 +161,8 @@ public class ReportCommands implements Commands, Listener { final boolean crossServer = args.hasFlag('a'); ReportSearchRequest request = ReportSearchRequest.create(page, PER_PAGE); - request = crossServer ? request.forFamilies(reportConfiguration.families()) - : request.forServer(localServer); - if(userResult != null) { - request = request.forPlayer(userResult.user); - } + request = userResult != null ? request.forPlayer(userResult.user) + : request.forServer(localServer, crossServer); syncExecutor.callback( reportService.find(request), diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportConfiguration.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportConfiguration.java index 2780132..702b3ab 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportConfiguration.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/report/ReportConfiguration.java @@ -1,6 +1,5 @@ package tc.oc.commons.bukkit.report; -import java.util.List; import javax.inject.Inject; import org.bukkit.configuration.Configuration; @@ -22,12 +21,4 @@ public class ReportConfiguration { public Duration cooldown() { return ConfigUtils.getDuration(config, "reports.cooldown", Duration.ZERO); } - - public List families() { - return config.getStringList("reports.families"); - } - - public boolean crossServer() { - return config.getBoolean("reports.cross-server", false); - } } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/teleport/TeleportListener.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/teleport/TeleportListener.java index e2af2e0..5bfc050 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/teleport/TeleportListener.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/teleport/TeleportListener.java @@ -77,7 +77,7 @@ public class TeleportListener implements MessageListener, Listener, PluginFacet, @Override public void enable() { - permissionRegistry.addPermission(Teleporter.PERMISSION); + permissionRegistry.register(Teleporter.PERMISSION); primaryQueue.subscribe(this, syncExecutor); primaryQueue.bind(PlayerTeleportRequest.class); } diff --git a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/whisper/WhisperFormatter.java b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/whisper/WhisperFormatter.java index 2e65d72..1713a13 100644 --- a/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/whisper/WhisperFormatter.java +++ b/Commons/bukkit/src/main/java/tc/oc/commons/bukkit/whisper/WhisperFormatter.java @@ -11,11 +11,9 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import tc.oc.api.docs.Server; import tc.oc.api.docs.Whisper; -import tc.oc.api.docs.virtual.ServerDoc; import tc.oc.api.servers.ServerStore; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.BukkitSound; -import tc.oc.commons.bukkit.chat.ConsoleAudience; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; import tc.oc.commons.bukkit.chat.UserTextComponent; @@ -42,16 +40,14 @@ public class WhisperFormatter { private final Server localServer; private final SettingManagerProvider playerSettings; private final Audiences audiences; - private final ConsoleAudience consoleAudience; - @Inject WhisperFormatter(IdentityProvider identities, MiscFormatter miscFormatter, ServerStore serverStore, Server localServer, SettingManagerProvider playerSettings, Audiences audiences, ConsoleAudience consoleAudience) { + @Inject WhisperFormatter(IdentityProvider identities, MiscFormatter miscFormatter, ServerStore serverStore, Server localServer, SettingManagerProvider playerSettings, Audiences audiences) { this.identities = identities; this.miscFormatter = miscFormatter; this.serverStore = serverStore; this.localServer = localServer; this.playerSettings = playerSettings; this.audiences = audiences; - this.consoleAudience = consoleAudience; this.serverFormatter = ServerFormatter.dark; } @@ -81,7 +77,7 @@ public class WhisperFormatter { .extra(new Component(new UserTextComponent(sender, whisper.content()), ChatColor.WHITE)); audience.sendMessage(display); - consoleAudience.sendMessage(display); + audiences.console().sendMessage(display); } public void receive(Player viewer, Whisper whisper) { @@ -135,7 +131,7 @@ public class WhisperFormatter { audience.sendMessage(display); if(!local) { - consoleAudience.sendMessage(display); + audiences.console().sendMessage(display); } } diff --git a/Commons/bukkit/src/main/resources/config.yml b/Commons/bukkit/src/main/resources/config.yml index 3ea5e7a..55a7a04 100644 --- a/Commons/bukkit/src/main/resources/config.yml +++ b/Commons/bukkit/src/main/resources/config.yml @@ -29,8 +29,6 @@ join-messages: reports: enabled: true cooldown: 0s - families: [] - cross-server: true datadog: enabled: false diff --git a/Commons/bukkit/src/main/resources/plugin.yml b/Commons/bukkit/src/main/resources/plugin.yml index da0365e..98ccd6a 100644 --- a/Commons/bukkit/src/main/resources/plugin.yml +++ b/Commons/bukkit/src/main/resources/plugin.yml @@ -6,7 +6,7 @@ website: ${url} main: ${plugin.mainClass} prefix: ${plugin.prefix} isolate: true -depend: [API, BukkitSettings, Channels] +depend: [API, BukkitSettings] permissions: sudo: diff --git a/Commons/core/src/main/i18n/templates/commons/Commons.properties b/Commons/core/src/main/i18n/templates/commons/Commons.properties index eabe151..4abf62f 100644 --- a/Commons/core/src/main/i18n/templates/commons/Commons.properties +++ b/Commons/core/src/main/i18n/templates/commons/Commons.properties @@ -14,6 +14,8 @@ command.error.invalidEnum = Invalid enum option '{0}' command.error.invalidNumber = Invalid number '{0}' command.error.invalidPage = There is no page {0}. Pages run from 1 to {1}. command.error.emptyResult = Empty result +command.error.invalidOption = There is no option named '{0}'. Here are the possible options: \ + {1} command.onlyPlayers = You must be a player to use this command. command.error.internal = Sorry, there was an internal error while processing your command. @@ -130,6 +132,10 @@ game.description.party = Special events tip.teleportTo = Teleport to {0} tip.connectTo = Connect to {0} +tip.sentBy = Sent by {0} + +broadcast.prefix = Broadcast + # {0} = Sender # {1} = Recipient # {2} = Relative time @@ -174,6 +180,11 @@ appealNotification.count = You have - {0} - unread {1} misc.appeals.singular = appeal misc.appeals.plural = appeals +# {0} = chat channel type +channels.default.alreadySet = Your default channel is already {0} chat +channels.default.set = Your default channel is now {0} chat +channels.unavailable = {0} chat is currently unavailable. + misc.enabled = enabled misc.disabled = disabled diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index 82fc73a..cb00de6 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -151,6 +151,7 @@ start.needMorePlayers.ffa.plural = Waiting for {0} more players to join start.needMorePlayers.team.singular = Waiting for {0} more player to join {1} start.needMorePlayers.team.plural = Waiting for {0} more players to join {1} +defuse.broadcast = {0} defused {1}'s TNT defuse.world = You defused world TNT. # {0} = the player(s) defuse.player = You defused {0}'s TNT. diff --git a/Commons/core/src/main/i18n/templates/projectares/PAMessages.properties b/Commons/core/src/main/i18n/templates/projectares/PAMessages.properties index 6434be4..61b30e8 100644 --- a/Commons/core/src/main/i18n/templates/projectares/PAMessages.properties +++ b/Commons/core/src/main/i18n/templates/projectares/PAMessages.properties @@ -41,6 +41,9 @@ command.nick.setOther.queued = {1} will be disguised as {0} the next time they c command.freeze.frozen = You have frozen {0} command.freeze.unfrozen = You have unfrozen {0} +freeze.frozen.broadcast = {0} has been frozen by {1} +freeze.unfrozen.broadcast = {0} has been unfrozen by {1} + # {0} = the server name command.server.teleporting = Teleporting you to {0} command.server.currentServer = You are currently on {0} diff --git a/PGM/src/main/java/tc/oc/pgm/PGM.java b/PGM/src/main/java/tc/oc/pgm/PGM.java index ff50133..9f1afcd 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGM.java +++ b/PGM/src/main/java/tc/oc/pgm/PGM.java @@ -19,7 +19,6 @@ import tc.oc.commons.core.commands.CommandRegistry; import tc.oc.inject.ProtectedBinder; import tc.oc.minecraft.logging.BetterRaven; import tc.oc.pgm.antigrief.CraftingProtect; -import tc.oc.pgm.channels.ChannelCommands; import tc.oc.pgm.commands.MapCommands; import tc.oc.pgm.commands.PollCommands; import tc.oc.pgm.commands.RotationControlCommands; @@ -195,7 +194,6 @@ public final class PGM extends JavaPlugin { private void setupCommands() { commands.register(MapCommands.class); - commands.register(ChannelCommands.class); commands.register(PollCommands.class); commands.register(RotationEditCommands.RotationEditParent.class); commands.register(RotationControlCommands.RotationControlParent.class); diff --git a/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java b/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java index f0b36f6..2a1561f 100644 --- a/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/PGMModulesManifest.java @@ -1,5 +1,6 @@ package tc.oc.pgm; +import tc.oc.pgm.channels.ChannelManifest; import tc.oc.commons.core.inject.HybridManifest; import tc.oc.pgm.animation.AnimationManifest; import tc.oc.pgm.broadcast.BroadcastManifest; @@ -70,6 +71,7 @@ public class PGMModulesManifest extends HybridManifest { install(new LaneManifest()); install(new BroadcastManifest()); install(new StatsManifest()); + install(new ChannelManifest()); install(new RaindropManifest()); install(new TokenManifest()); install(new ObjectiveModeManifest()); diff --git a/PGM/src/main/java/tc/oc/pgm/antigrief/DefuseListener.java b/PGM/src/main/java/tc/oc/pgm/antigrief/DefuseListener.java index 707e117..98cdbfd 100644 --- a/PGM/src/main/java/tc/oc/pgm/antigrief/DefuseListener.java +++ b/PGM/src/main/java/tc/oc/pgm/antigrief/DefuseListener.java @@ -12,6 +12,7 @@ import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Sound; import org.bukkit.block.Block; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; @@ -25,14 +26,18 @@ import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemFlag; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.ItemMeta; -import tc.oc.commons.bukkit.channels.AdminChannel; +import tc.oc.commons.bukkit.channels.admin.AdminChannel; import tc.oc.commons.bukkit.chat.ComponentRenderers; import tc.oc.commons.bukkit.chat.ListComponent; import tc.oc.commons.bukkit.chat.NameStyle; +import tc.oc.commons.bukkit.chat.PlayerComponent; import tc.oc.commons.bukkit.event.ObserverKitApplyEvent; +import tc.oc.commons.bukkit.nick.IdentityProvider; import tc.oc.commons.core.inject.Proxied; import tc.oc.commons.core.plugin.PluginFacet; import tc.oc.pgm.PGMTranslations; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchFinder; import tc.oc.pgm.match.MatchManager; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.ParticipantState; @@ -43,9 +48,10 @@ public class DefuseListener implements PluginFacet, Listener { public static final Material DEFUSE_ITEM = Material.SHEARS; public static final int DEFUSE_SLOT = 4; - private final MatchManager mm; - private final EntityResolver entityResolver; - private final AdminChannel adminChannel; + @Inject MatchFinder mm; + @Inject AdminChannel adminChannel; + @Inject IdentityProvider identityProvider; + @Inject @Proxied EntityResolver entityResolver; @Inject DefuseListener(MatchManager mm, @Proxied EntityResolver entityResolver, AdminChannel adminChannel) { this.mm = mm; @@ -72,10 +78,12 @@ public class DefuseListener implements PluginFacet, Listener { // check tnt if(!(entity instanceof TNTPrimed)) return; - TNTMatchModule tntmm = mm.getMatch(player.getWorld()).getMatchModule(TNTMatchModule.class); + final Match match = mm.needMatch((CommandSender) player); + + TNTMatchModule tntmm = match.getMatchModule(TNTMatchModule.class); if(tntmm != null && !tntmm.getProperties().friendlyDefuse) return; - MatchPlayer clicker = this.mm.getPlayer(player); + MatchPlayer clicker = match.getPlayer(player); if(clicker == null || !clicker.canInteract()) return; // check water @@ -86,17 +94,19 @@ public class DefuseListener implements PluginFacet, Listener { } // check owner - MatchPlayer owner = this.mm.getPlayer(entityResolver.getOwner(entity)); + MatchPlayer owner = match.getPlayer(entityResolver.getOwner(entity)); if(owner == null || (owner != clicker && owner.getParty() == clicker.getParty())) { // cannot defuse own TNT // defuse TNT entity.remove(); if(owner != null) { this.notifyDefuse(clicker, entity, ChatColor.RED + PGMTranslations.t("defuse.player", clicker, owner.getDisplayName(clicker) + ChatColor.RED)); - adminChannel.broadcast(clicker.getDisplayName() + - ChatColor.WHITE + " defused " + - owner.getDisplayName() - + ChatColor.WHITE + "'s " + - ChatColor.DARK_RED + "TNT"); + adminChannel.sendMessage( + new TranslatableComponent( + "defuse.broadcast", + new PlayerComponent(identityProvider.currentIdentity(clicker.getPlayerId()), NameStyle.VERBOSE), + new PlayerComponent(identityProvider.currentIdentity(owner.getPlayerId()), NameStyle.VERBOSE) + ) + ); } else { this.notifyDefuse(clicker, entity, ChatColor.RED + PGMTranslations.t("defuse.world", clicker)); } diff --git a/PGM/src/main/java/tc/oc/pgm/channels/ChannelCommands.java b/PGM/src/main/java/tc/oc/pgm/channels/ChannelCommands.java deleted file mode 100644 index fb33f84..0000000 --- a/PGM/src/main/java/tc/oc/pgm/channels/ChannelCommands.java +++ /dev/null @@ -1,44 +0,0 @@ -package tc.oc.pgm.channels; - -import com.github.rmsy.channels.Channel; -import com.sk89q.minecraft.util.commands.Command; -import com.sk89q.minecraft.util.commands.CommandContext; -import com.sk89q.minecraft.util.commands.CommandException; -import com.sk89q.minecraft.util.commands.CommandPermissionsException; -import net.md_5.bungee.api.chat.TranslatableComponent; -import org.bukkit.ChatColor; -import org.bukkit.command.CommandSender; -import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.PGMTranslations; -import tc.oc.pgm.commands.CommandUtils; - -public class ChannelCommands { - @Command( - aliases = "t", - desc = "Sends a message to the team channel (or sets the team channel to your default channel).", - usage = "[message...]", - min = 0, - max = -1, - anyFlags = true - ) - public static void teamChat(CommandContext args, CommandSender sender) throws CommandException { - MatchPlayer player = CommandUtils.senderToMatchPlayer(sender); - - if (player.getBukkit().hasPermission(ChannelMatchModule.TEAM_SEND_PERMISSION)) { - ChannelMatchModule cmm = player.getMatch().needMatchModule(ChannelMatchModule.class); - - if (args.argsLength() == 0) { - cmm.setTeamChat(player, true); - player.sendMessage(new TranslatableComponent("command.chat.team.switchSuccess")); - } else { - Channel channel = cmm.getChannel(player.getParty()); - channel.sendMessage(args.getJoinedStrings(0), player.getBukkit()); - if (!player.getBukkit().hasPermission(channel.getListeningPermission())) { - sender.sendMessage(ChatColor.YELLOW + PGMTranslations.t("command.chat.team.success", player)); - } - } - } else { - throw new CommandPermissionsException(); - } - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/channels/ChannelManifest.java b/PGM/src/main/java/tc/oc/pgm/channels/ChannelManifest.java new file mode 100644 index 0000000..6dbe8eb --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/channels/ChannelManifest.java @@ -0,0 +1,12 @@ +package tc.oc.pgm.channels; + +import tc.oc.commons.core.inject.HybridManifest; +import tc.oc.pgm.match.inject.MatchModuleFixtureManifest; + +public class ChannelManifest extends HybridManifest { + @Override + protected void configure() { + installFactory(PartyChannel.Factory.class); + install(new MatchModuleFixtureManifest(){}); + } +} diff --git a/PGM/src/main/java/tc/oc/pgm/channels/ChannelMatchModule.java b/PGM/src/main/java/tc/oc/pgm/channels/ChannelMatchModule.java index 9ce18ea..4ad4bf9 100644 --- a/PGM/src/main/java/tc/oc/pgm/channels/ChannelMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/channels/ChannelMatchModule.java @@ -1,206 +1,74 @@ package tc.oc.pgm.channels; -import java.util.HashMap; -import java.util.Map; -import javax.inject.Inject; - -import com.github.rmsy.channels.Channel; -import com.github.rmsy.channels.ChannelsPlugin; -import com.github.rmsy.channels.PlayerManager; -import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; -import org.bukkit.permissions.Permission; -import org.bukkit.permissions.PermissionDefault; -import org.bukkit.plugin.Plugin; -import tc.oc.chatmoderator.ChatModeratorPlugin; -import tc.oc.commons.bukkit.chat.ComponentRenderers; -import tc.oc.commons.bukkit.util.NullCommandSender; -import tc.oc.commons.bukkit.util.OnlinePlayerMapAdapter; -import tc.oc.commons.core.util.DefaultMapAdapter; +import tc.oc.commons.bukkit.channels.Channel; +import tc.oc.commons.bukkit.channels.ChannelRouter; +import tc.oc.pgm.events.CompetitorAddEvent; +import tc.oc.pgm.events.CompetitorRemoveEvent; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.events.PartyAddEvent; import tc.oc.pgm.events.PartyRemoveEvent; -import tc.oc.pgm.events.PlayerJoinPartyEvent; -import tc.oc.pgm.events.PlayerLeavePartyEvent; -import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.match.MultiPlayerParty; import tc.oc.pgm.match.Party; +import javax.inject.Inject; +import java.util.HashMap; +import java.util.Map; + @ListenerScope(MatchScope.LOADED) public class ChannelMatchModule extends MatchModule implements Listener { - public static final String RECEIVE_ALL_PERMISSION = "pgm.chat.all.receive"; - public static final String TEAM_RECEIVE_PERMISSION = "pgm.chat.team.receive"; - public static final String TEAM_SEND_PERMISSION = "pgm.chat.team.send"; + private final Map channels = new HashMap<>(); - // This dynamic permission has all party channel listening permissions as children. - // A player with this permission receives all channels simultaneously. It is granted - // automatically to observers that have the RECEIVE_ALL_PERMISSION. - private final Permission matchListeningPermission; + @Inject + ChannelRouter channelRouter; + @Inject PartyChannel.Factory channelFactory; - private final ChannelsPlugin channelsPlugin = ChannelsPlugin.get(); - private final Map partyChannels = new HashMap<>(); - - // This is used to keep track of players' global/team chat preference. We can't just - // check their channel through the Channels plugin because FFA players always use the - // global channel. All players are set to team chat when they join a match, and the /t - // command sets them to team chat. They can switch to global chat with /g, but we don't - // know when this happens, so we check for it whenever they switch parties. - private final Map teamChatters; - - @Inject ChannelMatchModule(Match match, Plugin plugin) { - this.matchListeningPermission = new Permission("pgm.chat.all." + match.getId() + ".receive", PermissionDefault.FALSE); - - final OnlinePlayerMapAdapter map = new OnlinePlayerMapAdapter<>(plugin); - map.enable(); - this.teamChatters = new DefaultMapAdapter<>(map, true, false); - } - - protected void updatePlayerChannel(MatchPlayer player) { - if(teamChatters.get(player.getBukkit())) { - channelsPlugin.getPlayerManager().setMembershipChannel(player.getBukkit(), getChannel(player.getParty())); - } else { - channelsPlugin.getPlayerManager().setMembershipChannel(player.getBukkit(), channelsPlugin.getGlobalChannel()); + public void create(Party party) { + if(!channels.containsKey(party) && party instanceof MultiPlayerParty) { + channels.put(party, channelFactory.create(party)); } } - public void setTeamChat(MatchPlayer player, boolean teamChat) { - teamChatters.put(player.getBukkit(), teamChat); - updatePlayerChannel(player); - } - - public Channel getChannel(Party party) { - if(party instanceof MultiPlayerParty) { - return partyChannels.get(party); - } else { - return channelsPlugin.getGlobalChannel(); - } - } - - protected Permission createChannelPermission(Party party) { - Permission permission = new Permission("pgm.chat.team." + this.match.getId() + '-' + party.hashCode() + ".receive", PermissionDefault.FALSE); - getMatch().getPluginManager().addPermission(permission); - permission.addParent(matchListeningPermission, true); - return permission; - } - - protected void removeChannelPermission(Channel channel) { - matchListeningPermission.getChildren().remove(channel.getListeningPermission().getName()); - matchListeningPermission.recalculatePermissibles(); - getMatch().getPluginManager().removePermission(channel.getListeningPermission()); - } - - protected void createChannel(Party party) { - if(party instanceof MultiPlayerParty) { - logger.fine("Creating channel for " + party); - - String format = ComponentRenderers.toLegacyText(party.getChatPrefix(), NullCommandSender.INSTANCE) + "{1}§f: {3}"; - - PartyChannel channel; - if (getMatch().getPluginManager().getPlugin("ChatModerator") == null) { - channel = new UnfilteredPartyChannel(format, - createChannelPermission(party), - party); - } else { - channel = new FilteredPartyChannel(format, - createChannelPermission(party), - party, - ChatModeratorPlugin.MINIMUM_SCORE_NO_SEND, - ChatModeratorPlugin.PARTIALLY_OFFENSIVE_RATIO); - } - - if(partyChannels.put((MultiPlayerParty) party, channel) != null) { - throw new IllegalStateException("Party added multiple times"); - } - } + public void remove(Party party) { + channels.remove(party); } @Override public void load() { - super.load(); - - // Let the console receive all channels - match.getPluginManager().addPermission(matchListeningPermission); - getMatch().getServer().getConsoleSender().addAttachment(getMatch().getPlugin()).setPermission(matchListeningPermission, true); - - // Parties may be created before the module loads - for(Party party : getMatch().getParties()) { - createChannel(party); - } + match.getParties().forEach(this::create); + channelRouter.setTeamChannelFunction(player -> channels.get(match.player(player).map(MatchPlayer::getParty).orElse(null))); } @Override public void unload() { - getMatch().getServer().getConsoleSender().removeAttachments(matchListeningPermission); - getMatch().getPluginManager().removePermission(matchListeningPermission); - - super.unload(); + channelRouter.setTeamChannelFunction(null); + match.getParties().forEach(this::remove); } @EventHandler(priority = EventPriority.LOWEST) - public void partyAdd(final PartyAddEvent event) { - createChannel(event.getParty()); + public void onPartyAdd(PartyAddEvent event) { + create(event.getParty()); } - @EventHandler(priority = EventPriority.MONITOR) - public void partyRemove(final PartyRemoveEvent event) { - if(event.getParty() instanceof MultiPlayerParty) { - PartyChannel channel = partyChannels.remove(event.getParty()); - if(channel != null) { - removeChannelPermission(channel); - } - } + @EventHandler(priority = EventPriority.LOWEST) + public void onPartyRemove(PartyRemoveEvent event) { + remove(event.getParty()); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void partyJoin(PlayerJoinPartyEvent event) { - if(event.getNewParty() instanceof MultiPlayerParty) { - PlayerManager playerManager = channelsPlugin.getPlayerManager(); - Player bukkitPlayer = event.getPlayer().getBukkit(); - PartyChannel channel = partyChannels.get(event.getNewParty()); - - if(channel != null) { - if(event.getNewParty().isObservingType() && bukkitPlayer.hasPermission(RECEIVE_ALL_PERMISSION)) { - // If the player is joining observers and they have the receive-all perm, let them listen to all channels - bukkitPlayer.addAttachment(getMatch().getPlugin()).setPermission(matchListeningPermission, true); - } else if(bukkitPlayer.hasPermission(TEAM_RECEIVE_PERMISSION)) { - // Give the player listening permission for the team's channel - bukkitPlayer.addAttachment(getMatch().getPlugin()).setPermission(channel.getListeningPermission(), true); - } - - // If their sending channel was previously set to a team channel, switch it to the new team's channel - if(playerManager.getMembershipChannel(bukkitPlayer) instanceof PartyChannel) { - playerManager.setMembershipChannel(bukkitPlayer, channel); - } - } - } - - updatePlayerChannel(event.getPlayer()); + @EventHandler(priority = EventPriority.LOWEST) + public void onCompetitorAdd(CompetitorAddEvent event) { + create(event.getParty()); } - @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) - public void partyLeave(PlayerLeavePartyEvent event) { - if(event.getOldParty() instanceof MultiPlayerParty) { - PlayerManager playerManager = channelsPlugin.getPlayerManager(); - Player bukkitPlayer = event.getPlayer().getBukkit(); - PartyChannel channel = partyChannels.get(event.getOldParty()); - - if(channel != null) { - bukkitPlayer.removeAttachments(channel.getListeningPermission()); - bukkitPlayer.removeAttachments(matchListeningPermission); - - // Whenever the player leaves a party with its own channel, check if that is the player's current channel, - // and if it's not, then set their team chat setting to false. This is the only way to find out when they - // switch to global chat, because the Channels plugin doesn't provide any notifcation. - if(playerManager.getMembershipChannel(bukkitPlayer) != channel) { - teamChatters.put(bukkitPlayer, false); - } - } - } + @EventHandler(priority = EventPriority.LOWEST) + public void onCompetitorRemove(CompetitorRemoveEvent event) { + remove(event.getParty()); } + } diff --git a/PGM/src/main/java/tc/oc/pgm/channels/FilteredPartyChannel.java b/PGM/src/main/java/tc/oc/pgm/channels/FilteredPartyChannel.java deleted file mode 100644 index 4e04292..0000000 --- a/PGM/src/main/java/tc/oc/pgm/channels/FilteredPartyChannel.java +++ /dev/null @@ -1,21 +0,0 @@ -package tc.oc.pgm.channels; - -import com.google.common.base.Preconditions; -import org.bukkit.permissions.Permission; -import tc.oc.chatmoderator.channels.SimpleFilteredChannel; -import tc.oc.pgm.match.Party; - -public class FilteredPartyChannel extends SimpleFilteredChannel implements PartyChannel { - - private final Party party; - - public FilteredPartyChannel(String format, final Permission permission, final Party party, int minimumScoreNoSend, float partial) { - super(format, permission, minimumScoreNoSend, partial); - this.party = Preconditions.checkNotNull(party, "party"); - } - - @Override - public Party getParty() { - return this.party; - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/channels/PartyChannel.java b/PGM/src/main/java/tc/oc/pgm/channels/PartyChannel.java index 9474e8e..907f6d9 100644 --- a/PGM/src/main/java/tc/oc/pgm/channels/PartyChannel.java +++ b/PGM/src/main/java/tc/oc/pgm/channels/PartyChannel.java @@ -1,8 +1,69 @@ package tc.oc.pgm.channels; -import com.github.rmsy.channels.Channel; +import com.google.inject.assistedinject.Assisted; +import net.md_5.bungee.api.chat.BaseComponent; +import org.bukkit.command.CommandSender; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.bukkit.channels.SimpleChannel; +import tc.oc.commons.bukkit.chat.PlayerComponent; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.MultiAudience; +import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.Party; -public interface PartyChannel extends Channel { - Party getParty(); +import javax.inject.Inject; +import java.util.stream.Stream; + +public class PartyChannel extends SimpleChannel implements MultiAudience { + + public static final String RECEIVE_ALL_PERMISSION = "pgm.chat.receive.all"; + + public interface Factory { + PartyChannel create(Party party); + } + + private final Party party; + + @Inject PartyChannel(@Assisted Party party) { + this.party = party; + } + + public Party party() { + return party; + } + + @Override + public BaseComponent prefix() { + return party.getChatPrefix(); + } + + @Override + public BaseComponent format(PlayerComponent player, String message) { + return new Component(player).extra(": ").extra(message); + } + + @Override + public ChatDoc.Type type() { + return ChatDoc.Type.TEAM; + } + + @Override + public boolean sendable(CommandSender sender) { + return party.getPlayers().contains(party.getMatch().getPlayer(sender)); + } + + @Override + public boolean viewable(CommandSender sender) { + final MatchPlayer player = party.getMatch().getPlayer(sender); + return sendable(sender) || + player == null || + (sender.hasPermission(RECEIVE_ALL_PERMISSION) && player.isObserving()); + } + + @Override + public Stream audiences() { + return party.players(); + } + } diff --git a/PGM/src/main/java/tc/oc/pgm/channels/UnfilteredPartyChannel.java b/PGM/src/main/java/tc/oc/pgm/channels/UnfilteredPartyChannel.java deleted file mode 100644 index 19aba38..0000000 --- a/PGM/src/main/java/tc/oc/pgm/channels/UnfilteredPartyChannel.java +++ /dev/null @@ -1,21 +0,0 @@ -package tc.oc.pgm.channels; - -import com.github.rmsy.channels.impl.SimpleChannel; -import com.google.common.base.Preconditions; -import org.bukkit.permissions.Permission; -import tc.oc.pgm.match.Party; - -public class UnfilteredPartyChannel extends SimpleChannel implements PartyChannel { - - private Party party; - - public UnfilteredPartyChannel(String format, final Permission permission, Party party) { - super(format, permission); - this.party = Preconditions.checkNotNull(party, "party"); - } - - @Override - public Party getParty() { - return this.party; - } -} diff --git a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java index e820947..aee1fcd 100644 --- a/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/commands/MapCommands.java @@ -23,7 +23,7 @@ import org.bukkit.command.CommandSender; import tc.oc.api.docs.User; import tc.oc.api.docs.virtual.MapDoc; import tc.oc.api.util.Permissions; -import tc.oc.commons.bukkit.chat.BukkitAudiences; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.ComponentRenderContext; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; @@ -166,7 +166,7 @@ public class MapCommands implements Commands { if(args.getSuggestionContext() != null) { return CommandUtils.completeMapName(args.getJoinedStrings(0)); } - final Audience audience = BukkitAudiences.getAudience(sender); + final Audience audience = Audiences.Deprecated.get(sender); final PGMMap map; if(args.argsLength() > 0) { map = CommandUtils.getMap(args.getJoinedStrings(0), sender); diff --git a/PGM/src/main/java/tc/oc/pgm/ffa/Tribute.java b/PGM/src/main/java/tc/oc/pgm/ffa/Tribute.java index e0139d4..1523fcb 100644 --- a/PGM/src/main/java/tc/oc/pgm/ffa/Tribute.java +++ b/PGM/src/main/java/tc/oc/pgm/ffa/Tribute.java @@ -2,6 +2,7 @@ package tc.oc.pgm.ffa; import java.util.Collections; import java.util.Set; +import java.util.stream.Stream; import javax.annotation.Nullable; import javax.inject.Inject; @@ -223,8 +224,8 @@ public class Tribute implements Competitor { } @Override - public Audience audience() { - return player != null ? player : NullAudience.INSTANCE; + public Stream audiences() { + return player != null ? Stream.of(player) : Stream.empty(); } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/freeze/Freeze.java b/PGM/src/main/java/tc/oc/pgm/freeze/Freeze.java index 56364e6..546a837 100644 --- a/PGM/src/main/java/tc/oc/pgm/freeze/Freeze.java +++ b/PGM/src/main/java/tc/oc/pgm/freeze/Freeze.java @@ -14,15 +14,13 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Entity; import org.bukkit.entity.Player; import org.bukkit.entity.TNTPrimed; -import tc.oc.commons.bukkit.channels.AdminChannel; +import tc.oc.commons.bukkit.channels.admin.AdminChannel; import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.BukkitSound; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; -import tc.oc.commons.bukkit.commands.CommandUtils; import tc.oc.commons.bukkit.freeze.FrozenPlayer; import tc.oc.commons.bukkit.freeze.PlayerFreezer; -import tc.oc.commons.bukkit.nick.Identity; import tc.oc.commons.bukkit.nick.IdentityProvider; import tc.oc.commons.bukkit.util.OnlinePlayerMapAdapter; import tc.oc.commons.core.chat.Audience; @@ -81,20 +79,15 @@ public class Freeze implements PluginFacet { )); } - final Identity freezerIdentity = identityProvider.createIdentity(freezer); + final PlayerComponent freezerComponent = new PlayerComponent(identityProvider.createIdentity(freezer), NameStyle.VERBOSE); + final PlayerComponent freezeeComponent = new PlayerComponent(identityProvider.createIdentity(freezee), NameStyle.VERBOSE); final Audience freezeeAudience = audiences.get(freezee); final FrozenPlayer frozenPlayer = frozenPlayers.get(freezee); if(frozen && frozenPlayer == null) { frozenPlayers.put(freezee, playerFreezer.freeze(freezee)); - final BaseComponent freezeeMessage = new Component( - new TranslatableComponent( - "freeze.frozen", - new PlayerComponent(freezerIdentity, NameStyle.FANCY) - ), - ChatColor.RED - ); + final BaseComponent freezeeMessage = new Component(new TranslatableComponent("freeze.frozen", freezerComponent), ChatColor.RED); freezeeAudience.playSound(FREEZE_SOUND); freezeeAudience.sendWarning(freezeeMessage, false); @@ -106,26 +99,15 @@ public class Freeze implements PluginFacet { removeEntities(((Player) freezer).getLocation(), config.tntSenderRadius()); } - adminChannel.broadcast(CommandUtils.getDisplayName(freezer) + - ChatColor.RED + " froze " + - CommandUtils.getDisplayName(freezee)); + adminChannel.sendMessage(new TranslatableComponent("freeze.frozen.broadcast", freezeeComponent, freezerComponent)); } else if(!frozen && frozenPlayer != null) { frozenPlayer.thaw(); frozenPlayers.remove(freezee); freezeeAudience.hideTitle(); freezeeAudience.playSound(THAW_SOUND); - freezeeAudience.sendMessage(new Component( - new TranslatableComponent( - "freeze.unfrozen", - new PlayerComponent(freezerIdentity, NameStyle.FANCY) - ), - ChatColor.GREEN - )); - - adminChannel.broadcast(CommandUtils.getDisplayName(freezer) + - ChatColor.RED + " unfroze " + - CommandUtils.getDisplayName(freezee)); + freezeeAudience.sendMessage(new Component(new TranslatableComponent("freeze.unfrozen", freezerComponent), ChatColor.GREEN)); + adminChannel.sendMessage(new TranslatableComponent("freeze.unfrozen.broadcast", freezeeComponent, freezerComponent)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/goals/TouchableGoal.java b/PGM/src/main/java/tc/oc/pgm/goals/TouchableGoal.java index 6932ed5..226fc5c 100644 --- a/PGM/src/main/java/tc/oc/pgm/goals/TouchableGoal.java +++ b/PGM/src/main/java/tc/oc/pgm/goals/TouchableGoal.java @@ -197,11 +197,11 @@ public abstract class TouchableGoal extends P if(toucher != null) { if(includeToucher) { - toucher.getAudience().sendMessage(getTouchMessage(toucher, true)); + toucher.sendMessage(getTouchMessage(toucher, true)); } if(getDeferTouches()) { - toucher.getAudience().sendMessage(new TranslatableComponent("match.touch.destroyable.deferredNotice")); + toucher.sendMessage(new TranslatableComponent("match.touch.destroyable.deferredNotice")); } } } diff --git a/PGM/src/main/java/tc/oc/pgm/listeners/BlockTransformListener.java b/PGM/src/main/java/tc/oc/pgm/listeners/BlockTransformListener.java index 5acae64..5393c62 100644 --- a/PGM/src/main/java/tc/oc/pgm/listeners/BlockTransformListener.java +++ b/PGM/src/main/java/tc/oc/pgm/listeners/BlockTransformListener.java @@ -478,7 +478,7 @@ public class BlockTransformListener implements PluginFacet, Listener { event.getCancelMessage() != null && event.isManual()) { - ((PlayerBlockTransformEvent) event).getPlayerState().getAudience().sendWarning(event.getCancelMessage(), false); + ((PlayerBlockTransformEvent) event).getPlayerState().sendWarning(event.getCancelMessage(), false); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java b/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java index 28d0335..278c023 100644 --- a/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mapratings/MapRatingsMatchModule.java @@ -246,7 +246,7 @@ public class MapRatingsMatchModule extends MatchModule implements Listener { } if(oldScore != null && score == oldScore) { - player.sendWarning(PGMTranslations.t("rating.sameRating", player, score), true); + player.sendWarning(PGMTranslations.t("rating.sameRating", player, score), false); return; } diff --git a/PGM/src/main/java/tc/oc/pgm/match/Match.java b/PGM/src/main/java/tc/oc/pgm/match/Match.java index 22b9915..0b5e02d 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/Match.java +++ b/PGM/src/main/java/tc/oc/pgm/match/Match.java @@ -25,7 +25,7 @@ import java.time.Duration; import java.time.Instant; import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.UserId; -import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.MultiAudience; import tc.oc.commons.core.inject.InjectionScopable; import tc.oc.commons.core.random.Entropy; import tc.oc.commons.core.util.ArrayUtils; @@ -47,7 +47,7 @@ import tc.oc.pgm.module.ModuleLoadException; import tc.oc.pgm.time.TickClock; import tc.oc.pgm.time.TickTime; -public interface Match extends Audience, IMatchQuery, Filterable, MatchPlayerFinder, InjectionScopable { +public interface Match extends MultiAudience, IMatchQuery, Filterable, MatchPlayerFinder, InjectionScopable { /** * Unique ID for this match diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchAudiences.java b/PGM/src/main/java/tc/oc/pgm/match/MatchAudiences.java index ae90b5e..e666b24 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchAudiences.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchAudiences.java @@ -1,50 +1,46 @@ package tc.oc.pgm.match; -import javax.inject.Inject; - -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; -import tc.oc.commons.bukkit.chat.ConsoleAudience; -import tc.oc.commons.core.chat.AbstractMultiAudience; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.core.chat.Audience; import tc.oc.commons.core.chat.MultiAudience; +import tc.oc.minecraft.api.entity.Player; import tc.oc.pgm.filters.Filter; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Provides various aggregate {@link Audience}s within a match */ +@Singleton public class MatchAudiences { private final Match match; - private final Audience participants; - private final Audience observers; - private final ImmutableSet console; + private final Audiences audiences; - @Inject MatchAudiences(Match match, ConsoleAudience console) { + @Inject + MatchAudiences(Match match, Audiences audiences) { this.match = match; - this.console = ImmutableSet.of(console); - this.participants = new MultiAudience(Iterables.concat(this.console, match.getParticipatingPlayers())); - this.observers = new MultiAudience(Iterables.concat(this.console, match.getObservingPlayers())); + this.audiences = audiences; } - public Audience all() { - return match; + public MultiAudience all() { + return () -> Stream.of(audiences.console(), audiences.playerFilter(player -> match.player(player).isPresent())); } - public Audience participants() { - return participants; + public MultiAudience participants() { + return () -> Stream.of(audiences.console(), audiences.playerFilter(player -> match.participant(player).isPresent())); } - public Audience observers() { - return observers; + public MultiAudience observers() { + return () -> Stream.of(audiences.console(), audiences.playerFilter(player -> match.getObservingPlayers().contains(match.player(player).orElse(null)))); } - public Audience filter(Filter filter) { - return new AbstractMultiAudience() { - @Override - protected Iterable getAudiences() { - return Iterables.concat(console, Iterables.filter(match.getPlayers(), player -> !filter.denies(player))); - } - }; + public MultiAudience filter(Filter filter) { + final Set allowed = match.players().filter(player -> !filter.denies(player)).map(MatchPlayer::getBukkit).collect(Collectors.toSet()); + return () -> Stream.of(audiences.console(), audiences.playerFilter(allowed::contains)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchImpl.java b/PGM/src/main/java/tc/oc/pgm/match/MatchImpl.java index a3c11fa..bf85432 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchImpl.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchImpl.java @@ -24,13 +24,10 @@ import com.google.common.collect.BiMap; import com.google.common.collect.BoundType; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Iterables; import com.google.common.collect.Maps; import com.google.common.collect.Multimaps; import com.google.common.collect.Range; import com.google.common.collect.SetMultimap; -import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.Server; import org.bukkit.World; import org.bukkit.entity.Player; @@ -45,10 +42,8 @@ import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.User; import tc.oc.api.docs.UserId; import tc.oc.api.model.IdFactory; -import tc.oc.commons.bukkit.chat.ConsoleAudience; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.core.chat.Audience; -import tc.oc.commons.core.chat.ForwardingAudience; -import tc.oc.commons.core.chat.MultiAudience; import tc.oc.commons.core.exception.ExceptionHandler; import tc.oc.commons.core.inject.ChildInjectorFactory; import tc.oc.commons.core.inject.FacetContext; @@ -99,7 +94,7 @@ import tc.oc.pgm.utils.WorldTickRandom; import static com.google.common.base.Preconditions.*; -public class MatchImpl implements Match, ForwardingAudience { +public class MatchImpl implements Match { private Logger logger; @@ -121,8 +116,7 @@ public class MatchImpl implements Match, ForwardingAudience { @Inject private FeatureDefinitionContext featureDefinitions; @Inject private World world; - @Inject private ConsoleAudience consoleAudience; - private Audience audience; + @Inject private Audiences audiences; // State management private final AtomicBoolean unloaded = new AtomicBoolean(true); // true before loading starts and after unloading finishes @@ -194,7 +188,6 @@ public class MatchImpl implements Match, ForwardingAudience { id = idFactory.newId(); url = new URL("http", "localhost:3000", "/matches/" + id); loadTime = clock.now(); - audience = new MultiAudience(Iterables.concat(ImmutableSet.of(consoleAudience), getPlayers())); setState(MatchState.Idle); } @@ -265,8 +258,8 @@ public class MatchImpl implements Match, ForwardingAudience { } @Override - public Audience audience() { - return audience; + public Stream audiences() { + return Stream.of(audiences.console(), audiences.filter(sender -> player(sender).isPresent())); } @@ -927,21 +920,4 @@ public class MatchImpl implements Match, ForwardingAudience { partyChanges.remove(player); } } - - - // -------------- - // ---- Chat ---- - // -------------- - - @Override - public void sendMessageExcept(BaseComponent message, MatchPlayer... except) { - consoleAudience.sendMessage(message); - Match.super.sendMessageExcept(message, except); - } - - @Override - public void sendMessageExcept(BaseComponent message, MatchPlayerState... except) { - consoleAudience.sendMessage(message); - Match.super.sendMessageExcept(message, except); - } } diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayer.java b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayer.java index 08936db..2f32996 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayer.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayer.java @@ -32,7 +32,7 @@ import tc.oc.api.docs.PlayerId; import tc.oc.api.docs.Server; import tc.oc.api.docs.User; import tc.oc.commons.bukkit.attribute.AttributeUtils; -import tc.oc.commons.bukkit.chat.BukkitAudiences; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.chat.BukkitSound; import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.Named; @@ -77,6 +77,7 @@ public class MatchPlayer extends MatchFacetContext implements @Inject private Server localServer; @Inject private OnlineFriends friendMap; @Inject private PlayerStates playerStates; + @Inject private Audiences audiences; @Inject MatchUserContext userContext; @@ -90,7 +91,7 @@ public class MatchPlayer extends MatchFacetContext implements private Logger logger; private SettingManager settings; private Audience audience; - @Inject private void init(Loggers loggers, SettingManagerProvider settingManagerProvider, BukkitAudiences audiences, Player player) { + @Inject private void init(Loggers loggers, SettingManagerProvider settingManagerProvider, Audiences audiences, Player player) { this.logger = loggers.get(match.getLogger(), getClass(), getName()); this.settings = settingManagerProvider.getManager(player); this.audience = audiences.get(player); @@ -519,19 +520,19 @@ public class MatchPlayer extends MatchFacetContext implements } @Override - public Audience audience() { - return audience; + public Optional audience() { + return Optional.ofNullable(audiences.get(bukkit)); } @Override public void sendWarning(String message, boolean audible) { - audience().sendWarning(message, audible); + audience().get().sendWarning(message, audible); if(audible) playWarningSound(); } @Override public void sendWarning(BaseComponent message, boolean audible) { - audience().sendWarning(message, audible); + audience().get().sendWarning(message, audible); if(audible) playWarningSound(); } diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerFinder.java b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerFinder.java index 8ecde7f..978401f 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerFinder.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerFinder.java @@ -34,6 +34,10 @@ public interface MatchPlayerFinder { return state == null ? null : getPlayer(state.getUniqueId()); } + default Optional player(@Nullable tc.oc.minecraft.api.entity.Player api) { + return Optional.ofNullable(getPlayer((Player) api)); + } + default Optional player(@Nullable Player bukkit) { return Optional.ofNullable(getPlayer(bukkit)); } @@ -54,6 +58,10 @@ public interface MatchPlayerFinder { return Optional.ofNullable(getPlayer(userId)); } + default Optional participant(@Nullable tc.oc.minecraft.api.entity.Player api) { + return player(api).filter(MatchPlayer::isParticipating); + } + default Optional participant(@Nullable Entity entity) { return player(entity).filter(MatchPlayer::isParticipating); } diff --git a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerState.java b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerState.java index 3e5b08b..344031f 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerState.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MatchPlayerState.java @@ -12,6 +12,7 @@ import tc.oc.commons.bukkit.chat.NameStyle; import tc.oc.commons.bukkit.chat.PlayerComponent; import tc.oc.commons.bukkit.nick.Identity; import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.ForwardingAudience; import tc.oc.commons.core.chat.NullAudience; import tc.oc.commons.core.util.Utils; import tc.oc.pgm.filters.query.IPlayerQuery; @@ -21,7 +22,7 @@ import static com.google.common.base.Preconditions.checkNotNull; /** * Represents a "snapshot" view of a {@link MatchPlayer}. */ -public class MatchPlayerState extends MatchEntityState implements IPlayerQuery { +public class MatchPlayerState extends MatchEntityState implements IPlayerQuery, ForwardingAudience { protected final Identity identity; protected final Party party; @@ -88,9 +89,9 @@ public class MatchPlayerState extends MatchEntityState implements IPlayerQuery { return getParty().isParticipating(); } - public Audience getAudience() { - MatchPlayer matchPlayer = getMatchPlayer(); - return matchPlayer == null ? NullAudience.INSTANCE : matchPlayer; + @Override + public Optional audience() { + return Optional.ofNullable(getMatchPlayer()); } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/match/MultiPlayerParty.java b/PGM/src/main/java/tc/oc/pgm/match/MultiPlayerParty.java index a2a807e..5072b4e 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/MultiPlayerParty.java +++ b/PGM/src/main/java/tc/oc/pgm/match/MultiPlayerParty.java @@ -2,12 +2,12 @@ package tc.oc.pgm.match; import java.util.HashSet; import java.util.Set; +import java.util.stream.Stream; import javax.annotation.Nullable; import org.bukkit.entity.Player; import tc.oc.api.bukkit.users.Users; import tc.oc.api.docs.PlayerId; -import tc.oc.commons.core.chat.AbstractMultiAudience; import tc.oc.commons.core.chat.Audience; public abstract class MultiPlayerParty implements Party { @@ -15,13 +15,6 @@ public abstract class MultiPlayerParty implements Party { protected final Match match; private final Set players = new HashSet<>(); - private final Audience audience = new AbstractMultiAudience() { - @Override - protected Iterable getAudiences() { - return getPlayers(); - } - }; - public MultiPlayerParty(Match match) { this.match = match; } @@ -64,7 +57,7 @@ public abstract class MultiPlayerParty implements Party { } @Override - public Audience audience() { - return audience; + public Stream audiences() { + return players(); } } diff --git a/PGM/src/main/java/tc/oc/pgm/match/Party.java b/PGM/src/main/java/tc/oc/pgm/match/Party.java index 78ae871..0e7b4e1 100644 --- a/PGM/src/main/java/tc/oc/pgm/match/Party.java +++ b/PGM/src/main/java/tc/oc/pgm/match/Party.java @@ -11,6 +11,7 @@ import tc.oc.commons.core.chat.Audience; import tc.oc.commons.bukkit.chat.Named; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.chat.Components; +import tc.oc.commons.core.chat.MultiAudience; import tc.oc.pgm.filters.Filterable; import tc.oc.pgm.filters.query.IPartyQuery; @@ -19,7 +20,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -public interface Party extends Named, IPartyQuery, Filterable { +public interface Party extends Named, IPartyQuery, Filterable, MultiAudience { enum Type { Participating, Observing } @@ -156,8 +157,6 @@ public interface Party extends Named, IPartyQuery, Filterable { return isObservingType() || !getMatch().isRunning(); } - Audience audience(); - @Override default Optional> filterableParent() { return Optional.of(getMatch()); diff --git a/PGM/src/main/java/tc/oc/pgm/module/MatchModulesManifest.java b/PGM/src/main/java/tc/oc/pgm/module/MatchModulesManifest.java index e54ab7a..68759db 100644 --- a/PGM/src/main/java/tc/oc/pgm/module/MatchModulesManifest.java +++ b/PGM/src/main/java/tc/oc/pgm/module/MatchModulesManifest.java @@ -5,7 +5,6 @@ import tc.oc.pgm.api.EngagementMatchModule; import tc.oc.pgm.api.MatchPublishingMatchModule; import tc.oc.pgm.api.ParticipationPublishingMatchModule; import tc.oc.pgm.bossbar.BossBarMatchModule; -import tc.oc.pgm.channels.ChannelMatchModule; import tc.oc.pgm.cycle.CycleMatchModule; import tc.oc.pgm.damage.HitboxMatchModule; import tc.oc.pgm.death.DeathMessageMatchModule; @@ -52,7 +51,6 @@ public class MatchModulesManifest extends HybridManifest { install(new MatchModuleFixtureManifest(){}); install(new MatchModuleFixtureManifest(){}); install(new MatchModuleFixtureManifest(){}); - install(new MatchModuleFixtureManifest(){}); install(new MatchModuleFixtureManifest(){}); install(new MatchModuleFixtureManifest(){}); install(new MatchModuleFixtureManifest(){}); diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java b/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java index dda9913..6aabf3d 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/command/MutationCommands.java @@ -167,7 +167,7 @@ public class MutationCommands implements NestedCommands { } } Audience origin = audiences.get(sender); - Audience all = audiences.localServer(); + Audience all = audiences.all(); String message = message(!queued, value, mutations.size() == 1); ListComponent changed = new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA))); if(queued) { diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java index 6f13e63..cb01c66 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java @@ -195,7 +195,7 @@ public class EntityMutation extends KitMutation { @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST) public void onPlayerSpawnEntity(PlayerSpawnEntityEvent event) { - match().participant(event.getPlayer()) + match().participant((Entity) event.getPlayer()) .ifPresent(player -> cast(event.getEntity(), type) .ifPresent(entity -> { register(entity, player); diff --git a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java index 0de0532..d525bd1 100644 --- a/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java +++ b/PGM/src/main/java/tc/oc/pgm/rotation/DynamicRotationListener.java @@ -66,7 +66,7 @@ public class DynamicRotationListener implements PluginFacet, Listener { rotationManager.setCurrentRotationName(rotation.name); logger.info("Changing to \"" + rotation.name + "\" rotation..."); - sendRotationChangeMessage(audiences.localServer(), oldRotation, rotation.name); + sendRotationChangeMessage(audiences.all(), oldRotation, rotation.name); } } } diff --git a/PGM/src/main/java/tc/oc/pgm/start/HuddleCountdown.java b/PGM/src/main/java/tc/oc/pgm/start/HuddleCountdown.java index f33d4cf..7b2385a 100644 --- a/PGM/src/main/java/tc/oc/pgm/start/HuddleCountdown.java +++ b/PGM/src/main/java/tc/oc/pgm/start/HuddleCountdown.java @@ -13,6 +13,8 @@ import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.HandlerList; import org.bukkit.event.Listener; +import tc.oc.api.docs.virtual.ChatDoc; +import tc.oc.commons.bukkit.channels.ChannelChatEvent; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.formatting.PeriodFormats; import tc.oc.commons.core.util.Comparables; @@ -59,7 +61,7 @@ public class HuddleCountdown extends PreMatchCountdown implements Listener { if(Comparables.greaterThan(total, Duration.ZERO)) { getMatch().getCompetitors().stream().filter(competitor -> competitor instanceof Team).forEach(competitor -> { - competitor.audience().sendMessage(new Component( + competitor.sendMessage(new Component( new TranslatableComponent("huddle.instructions", PeriodFormats.briefNaturalPrecise(total)), ChatColor.YELLOW @@ -69,12 +71,12 @@ public class HuddleCountdown extends PreMatchCountdown implements Listener { } @EventHandler - public void onChat(ChannelMessageEvent event) { - if(event.getChannel() == ChannelsPlugin.get().getGlobalChannel()) { + public void onChat(ChannelChatEvent event) { + if(event.channel().type().equals(ChatDoc.Type.SERVER)) { event.setCancelled(true); - MatchPlayer player = getMatch().getPlayer(event.getSender()); + MatchPlayer player = getMatch().getPlayer(event.sender()); if(player != null) { - player.sendWarning(new TranslatableComponent("huddle.globalChatDisabled")); + player.sendWarning(new TranslatableComponent("huddle.globalChatDisabled"), false); } } } diff --git a/PGM/src/main/java/tc/oc/pgm/teams/TeamMatchModule.java b/PGM/src/main/java/tc/oc/pgm/teams/TeamMatchModule.java index 51b13e9..6481d47 100644 --- a/PGM/src/main/java/tc/oc/pgm/teams/TeamMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/teams/TeamMatchModule.java @@ -477,7 +477,7 @@ public class TeamMatchModule extends MatchModule implements Listener, JoinHandle MatchPlayer player = shortList.get(i); if(even && areTeamsEven() && shortList.size() - i < getTeams().size()) { // Prevent join if even teams are required, and there aren't enough remaining players to go around - player.sendWarning(new TranslatableComponent("command.gameplay.join.uneven")); + player.sendWarning(new TranslatableComponent("command.gameplay.join.uneven"), false); } else { join(player, request, queryJoin(player, request, true)); } diff --git a/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java b/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java index 0ef5cbb..e55ae9d 100644 --- a/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/wool/WoolMatchModule.java @@ -168,9 +168,9 @@ public class WoolMatchModule extends MatchModule implements Listener { if(player != null) { // wool can only be placed by a player BaseComponent woolName = BukkitUtils.woolName(wool.getDyeColor()); if(!isValidWool(wool.getDyeColor(), event.getNewState())) { - player.getAudience().sendWarning(new TranslatableComponent("match.wool.placeWrong", woolName), true); + player.sendWarning(new TranslatableComponent("match.wool.placeWrong", woolName), true); } else if(wool.getOwner() != player.getParty()) { - player.getAudience().sendWarning(new TranslatableComponent("match.wool.placeOther", wool.getOwner().getComponentName(), woolName), true); + player.sendWarning(new TranslatableComponent("match.wool.placeOther", wool.getOwner().getComponentName(), woolName), true); } else { event.setCancelled(false); wool.markPlaced(); diff --git a/PGM/src/main/resources/plugin.yml b/PGM/src/main/resources/plugin.yml index 60fb7b3..2d72562 100644 --- a/PGM/src/main/resources/plugin.yml +++ b/PGM/src/main/resources/plugin.yml @@ -6,9 +6,6 @@ depend: - Commons - API - BukkitSettings -- Channels -softdepend: -- ChatModerator authors: - Overcast Network diff --git a/Tourney/src/net/anxuiz/tourney/Tourney.java b/Tourney/src/net/anxuiz/tourney/Tourney.java index 6ba2a02..603ffee 100644 --- a/Tourney/src/net/anxuiz/tourney/Tourney.java +++ b/Tourney/src/net/anxuiz/tourney/Tourney.java @@ -76,7 +76,7 @@ public class Tourney extends JavaPlugin { ); audiences.get(Bukkit.getConsoleSender()).sendMessage(message); - audiences.withPermission(TourneyPermissions.REFEREE).sendMessage(message); + audiences.permission(TourneyPermissions.REFEREE).sendMessage(message); } } ); diff --git a/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java b/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java index 2ee9030..7bbb319 100644 --- a/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java +++ b/Tourney/src/net/anxuiz/tourney/listener/TeamListener.java @@ -4,7 +4,6 @@ import javax.inject.Inject; import javax.inject.Provider; import javax.inject.Singleton; -import com.github.rmsy.channels.ChannelsPlugin; import net.anxuiz.tourney.MatchManager; import net.anxuiz.tourney.TeamManager; import net.anxuiz.tourney.Tourney; @@ -137,10 +136,6 @@ public class TeamListener implements Listener { Team team = teamManagerProvider.get().getTeam(player); if(team != null) { event.getMatch().setPlayerParty(event.getPlayer(), team, false); - ChannelsPlugin.get().getPlayerManager().setMembershipChannel( - player, - event.getMatch().needMatchModule(ChannelMatchModule.class).getChannel(team) - ); } } } diff --git a/Tourney/src/net/anxuiz/tourney/vote/VoteContext.java b/Tourney/src/net/anxuiz/tourney/vote/VoteContext.java index 128287d..db70a76 100644 --- a/Tourney/src/net/anxuiz/tourney/vote/VoteContext.java +++ b/Tourney/src/net/anxuiz/tourney/vote/VoteContext.java @@ -139,7 +139,7 @@ public class VoteContext { // broadcast to team if (classificationSelected) { for (Entrant participation : vote.getParticipatingTeams()) { - final Audience team = teamManager.entrantToTeam(participation).audience(); + final Audience team = teamManager.entrantToTeam(participation); team.sendMessage(StringUtils.dashedChatMessage(ChatColor.GRAY + " Veto Information", "-", ChatColor.RED + "" + ChatColor.STRIKETHROUGH)); team.sendMessage(ChatColor.GRAY + "Your team may now veto another map."); team.sendMessage(ChatColor.GRAY + "Note that any team member may veto. Please consult with your teammates and choose wisely."); @@ -149,7 +149,7 @@ public class VoteContext { } } else { for (Entrant participation : vote.getParticipatingTeams()) { - final Audience team = teamManager.entrantToTeam(participation).audience(); + final Audience team = teamManager.entrantToTeam(participation); team.sendMessage(StringUtils.dashedChatMessage(ChatColor.GRAY + " Veto Information", "-", ChatColor.RED + "" + ChatColor.STRIKETHROUGH)); team.sendMessage(ChatColor.GRAY + "Your team may now veto another classification."); team.sendMessage(ChatColor.GRAY + "Note that any team member may veto. Please consult with your teammates and choose wisely."); diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Audiences.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Audiences.java index e6c7483..9805475 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Audiences.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/Audiences.java @@ -1,6 +1,187 @@ package tc.oc.commons.bukkit.chat; +import net.md_5.bungee.api.ChatMessageType; +import net.md_5.bungee.api.chat.BaseComponent; import org.bukkit.command.CommandSender; +import org.bukkit.command.ConsoleCommandSender; +import tc.oc.commons.core.chat.AbstractAudiences; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; +import tc.oc.commons.core.chat.ConsoleAudience; +import tc.oc.commons.core.chat.NullAudience; +import tc.oc.commons.core.chat.Sound; +import tc.oc.commons.core.stream.Collectors; + +import javax.annotation.Nullable; +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.Optional; +import java.util.stream.Stream; + +import static tc.oc.minecraft.protocol.MinecraftVersion.lessThan; +import static tc.oc.minecraft.protocol.MinecraftVersion.MINECRAFT_1_8; + +@Singleton +public class Audiences extends AbstractAudiences { + + @Inject + ComponentRenderContext renderContext; + + @Override + public Audience get(@Nullable CommandSender sender) { + if(sender == null) { + return NullAudience.INSTANCE; + } if(sender instanceof org.bukkit.entity.Player) { + org.bukkit.entity.Player player = (org.bukkit.entity.Player) sender; + if(lessThan(MINECRAFT_1_8, player.getProtocolVersion())) { + return new LegacyPlayer((org.bukkit.entity.Player) sender); + } else { + return new Player((org.bukkit.entity.Player) sender); + } + } else if(sender instanceof ConsoleCommandSender) { + return new Console(sender.getServer().getConsoleSender()); + } else { + return new Sender(sender); + } + } + + abstract class Base implements Audience { + + private final T sender; + + protected Base(T sender) { + this.sender = sender; + } + + protected T sender() { + return sender; + } + + @Override + public void sendMessage(BaseComponent message) { + sender().sendMessage(render(message)); + } + + @Override + public void sendWarning(BaseComponent message, boolean audible) { + sendMessage(new WarningComponent(message)); + } + + protected BaseComponent render(@Nullable BaseComponent component) { + return component == null ? new Component() : renderContext.render(component, sender()); + } + + } + + class Sender extends Base { + + public Sender(CommandSender sender) { + super(sender); + } + + @Override + public void playSound(Sound sound) {} + + @Override + public void stopSound(Sound sound) {} + + @Override + public void sendHotbarMessage(BaseComponent message) { + sendMessage(message); + } + + @Override + public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + Optional.ofNullable(title).ifPresent(this::sendMessage); + Optional.ofNullable(subtitle).ifPresent(this::sendMessage); + } + + @Override + public void hideTitle() {} + + } + + class Player extends Base { + + public Player(org.bukkit.entity.Player player) { + super(player); + } + + @Override + public void sendHotbarMessage(BaseComponent message) { + sender().sendMessage(ChatMessageType.ACTION_BAR, message); + } + + @Override + public void hideTitle() { + sender().hideTitle(); + } + + @Override + public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + sender().showTitle(render(title), render(subtitle), inTicks, stayTicks, outTicks); + } + + @Override + public void playSound(Sound sound) { + sender().playSound(sender().getLocation(), sound.name(), sound.volume(), sound.pitch()); + } + + @Override + public void stopSound(Sound sound) { + sender().stopSound(sound.name()); + } + + } + + class LegacyPlayer extends Player { + + private BaseComponent recentHotbarMessage; + + public LegacyPlayer(org.bukkit.entity.Player player) { + super(player); + } + + protected void emphasize(BaseComponent message) { + sendMessage(Components.blank()); + sendMessage(message); + sendMessage(Components.blank()); + } + + @Override + public void sendHotbarMessage(BaseComponent message) { + // Do not spam hot bar messages, as the protocol converts + // them to regular chat messages. + if(!Components.equals(message, recentHotbarMessage)) { + emphasize(message); + recentHotbarMessage = message; + } + } + + @Override + public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + emphasize(Components.join(Components.space(), Stream.of(title, subtitle).filter(msg -> msg != null).collect(Collectors.toImmutableList()))); + } + } + + class Console extends Base implements ConsoleAudience { + + public Console(ConsoleCommandSender sender) { + super(sender); + } + + } + + public static class Deprecated { + + @Inject static Audiences audiences; + + @java.lang.Deprecated + public static Audience get(@Nullable CommandSender sender) { + return audiences.get(sender); + } + + } -public interface Audiences extends tc.oc.commons.core.chat.Audiences { } diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/BukkitAudiences.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/BukkitAudiences.java deleted file mode 100644 index a73918e..0000000 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/BukkitAudiences.java +++ /dev/null @@ -1,40 +0,0 @@ -package tc.oc.commons.bukkit.chat; - -import javax.inject.Singleton; - -import org.bukkit.command.CommandSender; -import org.bukkit.command.ConsoleCommandSender; -import org.bukkit.entity.Player; -import tc.oc.commons.core.chat.Audience; -import tc.oc.commons.core.chat.MinecraftAudiences; -import tc.oc.commons.core.chat.NullAudience; - -import static tc.oc.minecraft.protocol.MinecraftVersion.lessThan; -import static tc.oc.minecraft.protocol.MinecraftVersion.MINECRAFT_1_8; - -@Singleton -public class BukkitAudiences extends MinecraftAudiences implements Audiences { - - @Deprecated - public static Audience getAudience(CommandSender sender) { - if(sender == null) { - return NullAudience.INSTANCE; - } if(sender instanceof Player) { - Player player = (Player) sender; - if(lessThan(MINECRAFT_1_8, player.getProtocolVersion())) { - return new LegacyPlayerAudience(player); - } else { - return new PlayerAudience(player); - } - } else if(sender instanceof ConsoleCommandSender) { - return new ConsoleAudience(sender.getServer()); - } else { - return new CommandSenderAudience(sender); - } - } - - @Override - public Audience get(CommandSender viewer) { - return getAudience(viewer); - } -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/CommandSenderAudience.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/CommandSenderAudience.java deleted file mode 100644 index 9334445..0000000 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/CommandSenderAudience.java +++ /dev/null @@ -1,50 +0,0 @@ -package tc.oc.commons.bukkit.chat; - -import javax.annotation.Nullable; - -import net.md_5.bungee.api.chat.BaseComponent; -import org.bukkit.command.CommandSender; -import tc.oc.commons.core.chat.AbstractAudience; -import tc.oc.commons.core.chat.Sound; - -public class CommandSenderAudience extends AbstractAudience { - - protected final CommandSender sender; - - public CommandSenderAudience(CommandSender sender) { - this.sender = sender; - } - - protected CommandSender getCommandSender() { - return sender; - } - - @Override - public void sendMessage(String message) { - getCommandSender().sendMessage(message); - } - - @Override - public void sendMessage(BaseComponent message) { - ComponentRenderers.send(getCommandSender(), message); - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - sendMessage(message); - } - - @Override - public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - if(title != null) sendMessage(title); - if(subtitle != null) sendMessage(subtitle); - } - - @Override - public void hideTitle() { - } - - @Override - public void playSound(Sound sound) { - } -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ConsoleAudience.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ConsoleAudience.java deleted file mode 100644 index 882973b..0000000 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/ConsoleAudience.java +++ /dev/null @@ -1,26 +0,0 @@ -package tc.oc.commons.bukkit.chat; - -import javax.inject.Inject; - -import net.md_5.bungee.api.chat.BaseComponent; -import org.bukkit.Server; -import tc.oc.commons.core.chat.AbstractConsoleAudience; - -public class ConsoleAudience extends AbstractConsoleAudience { - - private final Server server; - - @Inject public ConsoleAudience(Server server) { - this.server = server; - } - - @Override - public void sendMessage(BaseComponent message) { - ComponentRenderers.send(server.getConsoleSender(), message); - } - - @Override - public void sendMessage(String message) { - server.getConsoleSender().sendMessage(message); - } -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/LegacyPlayerAudience.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/LegacyPlayerAudience.java deleted file mode 100644 index 0a2166c..0000000 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/LegacyPlayerAudience.java +++ /dev/null @@ -1,39 +0,0 @@ -package tc.oc.commons.bukkit.chat; - -import net.md_5.bungee.api.chat.BaseComponent; -import org.bukkit.entity.Player; -import tc.oc.commons.core.chat.Components; -import tc.oc.commons.core.stream.Collectors; - -import java.util.stream.Stream; - -public class LegacyPlayerAudience extends PlayerAudience { - - private BaseComponent recentHotbarMessage; - - public LegacyPlayerAudience(Player player) { - super(player); - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - // Do not spam hot bar messages, as the protocol converts - // them to regular chat messages. - if(!Components.equals(message, recentHotbarMessage)) { - emphasize(message); - recentHotbarMessage = message; - } - } - - @Override - public void showTitle(BaseComponent title, BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - emphasize(Components.join(Components.space(), Stream.of(title, subtitle).filter(msg -> msg != null).collect(Collectors.toImmutableList()))); - } - - protected void emphasize(BaseComponent message) { - sendMessage(Components.blank()); - sendMessage(message); - sendMessage(Components.blank()); - } - -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/PlayerAudience.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/PlayerAudience.java deleted file mode 100644 index 2db5a90..0000000 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/chat/PlayerAudience.java +++ /dev/null @@ -1,40 +0,0 @@ -package tc.oc.commons.bukkit.chat; - -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.chat.BaseComponent; -import org.bukkit.entity.Player; -import tc.oc.commons.core.chat.Component; -import tc.oc.commons.core.chat.Sound; - -public class PlayerAudience extends CommandSenderAudience { - - public PlayerAudience(Player player) { - super(player); - } - - protected Player getPlayer() { - return (Player) getCommandSender(); - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - getPlayer().sendMessage(ChatMessageType.ACTION_BAR, ComponentRenderers.render(message, getPlayer())); - } - - @Override - public void showTitle(BaseComponent title, BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - title = title == null ? new Component("") : ComponentRenderers.render(title, getPlayer()); - subtitle = subtitle == null ? new Component("") : ComponentRenderers.render(subtitle, getPlayer()); - getPlayer().showTitle(title, subtitle, inTicks, stayTicks, outTicks); - } - - @Override - public void hideTitle() { - getPlayer().hideTitle(); - } - - @Override - public void playSound(Sound sound) { - getPlayer().playSound(getPlayer().getLocation(), sound.name(), sound.volume(), sound.pitch()); - } -} diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/inject/BukkitServerManifest.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/inject/BukkitServerManifest.java index 67ecd6d..62ec513 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/inject/BukkitServerManifest.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/inject/BukkitServerManifest.java @@ -10,7 +10,7 @@ import com.google.inject.assistedinject.FactoryModuleBuilder; import org.bukkit.plugin.Plugin; import tc.oc.commons.bukkit.bossbar.BossBarFactory; import tc.oc.commons.bukkit.bossbar.BossBarFactoryImpl; -import tc.oc.commons.bukkit.chat.BukkitAudiences; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.item.RenderedItemBuilder; import tc.oc.commons.bukkit.logging.BukkitLoggerFactory; import tc.oc.commons.bukkit.permissions.BukkitPermissionRegistry; @@ -31,8 +31,8 @@ public class BukkitServerManifest extends SingletonManifest { bind(new TypeLiteral>(){}).to(BukkitPluginResolver.class); bind(Loggers.class).to(BukkitLoggerFactory.class); - bind(tc.oc.commons.core.chat.Audiences.class).to(tc.oc.commons.bukkit.chat.Audiences.class); - bind(tc.oc.commons.bukkit.chat.Audiences.class).to(BukkitAudiences.class); + bind(tc.oc.commons.core.chat.Audiences.class).to(Audiences.class); + requestStaticInjection(Audiences.Deprecated.class); bind(PermissionRegistry.class).to(BukkitPermissionRegistry.class); bind(BossBarFactory.class).to(BossBarFactoryImpl.class); } diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/listeners/PlayerMovementListener.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/listeners/PlayerMovementListener.java index 29ff1a3..991b58b 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/listeners/PlayerMovementListener.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/listeners/PlayerMovementListener.java @@ -24,7 +24,7 @@ import org.bukkit.event.player.PlayerTeleportEvent; import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; import org.bukkit.util.RayBlockIntersection; import org.bukkit.util.Vector; -import tc.oc.commons.bukkit.chat.BukkitAudiences; +import tc.oc.commons.bukkit.chat.Audiences; import tc.oc.commons.bukkit.event.BlockPunchEvent; import tc.oc.commons.bukkit.event.BlockTrampleEvent; import tc.oc.commons.bukkit.event.CoarsePlayerMoveEvent; @@ -42,6 +42,7 @@ import tc.oc.commons.core.plugin.PluginFacet; public class PlayerMovementListener implements PluginFacet, Listener { protected final EventBus eventBus; + protected final Audiences audiences; // The last location of a player that has been used to generate // coarse movement events. If a player is not in this list, then @@ -49,8 +50,9 @@ public class PlayerMovementListener implements PluginFacet, Listener { // on its own. private final Map lastToLocation = new WeakHashMap<>(); - @Inject PlayerMovementListener(EventBus eventBus) { + @Inject PlayerMovementListener(EventBus eventBus, Audiences audiences) { this.eventBus = eventBus; + this.audiences = audiences; } /** @@ -257,7 +259,7 @@ public class PlayerMovementListener implements PluginFacet, Listener { @EventHandler(priority = EventPriority.MONITOR) public void processCancelMessage(final CoarsePlayerMoveEvent event) { if(event.isCancelled() && event.getCancelMessage() != null) { - BukkitAudiences.getAudience(event.getPlayer()).sendWarning(event.getCancelMessage(), false); + audiences.get(event.getPlayer()).sendWarning(event.getCancelMessage(), false); } } } diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/BukkitPermissionRegistry.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/BukkitPermissionRegistry.java index 0e3216e..f56402d 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/BukkitPermissionRegistry.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/BukkitPermissionRegistry.java @@ -14,12 +14,12 @@ public class BukkitPermissionRegistry implements PermissionRegistry { } @Override - public void addPermission(Permission permission) { + public void register(Permission permission) { pluginManager.addPermission(permission); } @Override - public void removePermission(Permission permission) { + public void unregister(Permission permission) { pluginManager.removePermission(permission); } } diff --git a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/PermissionRegistry.java b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/PermissionRegistry.java index 9201b5e..9d19c2b 100644 --- a/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/PermissionRegistry.java +++ b/Util/bukkit/src/main/java/tc/oc/commons/bukkit/permissions/PermissionRegistry.java @@ -1,11 +1,36 @@ package tc.oc.commons.bukkit.permissions; import org.bukkit.permissions.Permission; +import org.bukkit.permissions.PermissionDefault; /** * Abstraction layer for Bukkit permission API */ public interface PermissionRegistry { - void addPermission(Permission perm); - void removePermission(Permission perm); + + void register(Permission perm); + + void unregister(Permission perm); + + default Permission create(String name, PermissionDefault def) { + Permission permission = new Permission(name, def); + register(permission); + return permission; + } + + default Permission create(String name) { + return create(name, Permission.DEFAULT_PERMISSION); + } + + default Permission createWithDefaultOp(String name) { + return create(name, PermissionDefault.OP); + } + + default Permission createWithDefaultFalse(String name) { + return create(name, PermissionDefault.FALSE); + } + + default Permission createWithDefaultTrue(String name) { + return create(name, PermissionDefault.TRUE); + } } diff --git a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/Audiences.java b/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/Audiences.java index d805d6b..a768e12 100644 --- a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/Audiences.java +++ b/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/Audiences.java @@ -1,6 +1,90 @@ package tc.oc.commons.bungee.chat; +import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.CommandSender; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import tc.oc.commons.core.chat.AbstractAudiences; +import tc.oc.commons.core.chat.Audience; +import tc.oc.commons.core.chat.ConsoleAudience; +import tc.oc.commons.core.chat.Sound; + +import javax.annotation.Nullable; +import javax.inject.Singleton; + +@Singleton +public class Audiences extends AbstractAudiences { + + @Override + public Audience get(CommandSender sender) { + if(sender instanceof ProxiedPlayer) { + return new Player((ProxiedPlayer) sender); + } else { + return new Console(); + } + } + + abstract class Base implements Audience { + + private final T sender; + + protected Base(T sender) { + this.sender = sender; + } + + protected T sender() { + return sender; + } + + @Override + public void sendMessage(BaseComponent message) { + sender().sendMessage(message); + } + + @Override + public void sendWarning(BaseComponent message, boolean audible) { + sendMessage(message); + } + + } + + class Player extends Base { + + public Player(ProxiedPlayer proxiedPlayer) { + super(proxiedPlayer); + } + + @Override + public void playSound(Sound sound) {} + + @Override + public void stopSound(Sound sound) {} + + @Override + public void sendHotbarMessage(BaseComponent message) { + sender().sendMessage(ChatMessageType.ACTION_BAR, message); + } + + @Override + public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + sender().sendTitle(ProxyServer.getInstance().createTitle().title(title).subTitle(subtitle).fadeIn(inTicks).stay(stayTicks).fadeOut(outTicks)); + } + + @Override + public void hideTitle() { + sender().sendTitle(ProxyServer.getInstance().createTitle().clear()); + } + + } + + class Console implements ConsoleAudience { + + @Override + public void sendMessage(BaseComponent message) { + ProxyServer.getInstance().getConsoleSender().sendMessage(message); + } + + } -public interface Audiences extends tc.oc.commons.core.chat.Audiences { } diff --git a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/BungeeAudiences.java b/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/BungeeAudiences.java deleted file mode 100644 index 0e19a60..0000000 --- a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/BungeeAudiences.java +++ /dev/null @@ -1,20 +0,0 @@ -package tc.oc.commons.bungee.chat; - -import javax.inject.Singleton; - -import net.md_5.bungee.api.CommandSender; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import tc.oc.commons.core.chat.Audience; -import tc.oc.commons.core.chat.MinecraftAudiences; - -@Singleton -public class BungeeAudiences extends MinecraftAudiences implements Audiences { - @Override - public Audience get(CommandSender sender) { - if(sender instanceof ProxiedPlayer) { - return new PlayerAudience((ProxiedPlayer) sender); - } else { - return new ConsoleAudience(); - } - } -} diff --git a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/ConsoleAudience.java b/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/ConsoleAudience.java deleted file mode 100644 index e4ade24..0000000 --- a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/ConsoleAudience.java +++ /dev/null @@ -1,18 +0,0 @@ -package tc.oc.commons.bungee.chat; - -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.BaseComponent; -import tc.oc.commons.core.chat.AbstractConsoleAudience; - -public class ConsoleAudience extends AbstractConsoleAudience { - - @Override - public void sendMessage(BaseComponent message) { - ProxyServer.getInstance().getConsole().sendMessage(message); - } - - @Override - public void sendMessage(String message) { - ProxyServer.getInstance().getConsole().sendMessage(message); - } -} diff --git a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/PlayerAudience.java b/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/PlayerAudience.java deleted file mode 100644 index 954ce1a..0000000 --- a/Util/bungee/src/main/java/tc/oc/commons/bungee/chat/PlayerAudience.java +++ /dev/null @@ -1,49 +0,0 @@ -package tc.oc.commons.bungee.chat; - -import javax.annotation.Nullable; - -import net.md_5.bungee.api.ChatMessageType; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import tc.oc.commons.core.chat.AbstractAudience; -import tc.oc.commons.core.chat.Sound; - -public class PlayerAudience extends AbstractAudience { - - private final ProxiedPlayer player; - - public PlayerAudience(ProxiedPlayer player) { - this.player = player; - } - - @Override - public void sendMessage(BaseComponent message) { - player.sendMessage(message); - } - - @Override - public void playSound(Sound sound) { - // Possible, but not worth the trouble - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - player.sendMessage(ChatMessageType.ACTION_BAR, message); - } - - @Override - public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - player.sendTitle(ProxyServer.getInstance().createTitle().title(title).subTitle(subtitle).fadeIn(inTicks).stay(stayTicks).fadeOut(outTicks)); - } - - @Override - public void hideTitle() { - player.sendTitle(ProxyServer.getInstance().createTitle().clear()); - } - - @Override - public void sendMessage(String message) { - player.sendMessage(message); - } -} diff --git a/Util/bungee/src/main/java/tc/oc/commons/bungee/inject/BungeeServerManifest.java b/Util/bungee/src/main/java/tc/oc/commons/bungee/inject/BungeeServerManifest.java index 73dfbef..e02ac05 100644 --- a/Util/bungee/src/main/java/tc/oc/commons/bungee/inject/BungeeServerManifest.java +++ b/Util/bungee/src/main/java/tc/oc/commons/bungee/inject/BungeeServerManifest.java @@ -2,7 +2,7 @@ package tc.oc.commons.bungee.inject; import com.google.inject.TypeLiteral; import net.md_5.bungee.api.plugin.Plugin; -import tc.oc.commons.bungee.chat.BungeeAudiences; +import tc.oc.commons.bungee.chat.Audiences; import tc.oc.commons.bungee.logging.BungeeLoggerFactory; import tc.oc.commons.bungee.plugin.BungeePluginResolver; import tc.oc.commons.core.inject.SingletonManifest; @@ -17,7 +17,6 @@ public class BungeeServerManifest extends SingletonManifest { bind(new TypeLiteral>(){}).to(BungeePluginResolver.class); bind(Loggers.class).to(BungeeLoggerFactory.class); - bind(tc.oc.commons.core.chat.Audiences.class).to(tc.oc.commons.bungee.chat.Audiences.class); - bind(tc.oc.commons.bungee.chat.Audiences.class).to(BungeeAudiences.class); + bind(tc.oc.commons.core.chat.Audiences.class).to(Audiences.class); } } diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudience.java deleted file mode 100644 index 9608f57..0000000 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudience.java +++ /dev/null @@ -1,17 +0,0 @@ -package tc.oc.commons.core.chat; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; - -public abstract class AbstractAudience implements Audience { - - @Override - public void sendWarning(BaseComponent message, boolean audible) { - sendMessage(new Component(ChatColor.RED).extra(new Component(" \u26a0 ", ChatColor.YELLOW), message)); - } - - @Override - public void sendWarning(String message, boolean audible) { - sendMessage(ChatColor.YELLOW + " \u26a0 " + ChatColor.RED + message); - } -} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudiences.java b/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudiences.java new file mode 100644 index 0000000..238d527 --- /dev/null +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractAudiences.java @@ -0,0 +1,35 @@ +package tc.oc.commons.core.chat; + +import com.google.common.collect.ImmutableList; +import tc.oc.minecraft.api.command.CommandSender; +import tc.oc.minecraft.api.server.LocalServer; + +import javax.inject.Inject; +import java.util.function.Predicate; +import java.util.stream.Stream; + +public abstract class AbstractAudiences implements Audiences { + + @Inject + LocalServer localServer; + + @Override + public MultiAudience all() { + return () -> senders().map(sender -> get((C) sender)); + } + + @Override + public MultiAudience filter(Predicate condition) { + return () -> senders().filter(sender -> condition.test((C) sender)).map(sender -> get((C) sender)); + } + + @Override + public Audience console() { + return get((C) localServer.getConsoleSender()); + } + + private Stream senders() { + return ImmutableList.builder().add(localServer.getConsoleSender()).addAll(localServer.getOnlinePlayers()).build().stream(); + } + +} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractConsoleAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractConsoleAudience.java deleted file mode 100644 index cd0b095..0000000 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractConsoleAudience.java +++ /dev/null @@ -1,25 +0,0 @@ -package tc.oc.commons.core.chat; - -import javax.annotation.Nullable; - -import net.md_5.bungee.api.chat.BaseComponent; - -public abstract class AbstractConsoleAudience extends AbstractAudience { - - @Override - public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - if(title != null) sendMessage(new Component("[Title] ").extra(title)); - if(subtitle != null) sendMessage(new Component("[Subtitle] ").extra(subtitle)); - } - - @Override - public void hideTitle() {} - - @Override - public void sendHotbarMessage(BaseComponent message) { - sendMessage(new Component("[Hotbar] ").extra(message)); - } - - @Override - public void playSound(Sound sound) {} -} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractMultiAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractMultiAudience.java deleted file mode 100644 index 17a212d..0000000 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/AbstractMultiAudience.java +++ /dev/null @@ -1,50 +0,0 @@ -package tc.oc.commons.core.chat; - -import javax.annotation.Nullable; - -import net.md_5.bungee.api.chat.BaseComponent; - -public abstract class AbstractMultiAudience implements Audience { - - protected abstract Iterable getAudiences(); - - @Override - public void sendMessage(BaseComponent message) { - for(Audience a : getAudiences()) a.sendMessage(message); - } - - @Override - public void sendWarning(BaseComponent message, boolean audible) { - for(Audience a : getAudiences()) a.sendWarning(message, audible); - } - - @Override - public void playSound(Sound sound) { - for(Audience a : getAudiences()) a.playSound(sound); - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - for(Audience a : getAudiences()) a.sendHotbarMessage(message); - } - - @Override - public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - for(Audience a : getAudiences()) a.showTitle(title, subtitle, inTicks, stayTicks, outTicks); - } - - @Override - public void hideTitle() { - for(Audience a : getAudiences()) a.hideTitle(); - } - - @Override - public void sendMessage(String message) { - for(Audience a : getAudiences()) a.sendMessage(message); - } - - @Override - public void sendWarning(String message, boolean audible) { - for(Audience a : getAudiences()) a.sendWarning(message, audible); - } -} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/Audience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/Audience.java index 9dcabc3..c2d93b7 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/Audience.java +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/Audience.java @@ -1,55 +1,49 @@ package tc.oc.commons.core.chat; -import java.util.stream.Stream; -import javax.annotation.Nullable; - import net.md_5.bungee.api.chat.BaseComponent; +import javax.annotation.Nullable; +import java.util.stream.Stream; + /** * Receiver of chat messages, sounds, titles, and other informational media. - * Can represent any number of actual recipients. */ public interface Audience { - /** Send a message to chat */ void sendMessage(BaseComponent message); default void sendMessages(BaseComponent... lines) { - for(BaseComponent line : lines) { - sendMessage(line); - } + Stream.of(lines).forEachOrdered(this::sendMessage); } default void sendMessages(Iterable lines) { - for(BaseComponent line : lines) { - sendMessage(line); - } - } - - default void sendMessages(Stream lines) { lines.forEach(this::sendMessage); } - /** Send a message to chat styled as a warning, with an optional audio cue */ + default void sendMessages(Stream lines) { + lines.forEachOrdered(this::sendMessage); + } + void sendWarning(BaseComponent message, boolean audible); - /** Play a sound (by raw asset name) */ void playSound(Sound sound); - /** Send a message to the display slot above the hotbar */ + void stopSound(Sound sound); + void sendHotbarMessage(BaseComponent message); - /** Show a title and/or subtitle */ void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks); - /** Hide any titles that are currently showing */ void hideTitle(); - /** Use {@link #sendMessage(BaseComponent) */ @Deprecated - void sendMessage(String message); + default void sendMessage(String message) { + sendMessage(Component.fromLegacyToComponent(message, true)); + } - /** Use {@link #sendWarning(BaseComponent, boolean) */ @Deprecated - void sendWarning(String message, boolean audible); + default void sendWarning(String message, boolean audible) { + sendWarning(Component.fromLegacyToComponent(message, true), audible); + } + } diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/Audiences.java b/Util/core/src/main/java/tc/oc/commons/core/chat/Audiences.java index 64dc541..d58ab7b 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/Audiences.java +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/Audiences.java @@ -1,16 +1,30 @@ package tc.oc.commons.core.chat; import tc.oc.minecraft.api.command.CommandSender; +import tc.oc.minecraft.api.entity.Player; + +import javax.annotation.Nullable; +import java.util.function.Predicate; /** * A factory to create {@link Audience}s for various purposes. - * - * Should be expanded to support other types. */ -public interface Audiences { - Audience get(T sender); +public interface Audiences { - Audience localServer(); + Audience get(@Nullable C sender); + + MultiAudience filter(Predicate condition); + + default MultiAudience playerFilter(Predicate condition) { + return filter(sender -> sender instanceof Player && condition.test((Player) sender)); + } + + default MultiAudience permission(@Nullable String permission) { + return filter(sender -> permission == null || permission.isEmpty() || sender.hasPermission(permission)); + } + + MultiAudience all(); + + Audience console(); - Audience withPermission(String permission); } diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/ConsoleAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/ConsoleAudience.java new file mode 100644 index 0000000..ae720a7 --- /dev/null +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/ConsoleAudience.java @@ -0,0 +1,35 @@ +package tc.oc.commons.core.chat; + +import net.md_5.bungee.api.chat.BaseComponent; + +import javax.annotation.Nullable; +import java.util.Optional; + +public interface ConsoleAudience extends Audience { + + @Override + default void playSound(Sound sound) {} + + @Override + default void stopSound(Sound sound) {} + + @Override + default void hideTitle() {} + + @Override + default void sendHotbarMessage(BaseComponent message) { + sendMessage(new Component("[Hotbar] ").extra(message)); + } + + @Override + default void sendWarning(BaseComponent component, boolean audible) { + sendMessage(new Component("[Warning] ").extra(component)); + } + + @Override + default void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + Optional.ofNullable(title).ifPresent(t -> sendMessage(new Component("[Title] ").extra(t))); + Optional.ofNullable(subtitle).ifPresent(s -> sendMessage(new Component("[Subtitle] ").extra(s))); + } + +} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/ForwardingAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/ForwardingAudience.java index 5cc4d6f..d7815f4 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/ForwardingAudience.java +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/ForwardingAudience.java @@ -1,50 +1,57 @@ package tc.oc.commons.core.chat; -import javax.annotation.Nullable; - import net.md_5.bungee.api.chat.BaseComponent; +import javax.annotation.Nullable; +import java.util.Optional; + +@FunctionalInterface public interface ForwardingAudience extends Audience { - Audience audience(); + Optional audience(); @Override default void sendMessage(BaseComponent message) { - audience().sendMessage(message); + audience().ifPresent(audience -> audience.sendMessage(message)); } @Override default void sendWarning(BaseComponent message, boolean audible) { - audience().sendWarning(message, audible); + audience().ifPresent(audience -> audience.sendWarning(message, audible)); } @Override default void playSound(Sound sound) { - audience().playSound(sound); + audience().ifPresent(audience -> audience.playSound(sound)); + } + + @Override + default void stopSound(Sound sound) { + audience().ifPresent(audience -> audience.stopSound(sound)); } @Override default void sendHotbarMessage(BaseComponent message) { - audience().sendHotbarMessage(message); + audience().ifPresent(audience -> audience.sendHotbarMessage(message)); } @Override default void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - audience().showTitle(title, subtitle, inTicks, stayTicks, outTicks); + audience().ifPresent(audience -> audience.showTitle(title, subtitle, inTicks, stayTicks, outTicks)); } @Override default void hideTitle() { - audience().hideTitle(); + audience().ifPresent(audience -> audience.hideTitle()); } @Override default void sendMessage(String message) { - audience().sendMessage(message); + audience().ifPresent(audience -> audience.sendMessage(message)); } @Override default void sendWarning(String message, boolean audible) { - audience().sendWarning(message, audible); + audience().ifPresent(audience -> audience.sendWarning(message, audible)); } } diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/MinecraftAudiences.java b/Util/core/src/main/java/tc/oc/commons/core/chat/MinecraftAudiences.java deleted file mode 100644 index 9e27aa5..0000000 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/MinecraftAudiences.java +++ /dev/null @@ -1,40 +0,0 @@ -package tc.oc.commons.core.chat; - -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.inject.Inject; - -import tc.oc.minecraft.api.command.CommandSender; -import tc.oc.minecraft.api.server.LocalServer; - -public abstract class MinecraftAudiences implements Audiences { - - @Inject private LocalServer localServer; - - @Override - public Audience localServer() { - return new AbstractMultiAudience() { - @Override - protected Iterable getAudiences() { - return Stream.concat(localServer.getOnlinePlayers().stream(), - Stream.of(localServer.getConsoleSender())) - .map(player -> get((T) player)) - .collect(Collectors.toSet()); - } - }; - } - - @Override - public Audience withPermission(String permission) { - return new AbstractMultiAudience() { - @Override - protected Iterable getAudiences() { - return Stream.concat(localServer.getOnlinePlayers().stream(), - Stream.of(localServer.getConsoleSender())) - .filter(player -> player.hasPermission(permission)) - .map(player -> get((T) player)) - .collect(Collectors.toSet()); - } - }; - } -} diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/MultiAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/MultiAudience.java index 3a1cf0c..33a7609 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/MultiAudience.java +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/MultiAudience.java @@ -1,15 +1,58 @@ package tc.oc.commons.core.chat; -public class MultiAudience extends AbstractMultiAudience { +import net.md_5.bungee.api.chat.BaseComponent; - private final Iterable audiences; +import javax.annotation.Nullable; +import java.util.stream.Stream; - public MultiAudience(Iterable audiences) { - this.audiences = audiences; +@FunctionalInterface +public interface MultiAudience extends Audience { + + Stream audiences(); + + @Override + default void sendMessage(BaseComponent message) { + audiences().forEach(audience -> audience.sendMessage(message)); } @Override - protected Iterable getAudiences() { - return audiences; + default void sendWarning(BaseComponent message, boolean audible) { + audiences().forEach(audience -> audience.sendWarning(message, audible)); } + + @Override + default void playSound(Sound sound) { + audiences().forEach(audience -> audience.playSound(sound)); + } + + @Override + default void stopSound(Sound sound) { + audiences().forEach(audience -> audience.stopSound(sound)); + } + + @Override + default void sendHotbarMessage(BaseComponent message) { + audiences().forEach(audience -> audience.sendHotbarMessage(message)); + } + + @Override + default void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { + audiences().forEach(audience -> audience.showTitle(title, subtitle, inTicks, stayTicks, outTicks)); + } + + @Override + default void hideTitle() { + audiences().forEach(audience -> audience.hideTitle()); + } + + @Override + default void sendMessage(String message) { + audiences().forEach(audience -> audience.sendMessage(message)); + } + + @Override + default void sendWarning(String message, boolean audible) { + audiences().forEach(audience -> audience.sendWarning(message, audible)); + } + } diff --git a/Util/core/src/main/java/tc/oc/commons/core/chat/NullAudience.java b/Util/core/src/main/java/tc/oc/commons/core/chat/NullAudience.java index 17684e5..890e602 100644 --- a/Util/core/src/main/java/tc/oc/commons/core/chat/NullAudience.java +++ b/Util/core/src/main/java/tc/oc/commons/core/chat/NullAudience.java @@ -1,43 +1,14 @@ package tc.oc.commons.core.chat; -import javax.annotation.Nullable; +import java.util.Optional; -import net.md_5.bungee.api.chat.BaseComponent; - -public class NullAudience implements Audience { - - private NullAudience() {} - public static final NullAudience INSTANCE = new NullAudience(); +public interface NullAudience extends ForwardingAudience { @Override - public void sendMessage(BaseComponent message) { + default Optional audience() { + return Optional.empty(); } - @Override - public void sendWarning(BaseComponent message, boolean audible) { - } + NullAudience INSTANCE = new NullAudience(){}; - @Override - public void playSound(Sound sound) { - } - - @Override - public void sendHotbarMessage(BaseComponent message) { - } - - @Override - public void showTitle(@Nullable BaseComponent title, @Nullable BaseComponent subtitle, int inTicks, int stayTicks, int outTicks) { - } - - @Override - public void hideTitle() { - } - - @Override - public void sendMessage(String message) { - } - - @Override - public void sendWarning(String message, boolean audible) { - } }