-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor Channels #1306
base: dev
Are you sure you want to change the base?
Refactor Channels #1306
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,118 @@ | ||||||||
package tc.oc.pgm.api.channels; | ||||||||
|
||||||||
import static net.kyori.adventure.text.Component.text; | ||||||||
|
||||||||
import cloud.commandframework.arguments.standard.StringArgument; | ||||||||
import cloud.commandframework.paper.PaperCommandManager; | ||||||||
import java.util.Collection; | ||||||||
import java.util.HashMap; | ||||||||
import java.util.Map; | ||||||||
import java.util.function.Predicate; | ||||||||
import net.kyori.adventure.text.Component; | ||||||||
import net.kyori.adventure.text.format.NamedTextColor; | ||||||||
import org.bukkit.command.CommandSender; | ||||||||
import org.jetbrains.annotations.Nullable; | ||||||||
import tc.oc.pgm.api.PGM; | ||||||||
import tc.oc.pgm.api.event.ChannelMessageEvent; | ||||||||
import tc.oc.pgm.api.player.MatchPlayer; | ||||||||
import tc.oc.pgm.api.setting.SettingValue; | ||||||||
import tc.oc.pgm.util.Players; | ||||||||
import tc.oc.pgm.util.named.NameStyle; | ||||||||
|
||||||||
public interface Channel<T> { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This API needs to be fully documented, explaining each method's purpose and params |
||||||||
|
||||||||
default String getDisplayName() { | ||||||||
return getAliases()[0]; | ||||||||
} | ||||||||
|
||||||||
String[] getAliases(); | ||||||||
|
||||||||
@Nullable | ||||||||
default Character getShortcut() { | ||||||||
return null; | ||||||||
} | ||||||||
|
||||||||
default SettingValue getSetting() { | ||||||||
return null; | ||||||||
} | ||||||||
|
||||||||
default boolean supportsRedirect() { | ||||||||
return false; | ||||||||
} | ||||||||
|
||||||||
default boolean canSendMessage(MatchPlayer sender) { | ||||||||
return true; | ||||||||
} | ||||||||
|
||||||||
T getTarget(MatchPlayer sender, Map<String, ?> arguments); | ||||||||
|
||||||||
Collection<MatchPlayer> getViewers(T target); | ||||||||
|
||||||||
default Collection<MatchPlayer> getBroadcastViewers(T target) { | ||||||||
return getViewers(target); | ||||||||
} | ||||||||
|
||||||||
default void sendMessage(ChannelMessageEvent<T> event) {} | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
either force the downstream to implement this, or have an actual default impl, but don't just leave an empty no-op method for something this critical. |
||||||||
|
||||||||
default Component formatMessage(T target, @Nullable MatchPlayer sender, Component message) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i believe this method should not default to global-chat behavior, force downstream to implement it too |
||||||||
if (sender == null) return message; | ||||||||
return text() | ||||||||
.append(text("<", NamedTextColor.WHITE)) | ||||||||
.append(sender.getName(NameStyle.VERBOSE)) | ||||||||
.append(text(">: ", NamedTextColor.WHITE)) | ||||||||
.append(message) | ||||||||
.build(); | ||||||||
} | ||||||||
|
||||||||
default void registerCommand(PaperCommandManager<CommandSender> manager) { | ||||||||
String name = getAliases()[0]; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if aliases is an empty array, this throws an out of bounds exception. Throw your own illegal argument exception instead |
||||||||
String[] aliases = new String[getAliases().length - 1]; | ||||||||
System.arraycopy(getAliases(), 1, aliases, 0, aliases.length); | ||||||||
Comment on lines
+69
to
+70
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to go into some util, you could also use
Suggested change
|
||||||||
|
||||||||
manager.command( | ||||||||
manager | ||||||||
.commandBuilder(name, aliases) | ||||||||
.handler( | ||||||||
context -> { | ||||||||
MatchPlayer sender = | ||||||||
context.inject(MatchPlayer.class).orElseThrow(IllegalStateException::new); | ||||||||
PGM.get().getChannelManager().setChannel(sender, this); | ||||||||
})); | ||||||||
|
||||||||
manager.command( | ||||||||
manager | ||||||||
.commandBuilder(name, aliases) | ||||||||
.argument( | ||||||||
StringArgument.<CommandSender>builder("message") | ||||||||
.greedy() | ||||||||
.withSuggestionsProvider(Players::suggestPlayers) | ||||||||
.build()) | ||||||||
.handler( | ||||||||
context -> { | ||||||||
MatchPlayer sender = | ||||||||
context.inject(MatchPlayer.class).orElseThrow(IllegalStateException::new); | ||||||||
PGM.get().getChannelManager().process(this, sender, context.asMap()); | ||||||||
})); | ||||||||
} | ||||||||
|
||||||||
default Map<String, Object> processChatShortcut(MatchPlayer sender, String message) { | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
you're not processing the shortcut (with a chat), you're processing the message (with a shortcut). Make sure the docs explain the rationale of why this is even needed cause it's nonobvious |
||||||||
Map<String, Object> arguments = new HashMap<String, Object>(); | ||||||||
if (message.length() == 1) { | ||||||||
PGM.get().getChannelManager().setChannel(sender, this); | ||||||||
return arguments; | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
|
||||||||
arguments.put("message", message.substring(1).trim()); | ||||||||
return arguments; | ||||||||
Comment on lines
+105
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||
} | ||||||||
|
||||||||
default void broadcastMessage(Component component, T target) { | ||||||||
broadcastMessage(component, target, player -> true); | ||||||||
} | ||||||||
|
||||||||
default void broadcastMessage(Component component, T target, Predicate<MatchPlayer> filter) { | ||||||||
Collection<MatchPlayer> viewers = getBroadcastViewers(target); | ||||||||
Component finalMessage = formatMessage(target, null, component); | ||||||||
viewers.stream().filter(filter).forEach(player -> player.sendMessage(finalMessage)); | ||||||||
} | ||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package tc.oc.pgm.api.event; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import org.bukkit.event.HandlerList; | ||
import org.jetbrains.annotations.Nullable; | ||
import tc.oc.pgm.api.channels.Channel; | ||
import tc.oc.pgm.api.player.MatchPlayer; | ||
import tc.oc.pgm.util.event.PreemptiveEvent; | ||
|
||
public class ChannelMessageEvent<T> extends PreemptiveEvent { | ||
|
||
private final Channel<T> channel; | ||
private @Nullable MatchPlayer sender; | ||
private final T target; | ||
private final List<MatchPlayer> viewers; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you probably should be able to set this, to be able to change who sees the message |
||
private String message; | ||
|
||
public ChannelMessageEvent( | ||
Channel<T> channel, | ||
@Nullable MatchPlayer sender, | ||
T target, | ||
Collection<MatchPlayer> viewers, | ||
String message) { | ||
this.channel = channel; | ||
this.sender = sender; | ||
this.target = target; | ||
this.viewers = new ArrayList<MatchPlayer>(viewers); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i'd avoid the array list, just keep the collection passed in |
||
this.message = message; | ||
} | ||
|
||
public Channel<T> getChannel() { | ||
return channel; | ||
} | ||
|
||
@Nullable | ||
public MatchPlayer getSender() { | ||
return sender; | ||
} | ||
|
||
public void setSender(@Nullable MatchPlayer sender) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably no reason to override the sender |
||
this.sender = sender; | ||
} | ||
|
||
public T getTarget() { | ||
return target; | ||
} | ||
|
||
public List<MatchPlayer> getViewers() { | ||
return viewers; | ||
} | ||
|
||
public String getMessage() { | ||
return message; | ||
} | ||
|
||
public void setMessage(String message) { | ||
this.message = message; | ||
} | ||
|
||
private static final HandlerList handlers = new HandlerList(); | ||
|
||
@Override | ||
public HandlerList getHandlers() { | ||
return handlers; | ||
} | ||
|
||
public static HandlerList getHandlerList() { | ||
return handlers; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,10 +3,14 @@ | |
import static tc.oc.pgm.util.Assert.assertNotNull; | ||
|
||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
import java.util.UUID; | ||
import java.util.concurrent.atomic.AtomicReference; | ||
import org.bukkit.entity.Player; | ||
import org.jetbrains.annotations.Nullable; | ||
import tc.oc.pgm.api.channels.Channel; | ||
import tc.oc.pgm.api.player.MatchPlayer; | ||
|
||
public final class Integration { | ||
|
@@ -23,6 +27,7 @@ private Integration() {} | |
new AtomicReference<VanishIntegration>(new NoopVanishIntegration()); | ||
private static final AtomicReference<SquadIntegration> SQUAD = | ||
new AtomicReference<>(new NoopSquadIntegration()); | ||
private static Set<Channel<?>> CHANNELS = new HashSet<Channel<?>>(); | ||
|
||
public static void setFriendIntegration(FriendIntegration integration) { | ||
FRIENDS.set(assertNotNull(integration)); | ||
|
@@ -44,6 +49,13 @@ public static void setSquadIntegration(SquadIntegration integration) { | |
SQUAD.set(assertNotNull(integration)); | ||
} | ||
|
||
public static void registerChannel(Channel<?> channel) { | ||
if (CHANNELS == null) | ||
throw new IllegalStateException( | ||
"New channels cannot be registered after ChannelManager has been initialised!"); | ||
CHANNELS.add(assertNotNull(channel)); | ||
} | ||
|
||
public static boolean isFriend(Player a, Player b) { | ||
return FRIENDS.get().isFriend(a, b); | ||
} | ||
|
@@ -79,6 +91,14 @@ public static boolean setVanished(MatchPlayer player, boolean vanish, boolean qu | |
return VANISH.get().setVanished(player, vanish, quiet); | ||
} | ||
|
||
public static Set<Channel<?>> getRegisteredChannels() { | ||
return Collections.unmodifiableSet(CHANNELS); | ||
} | ||
|
||
public static void finishRegisteringChannels() { | ||
CHANNELS = null; | ||
} | ||
Comment on lines
+98
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd suggest going for |
||
|
||
// No-op Implementations | ||
|
||
private static class NoopFriendIntegration implements FriendIntegration { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could extend Adventure's
Audience
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
given the extra overhead that a channel implies, it probably shouldn't.
You don't want someone treating this like it was just another casual audience, when it may not act like one at all (eg: requirement for a target to send within the channel). Effectively speaking the only channel that could properly implement audience would be global chat