Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ public Time time() {
return source.excludedErrorMessage();
}

@Override
public boolean contextDependent() {
return source.contextDependent();
}

@Override
public boolean matches(EventValue<?, ?> eventValue) {
return matches(eventValue.eventClass(), eventValue.valueClass(), eventValue.patterns());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ static <E extends Event, V> EventValue<E, V> simple(Class<E> eventClass, Class<V
@Contract(pure = true)
@Nullable String excludedErrorMessage();


/**
* Whether this event value's validation result depends on state outside of the event class
* (e.g. configuration, server state, registry contents). When {@code true}, resolutions that
* consider this event value are not cached by {@link EventValueRegistry}, ensuring the
* validator is consulted on every lookup.
* <p>
* Defaults to {@code false}; only set this when an {@link Builder#eventValidator(Function)
* event validator} is not pure for a given event class.
*
* @return {@code true} if resolutions involving this value should bypass the cache
*/
@Contract(pure = true)
boolean contextDependent();

/**
* Checks whether this event value matches the provided event value in terms of
* event class, value class, and identifier patterns.
Expand Down Expand Up @@ -453,6 +468,18 @@ default Builder<E, V> excludes(Class<? extends E> event1, Class<? extends E> eve
@Contract(value = "_ -> this", mutates = "this")
Builder<E, V> excludedErrorMessage(String excludedErrorMessage);

/**
* Marks this event value as context-dependent. Resolutions that consider this value will
* not be cached, so the {@linkplain #eventValidator(Function) event validator} is
* consulted on every lookup. Use this when the validator's answer for a given event
* class may change at runtime (e.g. because it consults external state).
*
* @return this builder
* @see EventValue#contextDependent()
*/
@Contract(value = " -> this", mutates = "this")
Builder<E, V> contextDependent();

/**
* Builds the event value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class EventValueImpl<E extends Event, V> implements EventValue<E, V> {
private final Time time;
private final Collection<Class<? extends E>> excludedEvents;
private final @Nullable String excludedErrorMessage;
private final boolean contextDepenent;

private SkriptPattern[] compiledPatterns;

Expand All @@ -51,6 +52,7 @@ private EventValueImpl(BuilderImpl<E, V> builder) {
this.time = builder.time;
this.excludedEvents = builder.excludedEvents;
this.excludedErrorMessage = builder.excludedErrorMessage;
this.contextDepenent = builder.contextDependent;
}

@Override
Expand Down Expand Up @@ -153,6 +155,11 @@ public Time time() {
return excludedErrorMessage;
}

@Override
public boolean contextDependent() {
return contextDepenent;
}

@Override
public boolean matches(EventValue<?, ?> eventValue) {
return matches(eventValue.eventClass(), eventValue.valueClass(), eventValue.patterns())
Expand Down Expand Up @@ -204,6 +211,7 @@ static class BuilderImpl<E extends Event, V> implements Builder<E, V> {
private Time time = Time.NOW;
private Collection<Class<? extends E>> excludedEvents = Collections.emptyList();
private @Nullable String excludedErrorMessage;
private boolean contextDependent = false;

BuilderImpl(Class<E> eventClass, Class<V> valueClass) {
this.eventClass = eventClass;
Expand Down Expand Up @@ -260,6 +268,12 @@ public Builder<E, V> excludedErrorMessage(String excludedErrorMessage) {
return this;
}

@Override
public Builder<E, V> contextDependent() {
this.contextDependent = true;
return this;
}

@Override
public EventValue<E, V> build() {
if (patterns == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public boolean isRegistered(Class<? extends Event> eventClass, Class<?> valueCla
return false;
}

private <E extends Event, V> void cache(Input<E, ?> input, Resolution<E, V> resolution, boolean contextDependent) {
if (contextDependent)
return;
eventValuesCache.put(input, resolution);
}

@Override
public <E extends Event, V> Resolution<E, V> resolve(Class<E> eventClass, String identifier) {
return resolve(eventClass, identifier, EventValue.Time.NOW);
Expand Down Expand Up @@ -96,22 +102,23 @@ public <E extends Event, V> Resolution<E, V> resolve(
return resolution;

//noinspection unchecked
resolution = Resolver.<E, V>builder(eventClass)
var output = Resolver.<E, V>builder(eventClass)
.filter(ev -> ClassUtils.isRelatedTo(ev.eventClass(), eventClass) && ev.matchesInput(identifier))
.comparator(Resolver.EVENT_DISTANCE_COMPARATOR)
.mapper(ev -> (EventValue<E, V>) ev.getConverted(eventClass, ev.valueClass()))
.build().resolve(eventValues(time));
resolution = output.resolution();

if (resolution.successful()) {
eventValuesCache.put(input, resolution);
if (resolution.successful() || resolution.errored()) {
cache(input, resolution, output.contextDependent());
return resolution;
}

if (flags.has(Flag.FALLBACK_TO_DEFAULT_TIME_STATE))
return resolve(eventClass, identifier, EventValue.Time.NOW, flags);

resolution = Resolution.empty();
eventValuesCache.put(input, resolution);
cache(input, resolution, output.contextDependent());
return resolution;
}

Expand Down Expand Up @@ -144,31 +151,36 @@ public <E extends Event, V> Resolution<E, V> resolve(
if (resolution != null)
return resolution;

resolution = resolveExact(eventClass, valueClass, time)
.anyOptional()
.map(eventValue -> Resolution.of(Collections.singletonList(eventValue)))
.orElse(Resolution.empty());
var output = Resolver.exact(eventClass, valueClass).resolve(eventValues(time));
resolution = output.resolution();

if (resolution.successful() || resolution.errored()) {
eventValuesCache.put(input, resolution);
cache(input, resolution, output.contextDependent());
return resolution;
}

resolution = resolveNearest(eventClass, valueClass, time);
output = resolveNearest(eventClass, valueClass, time);
resolution = output.resolution();

if (resolution.successful() || resolution.errored()) {
eventValuesCache.put(input, resolution);
cache(input, resolution, output.contextDependent());
return resolution;
}

if (flags.has(Flag.ALLOW_CONVERSION)) {
resolution = resolveWithDowncastConversion(eventClass, valueClass, time);
output = resolveWithDowncastConversion(eventClass, valueClass, time);
resolution = output.resolution();

if (resolution.successful() || resolution.errored()) {
eventValuesCache.put(input, resolution);
cache(input, resolution, output.contextDependent());
return resolution;
}

resolution = resolveWithConversion(eventClass, valueClass, time);
output = resolveWithConversion(eventClass, valueClass, time);
resolution = output.resolution();

if (resolution.successful() || resolution.errored()) {
eventValuesCache.put(input, resolution);
cache(input, resolution, output.contextDependent());
return resolution;
}
}
Expand All @@ -177,7 +189,6 @@ public <E extends Event, V> Resolution<E, V> resolve(
return resolve(eventClass, valueClass, EventValue.Time.NOW, flags);

resolution = Resolution.empty();
eventValuesCache.put(input, resolution);
return resolution;
}

Expand All @@ -187,17 +198,13 @@ public <E extends Event, V> Resolution<E, V> resolveExact(
Class<V> valueClass,
EventValue.Time time
) {
return Resolver.builder(eventClass, valueClass)
.filter(ev -> ev.eventClass().isAssignableFrom(eventClass) && ev.valueClass().equals(valueClass))
.comparator(Resolver.EVENT_DISTANCE_COMPARATOR)
.filterMatches()
.build().resolve(eventValues(time));
return Resolver.exact(eventClass, valueClass).resolve(eventValues(time)).resolution();
}

/**
* Resolves to the nearest event and value class without conversion.
*/
private <E extends Event, V> Resolution<E, ? extends V> resolveNearest(
private <E extends Event, V> Resolver.Output<E, V> resolveNearest(
Class<E> eventClass,
Class<V> valueClass,
EventValue.Time time
Expand All @@ -214,7 +221,7 @@ public <E extends Event, V> Resolution<E, V> resolveExact(
* Resolves using downcast conversion when the desired value class is a supertype
* of the registered value class.
*/
private <E extends Event, V> Resolution<E, V> resolveWithDowncastConversion(
private <E extends Event, V> Resolver.Output<E, V> resolveWithDowncastConversion(
Class<E> eventClass,
Class<V> valueClass,
EventValue.Time time
Expand All @@ -233,7 +240,7 @@ private <E extends Event, V> Resolution<E, V> resolveWithDowncastConversion(
/**
* Resolves using {@link Converters} to convert value type when needed.
*/
private <E extends Event, V> Resolution<E, V> resolveWithConversion(
private <E extends Event, V> Resolver.Output<E, V> resolveWithConversion(
Class<E> eventClass,
Class<V> valueClass,
EventValue.Time time
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,25 @@ private Resolver(
* Resolves the given list of {@link EventValue}s.
*
* @param eventValues The event values to resolve.
* @return The resolution containing the best candidates.
* @return The resolution containing the best candidates, along with a flag indicating
* whether any candidate considered was {@linkplain EventValue#contextDependent() context-dependent}.
*/
public EventValueRegistry.Resolution<E, V> resolve(List<EventValue<?, ?>> eventValues) {
public Output<E, V> resolve(List<EventValue<?, ?>> eventValues) {
List<EventValue<E, V>> best = new ArrayList<>();
EventValue<?, ?> bestMatch = null;
boolean contextDependent = false;

for (EventValue<?, ?> eventValue : eventValues) {
if (!filter.test(eventValue))
continue;

contextDependent |= eventValue.contextDependent();
switch (eventValue.validate(eventClass)) {
case INVALID -> {
continue;
}
case ABORT -> {
return EventValueRegistry.Resolution.error();
return new Output<>(EventValueRegistry.Resolution.error(), contextDependent);
}
}

Expand All @@ -109,9 +113,10 @@ public EventValueRegistry.Resolution<E, V> resolve(List<EventValue<?, ?>> eventV
best.add(converted);
}
}

if (valueClass != null && filterMatches)
return EventValueRegistry.Resolution.of(filterEventValues(valueClass, best));
return EventValueRegistry.Resolution.of(best);
return new Output<>(EventValueRegistry.Resolution.of(filterEventValues(valueClass, best)), contextDependent);
return new Output<>(EventValueRegistry.Resolution.of(best), contextDependent);
}

/**
Expand Down Expand Up @@ -173,6 +178,14 @@ static <E extends Event, V> Builder<E, V> builder(Class<E> eventClass, Class<V>
return new Builder<>(eventClass, valueClass);
}

static <E extends Event, V> Resolver<E, V> exact(Class<E> eventClass, Class<V> valueClass) {
return Resolver.builder(eventClass, valueClass)
.filter(ev -> ev.eventClass().isAssignableFrom(eventClass) && ev.valueClass().equals(valueClass))
.comparator(Resolver.EVENT_DISTANCE_COMPARATOR)
.filterMatches()
.build();
}

/**
* A builder for {@link Resolver}.
*
Expand Down Expand Up @@ -309,4 +322,19 @@ interface EventValueComparatorFactory {

}

/**
* The result of a {@link Resolver#resolve(List) resolve} call.
*
* @param resolution The resolution produced.
* @param contextDependent Whether any candidate considered during resolution was
* {@linkplain EventValue#contextDependent() context-dependent},
* meaning the result must not be cached.
* @param <E> The event type.
* @param <V> The value type.
*/
record Output<E extends Event, V>(
EventValueRegistry.Resolution<E, V> resolution,
boolean contextDependent
) {}

}
Loading