17
17
18
18
package com .comphenix .protocol .injector ;
19
19
20
+ import java .lang .reflect .Field ;
21
+ import java .lang .reflect .Modifier ;
20
22
import java .security .PublicKey ;
23
+ import java .util .List ;
21
24
import java .util .Map ;
22
- import java .util .Objects ;
23
25
import java .util .Optional ;
24
26
import java .util .concurrent .ConcurrentHashMap ;
27
+ import java .util .function .Function ;
25
28
import java .util .function .Supplier ;
26
29
27
30
import com .comphenix .protocol .PacketType ;
31
+ import com .comphenix .protocol .injector .packet .KnownPacketData ;
28
32
import com .comphenix .protocol .injector .packet .PacketRegistry ;
29
33
import com .comphenix .protocol .reflect .FuzzyReflection ;
30
34
import com .comphenix .protocol .reflect .StructureModifier ;
31
35
import com .comphenix .protocol .reflect .accessors .Accessors ;
32
36
import com .comphenix .protocol .reflect .accessors .ConstructorAccessor ;
37
+ import com .comphenix .protocol .reflect .accessors .FieldAccessor ;
38
+ import com .comphenix .protocol .reflect .fuzzy .FuzzyFieldContract ;
33
39
import com .comphenix .protocol .reflect .fuzzy .FuzzyMethodContract ;
34
40
import com .comphenix .protocol .reflect .instances .DefaultInstances ;
35
41
import com .comphenix .protocol .reflect .instances .InstanceCreator ;
41
47
import com .comphenix .protocol .utility .ZeroBuffer ;
42
48
import com .comphenix .protocol .wrappers .WrappedChatComponent ;
43
49
import com .comphenix .protocol .wrappers .WrappedStreamCodec ;
44
- import com .google .common .base .Preconditions ;
45
50
51
+ import com .google .common .base .Preconditions ;
46
52
import io .netty .buffer .ByteBuf ;
53
+ import io .netty .buffer .Unpooled ;
47
54
import net .bytebuddy .dynamic .DynamicType ;
48
55
import net .bytebuddy .dynamic .loading .ClassLoadingStrategy .Default ;
49
56
import net .bytebuddy .implementation .FixedValue ;
57
64
public class StructureCache {
58
65
59
66
// Structure modifiers
60
- private static final Map <Class <?>, Supplier <Object >> CACHED_INSTANCE_CREATORS = new ConcurrentHashMap <>();
67
+ private static final Map <Class <?>, Optional < Supplier <Object > >> CACHED_INSTANCE_CREATORS = new ConcurrentHashMap <>();
61
68
private static final Map <PacketType , StructureModifier <Object >> STRUCTURE_MODIFIER_CACHE = new ConcurrentHashMap <>();
62
69
63
70
// packet data serializer which always returns an empty nbt tag compound
64
71
private static final Object TRICK_INIT_LOCK = new Object ();
65
72
private static boolean TRICK_TRIED = false ;
66
73
67
- private static Supplier < Object > TRICKED_DATA_SERIALIZER_BASE ;
74
+ private static Function < ByteBuf , Object > TRICKED_DATA_SERIALIZER_BASE ;
68
75
private static Supplier <Object > TRICKED_DATA_SERIALIZER_JSON ;
69
76
70
77
/**
@@ -75,12 +82,40 @@ public static Object newPacket(Class<?> packetClass) {
75
82
return newInstance (packetClass );
76
83
}
77
84
85
+ public static boolean canCreateInstance (Class <?> clazz ) {
86
+ Optional <Supplier <Object >> creator = CACHED_INSTANCE_CREATORS .computeIfAbsent (clazz , x ->
87
+ Optional .ofNullable (determineBestCreator (clazz )));
88
+ return creator .isPresent ();
89
+ }
90
+
78
91
public static Object newInstance (Class <?> clazz ) {
79
- Supplier <Object > creator = CACHED_INSTANCE_CREATORS .computeIfAbsent (clazz , StructureCache ::determineBestCreator );
80
- return creator .get ();
92
+ Optional <Supplier <Object >> creator = CACHED_INSTANCE_CREATORS .computeIfAbsent (clazz , x ->
93
+ Optional .ofNullable (determineBestCreator (clazz )));
94
+ if (!creator .isPresent ()) {
95
+ throw new IllegalArgumentException ("Cannot create instance of " + clazz );
96
+ }
97
+ return creator .get ().get ();
81
98
}
82
99
83
100
static Supplier <Object > determineBestCreator (Class <?> clazz ) {
101
+ // certain packets are singletons which can't really be created
102
+ if (MinecraftReflection .isPacketClass (clazz )) {
103
+ FuzzyReflection fuzzy = FuzzyReflection .fromClass (clazz , false );
104
+ List <Field > singletons = fuzzy .getFieldList (FuzzyFieldContract .newBuilder ()
105
+ .typeExact (clazz )
106
+ .requireModifier (Modifier .STATIC )
107
+ .requireModifier (Modifier .PUBLIC )
108
+ .build ());
109
+ if (singletons .size () == 1 ) {
110
+ FieldAccessor accessor = Accessors .getFieldAccessor (singletons .get (0 ));
111
+ try {
112
+ accessor .get (null );
113
+ return () -> accessor .get (null );
114
+ } catch (Exception ignored ) {
115
+ }
116
+ }
117
+ }
118
+
84
119
try {
85
120
InstanceCreator creator = InstanceCreator .forClass (clazz );
86
121
if (creator .get () != null ) {
@@ -95,21 +130,40 @@ static Supplier<Object> determineBestCreator(Class<?> clazz) {
95
130
if (streamCodec != null && tryInitTrickDataSerializer ()) {
96
131
try {
97
132
// first try with the base accessor
98
- Object serializer = TRICKED_DATA_SERIALIZER_BASE .get ( );
133
+ Object serializer = TRICKED_DATA_SERIALIZER_BASE .apply ( new ZeroBuffer () );
99
134
streamCodec .decode (serializer ); // throwaway instance, for testing
100
135
101
136
// method is working
102
137
return () -> streamCodec .decode (serializer );
103
- } catch (Exception ignored ) {
138
+ } catch (Exception ex ) {
104
139
try {
105
- // try with the json accessor
106
- Object serializer = TRICKED_DATA_SERIALIZER_JSON .get ();
107
- streamCodec .decode (serializer ); // throwaway instance, for testing
140
+ byte [] data ;
141
+ if (clazz .equals (PacketType .Play .Server .MAP_CHUNK .getPacketClass ())) {
142
+ data = KnownPacketData .MAP_CHUNK ;
143
+ } else if (clazz .equals (PacketType .Play .Server .SCOREBOARD_OBJECTIVE .getPacketClass ())) {
144
+ data = KnownPacketData .SCOREBOARD_OBJECTIVE ;
145
+ } else {
146
+ throw ex ;
147
+ }
148
+
149
+ Object serializer = TRICKED_DATA_SERIALIZER_BASE .apply (Unpooled .copiedBuffer (data ));
150
+ streamCodec .decode (serializer );
108
151
109
- // method is working
110
- return () -> streamCodec .decode (serializer );
152
+ return () -> {
153
+ ((ByteBuf ) serializer ).readerIndex (0 );
154
+ return streamCodec .decode (serializer );
155
+ };
111
156
} catch (Exception ignored1 ) {
112
- // shrug, fall back to default behaviour
157
+ try {
158
+ // try with the json accessor
159
+ Object serializer = TRICKED_DATA_SERIALIZER_JSON .get ();
160
+ streamCodec .decode (serializer ); // throwaway instance, for testing
161
+
162
+ // method is working
163
+ return () -> streamCodec .decode (serializer );
164
+ } catch (Exception ignored2 ) {
165
+ // shrug, fall back to default behaviour
166
+ }
113
167
}
114
168
}
115
169
}
@@ -124,7 +178,7 @@ static Supplier<Object> determineBestCreator(Class<?> clazz) {
124
178
if (tryInitTrickDataSerializer ()) {
125
179
try {
126
180
// first try with the base accessor
127
- Object serializer = TRICKED_DATA_SERIALIZER_BASE .get ( );
181
+ Object serializer = TRICKED_DATA_SERIALIZER_BASE .apply ( new ZeroBuffer () );
128
182
serializerAccessor .invoke (serializer ); // throwaway instance, for testing
129
183
130
184
// method is working
@@ -145,12 +199,12 @@ static Supplier<Object> determineBestCreator(Class<?> clazz) {
145
199
}
146
200
}
147
201
148
- // try via DefaultInstances as fallback
149
- return () -> {
150
- Object packetInstance = DefaultInstances . DEFAULT . create ( clazz ) ;
151
- Objects . requireNonNull ( packetInstance , "Unable to create instance for class " + clazz + " - " + tryInitTrickDataSerializer () + " - " + streamCodec );
152
- return packetInstance ;
153
- } ;
202
+ Object instance = DefaultInstances . DEFAULT . create ( clazz );
203
+ if ( instance == null ) {
204
+ return null ;
205
+ }
206
+
207
+ return () -> DefaultInstances . DEFAULT . create ( clazz ) ;
154
208
}
155
209
156
210
/**
@@ -206,7 +260,7 @@ public static StructureModifier<Object> getStructure(final PacketType packetType
206
260
*/
207
261
public static Object newNullDataSerializer () {
208
262
tryInitTrickDataSerializer ();
209
- return TRICKED_DATA_SERIALIZER_BASE .get ( );
263
+ return TRICKED_DATA_SERIALIZER_BASE .apply ( new ZeroBuffer () );
210
264
}
211
265
212
266
static void initTrickDataSerializer () {
@@ -236,10 +290,10 @@ static void initTrickDataSerializer() {
236
290
.parameterDerivedOf (ByteBuf .class )
237
291
.parameterDerivedOf (MinecraftReflection .getRegistryAccessClass ())
238
292
.build ()));
239
- TRICKED_DATA_SERIALIZER_BASE = () -> accessor .invoke (new ZeroBuffer () , MinecraftRegistryAccess .get ());
293
+ TRICKED_DATA_SERIALIZER_BASE = (buf ) -> accessor .invoke (buf , MinecraftRegistryAccess .get ());
240
294
} else {
241
295
ConstructorAccessor accessor = Accessors .getConstructorAccessor (serializerBase , ByteBuf .class );
242
- TRICKED_DATA_SERIALIZER_BASE = () -> accessor . invoke ( new ZeroBuffer ()) ;
296
+ TRICKED_DATA_SERIALIZER_BASE = accessor :: invoke ;
243
297
}
244
298
245
299
//xtended builder which intercepts the read string method as well
0 commit comments