Skip to content

Commit 77ada09

Browse files
committed
Separate new and legacy data watcher impls
1 parent 389314c commit 77ada09

File tree

9 files changed

+879
-329
lines changed

9 files changed

+879
-329
lines changed

src/main/java/com/comphenix/protocol/reflect/cloning/BukkitCloner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private static void fromManual(Supplier<Class<?>> getClass, Function<Object, Obj
7272
static {
7373
fromManual(MinecraftReflection::getItemStackClass, source ->
7474
MinecraftReflection.getMinecraftItemStack(MinecraftReflection.getBukkitItemStack(source).clone()));
75-
fromWrapper(MinecraftReflection::getDataWatcherClass, WrappedDataWatcher::new);
75+
fromManual(MinecraftReflection::getDataWatcherClass, source -> new WrappedDataWatcher(source).deepClone().getHandle());
7676
fromConverter(MinecraftReflection::getBlockPositionClass, BlockPosition.getConverter());
7777
fromWrapper(MinecraftReflection::getServerPingClass, WrappedServerPing::fromHandle);
7878
fromConverter(MinecraftReflection::getMinecraftKeyClass, MinecraftKey.getConverter());
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package com.comphenix.protocol.wrappers;
2+
3+
import java.util.List;
4+
import java.util.Map;
5+
import java.util.Set;
6+
7+
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
8+
9+
import org.bukkit.entity.Entity;
10+
11+
public interface IDataWatcher extends Iterable<WrappedWatchableObject> {
12+
13+
Map<Integer, WrappedWatchableObject> asMap();
14+
15+
Set<Integer> getIndexes();
16+
17+
List<WrappedWatchableObject> getWatchableObjects();
18+
19+
int size();
20+
21+
WrappedWatchableObject getWatchableObject(int index);
22+
23+
WrappedWatchableObject remove(int index);
24+
25+
boolean hasIndex(int index);
26+
27+
void clear();
28+
29+
Object getObject(int index);
30+
31+
Object getObject(WrappedDataWatcherObject object);
32+
33+
void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update);
34+
35+
void setObject(WrappedDataWatcherObject object, Object value, boolean update);
36+
37+
IDataWatcher deepClone();
38+
39+
Object getHandle();
40+
41+
@Deprecated
42+
Entity getEntity();
43+
44+
@Deprecated
45+
void setEntity(Entity entity);
46+
}
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
package com.comphenix.protocol.wrappers;
2+
3+
import java.lang.reflect.Modifier;
4+
import java.util.HashMap;
5+
import java.util.Iterator;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Set;
9+
10+
import com.comphenix.protocol.injector.BukkitUnwrapper;
11+
import com.comphenix.protocol.reflect.FieldAccessException;
12+
import com.comphenix.protocol.reflect.FuzzyReflection;
13+
import com.comphenix.protocol.reflect.accessors.Accessors;
14+
import com.comphenix.protocol.reflect.accessors.ConstructorAccessor;
15+
import com.comphenix.protocol.reflect.accessors.FieldAccessor;
16+
import com.comphenix.protocol.reflect.fuzzy.FuzzyFieldContract;
17+
import com.comphenix.protocol.utility.MinecraftReflection;
18+
import com.comphenix.protocol.utility.MinecraftVersion;
19+
import com.comphenix.protocol.wrappers.WrappedDataWatcher.WrappedDataWatcherObject;
20+
21+
import com.google.common.collect.ImmutableMap;
22+
import com.google.common.collect.Lists;
23+
import org.bukkit.entity.Entity;
24+
import org.jetbrains.annotations.NotNull;
25+
import org.jetbrains.annotations.Nullable;
26+
27+
public final class InMemoryDataWatcher implements IDataWatcher {
28+
private Object entityHandle;
29+
private Map<Integer, WrappedWatchableObject> entries = new HashMap<>();
30+
31+
public InMemoryDataWatcher() {
32+
33+
}
34+
35+
public InMemoryDataWatcher(Object handle) {
36+
this.entityHandle = entityFromWatcherHandle(handle);
37+
this.populateFromHandle(handle);
38+
}
39+
40+
public InMemoryDataWatcher(Entity entity) {
41+
this.entityHandle = BukkitUnwrapper.getInstance().unwrapItem(entity);
42+
this.populateFromEntity(entity);
43+
}
44+
45+
public InMemoryDataWatcher(List<WrappedWatchableObject> objects) {
46+
for (WrappedWatchableObject obj : objects) {
47+
entries.put(obj.getIndex(), obj);
48+
}
49+
}
50+
51+
private static FieldAccessor ENTITY_WATCHER_FIELD;
52+
private static FieldAccessor WATCHER_ENTITY_FIELD;
53+
private static FieldAccessor ARRAY_FIELD;
54+
private static ConstructorAccessor CONSTRUCTOR;
55+
56+
private static boolean ARRAY_BACKED = MinecraftVersion.v1_20_4.atOrAbove();
57+
58+
private static Class<?> SYNCED_DATA_HOLDER_CLASS = ARRAY_BACKED
59+
? MinecraftReflection.getMinecraftClass("network.syncher.SyncedDataHolder")
60+
: MinecraftReflection.getEntityClass();
61+
62+
public static WrappedDataWatcher getEntityWatcher(Entity entity) {
63+
return new WrappedDataWatcher(entity);
64+
}
65+
66+
private static Object entityFromWatcherHandle(Object handle) {
67+
if (WATCHER_ENTITY_FIELD == null) {
68+
WATCHER_ENTITY_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getDataWatcherClass(), SYNCED_DATA_HOLDER_CLASS, true);
69+
}
70+
71+
return WATCHER_ENTITY_FIELD.get(handle);
72+
}
73+
74+
public void populateFromEntity(Entity entity) {
75+
populateFromEntity(BukkitUnwrapper.getInstance().unwrapItem(entity));
76+
}
77+
78+
public void applyToEntity(Entity entity) {
79+
applyToEntity(BukkitUnwrapper.getInstance().unwrapItem(entity));
80+
}
81+
82+
private void applyToEntity(Object entityHandle) {
83+
if (ENTITY_WATCHER_FIELD == null) {
84+
ENTITY_WATCHER_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
85+
}
86+
87+
Object handle = getHandle(entityHandle);
88+
ENTITY_WATCHER_FIELD.set(entityHandle, handle);
89+
}
90+
91+
private void populateFromHandle(Object handle) {
92+
if (ARRAY_FIELD == null) {
93+
try {
94+
FuzzyReflection fuzzy = FuzzyReflection.fromClass(handle.getClass(), true);
95+
ARRAY_FIELD = Accessors.getFieldAccessor(fuzzy.getField(FuzzyFieldContract
96+
.newBuilder()
97+
.banModifier(Modifier.STATIC)
98+
.typeDerivedOf(Object[].class)
99+
.build()));
100+
} catch (IllegalArgumentException ex) {
101+
throw new FieldAccessException("Failed to find watchable object array", ex);
102+
}
103+
}
104+
105+
Object[] backing = (Object[]) ARRAY_FIELD.get(handle);
106+
for (Object itemHandle : backing) {
107+
if (itemHandle == null) {
108+
continue;
109+
}
110+
111+
WrappedWatchableObject object = new WrappedWatchableObject(itemHandle);
112+
entries.put(object.getIndex(), object);
113+
}
114+
}
115+
116+
private void populateFromEntity(Object entityHandle) {
117+
if (ENTITY_WATCHER_FIELD == null) {
118+
ENTITY_WATCHER_FIELD = Accessors.getFieldAccessor(MinecraftReflection.getEntityClass(), MinecraftReflection.getDataWatcherClass(), true);
119+
}
120+
121+
Object handle = ENTITY_WATCHER_FIELD.get(entityHandle);
122+
populateFromHandle(handle);
123+
}
124+
125+
public Object getHandle() {
126+
return getHandle(entityHandle);
127+
}
128+
129+
public Object getHandle(Object entityHandle) {
130+
if (CONSTRUCTOR == null) {
131+
CONSTRUCTOR = Accessors.getConstructorAccessor(MinecraftReflection.getDataWatcherClass(),
132+
SYNCED_DATA_HOLDER_CLASS, MinecraftReflection.getArrayClass(MinecraftReflection.getDataWatcherItemClass()));
133+
}
134+
135+
if (CONSTRUCTOR == null) {
136+
throw new IllegalStateException("Cannot find constructor for DataWatcher.");
137+
}
138+
139+
Object[] items = new Object[entries.size()];
140+
for (int i = 0; i < items.length; i++) {
141+
items[i] = entries.get(i).getHandle();
142+
}
143+
144+
return CONSTRUCTOR.invoke(null, entityHandle, items);
145+
}
146+
147+
/**
148+
* @return
149+
*/
150+
@Override
151+
public IDataWatcher deepClone() {
152+
InMemoryDataWatcher clone = new InMemoryDataWatcher();
153+
clone.entries = new HashMap<>(this.entries.size());
154+
clone.entityHandle = this.entityHandle;
155+
156+
for (WrappedWatchableObject object : this) {
157+
clone.setObject(object.getWatcherObject(), object.getValue(), false);
158+
}
159+
160+
return clone;
161+
}
162+
163+
/**
164+
* @return
165+
*/
166+
@Override
167+
@Nullable
168+
@Deprecated
169+
public Entity getEntity() {
170+
return entityHandle != null ? (Entity) MinecraftReflection.getBukkitEntity(entityHandle) : null;
171+
}
172+
173+
/**
174+
* @param entity
175+
*/
176+
@Override
177+
@Deprecated
178+
public void setEntity(Entity entity) {
179+
this.entityHandle = BukkitUnwrapper.getInstance().unwrapItem(entity);
180+
}
181+
182+
/**
183+
* @return
184+
*/
185+
@Override
186+
public Map<Integer, WrappedWatchableObject> asMap() {
187+
return ImmutableMap.copyOf(entries);
188+
}
189+
190+
/**
191+
* @return
192+
*/
193+
@Override
194+
public Set<Integer> getIndexes() {
195+
return entries.keySet();
196+
}
197+
198+
/**
199+
* @return
200+
*/
201+
@Override
202+
public List<WrappedWatchableObject> getWatchableObjects() {
203+
return Lists.newArrayList(entries.values());
204+
}
205+
206+
/**
207+
* @return
208+
*/
209+
@Override
210+
public int size() {
211+
return entries.size();
212+
}
213+
214+
/**
215+
* @param index
216+
* @return
217+
*/
218+
@Override
219+
public WrappedWatchableObject getWatchableObject(int index) {
220+
return entries.get(index);
221+
}
222+
223+
/**
224+
* @param index
225+
* @return
226+
*/
227+
@Override
228+
public WrappedWatchableObject remove(int index) {
229+
return entries.remove(index);
230+
}
231+
232+
/**
233+
* @param index
234+
* @return
235+
*/
236+
@Override
237+
public boolean hasIndex(int index) {
238+
return entries.containsKey(index);
239+
}
240+
241+
/**
242+
*
243+
*/
244+
@Override
245+
public void clear() {
246+
entries.clear();
247+
}
248+
249+
@Override
250+
public Object getObject(int index) {
251+
WrappedWatchableObject obj = getWatchableObject(index);
252+
return obj != null ? obj.getValue() : null;
253+
}
254+
255+
/**
256+
* @param object
257+
* @return
258+
*/
259+
@Override
260+
public Object getObject(WrappedDataWatcherObject object) {
261+
return getObject(object.getIndex());
262+
}
263+
264+
/**
265+
* @param object
266+
* @param value
267+
* @param update
268+
*/
269+
@Override
270+
public void setObject(WrappedDataWatcherObject object, WrappedWatchableObject value, boolean update) {
271+
entries.put(object.getIndex(), value);
272+
273+
if (update) {
274+
value.setDirtyState(true);
275+
}
276+
}
277+
278+
/**
279+
* @param object
280+
* @param value
281+
* @param update
282+
*/
283+
@Override
284+
public void setObject(WrappedDataWatcherObject object, Object value, boolean update) {
285+
setObject(object, new WrappedWatchableObject(object, value), update);
286+
}
287+
288+
/**
289+
* @return
290+
*/
291+
@NotNull
292+
@Override
293+
public Iterator<WrappedWatchableObject> iterator() {
294+
return entries.values().iterator();
295+
}
296+
}

0 commit comments

Comments
 (0)