Skip to content

Commit 8fbe992

Browse files
committed
Create ExprPDC.java
1 parent f82c0f7 commit 8fbe992

1 file changed

Lines changed: 160 additions & 0 deletions

File tree

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package ch.njol.skript.expressions;
2+
3+
import ch.njol.skript.aliases.ItemType;
4+
import ch.njol.skript.classes.Changer;
5+
import ch.njol.skript.expressions.base.PropertyExpression;
6+
import ch.njol.skript.lang.Expression;
7+
import ch.njol.skript.lang.SkriptParser.ParseResult;
8+
import ch.njol.skript.registrations.Classes;
9+
import ch.njol.skript.util.slot.Slot;
10+
import ch.njol.util.Kleenean;
11+
import org.bukkit.NamespacedKey;
12+
import org.bukkit.block.Block;
13+
import org.bukkit.block.TileState;
14+
import org.bukkit.event.Event;
15+
import org.bukkit.inventory.ItemStack;
16+
import org.bukkit.persistence.PersistentDataContainer;
17+
import org.bukkit.persistence.PersistentDataHolder;
18+
import org.bukkit.persistence.PersistentDataType;
19+
import org.jetbrains.annotations.NotNull;
20+
import org.jetbrains.annotations.Nullable;
21+
22+
import java.nio.charset.StandardCharsets;
23+
import java.util.ArrayList;
24+
import java.util.List;
25+
import java.util.function.Consumer;
26+
27+
import static ch.njol.util.StringUtils.hexStringToByteArray;
28+
29+
public class ExprPDC extends PropertyExpression<Object, Object> {
30+
31+
static {
32+
register(ExprPDC.class, Object.class, "pdc tag %string%", "chunks/worlds/entities/blocks/itemtypes/offlineplayers");
33+
}
34+
35+
private Expression<String> tag;
36+
37+
@Override
38+
@SuppressWarnings("unchecked")
39+
public boolean init(Expression<?>[] expressions, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
40+
tag = (Expression<String>) expressions[matchedPattern];
41+
setExpr(expressions[matchedPattern == 0 ? 1 : 0]);
42+
return true;
43+
}
44+
45+
@Override
46+
protected Object[] get(Event event, Object[] source) {
47+
String tagName = tag.getSingle(event);
48+
if (tagName == null)
49+
return new Object[0];
50+
var key = NamespacedKey.fromString(tagName);
51+
52+
List<Object> values = new ArrayList<>();
53+
for (Object holder : source) {
54+
if (holder == null)
55+
continue;
56+
editPersistentDataContainer(holder, container -> {
57+
assert key != null;
58+
if (!container.has(key, PersistentDataType.STRING)) {
59+
// If the key does not exist, we skip this holder
60+
return;
61+
}
62+
String stringValue = container.get(key, PersistentDataType.STRING);
63+
Object object;
64+
if (stringValue != null && (object = deserialize(stringValue)) != null) {
65+
values.add(object);
66+
}
67+
});
68+
}
69+
return values.toArray(new Object[0]);
70+
}
71+
72+
@Override
73+
public Class<?> @Nullable [] acceptChange(Changer.ChangeMode mode) {
74+
return switch (mode) {
75+
case SET, DELETE -> new Class<?>[]{Object.class};
76+
default -> null;
77+
};
78+
}
79+
80+
@Override
81+
public void change(Event event, Object @Nullable [] delta, Changer.ChangeMode mode) {
82+
var tagName = tag.getSingle(event);
83+
if (tagName == null)
84+
return;
85+
var key = NamespacedKey.fromString(tagName);
86+
if (key == null)
87+
return; // Invalid key, cannot proceed
88+
89+
for (Object holder : getExpr().getArray(event)) {
90+
editPersistentDataContainer(holder, container -> {
91+
if (mode == Changer.ChangeMode.SET) {
92+
assert delta != null;
93+
container.set(key, PersistentDataType.STRING, serialize(delta[0]));
94+
} else if (mode == Changer.ChangeMode.DELETE) {
95+
container.remove(key);
96+
}
97+
});
98+
}
99+
}
100+
101+
private void editPersistentDataContainer(Object holder, Consumer<PersistentDataContainer> consumer) {
102+
if (holder instanceof PersistentDataHolder dataHolder)
103+
consumer.accept(dataHolder.getPersistentDataContainer());
104+
else if (holder instanceof ItemType itemType) {
105+
var meta = itemType.getItemMeta();
106+
consumer.accept(meta.getPersistentDataContainer());
107+
itemType.setItemMeta(meta);
108+
} else if (holder instanceof ItemStack itemStack) {
109+
if (!itemStack.hasItemMeta()) return;
110+
var meta = itemStack.getItemMeta();
111+
consumer.accept(meta.getPersistentDataContainer());
112+
itemStack.setItemMeta(meta);
113+
} else if (holder instanceof Slot slot) {
114+
var item = slot.getItem();
115+
if (item == null || !item.hasItemMeta()) return;
116+
var meta = item.getItemMeta();
117+
consumer.accept(meta.getPersistentDataContainer());
118+
item.setItemMeta(meta);
119+
slot.setItem(item);
120+
} else if (holder instanceof Block block && block.getState() instanceof TileState tileState) {
121+
consumer.accept(tileState.getPersistentDataContainer());
122+
tileState.update();
123+
}
124+
}
125+
126+
@Override
127+
public Class<?> getReturnType() {
128+
return Object.class;
129+
}
130+
131+
@Override
132+
public String toString(@Nullable Event event, boolean debug) {
133+
return "pdc tag " + tag.toString(event, debug) + " of " + getExpr().toString(event, debug);
134+
}
135+
136+
private @NotNull String serialize(Object object) {
137+
var value = Classes.serialize(object);
138+
assert value != null;
139+
return value.type + ":" + bytesToHex(value.data);
140+
}
141+
142+
private Object deserialize(@NotNull String input) {
143+
var values = input.split(":", 2);
144+
var type = values[0];
145+
var data = values[1];
146+
return Classes.deserialize(type, hexStringToByteArray(data));
147+
}
148+
149+
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
150+
151+
private static String bytesToHex(byte[] bytes) {
152+
byte[] hexChars = new byte[bytes.length * 2];
153+
for (int j = 0; j < bytes.length; j++) {
154+
int v = bytes[j] & 0xFF;
155+
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
156+
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
157+
}
158+
return new String(hexChars, StandardCharsets.UTF_8);
159+
}
160+
}

0 commit comments

Comments
 (0)