Replace an ArrayList that mimics a Map with an actual Map.

This commit is contained in:
Photon-GitHub 2022-07-25 13:05:18 +02:00
parent 84cb541866
commit b7876ac07a
1 changed files with 72 additions and 86 deletions

View File

@ -1,10 +1,10 @@
package com.comphenix.protocol;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.script.Invocable;
@ -30,7 +30,7 @@ import com.comphenix.protocol.events.PacketEvent;
/**
* A command to apply JavaScript filtering to the packet command.
*
*
* @author Kristian
*/
public class CommandFilter extends CommandBase {
@ -39,7 +39,7 @@ public class CommandFilter extends CommandBase {
public static final ReportType REPORT_PACKAGES_UNSUPPORTED_IN_ENGINE = new ReportType("Unable to initialize packages for JavaScript engine.");
public static final ReportType REPORT_FILTER_REMOVED_FOR_ERROR = new ReportType("Removing filter %s for causing %s.");
public static final ReportType REPORT_CANNOT_HANDLE_CONVERSATION = new ReportType("Cannot handle conversation.");
public interface FilterFailedHandler{
/**
* Invoked when a given filter has failed.
@ -48,18 +48,18 @@ public class CommandFilter extends CommandBase {
* @param ex - the failure.
* @return TRUE to keep processing this filter, FALSE to remove it.
*/
public boolean handle(PacketEvent event, Filter filter, Exception ex);
boolean handle(PacketEvent event, Filter filter, Exception ex);
}
/**
* Possible sub commands.
*
*
* @author Kristian
*/
private enum SubCommand {
ADD, REMOVE;
ADD, REMOVE
}
/**
* A filter that will be used to process a packet event.
* @author Kristian
@ -67,9 +67,9 @@ public class CommandFilter extends CommandBase {
public static class Filter {
private final String name;
private final String predicate;
private final Set<PacketType> packets;
/**
* Construct a new immutable filter.
* @param name - the unique name of the filter.
@ -81,7 +81,7 @@ public class CommandFilter extends CommandBase {
this.predicate = predicate;
this.packets = new HashSet<>(packets);
}
/**
* Retrieve the unique name of the filter.
* @return Unique name of the filter.
@ -89,7 +89,7 @@ public class CommandFilter extends CommandBase {
public String getName() {
return name;
}
/**
* Retrieve the JavaScript predicate that will be used to filter packet events.
* @return Predicate itself.
@ -97,7 +97,7 @@ public class CommandFilter extends CommandBase {
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.
@ -105,16 +105,16 @@ public class CommandFilter extends CommandBase {
public Set<PacketType> getRanges() {
return new HashSet<>(packets);
}
/**
* Determine whether or not a packet event needs to be passed to this filter.
* Determine whether 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 packets.contains(event.getPacketType());
}
/**
* Evaluate the current filter using the provided ScriptEngine as context.
* <p>
@ -129,21 +129,21 @@ public class CommandFilter extends CommandBase {
return true;
// Ensure that the predicate has been compiled
compile(context);
try {
Object result = ((Invocable) context).invokeFunction(name, event, event.getPacket().getHandle());
if (result instanceof Boolean)
return (Boolean) result;
else
throw new ScriptException("Filter result wasn't a boolean: " + result);
} 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.
@ -154,7 +154,7 @@ public class CommandFilter extends CommandBase {
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.
@ -163,7 +163,7 @@ public class CommandFilter extends CommandBase {
context.put(name, null);
}
}
private class CompilationSuccessCanceller implements MultipleConversationCanceller {
@Override
public boolean cancelBasedOnInput(ConversationContext context, String in) {
@ -179,59 +179,59 @@ public class CommandFilter extends CommandBase {
public boolean cancelBasedOnInput(ConversationContext context, String currentLine, StringBuilder lines, int lineCount) {
try {
engine.eval("function(event, packet) {\n" + lines.toString());
// It compiles - accept the filter!
return true;
} catch (ScriptException e) {
// We also have the function() line
int realLineCount = lineCount + 1;
// Only possible to recover from an error on the last line.
return e.getLineNumber() < realLineCount;
}
}
@Override
public CompilationSuccessCanceller clone() {
return new CompilationSuccessCanceller();
}
}
/**
* Name of this command.
*/
public static final String NAME = "filter";
// Default error handler
private FilterFailedHandler defaultFailedHandler;
// Currently registered filters
private List<Filter> filters = new ArrayList<Filter>();
private final Map<String, Filter> filters = new HashMap<>();
// Owner plugin
private final Plugin plugin;
// Whether or not the command is enabled
private ProtocolConfig config;
// Whether the command is enabled
private final ProtocolConfig config;
// Script engine
private ScriptEngine engine;
private boolean uninitialized;
public CommandFilter(ErrorReporter reporter, Plugin plugin, ProtocolConfig config) {
super(reporter, CommandBase.PERMISSION_ADMIN, NAME, 2);
this.plugin = plugin;
this.config = config;
// Tell the filter system to initialize the script first chance it gets
this.uninitialized = true;
}
private void initalizeScript() {
try {
// First attempt
initializeEngine();
// Oh for ..
if (!isInitialized()) {
throw new ScriptException("A JavaScript engine could not be found.");
@ -241,15 +241,15 @@ public class CommandFilter extends CommandBase {
} catch (ScriptException e1) {
// It's not a huge deal
printPackageWarning(e1);
if (!config.getScriptEngineName().equals("rhino")) {
reporter.reportWarning(this, Report.newBuilder(REPORT_FALLBACK_ENGINE));
config.setScriptEngineName("rhino");
config.saveAll();
try {
initializeEngine();
if (!isInitialized()) {
reporter.reportWarning(this, Report.newBuilder(REPORT_CANNOT_LOAD_FALLBACK_ENGINE));
}
@ -260,11 +260,11 @@ public class CommandFilter extends CommandBase {
}
}
}
private void printPackageWarning(ScriptException e) {
reporter.reportWarning(this, Report.newBuilder(REPORT_PACKAGES_UNSUPPORTED_IN_ENGINE).error(e));
}
/**
* Initialize the current configured engine.
* @throws ScriptException If we are unable to import packages.
@ -272,14 +272,14 @@ public class CommandFilter extends CommandBase {
private void initializeEngine() throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
engine = manager.getEngineByName(config.getScriptEngineName());
// Import useful packages
if (engine != null) {
engine.eval("importPackage(org.bukkit);");
engine.eval("importPackage(com.comphenix.protocol.reflect);");
}
}
/**
* Determine if the filter engine has been successfully initialized.
* @return TRUE if it has, FALSE otherwise.
@ -304,9 +304,9 @@ public class CommandFilter extends CommandBase {
}
return defaultFailedHandler;
}
/**
* Determine whether or not to pass the given packet event to the packet listeners.
* Determine whether to pass the given packet event to the packet listeners.
* <p>
* Uses a default filter failure handler that simply prints the error message and removes the filter.
* @param event - the event.
@ -315,17 +315,17 @@ public class CommandFilter extends CommandBase {
public boolean filterEvent(PacketEvent event) {
return filterEvent(event, getDefaultErrorHandler());
}
/**
* Determine whether or not to pass the given packet event to the packet listeners.
* Determine whether to pass the given packet event to the packet listeners.
* @param event - the event.
* @param handler - failure handler.
* @return TRUE if we should, FALSE otherwise.
*/
public boolean filterEvent(PacketEvent event, FilterFailedHandler handler) {
for (Iterator<Filter> it = filters.iterator(); it.hasNext(); ) {
for (Iterator<Filter> it = filters.values().iterator(); it.hasNext(); ) {
Filter filter = it.next();
try {
if (!filter.evaluate(engine, event)) {
return false;
@ -350,7 +350,7 @@ public class CommandFilter extends CommandBase {
initalizeScript();
}
}
/*
* Description: Adds or removes a simple packet filter.
Usage: /<command> add|remove name [packet IDs]
@ -358,7 +358,7 @@ public class CommandFilter extends CommandBase {
@Override
protected boolean handleCommand(CommandSender sender, String[] args) {
checkScriptStatus();
if (!config.isDebug()) {
sender.sendMessage(ChatColor.RED + "Debug mode must be enabled in the configuration first!");
return true;
@ -367,30 +367,31 @@ public class CommandFilter extends CommandBase {
sender.sendMessage(ChatColor.RED + "JavaScript engine was not present. Filter system is disabled.");
return true;
}
final SubCommand command = parseCommand(args, 0);
final String name = args[1];
final String lowerCaseName = name.toLowerCase();
switch (command) {
case ADD:
// Never overwrite an existing filter
if (findFilter(name) != null) {
if(filters.containsKey(lowerCaseName)) {
sender.sendMessage(ChatColor.RED + "Filter " + name + " already exists. Remove it first.");
return true;
}
// Prepare the input to the packet type parser
Deque<String> rangeArguments = toQueue(args, 2);
final PacketTypeParser parser = new PacketTypeParser();
final Set<PacketType> packets = parser.parseTypes(rangeArguments, PacketTypeParser.DEFAULT_MAX_RANGE);
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 CompilationSuccessCanceller(), "function(event, packet) {");
new ConversationFactory(plugin).
withFirstPrompt(prompt).
withEscapeSequence("CANCEL").
@ -400,19 +401,19 @@ public class CommandFilter extends CommandBase {
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);
final String predicate = prompt.removeAccumulatedInput(event.getContext());
final 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);
filters.put(lowerCaseName, filter);
whom.sendRawMessage(ChatColor.GOLD + "Added filter " + name);
} catch (ScriptException e) {
e.printStackTrace();
@ -438,12 +439,12 @@ public class CommandFilter extends CommandBase {
break;
case REMOVE:
Filter filter = findFilter(name);
final Filter filter = filters.get(lowerCaseName);
// See if it exists before we remove it
if (filter != null) {
filter.close(engine);
filters.remove(filter);
filters.remove(lowerCaseName);
sender.sendMessage(ChatColor.GOLD + "Removed filter " + name);
} else {
sender.sendMessage(ChatColor.RED + "Unable to find a filter by the name " + name);
@ -453,22 +454,7 @@ public class CommandFilter extends CommandBase {
return true;
}
/**
* 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();