17
17
18
18
package com .comphenix .protocol .injector ;
19
19
20
- import java .util .ArrayList ;
21
- import java .util .Collection ;
22
- import java .util .HashMap ;
23
- import java .util .List ;
24
- import java .util .Map ;
20
+ import java .util .*;
21
+ import java .util .concurrent .ConcurrentHashMap ;
25
22
26
23
import com .comphenix .protocol .reflect .FieldAccessException ;
27
24
import com .comphenix .protocol .reflect .FuzzyReflection ;
46
43
* @author Kristian
47
44
*/
48
45
class EntityUtilities {
49
- private static final boolean NEW_TRACKER = MinecraftVersion .atOrAbove (MinecraftVersion . VILLAGE_UPDATE );
46
+ private static final boolean NEW_TRACKER = MinecraftVersion .VILLAGE_UPDATE . atOrAbove ();
50
47
private static final EntityUtilities INSTANCE = new EntityUtilities ();
51
48
52
49
public static EntityUtilities getInstance () {
@@ -58,7 +55,6 @@ private EntityUtilities() { }
58
55
private FieldAccessor entityTrackerField ;
59
56
private FieldAccessor trackedEntitiesField ;
60
57
private FieldAccessor trackedPlayersField ;
61
- private FieldAccessor trackerField ;
62
58
63
59
private Map <Class <?>, MethodAccessor > scanPlayersMethods = new HashMap <>();
64
60
@@ -196,6 +192,9 @@ private Object getEntityTrackerEntry(World world, int entityID) {
196
192
return WrappedIntHashMap .fromHandle (trackedEntities ).get (entityID );
197
193
}
198
194
195
+ private Map <Class <?>, FieldAccessor > trackerFields = new ConcurrentHashMap <>();
196
+ private MethodAccessor getEntityFromId ;
197
+
199
198
/**
200
199
* Retrieve entity from a ID, even it it's newly created.
201
200
* @return The associated entity.
@@ -206,20 +205,40 @@ public Entity getEntityFromID(World world, int entityID) {
206
205
Validate .isTrue (entityID >= 0 , "entityID cannot be negative" );
207
206
208
207
try {
208
+ // first, try to read from the world
209
+ // this should be good enough for most cases, but only exists in 1.14+
210
+ if (NEW_TRACKER ) {
211
+ Object worldServer = BukkitUnwrapper .getInstance ().unwrapItem (world );
212
+
213
+ if (getEntityFromId == null ) {
214
+ FuzzyReflection fuzzy = FuzzyReflection .fromClass (worldServer .getClass (), false );
215
+ getEntityFromId = Accessors .getMethodAccessor (fuzzy .getMethod (FuzzyMethodContract .newBuilder ()
216
+ .parameterExactArray (int .class )
217
+ .returnTypeExact (MinecraftReflection .getEntityClass ())
218
+ .build ()));
219
+ }
220
+
221
+ Object entity = getEntityFromId .invoke (worldServer , entityID );
222
+ if (entity != null ) {
223
+ return (Entity ) MinecraftReflection .getBukkitEntity (entity );
224
+ }
225
+ }
226
+
227
+ // then go into the trackers
209
228
Object trackerEntry = getEntityTrackerEntry (world , entityID );
210
229
Object tracker = null ;
211
230
212
- // Handle NULL cases
213
231
if (trackerEntry != null ) {
214
- if (trackerField == null ) {
232
+ // plugins like citizens will use their own tracker
233
+ FieldAccessor trackerField = trackerFields .computeIfAbsent (trackerEntry .getClass (), x -> {
215
234
try {
216
- trackerField = Accessors .getFieldAccessor (trackerEntry .getClass (), "tracker" , true );
235
+ return Accessors .getFieldAccessor (trackerEntry .getClass (), "tracker" , true );
217
236
} catch (Exception e ) {
218
237
// Assume it's the first entity field then
219
- trackerField = Accessors .getFieldAccessor (FuzzyReflection .fromObject (trackerEntry , true )
238
+ return Accessors .getFieldAccessor (FuzzyReflection .fromObject (trackerEntry , true )
220
239
.getFieldByType ("tracker" , MinecraftReflection .getEntityClass ()));
221
240
}
222
- }
241
+ });
223
242
224
243
tracker = trackerField .get (trackerEntry );
225
244
}
0 commit comments