Skip to content

Commit 5dd71b8

Browse files
committed
basic implementations for Contains and Named properties
1 parent 4b1a30c commit 5dd71b8

14 files changed

Lines changed: 703 additions & 14 deletions

File tree

src/main/java/ch/njol/skript/Skript.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
import org.skriptlang.skript.lang.converter.Converters;
108108
import org.skriptlang.skript.lang.entry.EntryValidator;
109109
import org.skriptlang.skript.lang.experiment.ExperimentRegistry;
110+
import org.skriptlang.skript.lang.properties.Property;
111+
import org.skriptlang.skript.lang.properties.PropertyRegistry;
110112
import org.skriptlang.skript.lang.script.Script;
111113
import org.skriptlang.skript.lang.structure.Structure;
112114
import org.skriptlang.skript.lang.structure.StructureInfo;
@@ -370,6 +372,12 @@ public static ExperimentRegistry experiments() {
370372
return experimentRegistry;
371373
}
372374

375+
376+
private static PropertyRegistry propertyRegistry;
377+
public static PropertyRegistry getPropertyRegistry() {
378+
return propertyRegistry;
379+
}
380+
373381
/**
374382
* @return The folder containing all Scripts.
375383
*/
@@ -498,6 +506,9 @@ public void onEnable() {
498506
experimentRegistry = new ExperimentRegistry(this);
499507
Feature.registerAll(getAddonInstance(), experimentRegistry);
500508

509+
propertyRegistry = new PropertyRegistry(this);
510+
Property.registerDefaultProperties();
511+
501512
// Load classes which are always safe to use
502513
new JavaClasses(); // These may be needed in configuration
503514

@@ -523,7 +534,7 @@ public void onEnable() {
523534
if (pauseThreshold > -1) {
524535
Skript.warning("Minecraft server pausing is enabled!");
525536
Skript.warning("Scripts that interact with the world or entities may not work as intended when the server is paused and may crash your server.");
526-
Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.properties to make sure you don't encounter any issues.");
537+
Skript.warning("Consider setting 'pause-when-empty-seconds' to -1 in server.propertyRegistry to make sure you don't encounter any issues.");
527538
}
528539
}
529540

@@ -577,6 +588,7 @@ public void onEnable() {
577588
getAddonInstance().loadClasses("ch.njol.skript",
578589
"conditions", "effects", "events", "expressions", "entity", "sections", "structures");
579590
getAddonInstance().loadClasses("org.skriptlang.skript.bukkit", "misc");
591+
getAddonInstance().loadClasses("org.skriptlang.skript.lang", "properties");
580592
// todo: become proper module once registry api is merged
581593
FishingModule.load();
582594
BreedingModule.load();

src/main/java/ch/njol/skript/classes/ClassInfo.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
import ch.njol.skript.lang.DefaultExpression;
77
import ch.njol.skript.lang.util.SimpleLiteral;
88
import ch.njol.skript.localization.Noun;
9+
import ch.njol.skript.registrations.Classes;
910
import ch.njol.util.coll.iterator.ArrayIterator;
1011
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1112
import org.bukkit.event.Event;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.jetbrains.annotations.Nullable;
15+
import org.skriptlang.skript.lang.properties.Property;
16+
import org.skriptlang.skript.lang.properties.PropertyInfo;
1417

15-
import java.util.Arrays;
16-
import java.util.HashSet;
17-
import java.util.Iterator;
18-
import java.util.Set;
18+
import java.util.*;
1919
import java.util.function.Supplier;
2020
import java.util.regex.Pattern;
2121
import java.util.regex.PatternSyntaxException;
@@ -476,4 +476,29 @@ public String toString(final @Nullable Event event, final boolean debug) {
476476
return getName().getSingular();
477477
}
478478

479+
480+
private final Map<Property<?>, PropertyInfo<?>> propertyInfos = new HashMap<>();
481+
482+
public <Handler> ClassInfo<T> property(Property<Handler> property, @NotNull Handler handler) {
483+
if (propertyInfos.containsKey(property)) {
484+
throw new IllegalStateException("Property " + property.name() + " is already registered for the " + c.getName() + " type.");
485+
}
486+
propertyInfos.put(property, new PropertyInfo<>(property, handler));
487+
Classes.CLASS_INFOS_BY_PROPERTY.computeIfAbsent(property, k -> new ArrayList<>()).add(this);
488+
return this;
489+
}
490+
491+
public boolean hasProperty(Property<?> property) {
492+
return propertyInfos.containsKey(property);
493+
}
494+
495+
public <Handler> @Nullable PropertyInfo<Handler> getPropertyInfo(Property<Handler> property) {
496+
if (!propertyInfos.containsKey(property)) {
497+
return null;
498+
}
499+
//noinspection unchecked
500+
return (PropertyInfo<Handler>) propertyInfos.get(property);
501+
}
502+
503+
479504
}

src/main/java/ch/njol/skript/classes/data/BukkitClasses.java

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@
6060
import org.bukkit.potion.PotionEffectType;
6161
import org.bukkit.util.CachedServerIcon;
6262
import org.bukkit.util.Vector;
63+
import org.jetbrains.annotations.NotNull;
6364
import org.jetbrains.annotations.Nullable;
65+
import org.skriptlang.skript.lang.properties.Property;
66+
import org.skriptlang.skript.lang.properties.Property.NameHandler;
6467

6568
import java.io.StreamCorruptedException;
6669
import java.util.*;
@@ -580,7 +583,23 @@ public String getDebugMessage(final Inventory i) {
580583
public String toVariableNameString(final Inventory i) {
581584
return "inventory of " + Classes.toString(i.getHolder(), StringMode.VARIABLE_NAME);
582585
}
583-
}).changer(DefaultChangers.inventoryChanger));
586+
}).changer(DefaultChangers.inventoryChanger)
587+
.property(Property.CONTAINS, new Property.ContainsHandler<Inventory, Object>() {
588+
@Override
589+
public boolean contains(Inventory container, Object element) {
590+
if (element instanceof ItemType type) {
591+
return type.isContainedIn(container);
592+
} else if (element instanceof ItemStack stack) {
593+
return container.containsAtLeast(stack, stack.getAmount());
594+
}
595+
return false;
596+
}
597+
598+
@Override
599+
public Class<?>[] elementTypes() {
600+
return new Class[]{ItemType.class, ItemStack.class};
601+
}
602+
}));
584603

