Mirror von
https://github.com/ViaVersion/ViaVersion.git
synchronisiert 2024-10-08 11:10:06 +02:00
Optimize protocol path finding
Not perfect, but better. This prevents the path checks from exponentially increasing (if it weren't for the maxProtocolPathSize fail safe). By default, a path will never go to a protocol version that puts it farther from the desired server protocol version, even if a path existed. Otherwise as well as previously, *all* possible paths will be checked until a fitting one is found. Negative examples if the new boolean is set to true: A possible path from 3 to 5 in order of 3->10->5 will be dismissed. A possible path from 5 to 3 in order of 5->0->3 will be dismissed. Negative examples if set to false: While searching for a path from 3 to 5, 3->2->1 could be checked first before 3->4->5 is found. While searching for a path from 5 to 3, 5->6->7 could be checked first before 5->4->3 is found. Assuming custom platforms like Bedrock protocol use the normal registering methods, they will have to change the boolean to false to revert to previous behavior (tho still somewhat better optimized).
Dieser Commit ist enthalten in:
Ursprung
4011aee280
Commit
104fa4e29f
@ -109,6 +109,7 @@ public interface ProtocolManager {
|
||||
* @param protocol protocol to register
|
||||
* @param clientVersion supported client protocol versions
|
||||
* @param serverVersion output server protocol version the protocol converts to
|
||||
* @throws IllegalArgumentException if the client protocol version is equal to the server protocol version
|
||||
*/
|
||||
void registerProtocol(Protocol protocol, ProtocolVersion clientVersion, ProtocolVersion serverVersion);
|
||||
|
||||
@ -118,6 +119,7 @@ public interface ProtocolManager {
|
||||
* @param protocol protocol to register
|
||||
* @param supportedClientVersion supported client protocol versions
|
||||
* @param serverVersion output server protocol version the protocol converts to
|
||||
* @throws IllegalArgumentException if a supported client protocol version is equal to the server protocol version
|
||||
*/
|
||||
void registerProtocol(Protocol protocol, List<Integer> supportedClientVersion, int serverVersion);
|
||||
|
||||
@ -141,6 +143,37 @@ public interface ProtocolManager {
|
||||
*/
|
||||
@Nullable List<ProtocolPathEntry> getProtocolPath(int clientVersion, int serverVersion);
|
||||
|
||||
/**
|
||||
* Returns whether protocol path calculation expects the path to come closer to the expected version with each entry, true by default.
|
||||
* <p>
|
||||
* In practice, this means a path will never go to a protocol version that puts it farther from the desired
|
||||
* server protocol version, even if a path existed.
|
||||
* If this is set to false, *all* possible paths will be checked until a fitting one is found.
|
||||
* <p>
|
||||
* Negative examples if this returns true:
|
||||
* <ul>
|
||||
* A possible path from 3 to 5 in order of 3->10->5 will be dismissed.
|
||||
* A possible path from 5 to 3 in order of 5->0->3 will be dismissed.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Negative examples if this returns false:
|
||||
* <ul>
|
||||
* While searching for a path from 3 to 5, 3->2->1 could be checked first before 3->4->5 is found.
|
||||
* While searching for a path from 5 to 3, 5->6->7 could be checked first before 5->4->3 is found.
|
||||
* </ul>
|
||||
*
|
||||
* @return whether protocol path calculation expects the path to come closer to the expected version with each entry
|
||||
*/
|
||||
boolean onlyCheckLoweringPathEntries();
|
||||
|
||||
/**
|
||||
* Sets whether protocol path calculation expects the path to come closer to the expected version with each entry.
|
||||
*
|
||||
* @param onlyCheckLoweringPathEntries whether protocol path calculation expects the path to come closer to the expected version with each entry
|
||||
* @see #onlyCheckLoweringPathEntries()
|
||||
*/
|
||||
void setOnlyCheckLoweringPathEntries(boolean onlyCheckLoweringPathEntries);
|
||||
|
||||
/**
|
||||
* Returns the maximum protocol path size applied to {@link #getProtocolPath(int, int)}.
|
||||
*
|
||||
|
@ -113,26 +113,26 @@ public class ProtocolVersion {
|
||||
/**
|
||||
* Returns whether a protocol with the given protocol version is registered.
|
||||
*
|
||||
* @param id protocol version
|
||||
* @param version protocol version
|
||||
* @return true if this protocol version has been registered
|
||||
*/
|
||||
public static boolean isRegistered(int id) {
|
||||
return VERSIONS.containsKey(id);
|
||||
public static boolean isRegistered(int version) {
|
||||
return VERSIONS.containsKey(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link ProtocolVersion} instance, even if this protocol version
|
||||
* has not been registered. See {@link #isRegistered(int)} berorehand or {@link #isKnown()}.
|
||||
*
|
||||
* @param id protocol version
|
||||
* @param version protocol version
|
||||
* @return registered or unknown {@link ProtocolVersion}
|
||||
*/
|
||||
public static @NonNull ProtocolVersion getProtocol(int id) {
|
||||
ProtocolVersion protocolVersion = VERSIONS.get(id);
|
||||
public static @NonNull ProtocolVersion getProtocol(int version) {
|
||||
ProtocolVersion protocolVersion = VERSIONS.get(version);
|
||||
if (protocolVersion != null) {
|
||||
return protocolVersion;
|
||||
} else {
|
||||
return new ProtocolVersion(id, "Unknown (" + id + ")");
|
||||
return new ProtocolVersion(version, "Unknown (" + version + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,8 +65,10 @@ import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
|
||||
import com.viaversion.viaversion.protocols.protocol1_9to1_9_1.Protocol1_9To1_9_1;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import us.myles.ViaVersion.api.protocol.ProtocolRegistry;
|
||||
|
||||
@ -107,6 +109,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
private boolean mappingsLoaded;
|
||||
|
||||
private ServerProtocolVersion serverProtocolVersion = new ServerProtocolVersionSingleton(-1);
|
||||
private boolean onlyCheckLoweringPathEntries = true;
|
||||
private int maxProtocolPathSize = 50;
|
||||
|
||||
public ProtocolManagerImpl() {
|
||||
@ -173,8 +176,11 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
|
||||
protocols.put(protocol.getClass(), protocol);
|
||||
|
||||
for (int version : supportedClientVersion) {
|
||||
Int2ObjectMap<Protocol> protocolMap = registryMap.computeIfAbsent(version, s -> new Int2ObjectOpenHashMap<>(2));
|
||||
for (int clientVersion : supportedClientVersion) {
|
||||
// Throw an error if supported client version = server version
|
||||
Preconditions.checkArgument(clientVersion != serverVersion);
|
||||
|
||||
Int2ObjectMap<Protocol> protocolMap = registryMap.computeIfAbsent(clientVersion, s -> new Int2ObjectOpenHashMap<>(2));
|
||||
protocolMap.put(serverVersion, protocol);
|
||||
}
|
||||
|
||||
@ -212,32 +218,40 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
supportedVersions.clear();
|
||||
|
||||
supportedVersions.add(serverProtocolVersion.lowestSupportedVersion());
|
||||
for (ProtocolVersion versions : ProtocolVersion.getProtocols()) {
|
||||
List<ProtocolPathEntry> paths = getProtocolPath(versions.getVersion(), serverProtocolVersion.lowestSupportedVersion());
|
||||
if (paths == null) continue;
|
||||
supportedVersions.add(versions.getVersion());
|
||||
for (ProtocolPathEntry path : paths) {
|
||||
supportedVersions.add(path.getOutputProtocolVersion());
|
||||
for (ProtocolVersion version : ProtocolVersion.getProtocols()) {
|
||||
List<ProtocolPathEntry> protocolPath = getProtocolPath(version.getVersion(), serverProtocolVersion.lowestSupportedVersion());
|
||||
if (protocolPath == null) continue;
|
||||
|
||||
supportedVersions.add(version.getVersion());
|
||||
for (ProtocolPathEntry pathEntry : protocolPath) {
|
||||
supportedVersions.add(pathEntry.getOutputProtocolVersion());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<ProtocolPathEntry> getProtocolPath(int clientVersion, int serverVersion) {
|
||||
ProtocolPathKey protocolKey = new ProtocolPathKeyImpl(clientVersion, serverVersion);
|
||||
if (clientVersion == serverVersion) return null; // Nothing to do!
|
||||
|
||||
// Check cache
|
||||
ProtocolPathKey protocolKey = new ProtocolPathKeyImpl(clientVersion, serverVersion);
|
||||
List<ProtocolPathEntry> protocolList = pathCache.get(protocolKey);
|
||||
if (protocolList != null) {
|
||||
return protocolList;
|
||||
}
|
||||
|
||||
// Generate path
|
||||
List<ProtocolPathEntry> outputPath = getProtocolPath(new ArrayList<>(), clientVersion, serverVersion);
|
||||
// If it found a path, cache it.
|
||||
if (outputPath != null) {
|
||||
pathCache.put(protocolKey, outputPath);
|
||||
// Calculate path
|
||||
Int2ObjectSortedMap<Protocol> outputPath = getProtocolPath(new Int2ObjectLinkedOpenHashMap<>(), clientVersion, serverVersion);
|
||||
if (outputPath == null) {
|
||||
return null;
|
||||
}
|
||||
return outputPath;
|
||||
|
||||
List<ProtocolPathEntry> path = new ArrayList<>(outputPath.size());
|
||||
for (Int2ObjectMap.Entry<Protocol> entry : outputPath.int2ObjectEntrySet()) {
|
||||
path.add(new ProtocolPathEntryImpl(entry.getIntKey(), entry.getValue()));
|
||||
}
|
||||
pathCache.put(protocolKey, path);
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -248,44 +262,41 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
* @param serverVersion desired output version
|
||||
* @return path that has been generated, null if failed
|
||||
*/
|
||||
private @Nullable List<ProtocolPathEntry> getProtocolPath(List<ProtocolPathEntry> current, int clientVersion, int serverVersion) {
|
||||
//TODO optimize?
|
||||
if (clientVersion == serverVersion) return null; // We're already there
|
||||
private @Nullable Int2ObjectSortedMap<Protocol> getProtocolPath(Int2ObjectSortedMap<Protocol> current, int clientVersion, int serverVersion) {
|
||||
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);
|
||||
if (inputMap == null) {
|
||||
// First, check if there is any protocols for this
|
||||
Int2ObjectMap<Protocol> toServerProtocolMap = registryMap.get(clientVersion);
|
||||
if (toServerProtocolMap == null) {
|
||||
return null; // Not supported
|
||||
}
|
||||
|
||||
// Next check there isn't an obvious path
|
||||
Protocol protocol = inputMap.get(serverVersion);
|
||||
// Next, check if there is a direct, single Protocol path
|
||||
Protocol protocol = toServerProtocolMap.get(serverVersion);
|
||||
if (protocol != null) {
|
||||
current.add(new ProtocolPathEntryImpl(serverVersion, protocol));
|
||||
current.put(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<ProtocolPathEntry> shortest = null;
|
||||
for (Int2ObjectMap.Entry<Protocol> entry : inputMap.int2ObjectEntrySet()) {
|
||||
// Ensure it wasn't caught by the other loop
|
||||
if (entry.getIntKey() == serverVersion) continue;
|
||||
Int2ObjectSortedMap<Protocol> shortest = null;
|
||||
for (Int2ObjectMap.Entry<Protocol> entry : toServerProtocolMap.int2ObjectEntrySet()) {
|
||||
// Ensure we don't go back to already contained versions
|
||||
int translatedToVersion = entry.getIntKey();
|
||||
if (current.containsKey(translatedToVersion)) continue;
|
||||
|
||||
ProtocolPathEntry pathEntry = new ProtocolPathEntryImpl(entry.getIntKey(), entry.getValue());
|
||||
// Ensure no recursion
|
||||
if (current.contains(pathEntry)) continue;
|
||||
// Check if the new version is farther away than the current client version
|
||||
if (onlyCheckLoweringPathEntries && Math.abs(serverVersion - translatedToVersion) > Math.abs(serverVersion - clientVersion)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create a copy
|
||||
List<ProtocolPathEntry> newCurrent = new ArrayList<>(current);
|
||||
newCurrent.add(pathEntry);
|
||||
Int2ObjectSortedMap<Protocol> newCurrent = new Int2ObjectLinkedOpenHashMap<>(current);
|
||||
newCurrent.put(translatedToVersion, entry.getValue());
|
||||
|
||||
// Calculate the rest of the protocol using the current path entry
|
||||
newCurrent = getProtocolPath(newCurrent, entry.getIntKey(), serverVersion);
|
||||
|
||||
// If it's shorter then choose it
|
||||
if (newCurrent != null
|
||||
&& (shortest == null || shortest.size() > newCurrent.size())) {
|
||||
// Calculate the rest of the protocol starting from translatedToVersion and take the shortest
|
||||
newCurrent = getProtocolPath(newCurrent, translatedToVersion, serverVersion);
|
||||
if (newCurrent != null && (shortest == null || newCurrent.size() < shortest.size())) {
|
||||
shortest = newCurrent;
|
||||
}
|
||||
}
|
||||
@ -342,6 +353,16 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
return Collections.unmodifiableSortedSet(new TreeSet<>(supportedVersions));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnlyCheckLoweringPathEntries(boolean onlyCheckLoweringPathEntries) {
|
||||
this.onlyCheckLoweringPathEntries = onlyCheckLoweringPathEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onlyCheckLoweringPathEntries() {
|
||||
return onlyCheckLoweringPathEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxProtocolPathSize() {
|
||||
return maxProtocolPathSize;
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2021 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.common.dummy;
|
||||
|
||||
import com.viaversion.viaversion.ViaManagerImpl;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
|
||||
|
||||
public final class DummyInitializer {
|
||||
|
||||
public static void init() {
|
||||
Via.init(new ViaManagerImpl(new TestPlatform(), null, null, null));
|
||||
((ProtocolManagerImpl) Via.getManager().getProtocolManager()).registerProtocols();
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2021 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.common.dummy;
|
||||
|
||||
import com.viaversion.viaversion.configuration.AbstractViaConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class TestConfig extends AbstractViaConfig {
|
||||
|
||||
public TestConfig(File configFile) {
|
||||
super(configFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getDefaultConfigURL() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleConfig(Map<String, Object> config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getUnsupportedOptions() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2021 ViaVersion and contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.viaversion.viaversion.common.dummy;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.viaversion.viaversion.ViaAPIBase;
|
||||
import com.viaversion.viaversion.api.ViaAPI;
|
||||
import com.viaversion.viaversion.api.command.ViaCommandSender;
|
||||
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
|
||||
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
|
||||
import com.viaversion.viaversion.api.platform.PlatformTask;
|
||||
import com.viaversion.viaversion.api.platform.ViaPlatform;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public final class TestPlatform implements ViaPlatform {
|
||||
|
||||
private static final Logger log = Logger.getGlobal();
|
||||
private final TestConfig testConfig = new TestConfig(null);
|
||||
|
||||
@Override
|
||||
public Logger getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformName() {
|
||||
return "Test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPlatformVersion() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPluginVersion() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTask runAsync(Runnable runnable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTask runSync(Runnable runnable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTask runSync(Runnable runnable, long ticks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformTask runRepeatingSync(Runnable runnable, long ticks) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaCommandSender[] getOnlinePlayers() {
|
||||
return new ViaCommandSender[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(UUID uuid, String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean kickPlayer(UUID uuid, String message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPluginEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaAPI getApi() {
|
||||
return new ViaAPIBase() {
|
||||
@Override
|
||||
public int getPlayerVersion(Object player) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendRawPacket(Object player, ByteBuf packet) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViaVersionConfig getConf() {
|
||||
return testConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationProvider getConfigurationProvider() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDataFolder() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReload() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject getDump() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOldClientsAllowed() {
|
||||
return false;
|
||||
}
|
||||
}
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren