Skip to content

Commit 0c6fa46

Browse files
committed
Optimize class lookups
1 parent df3b68d commit 0c6fa46

File tree

10 files changed

+140
-93
lines changed

10 files changed

+140
-93
lines changed

src/main/java/com/comphenix/protocol/async/AsyncMarker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ public boolean isMinecraftAsync(PacketEvent event) throws FieldAccessException {
414414
} else if (methods.size() == 1) {
415415
// We're in 1.2.5
416416
alwaysSync = true;
417-
} else if (MinecraftVersion.getCurrentVersion().isAtLeast(MinecraftVersion.BOUNTIFUL_UPDATE)) {
417+
} else if (MinecraftVersion.BOUNTIFUL_UPDATE.atOrAbove()) {
418418
// The centralized async marker was removed in 1.8
419419
// Incoming chat packets can be async
420420
if (event.getPacketType() == PacketType.Play.Client.CHAT) {

src/main/java/com/comphenix/protocol/injector/StructureCache.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ public static StructureModifier<Object> getStructure(final PacketType packetType
137137
Class<?> packetClass = PacketRegistry.getPacketClassFromType(type);
138138

139139
// We need to map the Bundle Delimiter to the synthetic bundle packet which contains a list of all packets in a bundle
140-
if (MinecraftVersion.atOrAbove(MinecraftVersion.FEATURE_PREVIEW_2) && packetClass.equals(MinecraftReflection.getBundleDelimiterClass())) {
141-
packetClass = MinecraftReflection.getPackedBundlePacketClass();
140+
if (MinecraftReflection.isBundleDelimiter(packetClass)) {
141+
packetClass = MinecraftReflection.getPackedBundlePacketClass().get();
142142
}
143143

144144
return new StructureModifier<>(packetClass, MinecraftReflection.getPacketClass(), true);

src/main/java/com/comphenix/protocol/injector/netty/channel/NettyChannelInjector.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,10 @@ public boolean inject() {
204204
return false;
205205
}
206206

207-
String anchorHandler = "decoder";
208-
if (MinecraftVersion.FEATURE_PREVIEW_2.atOrAbove()) {
209-
anchorHandler = "unbundler";
210-
}
211-
212207
// inject our handlers
213208
this.wrappedChannel.pipeline().addAfter("encoder", WIRE_PACKET_ENCODER_NAME, WIRE_PACKET_ENCODER);
214209
this.wrappedChannel.pipeline().addAfter(
215-
anchorHandler,
210+
"decoder",
216211
INTERCEPTOR_NAME,
217212
new InboundPacketInterceptor(this, this.channelListener));
218213

src/main/java/com/comphenix/protocol/utility/CachedPackage.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ final class CachedPackage {
3636
* @param packageName - the name of the current package.
3737
* @param source - the class source.
3838
*/
39-
public CachedPackage(String packageName, ClassSource source) {
39+
CachedPackage(String packageName, ClassSource source) {
4040
this.source = source;
4141
this.packageName = packageName;
4242
this.cache = new ConcurrentHashMap<>();
@@ -71,20 +71,38 @@ public void setPackageClass(String className, Class<?> clazz) {
7171
}
7272
}
7373

74+
private Optional<Class<?>> resolveClass(String className) {
75+
return source.loadClass(combine(packageName, className));
76+
}
77+
78+
public Class<?> requireClass(String className) throws ClassNotFoundException {
79+
String canonicalName = combine(packageName, className);
80+
return source.loadClass(canonicalName)
81+
.orElseThrow(() -> new ClassNotFoundException(className));
82+
}
83+
7484
/**
7585
* Retrieve the class object of a specific class in the current package.
7686
*
7787
* @param className - the specific class.
7888
* @return Class object.
7989
* @throws RuntimeException If we are unable to find the given class.
8090
*/
81-
public Optional<Class<?>> getPackageClass(final String className) {
82-
return this.cache.computeIfAbsent(className, x -> {
83-
try {
84-
return Optional.ofNullable(this.source.loadClass(combine(this.packageName, className)));
85-
} catch (ClassNotFoundException ex) {
86-
return Optional.empty();
91+
public Optional<Class<?>> getPackageClass(String className, String... aliases) {
92+
return cache.computeIfAbsent(className, x -> {
93+
Optional<Class<?>> clazz = resolveClass(className);
94+
if (clazz.isPresent()) {
95+
return clazz;
8796
}
97+
98+
for (String alias : aliases) {
99+
clazz = resolveClass(className);
100+
if (clazz.isPresent()) {
101+
return clazz;
102+
}
103+
}
104+
105+
return Optional.empty();
88106
});
89107
}
90108
}

src/main/java/com/comphenix/protocol/utility/ClassSource.java

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import java.util.Collections;
44
import java.util.Map;
5+
import java.util.Optional;
6+
import java.util.function.Supplier;
57

68
/**
79
* Represents an abstract class loader that can only retrieve classes by their canonical name.
@@ -37,27 +39,25 @@ static ClassSource fromPackage(String packageName) {
3739
* @return The corresponding class source.
3840
*/
3941
static ClassSource fromClassLoader(final ClassLoader loader) {
40-
return loader::loadClass;
42+
return canonicalName -> {
43+
try {
44+
return Optional.of(loader.loadClass(canonicalName));
45+
} catch (ClassNotFoundException ignored) {
46+
return Optional.empty();
47+
}
48+
};
4149
}
4250

4351
/**
4452
* Construct a class source from a mapping of canonical names and the corresponding classes. If the map is null, it
4553
* will be interpreted as an empty map. If the map does not contain a Class with the specified name, or that string
46-
* maps to NULL explicitly, a {@link ClassNotFoundException} will be thrown.
54+
* maps to NULL explicitly, an empty optional will be returned
4755
*
4856
* @param map - map of class names and classes.
4957
* @return The class source.
5058
*/
5159
static ClassSource fromMap(final Map<String, Class<?>> map) {
52-
return canonicalName -> {
53-
Class<?> loaded = map == null ? null : map.get(canonicalName);
54-
if (loaded == null) {
55-
// Throw the appropriate exception if we can't load the class
56-
throw new ClassNotFoundException("The specified class could not be found by this ClassLoader.");
57-
}
58-
59-
return loaded;
60-
};
60+
return canonicalName -> Optional.ofNullable(map.get(canonicalName));
6161
}
6262

6363
/**
@@ -94,14 +94,8 @@ static String append(String a, String b) {
9494
* @param other - the other class source.
9595
* @return A new class source.
9696
*/
97-
default ClassSource retry(final ClassSource other) {
98-
return canonicalName -> {
99-
try {
100-
return ClassSource.this.loadClass(canonicalName);
101-
} catch (ClassNotFoundException e) {
102-
return other.loadClass(canonicalName);
103-
}
104-
};
97+
default ClassSource retry(ClassSource other) {
98+
return canonicalName -> Optionals.or(loadClass(canonicalName), () -> other.loadClass(canonicalName));
10599
}
106100

107101
/**
@@ -115,12 +109,9 @@ default ClassSource usingPackage(final String packageName) {
115109
}
116110

117111
/**
118-
* Retrieve a class by name.
119-
*
120-
* @param canonicalName - the full canonical name of the class.
121-
* @return The corresponding class. If the class is not found, NULL should <b>not</b> be returned, instead a {@code
122-
* ClassNotFoundException} exception should be thrown.
123-
* @throws ClassNotFoundException If the class could not be found.
112+
* Retrieve a class by its canonical name
113+
* @param canonicalName The class's canonical name, i.e. java.lang.Object
114+
* @return Optional that may contain a Class
124115
*/
125-
Class<?> loadClass(String canonicalName) throws ClassNotFoundException;
116+
Optional<Class<?>> loadClass(String canonicalName);
126117
}

src/main/java/com/comphenix/protocol/utility/MinecraftReflection.java

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.HashMap;
2626
import java.util.List;
2727
import java.util.Map;
28+
import java.util.Optional;
2829
import java.util.logging.Level;
2930
import java.util.regex.Matcher;
3031
import java.util.regex.Pattern;
@@ -628,16 +629,20 @@ public static Class<?> getIChatBaseComponentClass() {
628629
return getMinecraftClass("network.chat.IChatBaseComponent", "network.chat.IChatbaseComponent", "network.chat.Component", "IChatBaseComponent");
629630
}
630631

631-
public static Class<?> getPackedBundlePacketClass() {
632-
return getMinecraftClass("network.protocol.game.ClientboundBundlePacket", "ClientboundBundlePacket");
632+
public static Optional<Class<?>> getPackedBundlePacketClass() {
633+
return getOptionalNMS("network.protocol.game.ClientboundBundlePacket", "ClientboundBundlePacket");
633634
}
634635

635636
public static boolean isBundlePacket(Class<?> packetClass) {
636-
return MinecraftVersion.FEATURE_PREVIEW_2.atOrAbove() && packetClass.equals(getPackedBundlePacketClass());
637+
return Optionals.Equals(getPackedBundlePacketClass(), packetClass);
637638
}
638639

639-
public static Class<?> getBundleDelimiterClass() {
640-
return getMinecraftClass("network.protocol.BundleDelimiterPacket","BundleDelimiterPacket");
640+
public static boolean isBundleDelimiter(Class<?> packetClass) {
641+
return Optionals.Equals(getBundleDelimiterClass(), packetClass);
642+
}
643+
644+
public static Optional<Class<?>> getBundleDelimiterClass() {
645+
return getOptionalNMS("network.protocol.BundleDelimiterPacket","BundleDelimiterPacket");
641646
}
642647

643648
public static Class<?> getIChatBaseComponentArrayClass() {
@@ -1337,11 +1342,8 @@ public static Object getMinecraftItemStack(ItemStack specific) {
13371342
* @return The class.
13381343
*/
13391344
private static Class<?> getClass(String className) {
1340-
try {
1341-
return getClassSource().loadClass(className);
1342-
} catch (ClassNotFoundException e) {
1343-
throw new RuntimeException("Cannot find class " + className, e);
1344-
}
1345+
return getClassSource().loadClass(className)
1346+
.orElseThrow(() -> new RuntimeException("Cannot find class " + className));
13451347
}
13461348

13471349
/**
@@ -1376,12 +1378,34 @@ public static Class<?> getMinecraftClass(String className) {
13761378
.orElseThrow(() -> new RuntimeException("Failed to find NMS class: " + className));
13771379
}
13781380

1379-
public static Class<?> getNullableNMS(String className, String... aliases) {
1380-
try {
1381-
return getMinecraftClass(className, aliases);
1382-
} catch (RuntimeException ex) {
1383-
return null;
1381+
/**
1382+
* Optionally retrieve the class object of a NMS (net.minecraft.server) class.
1383+
* If the class does not exist, the optional will be empty
1384+
*
1385+
* @param className NMS class name
1386+
* @param aliases Potential aliases
1387+
* @return Optional that may contain the class
1388+
*/
1389+
private static Optional<Class<?>> getOptionalNMS(String className, String... aliases) {
1390+
if (minecraftPackage == null) {
1391+
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
13841392
}
1393+
1394+
return minecraftPackage.getPackageClass(className, aliases);
1395+
}
1396+
1397+
/**
1398+
* Retrieves a nullable NMS (net.minecraft.server) class. We will attempt to
1399+
* look up the class and its aliases, but will return null if none is found.
1400+
*
1401+
* @deprecated - Use getOptionalNMS where possible
1402+
* @param className NMS class name
1403+
* @param aliases Potential aliases
1404+
* @return The class, or null if not found
1405+
*/
1406+
@Deprecated
1407+
public static Class<?> getNullableNMS(String className, String... aliases) {
1408+
return getOptionalNMS(className, aliases).orElse(null);
13851409
}
13861410

13871411
/**
@@ -1418,29 +1442,8 @@ private static ClassSource getClassSource() {
14181442
* @throws RuntimeException If we are unable to find any of the given classes.
14191443
*/
14201444
public static Class<?> getMinecraftClass(String className, String... aliases) {
1421-
if (minecraftPackage == null) {
1422-
minecraftPackage = new CachedPackage(getMinecraftPackage(), getClassSource());
1423-
}
1424-
1425-
return minecraftPackage.getPackageClass(className).orElseGet(() -> {
1426-
Class<?> resolved = null;
1427-
for (String alias : aliases) {
1428-
// try to resolve the class and stop searching if we found it
1429-
resolved = minecraftPackage.getPackageClass(alias).orElse(null);
1430-
if (resolved != null) {
1431-
break;
1432-
}
1433-
}
1434-
1435-
// if we resolved the class cache it and return the result
1436-
if (resolved != null) {
1437-
minecraftPackage.setPackageClass(className, resolved);
1438-
return resolved;
1439-
}
1440-
1441-
// unable to find the class
1442-
throw new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases)));
1443-
});
1445+
return getOptionalNMS(className, aliases)
1446+
.orElseThrow(() -> new RuntimeException(String.format("Unable to find %s (%s)", className, String.join(", ", aliases))));
14441447
}
14451448

14461449
/**

src/main/java/com/comphenix/protocol/utility/MinecraftVersion.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ public static void setCurrentVersion(MinecraftVersion version) {
273273
currentVersion = version;
274274
}
275275

276-
public static boolean atOrAbove(MinecraftVersion version) {
276+
private static boolean atOrAbove(MinecraftVersion version) {
277277
return getCurrentVersion().isAtLeast(version);
278278
}
279279

@@ -354,7 +354,7 @@ public boolean isSnapshot() {
354354
*/
355355
public boolean atOrAbove() {
356356
if (this.atCurrentOrAbove == null) {
357-
this.atCurrentOrAbove = MinecraftVersion.atOrAbove(this);
357+
this.atCurrentOrAbove = atOrAbove(this);
358358
}
359359

360360
return this.atCurrentOrAbove;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package com.comphenix.protocol.utility;
2+
3+
import java.util.Optional;
4+
import java.util.function.Predicate;
5+
import java.util.function.Supplier;
6+
7+
/**
8+
* Utility methods for operating with Optionals
9+
*/
10+
public final class Optionals {
11+
12+
/**
13+
* Chains two optionals together by returning the secondary
14+
* optional if the primary does not contain a value
15+
* @param primary Primary optional
16+
* @param secondary Supplier of secondary optional
17+
* @return The resulting optional
18+
* @param <T> Type
19+
*/
20+
public static <T> Optional<T> or(Optional<T> primary, Supplier<Optional<T>> secondary) {
21+
return primary.isPresent() ? primary : secondary.get();
22+
}
23+
24+
/**
25+
* Evaluates the provided predicate against the optional only if it is present
26+
* @param optional Optional
27+
* @param predicate Test to run against potential value
28+
* @return True if the optional is present and the predicate passes
29+
* @param <T> Type
30+
*/
31+
public static <T> boolean TestIfPresent(Optional<T> optional, Predicate<T> predicate) {
32+
return optional.isPresent() && predicate.test(optional.get());
33+
}
34+
35+
/**
36+
* Check if the optional has a value and its value equals the provided value
37+
* @param optional Optional
38+
* @param contents Contents to test for
39+
* @return True if the optional has a value and that value equals the parameter
40+
* @param <T> Type
41+
*/
42+
public static <T> boolean Equals(Optional<T> optional, Class<?> contents) {
43+
return optional.isPresent() && contents.equals(optional.get());
44+
}
45+
}

src/main/java/com/comphenix/protocol/wrappers/TroveWrapper.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import java.lang.reflect.Method;
55
import java.util.List;
66
import java.util.Map;
7+
import java.util.Optional;
78
import java.util.Set;
89

910
import javax.annotation.Nonnull;
@@ -142,17 +143,11 @@ private static ClassSource getClassSource(Class<?> clazz) {
142143
private static Object getDecorated(@Nonnull Object trove) {
143144
if (trove == null)
144145
throw new IllegalArgumentException("trove instance cannot be non-null.");
145-
146+
146147
AbstractFuzzyMatcher<Class<?>> match = FuzzyMatchers.matchSuper(trove.getClass());
147-
Class<?> decorators = null;
148-
149-
try {
150-
// Attempt to get decorator class
151-
decorators = getClassSource(trove.getClass()).loadClass("TDecorators");
152-
} catch (ClassNotFoundException e) {
153-
throw new IllegalStateException(e.getMessage(), e);
154-
}
155-
148+
Class<?> decorators = getClassSource(trove.getClass()).loadClass("TDecorator")
149+
.orElseThrow(() -> new IllegalStateException("TDecorators class not found"));
150+
156151
// Find an appropriate wrapper method in TDecorators
157152
for (Method method : decorators.getMethods()) {
158153
Class<?>[] types = method.getParameterTypes();

src/main/java/com/comphenix/protocol/wrappers/WrappedAttributeModifier.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
* @author Kristian
2323
*/
2424
public class WrappedAttributeModifier extends AbstractWrapper {
25-
private static final boolean OPERATION_ENUM = MinecraftVersion.atOrAbove(MinecraftVersion.VILLAGE_UPDATE);
25+
private static final boolean OPERATION_ENUM = MinecraftVersion.VILLAGE_UPDATE.atOrAbove();
2626
private static final Class<?> OPERATION_CLASS;
2727
private static final EquivalentConverter<Operation> OPERATION_CONVERTER;
2828

0 commit comments

Comments
 (0)