585604
Classes.registerClass(new EnumClassInfo<>(InventoryAction.class, "inventoryaction", "inventory actions")
586605
.user("inventory ?actions?")
@@ -679,7 +698,18 @@ public String getDebugMessage(final Player p) {
679698
}
680699
})
681700
.changer(DefaultChangers.playerChanger)
682-
.serializeAs(OfflinePlayer.class));
701+
.serializeAs(OfflinePlayer.class)
702+
.property(Property.NAME, new NameHandler<Player, String>() {
703+
@Override
704+
public String name(Player player) {
705+
return player.getName();
706+
}
707+
708+
@Override
709+
public @NotNull Class<String> returnType() {
710+
return String.class;
711+
}
712+
}));
683713

684714
Classes.registerClass(new ClassInfo<>(OfflinePlayer.class, "offlineplayer")
685715
.user("offline ?players?")

src/main/java/ch/njol/skript/classes/data/JavaClasses.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import org.jetbrains.annotations.Contract;
1919
import org.jetbrains.annotations.Nullable;
2020
import org.joml.Quaternionf;
21+
import org.skriptlang.skript.lang.properties.Property;
22+
import org.skriptlang.skript.lang.properties.Property.ContainsHandler;
2123

2224
import java.io.StreamCorruptedException;
2325
import java.util.UUID;
@@ -301,6 +303,18 @@ public String deserialize(String s) {
301303
public boolean mustSyncDeserialization() {
302304
return false;
303305
}
306+
})
307+
.property(Property.CONTAINS, new ContainsHandler<String, String>() {
308+
@Override
309+
public boolean contains(String container, String element) {
310+
return StringUtils.contains(container, element, SkriptConfig.caseSensitive.value());
311+
}
312+
313+
@Override
314+
public Class<? extends String>[] elementTypes() {
315+
//noinspection unchecked
316+
return new Class[]{String.class};
317+
}
304318
}));
305319

306320
// joml type - for display entities

