Skip to content

Commit a43152d

Browse files
committed
Functional product (set/get tags, relatively safe)
1 parent 8209445 commit a43152d

5 files changed

Lines changed: 433 additions & 36 deletions

File tree

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

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,30 @@
33
import ch.njol.skript.aliases.Aliases;
44
import ch.njol.skript.bukkitutil.BurgerHelper;
55
import ch.njol.skript.classes.ClassInfo;
6-
import ch.njol.skript.classes.data.BukkitClasses;
7-
import ch.njol.skript.classes.data.BukkitEventValues;
8-
import ch.njol.skript.classes.data.DefaultComparators;
9-
import ch.njol.skript.classes.data.DefaultConverters;
10-
import ch.njol.skript.classes.data.DefaultFunctions;
11-
import ch.njol.skript.classes.data.DefaultOperations;
12-
import ch.njol.skript.classes.data.JavaClasses;
13-
import ch.njol.skript.classes.data.SkriptClasses;
6+
import ch.njol.skript.classes.data.*;
147
import ch.njol.skript.command.Commands;
158
import ch.njol.skript.doc.Documentation;
169
import ch.njol.skript.events.EvtSkript;
1710
import ch.njol.skript.expressions.arithmetic.ExprArithmetic;
1811
import ch.njol.skript.hooks.Hook;
1912
import ch.njol.skript.lang.*;
13+
import ch.njol.skript.lang.Effect;
2014
import ch.njol.skript.lang.Condition.ConditionType;
2115
import ch.njol.skript.lang.util.SimpleExpression;
2216
import ch.njol.skript.localization.Language;
2317
import ch.njol.skript.localization.Message;
2418
import ch.njol.skript.localization.PluralizingArgsMessage;
25-
import ch.njol.skript.log.BukkitLoggerFilter;
26-
import ch.njol.skript.log.CountingLogHandler;
27-
import ch.njol.skript.log.ErrorDescLogHandler;
28-
import ch.njol.skript.log.ErrorQuality;
29-
import ch.njol.skript.log.LogEntry;
30-
import ch.njol.skript.log.LogHandler;
31-
import ch.njol.skript.log.SkriptLogger;
32-
import ch.njol.skript.log.TestingLogHandler;
33-
import ch.njol.skript.log.Verbosity;
19+
import ch.njol.skript.log.*;
3420
import ch.njol.skript.registrations.Classes;
3521
import ch.njol.skript.registrations.EventValues;
3622
import ch.njol.skript.registrations.Feature;
37-
import ch.njol.skript.test.runner.EffObjectives;
38-
import ch.njol.skript.test.runner.SkriptAsyncJUnitTest;
39-
import ch.njol.skript.test.runner.SkriptJUnitTest;
40-
import ch.njol.skript.test.runner.SkriptTestEvent;
41-
import ch.njol.skript.test.runner.TestMode;
42-
import ch.njol.skript.test.runner.TestTracker;
23+
import ch.njol.skript.test.runner.*;
4324
import ch.njol.skript.timings.SkriptTimings;
4425
import ch.njol.skript.update.ReleaseManifest;
4526
import ch.njol.skript.update.ReleaseStatus;
4627
import ch.njol.skript.update.UpdateManifest;
4728
import ch.njol.skript.util.Date;
48-
import ch.njol.skript.util.EmptyStacktraceException;
49-
import ch.njol.skript.util.ExceptionUtils;
50-
import ch.njol.skript.util.FileUtils;
51-
import ch.njol.skript.util.Task;
52-
import ch.njol.skript.util.Utils;
53-
import ch.njol.skript.util.Version;
29+
import ch.njol.skript.util.*;
5430
import ch.njol.skript.util.chat.BungeeConverter;
5531
import ch.njol.skript.util.chat.ChatMessages;
5632
import ch.njol.skript.variables.Variables;
@@ -62,12 +38,7 @@
6238
import com.google.gson.Gson;
6339
import com.google.gson.GsonBuilder;
6440
import org.bstats.bukkit.Metrics;
65-
import org.bukkit.Bukkit;
66-
import org.bukkit.ChatColor;
67-
import org.bukkit.Material;
68-
import org.bukkit.OfflinePlayer;
69-
import org.bukkit.Server;
70-
import org.bukkit.World;
41+
import org.bukkit.*;
7142
import org.bukkit.command.CommandSender;
7243
import org.bukkit.command.PluginCommand;
7344
import org.bukkit.entity.Player;
@@ -101,6 +72,7 @@
10172
import org.skriptlang.skript.bukkit.itemcomponents.ItemComponentModule;
10273
import org.skriptlang.skript.bukkit.log.runtime.BukkitRuntimeErrorConsumer;
10374
import org.skriptlang.skript.bukkit.loottables.LootTableModule;
75+
import org.skriptlang.skript.bukkit.pdc.PDCModule;
10476
import org.skriptlang.skript.bukkit.registration.BukkitRegistryKeys;
10577
import org.skriptlang.skript.bukkit.registration.BukkitSyntaxInfos;
10678
import org.skriptlang.skript.bukkit.tags.TagModule;
@@ -599,7 +571,8 @@ public void onEnable() {
599571
new DamageSourceModule(),
600572
new ItemComponentModule(),
601573
new BrewingModule(),
602-
new CommonModule()
574+
new CommonModule(),
575+
new PDCModule()
603576
);
604577
} catch (final Exception e) {
605578
exception(e, "Could not load required .class files: " + e.getLocalizedMessage());
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.skriptlang.skript.bukkit.pdc;
2+
3+
import org.skriptlang.skript.addon.AddonModule;
4+
import org.skriptlang.skript.addon.SkriptAddon;
5+
import org.skriptlang.skript.bukkit.pdc.expressions.ExprPersistentData;
6+
7+
public class PDCModule implements AddonModule {
8+
@Override
9+
public void load(SkriptAddon addon) {
10+
ExprPersistentData.register(addon.syntaxRegistry());
11+
}
12+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package org.skriptlang.skript.bukkit.pdc;
2+
3+
import ch.njol.skript.classes.ClassInfo;
4+
import ch.njol.skript.classes.Serializer;
5+
import ch.njol.skript.registrations.Classes;
6+
import ch.njol.yggdrasil.Fields;
7+
import org.bukkit.Bukkit;
8+
import org.bukkit.NamespacedKey;
9+
import org.bukkit.persistence.PersistentDataAdapterContext;
10+
import org.bukkit.persistence.PersistentDataContainer;
11+
import org.bukkit.persistence.PersistentDataType;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.annotations.Unmodifiable;
14+
import org.skriptlang.skript.lang.converter.Converters;
15+
16+
import java.io.NotSerializableException;
17+
import java.io.StreamCorruptedException;
18+
import java.util.Collection;
19+
import java.util.Collections;
20+
import java.util.HashMap;
21+
import java.util.Map;
22+
23+
/**
24+
* A serializer that can serialize and deserialize Yggsdrasil serializable objects to and from PersistentDataContainers.
25+
*/
26+
public class PDCSerializer {
27+
28+
/**
29+
* Types that are directly serializable to PDC, and therefore do not need to be handled through Fields.
30+
* Never add a custom type that uses {@link PersistentDataContainer} as the primitive. That will cause
31+
* the {@link SkriptDataType} to not be used.
32+
*/
33+
private static final Map<Class<?>, PersistentDataType<?, ?>> REPRESENTABLE_TYPES = new HashMap<>();
34+
35+
static {
36+
REPRESENTABLE_TYPES.put(Byte.class, PersistentDataType.BYTE);
37+
REPRESENTABLE_TYPES.put(Short.class, PersistentDataType.SHORT);
38+
REPRESENTABLE_TYPES.put(Integer.class, PersistentDataType.INTEGER);
39+
REPRESENTABLE_TYPES.put(Long.class, PersistentDataType.LONG);
40+
REPRESENTABLE_TYPES.put(Double.class, PersistentDataType.DOUBLE);
41+
REPRESENTABLE_TYPES.put(Float.class, PersistentDataType.FLOAT);
42+
REPRESENTABLE_TYPES.put(Boolean.class, PersistentDataType.BOOLEAN);
43+
REPRESENTABLE_TYPES.put(String.class, PersistentDataType.STRING);
44+
}
45+
46+
public static @Unmodifiable Collection<PersistentDataType<?, ?>> getRepresentablePDCTypes() {
47+
return Collections.unmodifiableCollection(REPRESENTABLE_TYPES.values());
48+
}
49+
50+
public static PersistentDataType<?, ?> getPDCType(ClassInfo<?> classInfo) {
51+
if (REPRESENTABLE_TYPES.containsKey(classInfo.getC())) {
52+
return REPRESENTABLE_TYPES.get(classInfo.getC());
53+
} else {
54+
return SkriptDataType.get();
55+
}
56+
}
57+
58+
@SuppressWarnings("unchecked")
59+
public static @NotNull PersistentDataContainer serialize(
60+
@NotNull Object unserializedData,
61+
@NotNull PersistentDataAdapterContext context
62+
) {
63+
// temporary
64+
assert Bukkit.isPrimaryThread();
65+
66+
ClassInfo<?> classInfo = Classes.getSuperClassInfo(unserializedData.getClass());
67+
if (classInfo.getSerializeAs() != null) {
68+
classInfo = Classes.getExactClassInfo(classInfo.getSerializeAs());
69+
if (classInfo == null) {
70+
assert false : unserializedData.getClass();
71+
return null;
72+
}
73+
unserializedData = Converters.convert(unserializedData, classInfo.getC());
74+
if (unserializedData == null) {
75+
assert false : classInfo.getCodeName();
76+
return null;
77+
}
78+
}
79+
80+
var serializer = (Serializer<Object>) classInfo.getSerializer();
81+
if (serializer == null) // value cannot be saved
82+
throw new IllegalArgumentException("Cannot serialize " + classInfo.getCodeName() + " because it has no serializer");
83+
84+
assert !serializer.mustSyncDeserialization() || Bukkit.isPrimaryThread();
85+
var container = context.newPersistentDataContainer();
86+
87+
// shortcut for primitives
88+
if (REPRESENTABLE_TYPES.containsKey(classInfo.getC())) {
89+
container.set(new NamespacedKey("skript", "type"), PersistentDataType.STRING, classInfo.getCodeName());
90+
var tag = new NamespacedKey("skript", "value");
91+
var pdcType = (PersistentDataType<Object, Object>) REPRESENTABLE_TYPES.get(classInfo.getC());
92+
container.set(tag, pdcType, unserializedData);
93+
return container;
94+
}
95+
96+
// If not a primitive, serialize normally and use Fields to store data
97+
try {
98+
Fields fields = serializer.serialize(unserializedData);
99+
container.set(new NamespacedKey("skript", "type"), PersistentDataType.STRING, classInfo.getCodeName());
100+
for (var field : fields) {
101+
var tag = new NamespacedKey("skript", field.getID());
102+
var data = field.isPrimitive() ? field.getPrimitive() : field.getObject();
103+
if (data == null) {
104+
continue;
105+
}
106+
if (field.isPrimitive() || data instanceof String) {
107+
var type = REPRESENTABLE_TYPES.get(data.getClass());
108+
if (type == null) {
109+
throw new NotSerializableException("Unsupported primitive type: " + data.getClass());
110+
}
111+
container.set(tag, (PersistentDataType<Object, Object>) type, data);
112+
} else {
113+
// write a nested PDC
114+
data = PDCSerializer.serialize(data, context);
115+
container.set(tag, PersistentDataType.TAG_CONTAINER, (PersistentDataContainer) data);
116+
}
117+
}
118+
} catch (NotSerializableException | StreamCorruptedException e) {
119+
throw new RuntimeException(e);
120+
}
121+
return container;
122+
}
123+
124+
public static @NotNull Object deserialize(
125+
@NotNull PersistentDataContainer serializedData,
126+
@NotNull PersistentDataAdapterContext context
127+
) {
128+
String typeName = serializedData.get(new NamespacedKey("skript", "type"), PersistentDataType.STRING);
129+
if (typeName == null) {
130+
throw new IllegalArgumentException("Cannot deserialize PDC because it has no type");
131+
}
132+
ClassInfo<?> classInfo = Classes.getClassInfo(typeName);
133+
//noinspection unchecked
134+
var serializer = (Serializer<Object>) classInfo.getSerializer();
135+
if (serializer == null) {
136+
throw new IllegalArgumentException("Cannot deserialize " + classInfo.getCodeName() + " because it has no serializer");
137+
}
138+
139+
// shortcut for primitives
140+
if (REPRESENTABLE_TYPES.containsKey(classInfo.getC())) {
141+
var tag = new NamespacedKey("skript", "value");
142+
//noinspection unchecked
143+
var pdcType = (PersistentDataType<Object, Object>) REPRESENTABLE_TYPES.get(classInfo.getC());
144+
Object value = serializedData.get(tag, pdcType);
145+
if (value == null) {
146+
throw new IllegalArgumentException("Cannot deserialize " + classInfo.getCodeName() + " because its value is missing");
147+
}
148+
return value;
149+
}
150+
151+
// If not a primitive, deserialize normally using Fields
152+
try {
153+
Fields fields = new Fields();
154+
for (var key : serializedData.getKeys()) {
155+
if (key.getNamespace().equals("skript") && key.getKey().equals("type")) {
156+
continue;
157+
}
158+
Object data = null;
159+
boolean primitive = true;
160+
for (var entry : REPRESENTABLE_TYPES.entrySet()) {
161+
var type = entry.getValue();
162+
if (serializedData.has(key, type)) {
163+
data = serializedData.get(key, type);
164+
primitive = entry.getKey().isPrimitive() || isPrimitiveWrapper(entry.getKey());
165+
break;
166+
}
167+
}
168+
if (data == null) {
169+
if (serializedData.has(key, PersistentDataType.TAG_CONTAINER)) {
170+
PersistentDataContainer nestedContainer = serializedData.get(key, PersistentDataType.TAG_CONTAINER);
171+
assert nestedContainer != null;
172+
data = PDCSerializer.deserialize(nestedContainer, context);
173+
primitive = false;
174+
} else {
175+
throw new NotSerializableException("Unsupported data type for key: " + key);
176+
}
177+
}
178+
if (primitive) {
179+
fields.putPrimitive(key.getKey(), data);
180+
} else {
181+
fields.putObject(key.getKey(), data);
182+
}
183+
}
184+
assert !serializer.mustSyncDeserialization() || Bukkit.isPrimaryThread();
185+
return serializer.deserialize(classInfo.getC(), fields);
186+
} catch (Exception e) {
187+
throw new RuntimeException(e);
188+
}
189+
}
190+
191+
private static boolean isPrimitiveWrapper(Class<?> key) {
192+
return key == Byte.class || key == Short.class || key == Integer.class ||
193+
key == Long.class || key == Double.class || key == Float.class ||
194+
key == Boolean.class || key == Character.class;
195+
}
196+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package org.skriptlang.skript.bukkit.pdc;
2+
3+
import org.bukkit.persistence.PersistentDataAdapterContext;
4+
import org.bukkit.persistence.PersistentDataContainer;
5+
import org.bukkit.persistence.PersistentDataType;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
/**
9+
* A PersistentDataType that can store any Yggsdrasil-serializable object using PDCSerializer.
10+
*/
11+
public class SkriptDataType implements PersistentDataType<PersistentDataContainer, Object> {
12+
13+
private static SkriptDataType instance = null;
14+
15+
public static SkriptDataType get() {
16+
if (instance == null)
17+
instance = new SkriptDataType();
18+
return instance;
19+
}
20+
21+
@Override
22+
public @NotNull Class<PersistentDataContainer> getPrimitiveType() {
23+
return PersistentDataContainer.class;
24+
}
25+
26+
@Override
27+
public @NotNull Class<Object> getComplexType() {
28+
return Object.class;
29+
}
30+
31+
@Override
32+
public @NotNull PersistentDataContainer toPrimitive(@NotNull Object complex, @NotNull PersistentDataAdapterContext context) {
33+
return PDCSerializer.serialize(complex, context);
34+
}
35+
36+
@Override
37+
public @NotNull Object fromPrimitive(@NotNull PersistentDataContainer primitive, @NotNull PersistentDataAdapterContext context) {
38+
return PDCSerializer.deserialize(primitive, context);
39+
}
40+
41+
}

0 commit comments

Comments
 (0)