> eventValues = getEventValuesList(time);
for (int i = 0; i < eventValues.size(); i++) {
EventValueInfo, ?> info = eventValues.get(i);
- if (info.event != e ? info.event.isAssignableFrom(e) : info.c.isAssignableFrom(c)) {
- eventValues.add(i, new EventValueInfo<>(e, c, g, excludeErrorMessage, excludes));
+ // We don't care for exact duplicates. Prefer Skript's over any addon.
+ if (info.event.equals(event) && info.c.equals(type))
+ return;
+ // If the events don't match, we prefer the highest subclass event.
+ // If the events match, we prefer the highest subclass type.
+ if (!info.event.equals(event) ? info.event.isAssignableFrom(event) : info.c.isAssignableFrom(type)) {
+ eventValues.add(i, new EventValueInfo<>(event, type, getter, excludeErrorMessage, excludes));
return;
}
}
- eventValues.add(new EventValueInfo<>(e, c, g, excludeErrorMessage, excludes));
+ eventValues.add(new EventValueInfo<>(event, type, getter, excludeErrorMessage, excludes));
}
-
+
/**
* Gets a specific value from an event. Returns null if the event doesn't have such a value (conversions are done to try and get the desired value).
*
@@ -225,42 +233,74 @@ public static T getEventValue(E e, Class c, int time) {
return null;
}
+ /**
+ * Checks if an event has multiple getters, including default ones.
+ *
+ * @param event the event class the getter will be getting from.
+ * @param type type of getter.
+ * @param time the event-value's time.
+ * @return true or false if the event and type have multiple getters.
+ */
+ public static Kleenean hasMultipleGetters(Class event, Class type, int time) {
+ List> getters = getEventValueGetters(event, type, time, true);
+ if (getters == null)
+ return Kleenean.UNKNOWN;
+ return Kleenean.get(getters.size() > 1);
+ }
+
/**
* Returns a getter to get a value from in an event.
*
* Can print an error if the event value is blocked for the given event.
*
- * @param event the event class the getter will be getting from
- * @param c type of getter
- * @param time the event-value's time
- * @return A getter to get values for a given type of events
+ * @param event the event class the getter will be getting from.
+ * @param type type of getter.
+ * @param time the event-value's time.
+ * @return A getter to get values for a given type of events.
* @see #registerEventValue(Class, Class, Getter, int)
* @see EventValueExpression#EventValueExpression(Class)
*/
@Nullable
- public static Getter extends T, ? super E> getEventValueGetter(Class event, Class c, int time) {
- return getEventValueGetter(event, c, time, true);
+ public static Getter extends T, ? super E> getEventValueGetter(Class event, Class type, int time) {
+ return getEventValueGetter(event, type, time, true);
+ }
+
+ @Nullable
+ private static Getter extends T, ? super E> getEventValueGetter(Class event, Class type, int time, boolean allowDefault) {
+ List> list = getEventValueGetters(event, type, time, allowDefault);
+ if (list == null || list.isEmpty())
+ return null;
+ return list.get(0);
}
+ /*
+ * We need to be able to collect all possible event-values to a list for determining problematic collisions.
+ * Always return after the loop check if the list is not empty.
+ */
@Nullable
@SuppressWarnings("unchecked")
- private static Getter extends T, ? super E> getEventValueGetter(Class event, Class c, int time, boolean allowDefault) {
+ private static List> getEventValueGetters(Class event, Class type, int time, boolean allowDefault) {
List> eventValues = getEventValuesList(time);
+ List> list = new ArrayList<>();
// First check for exact classes matching the parameters.
- Getter extends T, ? super E> exact = (Getter extends T, ? super E>) getExactEventValueGetter(event, c, time);
- if (exact != null)
- return exact;
+ Getter extends T, ? super E> exact = (Getter extends T, ? super E>) getExactEventValueGetter(event, type, time);
+ if (exact != null) {
+ list.add(exact);
+ return list;
+ }
// Second check for assignable subclasses.
for (EventValueInfo, ?> eventValueInfo : eventValues) {
- if (!c.isAssignableFrom(eventValueInfo.c))
+ if (!type.isAssignableFrom(eventValueInfo.c))
continue;
if (!checkExcludes(eventValueInfo, event))
return null;
- if (eventValueInfo.event.isAssignableFrom(event))
- return (Getter extends T, ? super E>) eventValueInfo.getter;
+ if (eventValueInfo.event.isAssignableFrom(event)) {
+ list.add((Getter extends T, ? super E>) eventValueInfo.getter);
+ continue;
+ }
if (!event.isAssignableFrom(eventValueInfo.event))
continue;
- return new Getter() {
+ list.add(new Getter() {
@Override
@Nullable
public T get(E event) {
@@ -268,67 +308,80 @@ public T get(E event) {
return null;
return ((Getter extends T, E>) eventValueInfo.getter).get(event);
}
- };
+ });
+ continue;
}
+ if (!list.isEmpty())
+ return list;
// Most checks have returned before this below is called, but Skript will attempt to convert or find an alternative.
// Third check is if the returned object matches the class.
for (EventValueInfo, ?> eventValueInfo : eventValues) {
- if (!eventValueInfo.c.isAssignableFrom(c))
+ if (!eventValueInfo.c.isAssignableFrom(type))
continue;
boolean checkInstanceOf = !eventValueInfo.event.isAssignableFrom(event);
if (checkInstanceOf && !event.isAssignableFrom(eventValueInfo.event))
continue;
if (!checkExcludes(eventValueInfo, event))
return null;
- return new Getter() {
+ list.add(new Getter() {
@Override
@Nullable
public T get(E event) {
if (checkInstanceOf && !eventValueInfo.event.isInstance(event))
return null;
Object object = ((Getter super T, ? super E>) eventValueInfo.getter).get(event);
- if (c.isInstance(object))
+ if (type.isInstance(object))
return (T) object;
return null;
}
- };
+ });
+ continue;
}
+ if (!list.isEmpty())
+ return list;
// Fourth check will attempt to convert the event value to the requesting type.
// This first for loop will check that the events are exact. See issue #5016
for (EventValueInfo, ?> eventValueInfo : eventValues) {
if (!event.equals(eventValueInfo.event))
continue;
- Getter extends T, ? super E> getter = (Getter extends T, ? super E>) getConvertedGetter(eventValueInfo, c, false);
+ Getter extends T, ? super E> getter = (Getter extends T, ? super E>) getConvertedGetter(eventValueInfo, type, false);
if (getter == null)
continue;
if (!checkExcludes(eventValueInfo, event))
return null;
- return getter;
+ list.add(getter);
+ continue;
}
+ if (!list.isEmpty())
+ return list;
// This loop will attempt to look for converters assignable to the class of the provided event.
for (EventValueInfo, ?> eventValueInfo : eventValues) {
// The requesting event must be assignable to the event value's event. Otherwise it'll throw an error.
if (!event.isAssignableFrom(eventValueInfo.event))
continue;
- Getter extends T, ? super E> getter = (Getter extends T, ? super E>) getConvertedGetter(eventValueInfo, c, true);
+ Getter extends T, ? super E> getter = (Getter extends T, ? super E>) getConvertedGetter(eventValueInfo, type, true);
if (getter == null)
continue;
if (!checkExcludes(eventValueInfo, event))
return null;
- return getter;
+ list.add(getter);
+ continue;
}
+ if (!list.isEmpty())
+ return list;
// If the check should try again matching event values with a 0 time (most event values).
if (allowDefault && time != 0)
- return getEventValueGetter(event, c, 0, false);
+ return getEventValueGetters(event, type, 0, false);
return null;
}
/**
* Check if the event value states to exclude events.
+ * False if the current EventValueInfo cannot operate in the provided event.
*
* @param info The event value info that will be used to grab the value from
* @param event The event class to check the excludes against.
diff --git a/src/main/resources/lang/default.lang b/src/main/resources/lang/default.lang
index 4d0bfe3dff3..cd51c2e1efe 100644
--- a/src/main/resources/lang/default.lang
+++ b/src/main/resources/lang/default.lang
@@ -1987,7 +1987,7 @@ types:
inventory: inventor¦y¦ies @an
player: player¦s @a
offlineplayer: offline player¦s @a
- commandsender: player¦¦s¦/console @a
+ commandsender: command sender¦s @a
inventoryholder: inventory holder¦s @an
gamemode: gamemode¦s @a
material: material¦s @a