src/main/java/ch/njol/skript/classes/data/SkriptClasses.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import org.bukkit.Material;
2929
import org.bukkit.enchantments.Enchantment;
3030
import org.bukkit.inventory.ItemStack;
31+
import org.jetbrains.annotations.NotNull;
3132
import org.jetbrains.annotations.Nullable;
33+
import org.skriptlang.skript.lang.properties.Property;
3234
import org.skriptlang.skript.lang.script.Script;
3335
import org.skriptlang.skript.lang.util.SkriptQueue;
3436
import org.skriptlang.skript.util.Executable;
@@ -217,7 +219,31 @@ public String toVariableNameString(final ItemType t) {
217219
}
218220
})
219221
.cloner(ItemType::clone)
220-
.serializer(new YggdrasilSerializer<>()));
222+
.serializer(new YggdrasilSerializer<>())
223+
.property(Property.NAME, new Property.NameHandler<ItemType, String>() {
224+
@Override
225+
public String name(ItemType itemType) {
226+
return itemType.name();
227+
}
228+
229+
@Override
230+
public Class<?> @Nullable [] acceptChange(Changer.ChangeMode mode) {
231+
if (mode == Changer.ChangeMode.SET || mode == Changer.ChangeMode.RESET)
232+
return new Class[] {String.class};
233+
return null;
234+
}
235+
236+
@Override
237+
public void change(ItemType itemType, Object @Nullable [] delta, Changer.ChangeMode mode) {
238+
String name = delta != null ? (String) delta[0] : null;
239+
itemType.setName(name);
240+
}
241+
242+
@Override
243+
public @NotNull Class<String> returnType() {
244+
return String.class;
245+
}
246+
}));
221247

