Skip to content

Commit 416a628

Browse files
authored
Add priority to event prefilters. (#1378)
This allows for prefilters to be processed in order of least to highest complexity, leading to more efficient processing of events that don't match. For instance, matching a regex is more expensive than matching a boolean, so we check the boolean first, and if it doesn't match, then we don't try the regex match, whether or not it would match. General prefilter types have been given a default complexity, but as every prefilter is different, it's also possible for individual overrides to provide a custom priority instead.
1 parent 6279947 commit 416a628

28 files changed

Lines changed: 588 additions & 386 deletions

src/main/java/com/laytonsmith/abstraction/bukkit/events/BukkitAbstractEventMixin.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import com.laytonsmith.core.Static;
55
import com.laytonsmith.core.constructs.CString;
66
import com.laytonsmith.core.constructs.Target;
7-
import com.laytonsmith.core.events.AbstractEvent;
7+
import com.laytonsmith.core.events.AbstractGenericEvent;
88
import com.laytonsmith.core.events.BindableEvent;
99
import com.laytonsmith.core.events.EventMixinInterface;
1010
import com.laytonsmith.core.exceptions.EventException;
@@ -26,9 +26,9 @@
2626

2727
public class BukkitAbstractEventMixin implements EventMixinInterface {
2828

29-
AbstractEvent mySuper;
29+
AbstractGenericEvent mySuper;
3030

31-
public BukkitAbstractEventMixin(AbstractEvent mySuper) {
31+
public BukkitAbstractEventMixin(AbstractGenericEvent mySuper) {
3232
this.mySuper = mySuper;
3333
}
3434

Lines changed: 5 additions & 328 deletions
Original file line numberDiff line numberDiff line change
@@ -1,334 +1,11 @@
11
package com.laytonsmith.core.events;
22

3-
import com.laytonsmith.PureUtilities.ClassLoading.ClassDiscovery;
4-
import com.laytonsmith.PureUtilities.Common.StreamUtils;
5-
import com.laytonsmith.abstraction.MCCommandSender;
6-
import com.laytonsmith.abstraction.MCPlayer;
7-
import com.laytonsmith.abstraction.events.MCPlayerEvent;
8-
import com.laytonsmith.annotations.core;
9-
import com.laytonsmith.annotations.hide;
10-
import com.laytonsmith.core.Documentation;
11-
import com.laytonsmith.core.LogLevel;
12-
import com.laytonsmith.core.MSLog;
13-
import com.laytonsmith.core.MSLog.Tags;
14-
import com.laytonsmith.core.MethodScriptCompiler;
15-
import com.laytonsmith.core.ParseTree;
16-
import com.laytonsmith.core.Static;
17-
import com.laytonsmith.core.constructs.CArray;
18-
import com.laytonsmith.core.constructs.CNull;
19-
import com.laytonsmith.core.constructs.Target;
20-
import com.laytonsmith.core.environments.CommandHelperEnvironment;
21-
import com.laytonsmith.core.environments.Environment;
22-
import com.laytonsmith.core.environments.StaticRuntimeEnv;
23-
import com.laytonsmith.core.events.prefilters.Prefilter;
24-
import com.laytonsmith.core.events.prefilters.PrefilterBuilder;
25-
import com.laytonsmith.core.exceptions.CRE.CREFormatException;
26-
import com.laytonsmith.core.exceptions.CancelCommandException;
27-
import com.laytonsmith.core.exceptions.ConfigRuntimeException;
28-
import com.laytonsmith.core.exceptions.EventException;
29-
import com.laytonsmith.core.exceptions.FunctionReturnException;
30-
import com.laytonsmith.core.exceptions.PrefilterNonMatchException;
31-
import com.laytonsmith.core.exceptions.ProgramFlowManipulationException;
32-
import com.laytonsmith.core.natives.interfaces.Mixed;
33-
import com.laytonsmith.core.profiler.ProfilePoint;
34-
35-
import java.net.URL;
36-
import java.util.HashMap;
37-
import java.util.Map;
38-
393
/**
40-
* This helper class implements a few of the common functions in event, and most (all?) Events should extend this class.
41-
*
4+
* This class is deprecated, and events should extend AbstractGenericEvent directly instead.
5+
* @deprecated Use {@link AbstractGenericEvent} instead.
426
*/
43-
public abstract class AbstractEvent implements Event, Comparable<Event> {
44-
45-
private EventMixinInterface mixin;
46-
// protected EventHandlerInterface handler;
47-
//
48-
// protected AbstractEvent(EventHandlerInterface handler){
49-
// this.handler = handler;
50-
// }
51-
//
52-
53-
public final void setAbstractEventMixin(EventMixinInterface mixin) {
54-
this.mixin = mixin;
55-
}
56-
57-
/**
58-
* This function should return true if the event code should be run, based on implementation specific conditions
59-
* for the BindableEvent.
60-
*
61-
* @param e The bindable event itself
62-
* @return {@code true} if the event code should be run
63-
*/
64-
public boolean shouldFire(BindableEvent e) {
65-
return this.mixin.shouldFire(e);
66-
}
67-
68-
/**
69-
* If the event needs to run special code when a player binds the event, it can be done here. By default, an
70-
* UnsupportedOperationException is thrown, but is caught and ignored.
71-
*/
72-
@Override
73-
public void bind(BoundEvent event) {
74-
75-
}
76-
77-
/**
78-
* If the event needs to run special code when a player unbinds the event, it can be done here. By default, an
79-
* UnsupportedOperationException is thrown, but is caught and ignored.
80-
*/
81-
@Override
82-
public void unbind(BoundEvent event) {
83-
84-
}
85-
86-
/**
87-
* If the event needs to run special code at server startup, it can be done here. By default, nothing happens.
88-
*/
89-
@Override
90-
public void hook() {
91-
92-
}
93-
94-
@Override
95-
public boolean matches(Map<String, Mixed> prefilter, BindableEvent e) throws PrefilterNonMatchException {
96-
throw new UnsupportedOperationException();
97-
}
98-
99-
/**
100-
* This function is run when the actual event occurs.
101-
*
102-
* @param tree The compiled parse tree
103-
* @param b The bound event
104-
* @param env The operating environment
105-
* @param activeEvent The active event being executed
106-
*/
107-
@Override
108-
public final void execute(ParseTree tree, BoundEvent b, Environment env, BoundEvent.ActiveEvent activeEvent) throws ConfigRuntimeException {
109-
preExecution(env, activeEvent);
110-
111-
// Events can have a player to put into the CommandHelperEnvironment.
112-
if(activeEvent.getUnderlyingEvent() instanceof MCPlayerEvent playerEvent) {
113-
env.getEnv(CommandHelperEnvironment.class).SetPlayer(playerEvent.getPlayer());
114-
} else {
115-
// Probably not a player event, but might still have a player context.
116-
Mixed c = activeEvent.getParsedEvent().get("player");
117-
if(c != null) {
118-
if(c instanceof CNull) {
119-
// This is a CNull "player", likely from an entity event, so we need to ensure player() does
120-
// not return a player inherited from the bind's parent environment.
121-
if(env.getEnv(CommandHelperEnvironment.class).GetPlayer() != null) {
122-
env.getEnv(CommandHelperEnvironment.class).SetPlayer(null);
123-
}
124-
} else {
125-
MCCommandSender p = Static.getServer().getPlayer(c.val());
126-
if(p == null) {
127-
// Check if event (possibly from an extension) injected the player but didn't extend MCPlayerEvent
128-
p = Static.GetInjectedPlayer(c.val());
129-
}
130-
if(p != null) {
131-
env.getEnv(CommandHelperEnvironment.class).SetPlayer((MCPlayer) p);
132-
} else {
133-
MSLog.GetLogger().w(Tags.GENERAL, c.val() + " offline for " + b.getEventName(), tree.getTarget());
134-
// Set env CommandSender to prevent incorrect inherited player from being used in a player event.
135-
if(env.getEnv(CommandHelperEnvironment.class).GetPlayer() != null) {
136-
env.getEnv(CommandHelperEnvironment.class).SetPlayer(null);
137-
}
138-
}
139-
}
140-
}
141-
}
142-
143-
ProfilePoint event = null;
144-
if(env.getEnv(StaticRuntimeEnv.class).GetProfiler() != null) {
145-
event = env.getEnv(StaticRuntimeEnv.class).GetProfiler().start(
146-
"Event " + b.getEventName() + " (defined at " + b.getTarget().toString() + ")", LogLevel.ERROR);
147-
}
148-
try {
149-
try {
150-
MethodScriptCompiler.execute(tree, env, null, null);
151-
} catch (CancelCommandException ex) {
152-
if(ex.getMessage() != null && !ex.getMessage().isEmpty()) {
153-
StreamUtils.GetSystemOut().println(ex.getMessage());
154-
}
155-
} catch (FunctionReturnException ex) {
156-
//We simply allow this to end the event execution
157-
} catch (ProgramFlowManipulationException ex) {
158-
ConfigRuntimeException.HandleUncaughtException(new CREFormatException("Unexpected control flow operation used.", ex.getTarget()), env);
159-
}
160-
} finally {
161-
if(event != null) {
162-
event.stop();
163-
}
164-
// Finally, among other things, we need to clean-up injected players and entities
165-
postExecution(env, activeEvent);
166-
}
167-
}
168-
169-
/**
170-
* This method is called before the event handling code is run, and provides a place for the event code itself to
171-
* modify the environment or active event data.
172-
*
173-
* @param env The environment, at the time just before the event handler is called.
174-
* @param activeEvent The event handler code.
175-
*/
176-
public void preExecution(Environment env, BoundEvent.ActiveEvent activeEvent) {
177-
178-
}
179-
180-
/**
181-
* This method is called after the event handling code is run, and provides a place for the event code itself to
182-
* modify or cleanup the environment or active event data.
183-
*
184-
* @param env The environment, at the time just before the event handler is called.
185-
* @param activeEvent The event handler code.
186-
* @throws UnsupportedOperationException If the preExecution isn't supported, this may be thrown, and it will be
187-
* ignored.
188-
*/
189-
public void postExecution(Environment env, BoundEvent.ActiveEvent activeEvent) {
190-
191-
}
192-
193-
/**
194-
* For sorting and optimizing events, we need a comparison operation. By default it is compared by looking at the
195-
* event name.
196-
*
197-
* @param o
198-
* @return
199-
*/
200-
@Override
201-
public int compareTo(Event o) {
202-
return this.getName().compareTo(o.getName());
203-
}
204-
205-
/**
206-
* Since most events are minecraft events, we return true by default.
207-
*
208-
* @return
209-
*/
210-
@Override
211-
public boolean supportsExternal() {
212-
return true;
213-
}
214-
215-
/**
216-
* If it is ok to by default do a simple conversion from a CArray to a Map, this method can do it for you. Likely
217-
* this is not acceptable, so hard-coding the conversion will be necessary.
218-
*
219-
* @param manualObject
220-
* @return
221-
*/
222-
public static Object DoConvert(CArray manualObject) {
223-
Map<String, Mixed> map = new HashMap<>();
224-
for(String key : manualObject.stringKeySet()) {
225-
map.put(key, manualObject.get(key, Target.UNKNOWN));
226-
}
227-
return map;
228-
}
229-
230-
public Map<String, Mixed> evaluate_helper(BindableEvent e) throws EventException {
231-
return mixin.evaluate_helper(e);
232-
}
233-
234-
/**
235-
* By default, this function triggers the event by calling the mixin handler. If this is not the desired behavior,
236-
* this method can be overridden in the actual event (if it's an external event, for instance)
237-
*
238-
* @param o
239-
*/
240-
@Override
241-
public void manualTrigger(BindableEvent o) {
242-
mixin.manualTrigger(o);
243-
}
244-
245-
@Override
246-
public void cancel(BindableEvent o, boolean state) {
247-
mixin.cancel(o, state);
248-
}
249-
250-
@Override
251-
public boolean isCancellable(BindableEvent o) {
252-
return mixin.isCancellable(o);
253-
}
254-
255-
@Override
256-
public boolean isCancelled(BindableEvent o) {
257-
return mixin.isCancelled(o);
258-
}
259-
260-
@Override
261-
public URL getSourceJar() {
262-
return ClassDiscovery.GetClassContainer(this.getClass());
263-
}
264-
265-
/**
266-
* Returns true if the event is annotated with @hide
267-
*
268-
* @return
269-
*/
270-
@Override
271-
public final boolean appearInDocumentation() {
272-
return this.getClass().getAnnotation(hide.class) != null;
273-
}
274-
275-
private static final Class[] EMPTY_CLASS = new Class[0];
276-
277-
@Override
278-
public Class<? extends Documentation>[] seeAlso() {
279-
return EMPTY_CLASS;
280-
}
281-
282-
/**
283-
* Most events should return true for this, but passive events may override this to return null.
284-
*
285-
* @return
286-
*/
287-
@Override
288-
public boolean addCounter() {
289-
return true;
290-
}
291-
292-
@Override
293-
public final boolean isCore() {
294-
Class c = this.getClass();
295-
do {
296-
if(c.getAnnotation(core.class) != null) {
297-
return true;
298-
}
299-
c = c.getDeclaringClass();
300-
} while(c != null);
301-
return false;
302-
}
303-
304-
private Map<String, Prefilter<? extends BindableEvent>> prefilterCache = null;
305-
private volatile boolean isCacheSaturated = false;
306-
307-
@Override
308-
public final Map<String, Prefilter<? extends BindableEvent>> getPrefilters() {
309-
if(!isCacheSaturated) {
310-
synchronized(this) {
311-
if(!isCacheSaturated) {
312-
PrefilterBuilder builder = getPrefilterBuilder();
313-
if(builder != null) {
314-
prefilterCache = builder.build();
315-
}
316-
isCacheSaturated = true;
317-
}
318-
}
319-
}
320-
return prefilterCache;
321-
}
322-
323-
/**
324-
* Returns the prefilter builder for this subclass. This is built by AbstractEvent and cached, so that calls
325-
* to getPrefilters will be faster for future calls.
326-
* @return
327-
*/
328-
// TODO: Once everything has this, this should be re-added, since everything should have its own version.
329-
// @ForceImplementation
330-
protected PrefilterBuilder getPrefilterBuilder() {
331-
return null;
332-
}
7+
@Deprecated
8+
public abstract class AbstractEvent
9+
extends AbstractGenericEvent<BindableEvent> {
33310

33411
}

0 commit comments

Comments
 (0)