mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-28 05:35:28 +01:00
Added a simple filter system that utilizes JavaScript (Rhino)
This makes it possible to filter packet events with arbitrary code.
This commit is contained in:
parent
5720994a31
commit
15980d70fb
@ -55,6 +55,7 @@ abstract class CommandBase implements CommandExecutor {
|
|||||||
try {
|
try {
|
||||||
// Make sure we're dealing with the correct command
|
// Make sure we're dealing with the correct command
|
||||||
if (!command.getName().equalsIgnoreCase(name)) {
|
if (!command.getName().equalsIgnoreCase(name)) {
|
||||||
|
reporter.reportWarning(this, "Incorrect command assigned to " + this);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (permission != null && !sender.hasPermission(permission)) {
|
if (permission != null && !sender.hasPermission(permission)) {
|
||||||
@ -66,6 +67,7 @@ abstract class CommandBase implements CommandExecutor {
|
|||||||
if (args != null && args.length >= minimumArgumentCount) {
|
if (args != null && args.length >= minimumArgumentCount) {
|
||||||
return handleCommand(sender, args);
|
return handleCommand(sender, args);
|
||||||
} else {
|
} else {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Insufficient commands. You need at least " + minimumArgumentCount);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,394 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.script.Invocable;
|
||||||
|
import javax.script.ScriptEngine;
|
||||||
|
import javax.script.ScriptEngineManager;
|
||||||
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
|
import org.bukkit.ChatColor;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.conversations.Conversable;
|
||||||
|
import org.bukkit.conversations.Conversation;
|
||||||
|
import org.bukkit.conversations.ConversationAbandonedEvent;
|
||||||
|
import org.bukkit.conversations.ConversationAbandonedListener;
|
||||||
|
import org.bukkit.conversations.ConversationCanceller;
|
||||||
|
import org.bukkit.conversations.ConversationContext;
|
||||||
|
import org.bukkit.conversations.ConversationFactory;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
|
||||||
|
import com.comphenix.protocol.concurrency.IntegerSet;
|
||||||
|
import com.comphenix.protocol.error.ErrorReporter;
|
||||||
|
import com.comphenix.protocol.events.PacketEvent;
|
||||||
|
import com.google.common.collect.DiscreteDomains;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import com.google.common.collect.Ranges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command to apply JavaScript filtering to the packet command.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public class CommandFilter extends CommandBase {
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public static class FilterFailedException extends RuntimeException {
|
||||||
|
private Filter filter;
|
||||||
|
|
||||||
|
public FilterFailedException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterFailedException(String message, Filter filter, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
this.filter = filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Filter getFilter() {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Possible sub commands.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
private enum SubCommand {
|
||||||
|
ADD, REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A filter that will be used to process a packet event.
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
public static class Filter {
|
||||||
|
private final String name;
|
||||||
|
private final String predicate;
|
||||||
|
|
||||||
|
private final IntegerSet ranges;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a new immutable filter.
|
||||||
|
* @param name - the unique name of the filter.
|
||||||
|
* @param predicate - the JavaScript predicate that will be used to filter packet events.
|
||||||
|
* @param ranges - a list of valid packet ID ranges that this filter applies to.
|
||||||
|
*/
|
||||||
|
public Filter(String name, String predicate, Set<Integer> packets) {
|
||||||
|
this.name = name;
|
||||||
|
this.predicate = predicate;
|
||||||
|
this.ranges = new IntegerSet(Packets.MAXIMUM_PACKET_ID + 1);
|
||||||
|
this.ranges.addAll(packets);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the unique name of the filter.
|
||||||
|
* @return Unique name of the filter.
|
||||||
|
*/
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the JavaScript predicate that will be used to filter packet events.
|
||||||
|
* @return Predicate itself.
|
||||||
|
*/
|
||||||
|
public String getPredicate() {
|
||||||
|
return predicate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a copy of the set of packets this filter applies to.
|
||||||
|
* @return Set of packets this filter applies to.
|
||||||
|
*/
|
||||||
|
public Set<Integer> getRanges() {
|
||||||
|
return ranges.toSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether or not a packet event needs to be passed to this filter.
|
||||||
|
* @param event - the event to test.
|
||||||
|
* @return TRUE if it does, FALSE otherwise.
|
||||||
|
*/
|
||||||
|
private boolean isApplicable(PacketEvent event) {
|
||||||
|
return ranges.contains(event.getPacketID());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate the current filter using the provided ScriptEngine as context.
|
||||||
|
* <p>
|
||||||
|
* This context may be modified with additional code.
|
||||||
|
* @param context - the current script context.
|
||||||
|
* @param event - the packet event to evaluate.
|
||||||
|
* @return TRUE to pass this packet event on to the debug listeners, FALSE otherwise.
|
||||||
|
* @throws ScriptException If the compilation failed.
|
||||||
|
*/
|
||||||
|
public boolean evaluate(ScriptEngine context, PacketEvent event) throws ScriptException {
|
||||||
|
if (!isApplicable(event))
|
||||||
|
return true;
|
||||||
|
// Ensure that the predicate has been compiled
|
||||||
|
compile(context);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (Boolean) ((Invocable) context).invokeFunction(name, event, event.getPacket().getHandle());
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
// Must be a fault with the script engine itself
|
||||||
|
throw new IllegalStateException("Unable to compile " + name + " into current script engine.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force the compilation of a specific filter.
|
||||||
|
* @param context - the current script context.
|
||||||
|
* @throws ScriptException If the compilation failed.
|
||||||
|
*/
|
||||||
|
public void compile(ScriptEngine context) throws ScriptException {
|
||||||
|
if (context.get(name) == null) {
|
||||||
|
context.eval("var " + name + " = function(event, packet) {\n" + predicate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up all associated code from this filter in the provided script engine.
|
||||||
|
* @param context - the current script context.
|
||||||
|
*/
|
||||||
|
public void close(ScriptEngine context) {
|
||||||
|
context.put(name, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class BracketBalance implements ConversationCanceller {
|
||||||
|
private String KEY_BRACKET_COUNT = "bracket_balance.count";
|
||||||
|
|
||||||
|
// What to set the initial counter
|
||||||
|
private final int initialBalance;
|
||||||
|
|
||||||
|
public BracketBalance(int initialBalance) {
|
||||||
|
this.initialBalance = initialBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancelBasedOnInput(ConversationContext context, String in) {
|
||||||
|
Object stored = context.getSessionData(KEY_BRACKET_COUNT);
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
// Get the stored value
|
||||||
|
if (stored instanceof Integer) {
|
||||||
|
value = (Integer)stored;
|
||||||
|
} else {
|
||||||
|
value = initialBalance;
|
||||||
|
}
|
||||||
|
|
||||||
|
value += count(in, '{') - count(in, '}');
|
||||||
|
context.setSessionData(KEY_BRACKET_COUNT, value);
|
||||||
|
|
||||||
|
// Cancel if the bracket balance is zero
|
||||||
|
return value <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int count(String text, char character) {
|
||||||
|
int counter = 0;
|
||||||
|
|
||||||
|
for (int i=0; i < text.length(); i++) {
|
||||||
|
if (text.charAt(i) == character) {
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConversation(Conversation conversation) {
|
||||||
|
// Whatever
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ConversationCanceller clone() {
|
||||||
|
return new BracketBalance(initialBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of this command.
|
||||||
|
*/
|
||||||
|
public static final String NAME = "filter";
|
||||||
|
|
||||||
|
// Currently registered filters
|
||||||
|
private List<Filter> filters = new ArrayList<Filter>();
|
||||||
|
|
||||||
|
// Owner plugin
|
||||||
|
private final Plugin plugin;
|
||||||
|
|
||||||
|
// Script engine
|
||||||
|
private ScriptEngine engine;
|
||||||
|
|
||||||
|
public CommandFilter(ErrorReporter reporter, Plugin plugin) {
|
||||||
|
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
|
||||||
|
this.plugin = plugin;
|
||||||
|
|
||||||
|
// Start the engine
|
||||||
|
initalizeScript();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initalizeScript() {
|
||||||
|
ScriptEngineManager manager = new ScriptEngineManager();
|
||||||
|
engine = manager.getEngineByName("JavaScript");
|
||||||
|
|
||||||
|
// Import useful packages
|
||||||
|
try {
|
||||||
|
engine.eval("importPackage(org.bukkit);");
|
||||||
|
engine.eval("importPackage(com.comphenix.protocol.reflect);");
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
throw new IllegalStateException("Unable to initialize packages for JavaScript engine.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether or not to pass the given packet event to the packet listeners.
|
||||||
|
* @param event - the event.
|
||||||
|
* @return TRUE if we should, FALSE otherwise.
|
||||||
|
* @throws FilterFailedException If one of the filters failed.
|
||||||
|
*/
|
||||||
|
public boolean filterEvent(PacketEvent event) throws FilterFailedException {
|
||||||
|
for (Filter filter : filters) {
|
||||||
|
try {
|
||||||
|
if (!filter.evaluate(engine, event)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
throw new FilterFailedException("Filter failed.", filter, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pass!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Description: Adds or removes a simple packet listener.
|
||||||
|
Usage: /<command> add|remove name [packet IDs]
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean handleCommand(CommandSender sender, String[] args) {
|
||||||
|
final SubCommand command = parseCommand(args, 0);
|
||||||
|
final String name = args[1];
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case ADD:
|
||||||
|
// Never overwrite an existing filter
|
||||||
|
if (findFilter(name) != null) {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Filter " + name + " already exists. Remove it first.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Set<Integer> packets = parseRanges(args, 2);
|
||||||
|
sender.sendMessage("Enter filter program ('}' to complete or CANCEL):");
|
||||||
|
|
||||||
|
// Make sure we can use the conversable interface
|
||||||
|
if (sender instanceof Conversable) {
|
||||||
|
final MultipleLinesPrompt prompt =
|
||||||
|
new MultipleLinesPrompt(new BracketBalance(1), "function(event, packet) {");
|
||||||
|
|
||||||
|
new ConversationFactory(plugin).
|
||||||
|
withFirstPrompt(prompt).
|
||||||
|
withEscapeSequence("CANCEL").
|
||||||
|
withLocalEcho(false).
|
||||||
|
addConversationAbandonedListener(new ConversationAbandonedListener() {
|
||||||
|
@Override
|
||||||
|
public void conversationAbandoned(ConversationAbandonedEvent event) {
|
||||||
|
try {
|
||||||
|
final Conversable whom = event.getContext().getForWhom();
|
||||||
|
|
||||||
|
if (event.gracefulExit()) {
|
||||||
|
String predicate = prompt.removeAccumulatedInput(event.getContext());
|
||||||
|
Filter filter = new Filter(name, predicate, packets);
|
||||||
|
|
||||||
|
// Print the last line as well
|
||||||
|
whom.sendRawMessage(prompt.getPromptText(event.getContext()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Force early compilation
|
||||||
|
filter.compile(engine);
|
||||||
|
|
||||||
|
filters.add(filter);
|
||||||
|
whom.sendRawMessage(ChatColor.GOLD + "Added filter " + name);
|
||||||
|
} catch (ScriptException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
whom.sendRawMessage(ChatColor.GOLD + "Compilation error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Too bad
|
||||||
|
whom.sendRawMessage(ChatColor.RED + "Cancelled filter.");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
reporter.reportDetailed(this, "Cannot handle conversation.", e, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).
|
||||||
|
buildConversation((Conversable) sender).
|
||||||
|
begin();
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Only console and players are supported!");
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REMOVE:
|
||||||
|
Filter filter = findFilter(name);
|
||||||
|
|
||||||
|
// See if it exists before we remove it
|
||||||
|
if (filter != null) {
|
||||||
|
filter.close(engine);
|
||||||
|
filters.remove(filter);
|
||||||
|
sender.sendMessage(ChatColor.GOLD + "Removed filter " + name);
|
||||||
|
} else {
|
||||||
|
sender.sendMessage(ChatColor.RED + "Unable to find a filter by the name " + name);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Integer> parseRanges(String[] args, int start) {
|
||||||
|
List<Range<Integer>> ranges = RangeParser.getRanges(args, 2, args.length - 1, Ranges.closed(0, 255));
|
||||||
|
Set<Integer> flatten = new HashSet<Integer>();
|
||||||
|
|
||||||
|
if (ranges.isEmpty()) {
|
||||||
|
// Use every packet ID
|
||||||
|
ranges.add(Ranges.closed(0, 255));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, flatten it all
|
||||||
|
for (Range<Integer> range : ranges) {
|
||||||
|
flatten.addAll(range.asSet(DiscreteDomains.integers()));
|
||||||
|
}
|
||||||
|
return flatten;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup a filter by its name.
|
||||||
|
* @param name - the filter name.
|
||||||
|
* @return The filter, or NULL if not found.
|
||||||
|
*/
|
||||||
|
private Filter findFilter(String name) {
|
||||||
|
// We'll just use a linear scan for now - we don't expect that many filters
|
||||||
|
for (Filter filter : filters) {
|
||||||
|
if (filter.getName().equalsIgnoreCase(name)) {
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SubCommand parseCommand(String[] args, int index) {
|
||||||
|
String text = args[index].toUpperCase();
|
||||||
|
|
||||||
|
try {
|
||||||
|
return SubCommand.valueOf(text);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException(text + " is not a valid sub command. Must be add or remove.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -93,11 +93,15 @@ class CommandPacket extends CommandBase {
|
|||||||
private AbstractIntervalTree<Integer, DetailedPacketListener> clientListeners = createTree(ConnectionSide.CLIENT_SIDE);
|
private AbstractIntervalTree<Integer, DetailedPacketListener> clientListeners = createTree(ConnectionSide.CLIENT_SIDE);
|
||||||
private AbstractIntervalTree<Integer, DetailedPacketListener> serverListeners = createTree(ConnectionSide.SERVER_SIDE);
|
private AbstractIntervalTree<Integer, DetailedPacketListener> serverListeners = createTree(ConnectionSide.SERVER_SIDE);
|
||||||
|
|
||||||
public CommandPacket(ErrorReporter reporter, Plugin plugin, Logger logger, ProtocolManager manager) {
|
// Filter packet events
|
||||||
|
private CommandFilter filter;
|
||||||
|
|
||||||
|
public CommandPacket(ErrorReporter reporter, Plugin plugin, Logger logger, CommandFilter filter, ProtocolManager manager) {
|
||||||
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 1);
|
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 1);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.manager = manager;
|
this.manager = manager;
|
||||||
|
this.filter = filter;
|
||||||
this.chatter = new ChatExtensions(manager);
|
this.chatter = new ChatExtensions(manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +366,6 @@ class CommandPacket extends CommandBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DetailedPacketListener createPacketListener(final ConnectionSide side, int idStart, int idStop, final boolean detailed) {
|
public DetailedPacketListener createPacketListener(final ConnectionSide side, int idStart, int idStop, final boolean detailed) {
|
||||||
|
|
||||||
Set<Integer> range = Ranges.closed(idStart, idStop).asSet(DiscreteDomains.integers());
|
Set<Integer> range = Ranges.closed(idStart, idStop).asSet(DiscreteDomains.integers());
|
||||||
Set<Integer> packets;
|
Set<Integer> packets;
|
||||||
|
|
||||||
@ -386,14 +389,14 @@ class CommandPacket extends CommandBase {
|
|||||||
return new DetailedPacketListener() {
|
return new DetailedPacketListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onPacketSending(PacketEvent event) {
|
public void onPacketSending(PacketEvent event) {
|
||||||
if (side.isForServer()) {
|
if (side.isForServer() && filter.filterEvent(event)) {
|
||||||
printInformation(event);
|
printInformation(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPacketReceiving(PacketEvent event) {
|
public void onPacketReceiving(PacketEvent event) {
|
||||||
if (side.isForClient()) {
|
if (side.isForClient() && filter.filterEvent(event)) {
|
||||||
printInformation(event);
|
printInformation(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package com.comphenix.protocol;
|
||||||
|
|
||||||
|
import org.bukkit.conversations.ConversationCanceller;
|
||||||
|
import org.bukkit.conversations.ConversationContext;
|
||||||
|
import org.bukkit.conversations.ExactMatchConversationCanceller;
|
||||||
|
import org.bukkit.conversations.Prompt;
|
||||||
|
import org.bukkit.conversations.StringPrompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a conversation prompt that accepts a list of lines.
|
||||||
|
*
|
||||||
|
* @author Kristian
|
||||||
|
*/
|
||||||
|
class MultipleLinesPrompt extends StringPrompt {
|
||||||
|
// Feels a bit like Android
|
||||||
|
private static final String KEY = "multiple_lines_prompt";
|
||||||
|
private static final String KEY_LAST = KEY + ".last_line";
|
||||||
|
|
||||||
|
private final ConversationCanceller endMarker;
|
||||||
|
private final String initialPrompt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve and remove the current accumulated input.
|
||||||
|
* @param context - conversation context.
|
||||||
|
* @return The accumulated input, or NULL if not found.
|
||||||
|
*/
|
||||||
|
public String removeAccumulatedInput(ConversationContext context) {
|
||||||
|
Object result = context.getSessionData(KEY);
|
||||||
|
|
||||||
|
if (result instanceof StringBuilder) {
|
||||||
|
context.setSessionData(KEY, null);
|
||||||
|
return ((StringBuilder) result).toString();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a multiple lines input prompt with a specific end marker.
|
||||||
|
* <p>
|
||||||
|
* This is usually an empty string.
|
||||||
|
* @param endMarker - the end marker.
|
||||||
|
*/
|
||||||
|
public MultipleLinesPrompt(String endMarker, String initialPrompt) {
|
||||||
|
this(new ExactMatchConversationCanceller(endMarker), initialPrompt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultipleLinesPrompt(ConversationCanceller endMarker, String initialPrompt) {
|
||||||
|
this.endMarker = endMarker;
|
||||||
|
this.initialPrompt = initialPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Prompt acceptInput(ConversationContext context, String in) {
|
||||||
|
StringBuilder result = (StringBuilder) context.getSessionData(KEY);
|
||||||
|
|
||||||
|
if (result == null) {
|
||||||
|
context.setSessionData(KEY, result = new StringBuilder());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the last line as well
|
||||||
|
context.setSessionData(KEY_LAST, in);
|
||||||
|
result.append(in);
|
||||||
|
|
||||||
|
// And we're done
|
||||||
|
if (endMarker.cancelBasedOnInput(context, in))
|
||||||
|
return Prompt.END_OF_CONVERSATION;
|
||||||
|
else
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPromptText(ConversationContext context) {
|
||||||
|
Object last = context.getSessionData(KEY_LAST);
|
||||||
|
|
||||||
|
if (last instanceof String)
|
||||||
|
return (String) last;
|
||||||
|
else
|
||||||
|
return initialPrompt;
|
||||||
|
}
|
||||||
|
}
|
@ -103,6 +103,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Commands
|
// Commands
|
||||||
private CommandProtocol commandProtocol;
|
private CommandProtocol commandProtocol;
|
||||||
private CommandPacket commandPacket;
|
private CommandPacket commandPacket;
|
||||||
|
private CommandFilter commandFilter;
|
||||||
|
|
||||||
// Whether or not disable is not needed
|
// Whether or not disable is not needed
|
||||||
private boolean skipDisable;
|
private boolean skipDisable;
|
||||||
@ -161,7 +162,8 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
|
|
||||||
// Initialize command handlers
|
// Initialize command handlers
|
||||||
commandProtocol = new CommandProtocol(detailedReporter, this, updater, config);
|
commandProtocol = new CommandProtocol(detailedReporter, this, updater, config);
|
||||||
commandPacket = new CommandPacket(detailedReporter, this, logger, protocolManager);
|
commandFilter = new CommandFilter(detailedReporter, this);
|
||||||
|
commandPacket = new CommandPacket(detailedReporter, this, logger, commandFilter, protocolManager);
|
||||||
|
|
||||||
// Send logging information to player listeners too
|
// Send logging information to player listeners too
|
||||||
setupBroadcastUsers(PERMISSION_INFO);
|
setupBroadcastUsers(PERMISSION_INFO);
|
||||||
@ -256,6 +258,7 @@ public class ProtocolLibrary extends JavaPlugin {
|
|||||||
// Set up command handlers
|
// Set up command handlers
|
||||||
registerCommand(CommandProtocol.NAME, commandProtocol);
|
registerCommand(CommandProtocol.NAME, commandProtocol);
|
||||||
registerCommand(CommandPacket.NAME, commandPacket);
|
registerCommand(CommandPacket.NAME, commandPacket);
|
||||||
|
registerCommand(CommandFilter.NAME, commandFilter);
|
||||||
|
|
||||||
// Player login and logout events
|
// Player login and logout events
|
||||||
protocolManager.registerEvents(manager, this);
|
protocolManager.registerEvents(manager, this);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package com.comphenix.protocol.concurrency;
|
package com.comphenix.protocol.concurrency;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@ -60,6 +61,16 @@ public class IntegerSet {
|
|||||||
array[element] = true;
|
array[element] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the given collection of elements to the set.
|
||||||
|
* @param packets - elements to add.
|
||||||
|
*/
|
||||||
|
public void addAll(Collection<Integer> packets) {
|
||||||
|
for (Integer id : packets) {
|
||||||
|
add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the given element from the set, or do nothing if it's already removed.
|
* Remove the given element from the set, or do nothing if it's already removed.
|
||||||
* @param element - element to remove.
|
* @param element - element to remove.
|
||||||
|
@ -17,6 +17,12 @@ commands:
|
|||||||
usage: /<command> add|remove|names client|server [ID start]-[ID stop] [detailed]
|
usage: /<command> add|remove|names client|server [ID start]-[ID stop] [detailed]
|
||||||
permission: protocol.admin
|
permission: protocol.admin
|
||||||
permission-message: You don't have <permission>
|
permission-message: You don't have <permission>
|
||||||
|
filter:
|
||||||
|
description: Add or remove programmable filters to the packet listeners.
|
||||||
|
usage: /<command> add|remove name [ID start]-[ID stop]
|
||||||
|
aliases: [packet_filter]
|
||||||
|
permission: protocol.admin
|
||||||
|
permission-message: You don't have <permission>
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
protocol.*:
|
protocol.*:
|
||||||
|
Loading…
Reference in New Issue
Block a user