222248
Classes.registerClass(new ClassInfo<>(Time.class, "time")
223249
.user("times?")

src/main/java/ch/njol/skript/lang/ExpressionList.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,13 +149,14 @@ public boolean check(Event event, Predicate<? super T> checker) {
149149
@SuppressWarnings("unchecked")
150150
public <R> @Nullable Expression<? extends R> getConvertedExpression(Class<R>... to) {
151151
Expression<? extends R>[] exprs = new Expression[expressions.length];
152-
Class<?>[] returnTypes = new Class[expressions.length];
152+
Set<Class<?>> possibleReturnTypeSet = new HashSet<>();
153153
for (int i = 0; i < exprs.length; i++) {
154154
if ((exprs[i] = expressions[i].getConvertedExpression(to)) == null)
155155
return null;
156-
returnTypes[i] = exprs[i].getReturnType();
156+
possibleReturnTypeSet.addAll(Arrays.asList(exprs[i].possibleReturnTypes()));
157157
}
158-
return new ExpressionList<>(exprs, (Class<R>) Classes.getSuperClassInfo(returnTypes).getC(), returnTypes, and, this);
158+
Class<?>[] possibleReturnTypes = possibleReturnTypeSet.toArray(new Class[0]);
159+
return new ExpressionList<>(exprs, (Class<R>) Classes.getSuperClassInfo(possibleReturnTypes).getC(), possibleReturnTypes, and, this);
159160
}
160161

161162
@Override

src/main/java/ch/njol/skript/registrations/Classes.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,11 @@
2929
import org.bukkit.Bukkit;
3030
import org.bukkit.ChatColor;
3131
import org.bukkit.Chunk;
32-
import org.jetbrains.annotations.Contract;
33-
import org.jetbrains.annotations.Nullable;
34-
import org.jetbrains.annotations.Unmodifiable;
32+
import org.jetbrains.annotations.*;
3533
import org.skriptlang.skript.lang.converter.Converter;
3634
import org.skriptlang.skript.lang.converter.ConverterInfo;
3735
import org.skriptlang.skript.lang.converter.Converters;
36+
import org.skriptlang.skript.lang.properties.Property;
3837

3938
import java.io.*;
4039
import java.lang.reflect.Array;
@@ -345,6 +344,15 @@ public static <T> List<ClassInfo<? super T>> getAllSuperClassInfos(Class<T> c) {
345344
return list;
346345
}
347346

347+
348+
@ApiStatus.Internal
349+
public static final Map<Property<?>, List<ClassInfo<?>>> CLASS_INFOS_BY_PROPERTY = new HashMap<>();
350+
351+
public static @NotNull List<ClassInfo<?>> getClassInfosByProperty(@NotNull Property<?> property) {
352+
return CLASS_INFOS_BY_PROPERTY.getOrDefault(property, Collections.emptyList());
353+
}
354+
355+
348356
/**
349357
* Gets a class by its code name
350358
*
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package org.skriptlang.skript.lang.properties;
2+
3+
import ch.njol.skript.Skript;
4+
import ch.njol.skript.lang.Condition;
5+
import ch.njol.skript.lang.Expression;
6+
import ch.njol.skript.lang.SkriptParser.ParseResult;
7+
import ch.njol.skript.lang.util.SimpleExpression;
8+
import ch.njol.util.Kleenean;
9+
import org.bukkit.event.Event;
10+
import org.jetbrains.annotations.Nullable;
11+
import org.skriptlang.skript.lang.comparator.Comparators;
12+
import org.skriptlang.skript.lang.comparator.Relation;
13+
import org.skriptlang.skript.lang.properties.Property.ContainsHandler;
14+
import org.skriptlang.skript.lang.properties.PropertyUtils.PropertyMap;
15+
16+
public class PropCondContains extends Condition {
17+
18+
static {
19+
Skript.registerCondition(PropCondContains.class,
20+
"property %inventories% (has|have) %itemtypes% [in [(the[ir]|his|her|its)] inventory]",
21+
"property %inventories% (doesn't|does not|do not|don't) have %itemtypes% [in [(the[ir]|his|her|its)] inventory]",
22+
"property %inventories/strings/objects% contain[(1¦s)] %itemtypes/strings/objects%",
23+
"property %inventories/strings/objects% (doesn't|does not|do not|don't) contain %itemtypes/strings/objects%");
24+
}
25+
26+
private Expression<?> haystack;
27+
private Expression<?> needles;
28+
private PropertyMap<ContainsHandler<?, ?>> properties;
29+
30+
boolean explicitSingle = false;
31+
32+
@Override
33+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
34+
35+
this.haystack = PropertyUtils.asProperty(Property.CONTAINS, expressions[0]);
36+
if (haystack == null) {
37+
Skript.error("The expression " + expressions[0] + " returns types that do not contain anything.");
38+
return false;
39+
}
40+
// determine if the expression truly has a name property
41+
42+
properties = PropertyUtils.getPossiblePropertyInfos(Property.CONTAINS, haystack);
43+
if (properties.isEmpty()) {
44+
Skript.error("The expression " + haystack + " returns types that do not contain anything.");
45+
return false; // no name property found
46+
}
47+
// determine possible return types
48+
// elementTypes = getElementTypes(properties);
49+
this.needles = expressions[1];
50+
explicitSingle = matchedPattern == 2 && parseResult.mark != 1 || haystack.isSingle();
51+
52+
needles = expressions[1];
53+
return true;
54+
}
55+
56+
private Class<?>[][] getElementTypes(PropertyMap<ContainsHandler<?, ?>> properties) {
57+
return properties.values().stream()
58+
.map((propertyInfo) -> propertyInfo.handler().elementTypes())
59+
.toArray(Class<?>[][]::new);
60+
}
61+
62+
@Override
63+
public boolean check(Event event) {
64+
Object[] haystacks = haystack.getAll(event);
65+
boolean haystackAnd = haystack.getAnd();
66+
Object[] needles = this.needles.getAll(event);
67+
boolean needlesAnd = this.needles.getAnd();
68+
if (haystacks.length == 0) {
69+
return isNegated();
70+
}
71+
72+
// We should compare the contents of the haystacks to the needles
73+
if (explicitSingle) {
74+
// use properties
75+
return SimpleExpression.check(haystacks, (haystack) -> {
76+
// for each haystack, determine property
77+
//noinspection unchecked
78+
var handler = (ContainsHandler<Object, Object>) properties.getHandler(haystack.getClass());
79+
if (handler == null) {
80+
return false;
81+
}
82+
// if found, use it to check against needles
83+
return SimpleExpression.check(needles, (needle) ->
84+
handler.canContain(needle.getClass())
85+
&& handler.contains(haystack, needle),
86+
false, needlesAnd);
87+
}, isNegated(), haystackAnd);
88+
89+
// compare the haystacks themselves to the needles
90+
} else {
91+
return this.needles.check(event, o1 -> {
92+
for (Object o2 : haystacks) {
93+
if (Comparators.compare(o1, o2) == Relation.EQUAL)
94+
return true;
95+
}
96+
return false;
97+
}, isNegated());
98+
}
99+
}
100+
101+
@Override
102+
public String toString(@Nullable Event event, boolean debug) {
103+
return "x contains y";
104+
}
105+
}

0 commit comments

Comments
 (0)