ViaVersion/common/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolRegistry.java

425 lines
19 KiB
Java
Raw Normal View History

package us.myles.ViaVersion.api.protocol;
import com.google.common.collect.Lists;
2018-07-03 14:58:00 +02:00
import com.google.common.collect.Range;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.jetbrains.annotations.Nullable;
import us.myles.ViaVersion.api.Pair;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.MappingDataLoader;
import us.myles.ViaVersion.protocols.base.BaseProtocol;
import us.myles.ViaVersion.protocols.base.BaseProtocol1_16;
2018-07-03 14:58:00 +02:00
import us.myles.ViaVersion.protocols.base.BaseProtocol1_7;
2016-06-08 16:38:59 +02:00
import us.myles.ViaVersion.protocols.protocol1_10to1_9_3.Protocol1_10To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_11_1to1_11.Protocol1_11_1To1_11;
2016-10-04 20:04:49 +02:00
import us.myles.ViaVersion.protocols.protocol1_11to1_10.Protocol1_11To1_10;
import us.myles.ViaVersion.protocols.protocol1_12_1to1_12.Protocol1_12_1To1_12;
import us.myles.ViaVersion.protocols.protocol1_12_2to1_12_1.Protocol1_12_2To1_12_1;
import us.myles.ViaVersion.protocols.protocol1_12to1_11_1.Protocol1_12To1_11_1;
2019-06-03 20:21:33 +02:00
import us.myles.ViaVersion.protocols.protocol1_13_1to1_13.Protocol1_13_1To1_13;
2018-10-17 15:38:59 +02:00
import us.myles.ViaVersion.protocols.protocol1_13_2to1_13_1.Protocol1_13_2To1_13_1;
2018-08-01 20:20:52 +02:00
import us.myles.ViaVersion.protocols.protocol1_13to1_12_2.Protocol1_13To1_12_2;
2019-05-07 18:19:34 +02:00
import us.myles.ViaVersion.protocols.protocol1_14_1to1_14.Protocol1_14_1To1_14;
2019-05-16 19:21:26 +02:00
import us.myles.ViaVersion.protocols.protocol1_14_2to1_14_1.Protocol1_14_2To1_14_1;
2019-06-03 20:21:33 +02:00
import us.myles.ViaVersion.protocols.protocol1_14_3to1_14_2.Protocol1_14_3To1_14_2;
2019-07-03 19:36:30 +02:00
import us.myles.ViaVersion.protocols.protocol1_14_4to1_14_3.Protocol1_14_4To1_14_3;
2019-04-23 17:02:18 +02:00
import us.myles.ViaVersion.protocols.protocol1_14to1_13_2.Protocol1_14To1_13_2;
2019-12-12 16:32:53 +01:00
import us.myles.ViaVersion.protocols.protocol1_15_1to1_15.Protocol1_15_1To1_15;
2020-01-16 15:58:35 +01:00
import us.myles.ViaVersion.protocols.protocol1_15_2to1_15_1.Protocol1_15_2To1_15_1;
import us.myles.ViaVersion.protocols.protocol1_15to1_14_4.Protocol1_15To1_14_4;
2020-06-24 14:10:33 +02:00
import us.myles.ViaVersion.protocols.protocol1_16_1to1_16.Protocol1_16_1To1_16;
import us.myles.ViaVersion.protocols.protocol1_16_2to1_16_1.Protocol1_16_2To1_16_1;
2020-09-07 15:11:11 +02:00
import us.myles.ViaVersion.protocols.protocol1_16_3to1_16_2.Protocol1_16_3To1_16_2;
2020-10-07 12:32:52 +02:00
import us.myles.ViaVersion.protocols.protocol1_16_4to1_16_3.Protocol1_16_4To1_16_3;
2020-02-05 20:09:06 +01:00
import us.myles.ViaVersion.protocols.protocol1_16to1_15_2.Protocol1_16To1_15_2;
2020-11-04 21:52:38 +01:00
import us.myles.ViaVersion.protocols.protocol1_17to1_16_4.Protocol1_17To1_16_4;
import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4.Protocol1_9_1_2To1_9_3_4;
import us.myles.ViaVersion.protocols.protocol1_9_1to1_9.Protocol1_9_1To1_9;
import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3To1_9_1_2;
import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import us.myles.ViaVersion.protocols.protocol1_9to1_9_1.Protocol1_9To1_9_1;
2020-04-03 00:05:44 +02:00
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
2020-04-03 00:05:44 +02:00
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ProtocolRegistry {
public static final Protocol BASE_PROTOCOL = new BaseProtocol();
public static int SERVER_PROTOCOL = -1;
public static int maxProtocolPathSize = 50;
// Input Version -> Output Version & Protocol (Allows fast lookup)
private static final Int2ObjectMap<Int2ObjectMap<Protocol>> registryMap = new Int2ObjectOpenHashMap<>(32);
private static final Map<Class<? extends Protocol>, Protocol> protocols = new HashMap<>();
2016-09-27 18:31:10 +02:00
private static final Map<Pair<Integer, Integer>, List<Pair<Integer, Protocol>>> pathCache = new ConcurrentHashMap<>();
private static final Set<Integer> supportedVersions = new HashSet<>();
2018-07-03 14:58:00 +02:00
private static final List<Pair<Range<Integer>, Protocol>> baseProtocols = Lists.newCopyOnWriteArrayList();
private static final List<Protocol> registerList = new ArrayList<>();
2020-04-03 00:05:44 +02:00
private static final Object MAPPING_LOADER_LOCK = new Object();
private static Map<Class<? extends Protocol>, CompletableFuture<Void>> mappingLoaderFutures = new HashMap<>();
private static ThreadPoolExecutor mappingLoaderExecutor;
2020-04-03 00:05:44 +02:00
private static boolean mappingsLoaded;
static {
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("Via-Mappingloader-%d").build();
mappingLoaderExecutor = new ThreadPoolExecutor(5, 16, 45L, TimeUnit.SECONDS, new SynchronousQueue<>(), threadFactory);
mappingLoaderExecutor.allowCoreThreadTimeOut(true);
// Base Protocol
2018-07-03 14:58:00 +02:00
registerBaseProtocol(BASE_PROTOCOL, Range.lessThan(Integer.MIN_VALUE));
2020-10-16 18:21:45 +02:00
registerBaseProtocol(new BaseProtocol1_7(), Range.lessThan(ProtocolVersion.v1_16.getVersion()));
registerBaseProtocol(new BaseProtocol1_16(), Range.atLeast(ProtocolVersion.v1_16.getVersion()));
2018-07-03 14:58:00 +02:00
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_9To1_8(), ProtocolVersion.v1_9, ProtocolVersion.v1_8);
2020-10-16 18:21:45 +02:00
registerProtocol(new Protocol1_9_1To1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getVersion(), ProtocolVersion.v1_9_2.getVersion()), ProtocolVersion.v1_9.getVersion());
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_9_3To1_9_1_2(), ProtocolVersion.v1_9_3, ProtocolVersion.v1_9_2);
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_9To1_9_1(), ProtocolVersion.v1_9, ProtocolVersion.v1_9_2);
2020-10-16 18:21:45 +02:00
registerProtocol(new Protocol1_9_1_2To1_9_3_4(), Arrays.asList(ProtocolVersion.v1_9_1.getVersion(), ProtocolVersion.v1_9_2.getVersion()), ProtocolVersion.v1_9_3.getVersion());
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_10To1_9_3_4(), ProtocolVersion.v1_10, ProtocolVersion.v1_9_3);
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_11To1_10(), ProtocolVersion.v1_11, ProtocolVersion.v1_10);
registerProtocol(new Protocol1_11_1To1_11(), ProtocolVersion.v1_11_1, ProtocolVersion.v1_11);
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_12To1_11_1(), ProtocolVersion.v1_12, ProtocolVersion.v1_11_1);
registerProtocol(new Protocol1_12_1To1_12(), ProtocolVersion.v1_12_1, ProtocolVersion.v1_12);
registerProtocol(new Protocol1_12_2To1_12_1(), ProtocolVersion.v1_12_2, ProtocolVersion.v1_12_1);
2017-10-26 15:39:57 +02:00
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_13To1_12_2(), ProtocolVersion.v1_13, ProtocolVersion.v1_12_2);
registerProtocol(new Protocol1_13_1To1_13(), ProtocolVersion.v1_13_1, ProtocolVersion.v1_13);
registerProtocol(new Protocol1_13_2To1_13_1(), ProtocolVersion.v1_13_2, ProtocolVersion.v1_13_1);
2019-04-23 17:02:18 +02:00
2019-08-23 22:14:32 +02:00
registerProtocol(new Protocol1_14To1_13_2(), ProtocolVersion.v1_14, ProtocolVersion.v1_13_2);
registerProtocol(new Protocol1_14_1To1_14(), ProtocolVersion.v1_14_1, ProtocolVersion.v1_14);
registerProtocol(new Protocol1_14_2To1_14_1(), ProtocolVersion.v1_14_2, ProtocolVersion.v1_14_1);
registerProtocol(new Protocol1_14_3To1_14_2(), ProtocolVersion.v1_14_3, ProtocolVersion.v1_14_2);
registerProtocol(new Protocol1_14_4To1_14_3(), ProtocolVersion.v1_14_4, ProtocolVersion.v1_14_3);
registerProtocol(new Protocol1_15To1_14_4(), ProtocolVersion.v1_15, ProtocolVersion.v1_14_4);
2019-12-17 16:28:43 +01:00
registerProtocol(new Protocol1_15_1To1_15(), ProtocolVersion.v1_15_1, ProtocolVersion.v1_15);
2020-01-19 19:09:57 +01:00
registerProtocol(new Protocol1_15_2To1_15_1(), ProtocolVersion.v1_15_2, ProtocolVersion.v1_15_1);
2020-02-05 20:09:06 +01:00
registerProtocol(new Protocol1_16To1_15_2(), ProtocolVersion.v1_16, ProtocolVersion.v1_15_2);
2020-06-24 14:10:33 +02:00
registerProtocol(new Protocol1_16_1To1_16(), ProtocolVersion.v1_16_1, ProtocolVersion.v1_16);
registerProtocol(new Protocol1_16_2To1_16_1(), ProtocolVersion.v1_16_2, ProtocolVersion.v1_16_1);
2020-09-07 15:11:11 +02:00
registerProtocol(new Protocol1_16_3To1_16_2(), ProtocolVersion.v1_16_3, ProtocolVersion.v1_16_2);
2020-10-07 12:32:52 +02:00
registerProtocol(new Protocol1_16_4To1_16_3(), ProtocolVersion.v1_16_4, ProtocolVersion.v1_16_3);
2020-11-04 21:52:38 +01:00
registerProtocol(new Protocol1_17To1_16_4(), ProtocolVersion.v1_17, ProtocolVersion.v1_16_4);
2019-08-23 22:14:32 +02:00
}
2020-03-25 18:36:06 +01:00
public static void init() {
// Empty method to trigger static initializer once
}
2019-08-23 22:14:32 +02:00
/**
* Register a protocol
*
* @param protocol The protocol to register.
* @param supported Supported client versions.
* @param output The output server version it converts to.
*/
public static void registerProtocol(Protocol protocol, ProtocolVersion supported, ProtocolVersion output) {
2020-10-16 18:21:45 +02:00
registerProtocol(protocol, Collections.singletonList(supported.getVersion()), output.getVersion());
}
/**
* Register a protocol
*
* @param protocol The protocol to register.
* @param supported Supported client versions.
* @param output The output server version it converts to.
*/
public static void registerProtocol(Protocol protocol, List<Integer> supported, int output) {
2016-03-23 15:00:48 +01:00
// Clear cache as this may make new routes.
if (!pathCache.isEmpty()) {
2016-03-23 15:00:48 +01:00
pathCache.clear();
}
2016-03-23 15:00:48 +01:00
protocols.put(protocol.getClass(), protocol);
for (int version : supported) {
2020-06-09 08:38:22 +02:00
Int2ObjectMap<Protocol> protocolMap = registryMap.computeIfAbsent(version, s -> new Int2ObjectOpenHashMap<>(2));
protocolMap.put(output, protocol);
}
if (Via.getPlatform().isPluginEnabled()) {
2016-09-25 21:05:58 +02:00
protocol.register(Via.getManager().getProviders());
refreshVersions();
} else {
registerList.add(protocol);
}
if (protocol.hasMappingDataToLoad()) {
if (mappingLoaderExecutor != null) {
// Submit mapping data loading
2020-03-23 12:22:34 +01:00
addMappingLoaderFuture(protocol.getClass(), protocol::loadMappingData);
} else {
// Late protocol adding - just do it on the current thread
protocol.loadMappingData();
}
}
}
2018-07-03 14:58:00 +02:00
/**
* Registers a base protocol.
* Base Protocols registered later have higher priority
* Only one base protocol will be added to pipeline
*
2018-10-27 13:25:42 +02:00
* @param baseProtocol Base Protocol to register
2018-07-03 14:58:00 +02:00
* @param supportedProtocols Versions that baseProtocol supports
*/
public static void registerBaseProtocol(Protocol baseProtocol, Range<Integer> supportedProtocols) {
baseProtocols.add(new Pair<>(supportedProtocols, baseProtocol));
if (Via.getPlatform().isPluginEnabled()) {
baseProtocol.register(Via.getManager().getProviders());
refreshVersions();
} else {
registerList.add(baseProtocol);
}
}
public static void refreshVersions() {
supportedVersions.clear();
supportedVersions.add(ProtocolRegistry.SERVER_PROTOCOL);
for (ProtocolVersion versions : ProtocolVersion.getProtocols()) {
2020-10-16 18:21:45 +02:00
List<Pair<Integer, Protocol>> paths = getProtocolPath(versions.getVersion(), ProtocolRegistry.SERVER_PROTOCOL);
if (paths == null) continue;
2020-10-16 18:21:45 +02:00
supportedVersions.add(versions.getVersion());
2020-04-03 00:05:44 +02:00
for (Pair<Integer, Protocol> path : paths) {
supportedVersions.add(path.getKey());
2020-04-03 00:05:44 +02:00
}
}
}
/**
* Get the versions compatible with the server.
*
* @return Read-only set of the versions.
*/
public static SortedSet<Integer> getSupportedVersions() {
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
}
/**
* Check if this plugin is useful to the server.
*
* @return True if there is a useful pipe
*/
public static boolean isWorkingPipe() {
for (Int2ObjectMap<Protocol> map : registryMap.values()) {
if (map.containsKey(SERVER_PROTOCOL)) return true;
}
return false; // No destination for protocol
}
/**
* Called when the server is enabled, to register any non registered listeners.
*/
public static void onServerLoaded() {
for (Protocol protocol : registerList) {
2016-09-25 21:05:58 +02:00
protocol.register(Via.getManager().getProviders());
}
registerList.clear();
}
/**
* Calculate a path to get from an input protocol to the servers protocol.
*
* @param current The current items in the path
* @param clientVersion The current input version
* @param serverVersion The desired output version
* @return The path which has been generated, null if failed.
*/
@Nullable
private static List<Pair<Integer, Protocol>> getProtocolPath(List<Pair<Integer, Protocol>> current, int clientVersion, int serverVersion) {
if (clientVersion == serverVersion) return null; // We're already there
if (current.size() > maxProtocolPathSize) return null; // Fail safe, protocol too complicated.
// First check if there is any protocols for this
Int2ObjectMap<Protocol> inputMap = registryMap.get(clientVersion);
2018-09-30 20:48:23 +02:00
if (inputMap == null) {
return null; // Not supported
}
// Next check there isn't an obvious path
2018-09-30 20:48:23 +02:00
Protocol protocol = inputMap.get(serverVersion);
if (protocol != null) {
current.add(new Pair<>(serverVersion, protocol));
return current; // Easy solution
}
// There might be a more advanced solution... So we'll see if any of the others can get us there
List<Pair<Integer, Protocol>> shortest = null;
for (Int2ObjectMap.Entry<Protocol> entry : inputMap.int2ObjectEntrySet()) {
// Ensure it wasn't caught by the other loop
if (entry.getIntKey() == (serverVersion)) continue;
Pair<Integer, Protocol> pair = new Pair<>(entry.getIntKey(), entry.getValue());
// Ensure no recursion
if (current.contains(pair)) continue;
// Create a copy
List<Pair<Integer, Protocol>> newCurrent = new ArrayList<>(current);
newCurrent.add(pair);
// Calculate the rest of the protocol using the current
newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion);
if (newCurrent != null) {
// If it's shorter then choose it
if (shortest == null || shortest.size() > newCurrent.size()) {
shortest = newCurrent;
}
}
}
return shortest; // null if none found
}
/**
* Calculate a path from a client version to server version.
*
* @param clientVersion The input client version
* @param serverVersion The desired output server version
* @return The path it generated, null if it failed.
*/
@Nullable
public static List<Pair<Integer, Protocol>> getProtocolPath(int clientVersion, int serverVersion) {
2016-03-23 15:00:48 +01:00
Pair<Integer, Integer> protocolKey = new Pair<>(clientVersion, serverVersion);
// Check cache
2018-09-30 20:48:23 +02:00
List<Pair<Integer, Protocol>> protocolList = pathCache.get(protocolKey);
if (protocolList != null) {
return protocolList;
2016-03-23 15:00:48 +01:00
}
// Generate path
List<Pair<Integer, Protocol>> outputPath = getProtocolPath(new ArrayList<>(), clientVersion, serverVersion);
2016-03-23 15:00:48 +01:00
// If it found a path, cache it.
if (outputPath != null) {
pathCache.put(protocolKey, outputPath);
}
return outputPath;
}
2018-07-03 14:58:00 +02:00
/**
* Returns a protocol instance by its class.
*
* @param protocolClass class of the protocol
* @return protocol if present
*/
@Nullable
public static Protocol getProtocol(Class<? extends Protocol> protocolClass) {
return protocols.get(protocolClass);
}
2018-07-03 14:58:00 +02:00
public static Protocol getBaseProtocol(int serverVersion) {
2018-07-03 16:26:00 +02:00
for (Pair<Range<Integer>, Protocol> rangeProtocol : Lists.reverse(baseProtocols)) {
if (rangeProtocol.getKey().contains(serverVersion)) {
2018-07-03 14:58:00 +02:00
return rangeProtocol.getValue();
2018-07-03 16:26:00 +02:00
}
}
2018-07-03 14:58:00 +02:00
throw new IllegalStateException("No Base Protocol for " + serverVersion);
}
2018-07-03 16:19:32 +02:00
public static boolean isBaseProtocol(Protocol protocol) {
2018-07-03 16:26:00 +02:00
for (Pair<Range<Integer>, Protocol> p : baseProtocols) {
if (p.getValue() == protocol) {
2018-07-03 16:19:32 +02:00
return true;
2018-07-03 16:26:00 +02:00
}
}
2018-07-03 16:19:32 +02:00
return false;
}
/**
* Ensure that mapping data for that protocol has already been loaded, completes it otherwise.
*
* @param protocolClass protocol class
*/
public static void completeMappingDataLoading(Class<? extends Protocol> protocolClass) throws Exception {
2020-04-03 00:05:44 +02:00
if (mappingsLoaded) return;
2020-04-03 00:05:44 +02:00
CompletableFuture<Void> future = getMappingLoaderFuture(protocolClass);
if (future == null) return;
future.get();
}
/**
* Shuts down the executor and uncaches mappings if all futures have been completed.
*
* @return true if the executor has now been shut down
*/
public static boolean checkForMappingCompletion() {
2020-04-03 00:05:44 +02:00
synchronized (MAPPING_LOADER_LOCK) {
if (mappingsLoaded) return false;
for (CompletableFuture<Void> future : mappingLoaderFutures.values()) {
// Return if any future hasn't completed yet
if (!future.isDone()) {
return false;
}
2020-04-03 00:05:44 +02:00
}
shutdownLoaderExecutor();
return true;
}
}
private static void shutdownLoaderExecutor() {
Via.getPlatform().getLogger().info("Finished mapping loading, shutting down loader executor!");
2020-04-03 00:05:44 +02:00
mappingsLoaded = true;
mappingLoaderExecutor.shutdown();
mappingLoaderExecutor = null;
mappingLoaderFutures.clear();
mappingLoaderFutures = null;
2020-04-03 00:05:44 +02:00
if (MappingDataLoader.isCacheJsonMappings()) {
MappingDataLoader.getMappingsCache().clear();
}
}
2020-03-23 12:22:34 +01:00
public static void addMappingLoaderFuture(Class<? extends Protocol> protocolClass, Runnable runnable) {
2020-04-03 00:05:44 +02:00
synchronized (MAPPING_LOADER_LOCK) {
CompletableFuture<Void> future = CompletableFuture.runAsync(runnable, mappingLoaderExecutor).exceptionally(throwable -> {
Via.getPlatform().getLogger().severe("Error during mapping loading of " + protocolClass.getSimpleName());
throwable.printStackTrace();
return null;
});
2020-04-03 00:05:44 +02:00
mappingLoaderFutures.put(protocolClass, future);
}
2020-03-23 10:40:57 +01:00
}
public static void addMappingLoaderFuture(Class<? extends Protocol> protocolClass, Class<? extends Protocol> dependsOn, Runnable runnable) {
synchronized (MAPPING_LOADER_LOCK) {
CompletableFuture<Void> future = getMappingLoaderFuture(dependsOn)
.whenCompleteAsync((v, throwable) -> runnable.run(), mappingLoaderExecutor).exceptionally(throwable -> {
Via.getPlatform().getLogger().severe("Error during mapping loading of " + protocolClass.getSimpleName());
throwable.printStackTrace();
return null;
});
mappingLoaderFutures.put(protocolClass, future);
}
}
@Nullable
2020-03-23 12:22:34 +01:00
public static CompletableFuture<Void> getMappingLoaderFuture(Class<? extends Protocol> protocolClass) {
2020-04-03 00:05:44 +02:00
synchronized (MAPPING_LOADER_LOCK) {
if (mappingsLoaded) return null;
return mappingLoaderFutures.get(protocolClass);
}
}
}