Compare commits

...
26 Commits
Author SHA1 Message Date
Nassim Jahnke adb10f5fe7 4.4.2 Release 2022-08-21 09:42:05 +02:00
Pablo Herrera a268d3d298 Fix userconnection availability on join event (PoC) (#2849) 2022-08-19 11:59:48 +02:00
creeper123123321 b60771e96e Fix block connection on non-full chunks (#3068) 2022-08-15 11:58:57 +02:00
Nassim Jahnke 435a986068 Properly enclose limiting of nbt reading
Fixes #3093
2022-08-14 22:24:31 +02:00
Nassim Jahnke 6c58f0980c Add utility method for initializing entity types 2022-08-13 15:36:57 +02:00
Nassim Jahnke e3dde71821 And the default return value 2022-08-11 19:09:33 +02:00
Nassim Jahnke 47b15e5fef Forgot a lock 2022-08-11 19:08:33 +02:00
Nassim Jahnke ed196bdf99 Clean up proxy protocol detector service 2022-08-11 19:04:22 +02:00
Nassim Jahnke 9247e87bbf Update InformativeException message 2022-08-08 21:01:27 +02:00
Nassim Jahnke 08727f959f Update 1.19.1 protocol version string 2022-08-05 16:05:31 +02:00
Nassim Jahnke 78342da180 And make the 1.19 chat registry nbt public 2022-08-04 16:29:35 +02:00
Nassim Jahnke 2b735172bd Make 1.19 chat decoration method public 2022-08-04 16:23:21 +02:00
_tomcraft b032a868bb add flamecord warking message (#3079) 2022-08-04 16:19:08 +02:00
Nassim Jahnke c4fa3b0ff8 Fix worldDataTrackerHandlerByKey world index 2022-08-04 15:51:51 +02:00
Nassim Jahnke 0c3a1803e1 Send block entity data after block break cancellation
Fixes #3077
2022-08-03 19:45:00 +02:00
Gero 04f9971dee Alter velocity modern forwarding version (#3078) 2022-08-03 16:17:52 +02:00
Elioby 9c3395b76b Use default painting mapping of 0 instead of -1 (#3073) 2022-08-01 21:07:40 +02:00
Nassim Jahnke f9bbb5b1a3 Fixup config comment saving
Fixes #3052, #2536
2022-08-01 12:28:44 +02:00
Nassim Jahnke 087bbdce1f Fix 1.19+ sign opening on ~1.12 servers
Fixes #3027
2022-07-30 18:10:45 +02:00
Nassim Jahnke 494bf62535 Bump version to 4.4.2-SNAPSHOT 2022-07-30 18:05:04 +02:00
Nassim Jahnke 4f2a5bb7e2 4.4.1 Release 2022-07-28 18:49:40 +02:00
Nassim Jahnke 1c511a82b2 Simplify component building 2022-07-28 14:56:35 +02:00
Nassim Jahnke 0c39723d1a Use translation key as text if not found 2022-07-28 13:43:08 +02:00
Nassim Jahnke 322af00e80 Handle 1.19 chat types 2022-07-28 13:14:28 +02:00
Nassim Jahnke a88747d904 Check for dimension equality after adding/replacing new fields 2022-07-28 11:47:12 +02:00
Nassim Jahnke e194d27bea Allow for more control over max path distance delta increase 2022-07-28 11:29:02 +02:00
52 changed files with 1468 additions and 651 deletions

No files matched your search

@@ -25,6 +25,7 @@ package com.viaversion.viaversion.api;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.configuration.ViaVersionConfig;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.ViaServerProxyPlatform;
public final class Via {
private static ViaManager manager;
@@ -63,6 +64,11 @@ public final class Via {
return manager().getPlatform();
}
public static ViaServerProxyPlatform<?> proxyPlatform() {
Preconditions.checkArgument(manager().getPlatform() instanceof ViaServerProxyPlatform, "Platform is not proxying Minecraft servers!");
return (ViaServerProxyPlatform<?>) manager().getPlatform();
}
/**
* Register the ViaManager associated with the platform.
*
@@ -64,7 +64,7 @@ public interface ViaAPI<T> {
* @return API version incremented with meaningful API changes
*/
default int apiVersion() {
return 13;
return 15;
}
/**
@@ -28,6 +28,7 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.WorldIdentifiers;
import com.viaversion.viaversion.api.protocol.version.BlockedProtocolVersions;
import it.unimi.dsi.fastutil.ints.IntSet;
import org.checkerframework.checker.nullness.qual.Nullable;
public interface ViaVersionConfig {
@@ -455,4 +456,6 @@ public interface ViaVersionConfig {
WorldIdentifiers get1_16WorldNamesMap();
boolean cache1_17Light();
@Nullable String chatTypeFormat(String translationKey);
}
@@ -269,15 +269,6 @@ public enum Entity1_19Types implements EntityType {
}
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
for (final Entity1_19Types type : values()) {
if (type.isAbstractType()) {
continue;
}
final int id = protocol.getMappingData().getEntityMappings().mappedId(type.identifier());
Preconditions.checkArgument(id != -1, "Entity type %s has no id", type.identifier());
type.id = id;
}
EntityTypeUtil.fill(values(), TYPES);
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
}
}
@@ -0,0 +1,65 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.platform;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
public interface ProtocolDetectorService {
/**
* Returns the protocol version of the proxied server, or -1 if unknown.
*
* @param serverName name of the proxied server
* @return protocol version of the proxied server, or -1 if unknown
*/
int serverProtocolVersion(String serverName);
/**
* Probes all registered proxied servers for their protocol version.
* This is executed automatically in the interval set in the ViaVersion config.
*/
void probeAllServers();
/**
* Sets the stored protocol version of a proxied server.
*
* @param serverName name of the proxied server
* @param protocolVersion protocol version of the server
*/
void setProtocolVersion(String serverName, int protocolVersion);
/**
* Uncaches and returns the previously stored protocol version of the proxied server. Returns -1 if none was stored.
*
* @param serverName name of the proxied server
* @return previously stored protocol version of the proxied server, or -1 if none was present
*/
int uncacheProtocolVersion(String serverName);
/**
* Returns an unmodifiable map of detected protocol versions.
*
* @return unmodifiable map of detected protocol versions
*/
Object2IntMap<String> detectedProtocolVersions();
}
@@ -0,0 +1,42 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 ViaVersion and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.viaversion.viaversion.api.platform;
import com.google.common.annotations.Beta;
/**
* Represents a platform that proxies a list of Minecraft servers (Velocity, Bungee).
* A platform might not be a proxy platform, even if {@link ViaPlatform#isProxy()} is true.
*
* @param <T> platform player type
*/
@Beta
public interface ViaServerProxyPlatform<T> extends ViaPlatform<T> {
/**
* Returns a service to get protocol versions for proxied servers.
*
* @return protocol detector service
*/
ProtocolDetectorService protocolDetectorService();
}
@@ -300,9 +300,9 @@ public interface Protocol<C1 extends ClientboundPacketType, C2 extends Clientbou
* <p>
* To be overridden if needed.
*
* @param userConnection The user to initialise
* @param connection user to initialise
*/
default void init(UserConnection userConnection) {
default void init(UserConnection connection) {
}
/**
@@ -177,35 +177,49 @@ public interface ProtocolManager {
@Nullable Class<S> serverboundPacketsClass);
/**
* Returns whether protocol path calculation expects the path to come closer to the expected version with each entry, true by default.
* Sets the max delta the path calculation allows the distance to the target protocol version to increase.
* <p>
* In practice, this means a path will never go to a protocol version that puts it farther from the desired
* If set to 0, protocol paths will have to come closer to the target protocol version with every entry,
* never going farther away from it (1→5→10 and 1→11→10 are ok, 1→20→10 is not ok).
* If set to -1, no distance checks will be applied (1→20→10 is ok).
*
* @param maxPathDeltaIncrease the max delta the path calculation allows the distance to the target protocol version to increase
* @see #onlyCheckLoweringPathEntries()
*/
void setMaxPathDeltaIncrease(int maxPathDeltaIncrease);
/**
* Returns the max delta the path calculation allows the distance to the target protocol version to increase. 0 by default.
* <p>
* In practice, a value of 0 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.
* If this is set to -1, *all* possible paths will be checked until a fitting one is found.
* <p>
* Negative examples if this returns true:
* Negative examples if this returns 0:
* <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:
* Negative examples if this returns -1:
* <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
* @return max delta the path calculation allows the distance to the target protocol version to increase
*/
boolean onlyCheckLoweringPathEntries();
int getMaxPathDeltaIncrease();
/**
* 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);
@Deprecated/*(forRemoval = true)*/
default void setOnlyCheckLoweringPathEntries(final boolean onlyCheckLoweringPathEntries) {
setMaxPathDeltaIncrease(onlyCheckLoweringPathEntries ? 0 : -1);
}
@Deprecated/*(forRemoval = true)*/
default boolean onlyCheckLoweringPathEntries() {
return getMaxPathDeltaIncrease() != -1;
}
/**
* Returns the maximum protocol path size applied to {@link #getProtocolPath(int, int)}.
@@ -81,7 +81,7 @@ public class ProtocolVersion {
public static final ProtocolVersion v1_18 = register(757, "1.18/1.18.1", new VersionRange("1.18", 0, 1));
public static final ProtocolVersion v1_18_2 = register(758, "1.18.2");
public static final ProtocolVersion v1_19 = register(759, "1.19");
public static final ProtocolVersion v1_19_1 = register(760, "1.19.1");
public static final ProtocolVersion v1_19_1 = register(760, "1.19.1/2", new VersionRange("1.19", 1, 2));
public static final ProtocolVersion unknown = register(-1, "UNKNOWN");
public static ProtocolVersion register(int version, String name) {
@@ -24,7 +24,7 @@ package com.viaversion.viaversion.api.type.types.minecraft;
import com.github.steveice10.opennbt.NBTIO;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.common.base.Preconditions;
import com.github.steveice10.opennbt.tag.limiter.TagLimiter;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
@@ -35,21 +35,22 @@ import java.io.DataOutput;
public class NBTType extends Type<CompoundTag> {
private static final int MAX_NBT_BYTES = 2097152; // 2mb
private static final int MAX_NESTING_LEVEL = 512;
public NBTType() {
super(CompoundTag.class);
}
@Override
public CompoundTag read(ByteBuf buffer) throws Exception {
Preconditions.checkArgument(buffer.readableBytes() <= 2097152, "Cannot read NBT (got %s bytes)", buffer.readableBytes());
int readerIndex = buffer.readerIndex();
byte b = buffer.readByte();
if (b == 0) {
return null;
} else {
buffer.readerIndex(readerIndex);
return NBTIO.readTag((DataInput) new ByteBufInputStream(buffer));
return NBTIO.readTag((DataInput) new ByteBufInputStream(buffer), TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL));
}
}
@@ -48,7 +48,7 @@ public class InformativeException extends Exception {
@Override
public String getMessage() {
StringBuilder builder = new StringBuilder("Please post this error to https://github.com/ViaVersion/ViaVersion/issues and follow the issue template\n{");
StringBuilder builder = new StringBuilder("Please report this on the Via support Discord or open an issue on the relevant GitHub repository\n");
boolean first = true;
for (Map.Entry<String, Object> entry : info.entrySet()) {
if (!first) {
@@ -57,11 +57,11 @@ public class InformativeException extends Exception {
builder.append(entry.getKey()).append(": ").append(entry.getValue());
first = false;
}
return builder.append("}\nActual Error: ").toString();
return builder.toString();
}
@Override
public synchronized Throwable fillInStackTrace() {
public Throwable fillInStackTrace() {
// Don't record this stack
return this;
}
@@ -22,14 +22,18 @@
*/
package com.viaversion.viaversion.util;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.protocol.Protocol;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class EntityTypeUtil {
public final class EntityTypeUtil {
private static final EntityType[] EMPTY_ARRAY = new EntityType[0];
/**
* Returns an ordered array with each index representing the actual entity id.
@@ -46,7 +50,29 @@ public class EntityTypeUtil {
}
types.sort(Comparator.comparingInt(EntityType::getId));
return types.toArray(new EntityType[0]);
return types.toArray(EMPTY_ARRAY);
}
/**
* Sets entity type ids based on the protocol's mapping data and fills the given typesToFill array with the index corresponding to the id.
*
* @param values full enum values
* @param typesToFill yet unfilled array to be filled with types ordered by id
* @param protocol protocol to get entity types from
* @param idSetter function to set the internal entity id
* @param <T> entity type
*/
public static <T extends EntityType> void initialize(final T[] values, final EntityType[] typesToFill, final Protocol<?, ?, ?, ?> protocol, final EntityIdSetter<T> idSetter) {
for (final T type : values) {
if (type.isAbstractType()) {
continue;
}
final int id = protocol.getMappingData().getEntityMappings().mappedId(type.identifier());
Preconditions.checkArgument(id != -1, "Entity type %s has no id", type.identifier());
idSetter.setId(type, id);
typesToFill[id] = type;
}
}
public static EntityType[] createSizedArray(final EntityType[] values) {
@@ -56,15 +82,7 @@ public class EntityTypeUtil {
count++;
}
}
return new EntityType[(int) count];
}
public static void fill(final EntityType[] values, final EntityType[] toFill) {
for (final EntityType type : values) {
if (!type.isAbstractType()) {
toFill[type.getId()] = type;
}
}
return new EntityType[count];
}
/**
@@ -83,4 +101,10 @@ public class EntityTypeUtil {
}
return type;
}
@FunctionalInterface
public interface EntityIdSetter<T extends EntityType> {
void setId(T entityType, int id);
}
}
@@ -286,7 +286,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
@Override
public final Collection<UnsupportedSoftware> getUnsupportedSoftwareClasses() {
List<UnsupportedSoftware> list = new ArrayList<>(ViaPlatform.super.getUnsupportedSoftwareClasses());
final List<UnsupportedSoftware> list = new ArrayList<>(ViaPlatform.super.getUnsupportedSoftwareClasses());
list.add(new UnsupportedServerSoftware.Builder().name("Yatopia").reason(UnsupportedServerSoftware.Reason.DANGEROUS_SERVER_SOFTWARE)
.addClassName("org.yatopiamc.yatopia.server.YatopiaConfig")
.addClassName("net.yatopia.api.event.PlayerAttackEntityEvent")
@@ -100,4 +100,8 @@ public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaCode
cause.printStackTrace(); // Print if CB doesn't already do it
}
}
public UserConnection getInfo() {
return info;
}
}
@@ -0,0 +1,116 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.bukkit.listeners;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import io.netty.channel.Channel;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.logging.Level;
public class JoinListener implements Listener {
private static final Method GET_HANDLE;
private static final Field CONNECTION;
private static final Field NETWORK_MANAGER;
private static final Field CHANNEL;
static {
Method gh = null;
Field conn = null, nm = null, ch = null;
try {
gh = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
conn = findField(gh.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
nm = findField(conn.getType(), "NetworkManager", "Connection");
ch = findField(nm.getType(), "Channel");
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
Via.getPlatform().getLogger().log(
Level.WARNING,
"Couldn't find reflection methods/fields to access Channel from player.\n" +
"Login race condition fixer will be disabled.\n" +
" Some plugins that use ViaAPI on join event may work incorrectly.", e);
}
GET_HANDLE = gh;
CONNECTION = conn;
NETWORK_MANAGER = nm;
CHANNEL = ch;
}
// Loosely search a field with any name, as long as it matches a type name.
private static Field findField(Class<?> cl, String... types) throws NoSuchFieldException {
for (Field field : cl.getDeclaredFields()) {
for (String type : types)
if (field.getType().getSimpleName().equals(type))
return field;
}
throw new NoSuchFieldException(types[0]);
}
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e) {
if (CHANNEL == null) return;
Player player = e.getPlayer();
Channel channel;
try {
channel = getChannel(player);
} catch (Exception ex) {
Via.getPlatform().getLogger().log(Level.WARNING, ex,
() -> "Could not find Channel for logging-in player " + player.getUniqueId());
return;
}
// The connection has already closed, that was a quick leave
if (!channel.isOpen()) return;
UserConnection user = getUserConnection(channel);
if (user == null) {
Via.getPlatform().getLogger().log(Level.WARNING,
"Could not find UserConnection for logging-in player {0}",
player.getUniqueId());
return;
}
ProtocolInfo info = user.getProtocolInfo();
info.setUuid(player.getUniqueId());
info.setUsername(player.getName());
Via.getManager().getConnectionManager().onLoginSuccess(user);
}
private @Nullable UserConnection getUserConnection(Channel channel) {
BukkitEncodeHandler encoder = channel.pipeline().get(BukkitEncodeHandler.class);
return encoder != null ? encoder.getInfo() : null;
}
private Channel getChannel(Player player) throws Exception {
Object entityPlayer = GET_HANDLE.invoke(player);
Object pc = CONNECTION.get(entityPlayer);
Object nm = NETWORK_MANAGER.get(pc);
return (Channel) CHANNEL.get(nm);
}
}
@@ -0,0 +1,70 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.bukkit.listeners.protocol1_19to1_18_2;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.listeners.ViaBukkitListener;
import com.viaversion.viaversion.bukkit.util.NMSUtil;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.block.BlockBreakEvent;
public final class BlockBreakListener extends ViaBukkitListener {
private static final Class<?> CRAFT_BLOCK_STATE_CLASS;
static {
try {
CRAFT_BLOCK_STATE_CLASS = NMSUtil.obc("block.CraftBlockState");
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public BlockBreakListener(ViaVersionPlugin plugin) {
super(plugin, Protocol1_19To1_18_2.class);
}
@EventHandler(priority = EventPriority.MONITOR)
public void blockBreak(BlockBreakEvent event) {
final Block block = event.getBlock();
if (!event.isCancelled() || !isBlockEntity(block.getState())) {
return;
}
// We need to resend the block entity data after an ack has been sent out
final int serverProtocolVersion = Via.getAPI().getServerVersion().highestSupportedVersion();
final long delay = serverProtocolVersion > ProtocolVersion.v1_8.getVersion() && serverProtocolVersion < ProtocolVersion.v1_14.getVersion() ? 2 : 1;
getPlugin().getServer().getScheduler().runTaskLater(getPlugin(), () -> {
final BlockState state = block.getState();
if (isBlockEntity(state)) {
state.update(true, false);
}
}, delay);
}
private boolean isBlockEntity(final BlockState state) {
// We love legacy versions
return state.getClass() != CRAFT_BLOCK_STATE_CLASS;
}
}
@@ -24,9 +24,11 @@ import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.platform.ViaPlatformLoader;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
import com.viaversion.viaversion.bukkit.listeners.JoinListener;
import com.viaversion.viaversion.bukkit.listeners.UpdateListener;
import com.viaversion.viaversion.bukkit.listeners.multiversion.PlayerSneakListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_15to1_14_4.EntityToggleGlideListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_19to1_18_2.BlockBreakListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.ArmorListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.BlockListener;
import com.viaversion.viaversion.bukkit.listeners.protocol1_9to1_8.DeathListener;
@@ -79,6 +81,9 @@ public class BukkitViaLoader implements ViaPlatformLoader {
// Update Listener
registerListener(new UpdateListener());
// Login listener
registerListener(new JoinListener());
/* Base Protocol */
final ViaVersionPlugin plugin = (ViaVersionPlugin) Bukkit.getPluginManager().getPlugin("ViaVersion");
@@ -177,6 +182,7 @@ public class BukkitViaLoader implements ViaPlatformLoader {
}
if (serverProtocolVersion < ProtocolVersion.v1_19.getVersion()) {
Via.getManager().getProviders().use(AckSequenceProvider.class, new BukkitAckSequenceProvider(plugin));
storeListener(new BlockBreakListener(plugin)).register();
}
}
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.bukkit.providers;
import com.viaversion.viaversion.ViaVersionPlugin;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.bukkit.tasks.protocol1_19to1_18_2.AckSequenceTask;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.provider.AckSequenceProvider;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.storage.SequenceStorage;
@@ -36,7 +37,9 @@ public final class BukkitAckSequenceProvider extends AckSequenceProvider {
final SequenceStorage sequenceStorage = connection.get(SequenceStorage.class);
final int previousSequence = sequenceStorage.setSequenceId(sequence);
if (previousSequence == -1) {
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new AckSequenceTask(connection, sequenceStorage), 1);
final int serverProtocolVersion = connection.getProtocolInfo().getServerProtocolVersion();
final long delay = serverProtocolVersion > ProtocolVersion.v1_8.getVersion() && serverProtocolVersion < ProtocolVersion.v1_14.getVersion() ? 2 : 1;
plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, new AckSequenceTask(connection, sequenceStorage), delay);
}
}
}
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.ViaAPI;
@@ -24,17 +25,19 @@ import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.platform.PlatformTask;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.UnsupportedSoftware;
import com.viaversion.viaversion.api.platform.ViaServerProxyPlatform;
import com.viaversion.viaversion.bungee.commands.BungeeCommand;
import com.viaversion.viaversion.bungee.commands.BungeeCommandHandler;
import com.viaversion.viaversion.bungee.commands.BungeeCommandSender;
import com.viaversion.viaversion.bungee.platform.BungeeViaTask;
import com.viaversion.viaversion.bungee.platform.BungeeViaAPI;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.platform.BungeeViaInjector;
import com.viaversion.viaversion.bungee.platform.BungeeViaLoader;
import com.viaversion.viaversion.bungee.platform.BungeeViaTask;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.unsupported.UnsupportedServerSoftware;
import com.viaversion.viaversion.util.GsonUtil;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
@@ -49,14 +52,15 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>, Listener {
public class BungeePlugin extends Plugin implements ViaServerProxyPlatform<ProxiedPlayer>, Listener {
private final ProtocolDetectorService protocolDetectorService = new ProtocolDetectorService();
private BungeeViaAPI api;
private BungeeViaConfig config;
@Override
public void onLoad() {
try {
ProtocolConstants.class.getField("MINECRAFT_1_19");
ProtocolConstants.class.getField("MINECRAFT_1_19_1");
} catch (NoSuchFieldException e) {
getLogger().warning(" / \\");
getLogger().warning(" / \\");
@@ -197,7 +201,7 @@ public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>,
));
platformSpecific.add("plugins", GsonUtil.getGson().toJsonTree(plugins));
platformSpecific.add("servers", GsonUtil.getGson().toJsonTree(ProtocolDetectorService.getDetectedIds()));
platformSpecific.add("servers", GsonUtil.getGson().toJsonTree(protocolDetectorService.detectedProtocolVersions()));
return platformSpecific;
}
@@ -206,8 +210,24 @@ public class BungeePlugin extends Plugin implements ViaPlatform<ProxiedPlayer>,
return true;
}
@Override
public Collection<UnsupportedSoftware> getUnsupportedSoftwareClasses() {
final Collection<UnsupportedSoftware> list = new ArrayList<>(ViaServerProxyPlatform.super.getUnsupportedSoftwareClasses());
list.add(new UnsupportedServerSoftware.Builder()
.name("FlameCord")
.addClassName("dev._2lstudios.flamecord.FlameCord")
.reason(UnsupportedServerSoftware.Reason.BREAKING_PROXY_SOFTWARE)
.build());
return ImmutableList.copyOf(list);
}
@Override
public boolean hasPlugin(final String name) {
return getProxy().getPluginManager().getPlugin(name) != null;
}
@Override
public ProtocolDetectorService protocolDetectorService() {
return protocolDetectorService;
}
}
@@ -21,7 +21,6 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
public class ProbeSubCmd extends ViaSubCommand {
@Override
@@ -38,7 +37,7 @@ public class ProbeSubCmd extends ViaSubCommand {
@Override
public boolean execute(ViaCommandSender sender, String[] args) {
ProtocolDetectorService.getInstance().run();
Via.proxyPlatform().protocolDetectorService().probeAllServers();
sendMessage(sender, "&6Started searching for protocol versions");
return true;
}
@@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.bungee.storage.BungeeStorage;
import com.viaversion.viaversion.protocols.protocol1_13to1_12_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.ClientboundPackets1_9;
@@ -96,7 +95,7 @@ public class BungeeServerHandler implements Listener {
user.put(new BungeeStorage(e.getPlayer()));
}
int protocolId = ProtocolDetectorService.getProtocolId(e.getTarget().getName());
int protocolId = Via.proxyPlatform().protocolDetectorService().serverProtocolVersion(e.getTarget().getName());
List<ProtocolPathEntry> protocols = Via.getManager().getProtocolManager().getProtocolPath(user.getProtocolInfo().getProtocolVersion(), protocolId);
// Check if ViaVersion can support that version
@@ -164,7 +163,7 @@ public class BungeeServerHandler implements Listener {
storage.setCurrentServer(serverName);
int protocolId = ProtocolDetectorService.getProtocolId(serverName);
int protocolId = Via.proxyPlatform().protocolDetectorService().serverProtocolVersion(serverName);
if (protocolId <= ProtocolVersion.v1_8.getVersion()) { // 1.8 doesn't have BossBar packet
if (storage.getBossbar() != null) {
@@ -18,6 +18,7 @@
package com.viaversion.viaversion.bungee.platform;
import com.viaversion.viaversion.ViaAPIBase;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import io.netty.buffer.ByteBuf;
import net.md_5.bungee.api.config.ServerInfo;
@@ -41,6 +42,6 @@ public class BungeeViaAPI extends ViaAPIBase<ProxiedPlayer> {
* @param serverInfo The serverinfo to probe
*/
public void probeServer(ServerInfo serverInfo) {
ProtocolDetectorService.probeServer(serverInfo);
((ProtocolDetectorService) Via.proxyPlatform().protocolDetectorService()).probeServer(serverInfo);
}
}
@@ -30,7 +30,6 @@ import com.viaversion.viaversion.bungee.providers.BungeeEntityIdProvider;
import com.viaversion.viaversion.bungee.providers.BungeeMainHandProvider;
import com.viaversion.viaversion.bungee.providers.BungeeMovementTransmitter;
import com.viaversion.viaversion.bungee.providers.BungeeVersionProvider;
import com.viaversion.viaversion.bungee.service.ProtocolDetectorService;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.BossBarProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.EntityIdProvider;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.providers.MainHandProvider;
@@ -44,10 +43,9 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
public class BungeeViaLoader implements ViaPlatformLoader {
private final BungeePlugin plugin;
private final Set<Listener> listeners = new HashSet<>();
private final Set<ScheduledTask> tasks = new HashSet<>();
private final BungeePlugin plugin;
public BungeeViaLoader(BungeePlugin plugin) {
this.plugin = plugin;
@@ -82,7 +80,7 @@ public class BungeeViaLoader implements ViaPlatformLoader {
if (plugin.getConf().getBungeePingInterval() > 0) {
tasks.add(plugin.getProxy().getScheduler().schedule(
plugin,
new ProtocolDetectorService(plugin),
() -> Via.proxyPlatform().protocolDetectorService().probeAllServers(),
0, plugin.getConf().getBungeePingInterval(),
TimeUnit.SECONDS
));
@@ -17,93 +17,77 @@
*/
package com.viaversion.viaversion.bungee.service;
import com.viaversion.viaversion.BungeePlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.bungee.platform.BungeeViaConfig;
import com.viaversion.viaversion.bungee.providers.BungeeVersionProvider;
import net.md_5.bungee.api.Callback;
import net.md_5.bungee.api.ServerPing;
import com.viaversion.viaversion.platform.AbstractProtocolDetectorService;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.config.ServerInfo;
import java.util.HashMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
public class ProtocolDetectorService implements Runnable {
private static final Map<String, Integer> detectedProtocolIds = new ConcurrentHashMap<>();
private static ProtocolDetectorService instance;
private final BungeePlugin plugin;
public final class ProtocolDetectorService extends AbstractProtocolDetectorService {
public ProtocolDetectorService(BungeePlugin plugin) {
this.plugin = plugin;
instance = this;
}
public void probeServer(final ServerInfo serverInfo) {
final String serverName = serverInfo.getName();
serverInfo.ping((serverPing, throwable) -> {
// Ensure protocol is positive, some services will return -1
if (throwable != null || serverPing == null || serverPing.getVersion() == null || serverPing.getVersion().getProtocol() <= 0) {
return;
}
public static Integer getProtocolId(String serverName) {
// Step 1. Check Config
Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
Integer protocol = servers.get(serverName);
if (protocol != null) {
return protocol;
}
// Step 2. Check Detected
Integer detectedProtocol = detectedProtocolIds.get(serverName);
if (detectedProtocol != null) {
return detectedProtocol;
}
// Step 3. Use Default
Integer defaultProtocol = servers.get("default");
if (defaultProtocol != null) {
return defaultProtocol;
}
// Step 4: Use bungee lowest supported... *cries*
return BungeeVersionProvider.getLowestSupportedVersion();
}
final int oldProtocolVersion = serverProtocolVersion(serverName);
if (oldProtocolVersion == serverPing.getVersion().getProtocol()) {
// Same value as previously
return;
}
@Override
public void run() {
for (final Map.Entry<String, ServerInfo> lists : plugin.getProxy().getServers().entrySet()) {
probeServer(lists.getValue());
}
}
setProtocolVersion(serverName, serverPing.getVersion().getProtocol());
public static void probeServer(final ServerInfo serverInfo) {
final String key = serverInfo.getName();
serverInfo.ping(new Callback<ServerPing>() {
@Override
public void done(ServerPing serverPing, Throwable throwable) {
if (throwable == null && serverPing != null && serverPing.getVersion() != null) {
// Ensure protocol is positive, some services will return -1
if (serverPing.getVersion().getProtocol() > 0) {
detectedProtocolIds.put(key, serverPing.getVersion().getProtocol());
if (((BungeeViaConfig) Via.getConfig()).isBungeePingSave()) {
Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
Integer protocol = servers.get(key);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(key, serverPing.getVersion().getProtocol());
}
// Save
Via.getPlatform().getConfigurationProvider().saveConfig();
}
}
if (((BungeeViaConfig) Via.getConfig()).isBungeePingSave()) {
final Map<String, Integer> servers = ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
final Integer protocol = servers.get(serverName);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(serverName, serverPing.getVersion().getProtocol());
}
Via.getPlatform().getConfigurationProvider().saveConfig();
}
});
}
public static Map<String, Integer> getDetectedIds() {
return new HashMap<>(detectedProtocolIds);
@Override
public void probeAllServers() {
final Collection<ServerInfo> servers = ProxyServer.getInstance().getServers().values();
final Set<String> serverNames = new HashSet<>(servers.size());
for (final ServerInfo serverInfo : servers) {
probeServer(serverInfo);
serverNames.add(serverInfo.getName());
}
// Remove servers that aren't registered anymore
lock.writeLock().lock();
try {
detectedProtocolIds.keySet().retainAll(serverNames);
} finally {
lock.writeLock().unlock();
}
}
public static ProtocolDetectorService getInstance() {
return instance;
@Override
protected Map<String, Integer> configuredServers() {
return ((BungeeViaConfig) Via.getConfig()).getBungeeServerProtocols();
}
public BungeePlugin getPlugin() {
return plugin;
@Override
protected int lowestSupportedProtocolVersion() {
return BungeeVersionProvider.getLowestSupportedVersion();
}
}
@@ -157,7 +157,7 @@ public class ViaManagerImpl implements ViaManager {
loader.load();
// Common tasks
mappingLoadingTask = Via.getPlatform().runRepeatingSync(() -> {
if (protocolManager.checkForMappingCompletion()) {
if (protocolManager.checkForMappingCompletion() && mappingLoadingTask != null) {
mappingLoadingTask.cancel();
mappingLoadingTask = null;
}
@@ -237,7 +237,7 @@ public class ViaManagerImpl implements ViaManager {
private void unsupportedSoftwareWarning() {
boolean found = false;
for (UnsupportedSoftware software : platform.getUnsupportedSoftwareClasses()) {
for (final UnsupportedSoftware software : platform.getUnsupportedSoftwareClasses()) {
if (!software.findMatch()) {
continue;
}
@@ -90,6 +90,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
private JsonElement resourcePack1_17PromptMessage;
private WorldIdentifiers map1_16WorldNames;
private boolean cache1_17Light;
private Map<String, String> chatTypeFormats;
protected AbstractViaConfig(File configFile) {
super(configFile);
@@ -158,6 +159,7 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
worlds.getOrDefault("nether", WorldIdentifiers.NETHER_DEFAULT),
worlds.getOrDefault("end", WorldIdentifiers.END_DEFAULT));
cache1_17Light = getBoolean("cache-1_17-light", true);
chatTypeFormats = get("chat-types-1_19", Map.class, new HashMap<String, String>());
}
private BlockedProtocolVersions loadBlockedProtocolVersions() {
@@ -526,4 +528,9 @@ public abstract class AbstractViaConfig extends Config implements ViaVersionConf
public boolean cache1_17Light() {
return cache1_17Light;
}
@Override
public @Nullable String chatTypeFormat(final String translationKey) {
return chatTypeFormats.get(translationKey);
}
}
@@ -20,6 +20,7 @@ package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ConnectionManager;
import com.viaversion.viaversion.api.connection.UserConnection;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -37,17 +38,29 @@ public class ConnectionManagerImpl implements ConnectionManager {
@Override
public void onLoginSuccess(UserConnection connection) {
Objects.requireNonNull(connection, "connection is null!");
connections.add(connection);
Channel channel = connection.getChannel();
// This user has already disconnected...
if (channel != null && !channel.isOpen()) return;
boolean newlyAdded = connections.add(connection);
if (isFrontEnd(connection)) {
UUID id = connection.getProtocolInfo().getUuid();
if (clients.put(id, connection) != null) {
UserConnection previous = clients.put(id, connection);
if (previous != null && previous != connection) {
Via.getPlatform().getLogger().warning("Duplicate UUID on frontend connection! (" + id + ")");
}
}
if (connection.getChannel() != null) {
connection.getChannel().closeFuture().addListener((ChannelFutureListener) future -> onDisconnect(connection));
if (channel != null) {
// We managed to add a user that had already disconnected!
// Let's clean up the mess here and now
if (!channel.isOpen()) {
onDisconnect(connection);
} else if (newlyAdded) { // Setup to clean-up on disconnect
channel.closeFuture().addListener((ChannelFutureListener) future -> onDisconnect(connection));
}
}
}
@@ -0,0 +1,100 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.platform;
import com.viaversion.viaversion.api.platform.ProtocolDetectorService;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class AbstractProtocolDetectorService implements ProtocolDetectorService {
protected final Object2IntMap<String> detectedProtocolIds = new Object2IntOpenHashMap<>();
protected final ReadWriteLock lock = new ReentrantReadWriteLock();
protected AbstractProtocolDetectorService() {
detectedProtocolIds.defaultReturnValue(-1);
}
@Override
public int serverProtocolVersion(final String serverName) {
// Step 1. Check detected
lock.readLock().lock();
final int detectedProtocol;
try {
detectedProtocol = detectedProtocolIds.getInt(serverName);
} finally {
lock.readLock().unlock();
}
if (detectedProtocol != -1) {
return detectedProtocol;
}
// Step 2. Check config (CME moment?)
final Map<String, Integer> servers = configuredServers();
final Integer protocol = servers.get(serverName);
if (protocol != null) {
return protocol;
}
// Step 3. Use Default (CME moment intensifies?)
final Integer defaultProtocol = servers.get("default");
if (defaultProtocol != null) {
return defaultProtocol;
}
// Step 4: Use the proxy's lowest supported... *cries*
return lowestSupportedProtocolVersion();
}
@Override
public void setProtocolVersion(final String serverName, final int protocolVersion) {
lock.writeLock().lock();
try {
detectedProtocolIds.put(serverName, protocolVersion);
} finally {
lock.writeLock().unlock();
}
}
@Override
public int uncacheProtocolVersion(final String serverName) {
lock.writeLock().lock();
try {
return detectedProtocolIds.removeInt(serverName);
} finally {
lock.writeLock().unlock();
}
}
@Override
public Object2IntMap<String> detectedProtocolVersions() {
lock.readLock().lock();
try {
return new Object2IntOpenHashMap<>(detectedProtocolIds);
} finally {
lock.readLock().unlock();
}
}
protected abstract Map<String, Integer> configuredServers();
protected abstract int lowestSupportedProtocolVersion();
}
@@ -119,7 +119,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
private boolean mappingsLoaded;
private ServerProtocolVersion serverProtocolVersion = new ServerProtocolVersionSingleton(-1);
private boolean onlyCheckLoweringPathEntries = true;
private int maxPathDeltaIncrease; // Only allow lowering path entries by default
private int maxProtocolPathSize = 50;
public ProtocolManagerImpl() {
@@ -317,7 +317,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
if (current.containsKey(translatedToVersion)) continue;
// Check if the new version is farther away than the current client version
if (onlyCheckLoweringPathEntries && Math.abs(serverVersion - translatedToVersion) > Math.abs(serverVersion - clientVersion)) {
if (maxPathDeltaIncrease != -1 && Math.abs(serverVersion - translatedToVersion) - Math.abs(serverVersion - clientVersion) > maxPathDeltaIncrease) {
continue;
}
@@ -385,13 +385,13 @@ public class ProtocolManagerImpl implements ProtocolManager {
}
@Override
public void setOnlyCheckLoweringPathEntries(boolean onlyCheckLoweringPathEntries) {
this.onlyCheckLoweringPathEntries = onlyCheckLoweringPathEntries;
public void setMaxPathDeltaIncrease(final int maxPathDeltaIncrease) {
this.maxPathDeltaIncrease = Math.max(-1, maxPathDeltaIncrease);
}
@Override
public boolean onlyCheckLoweringPathEntries() {
return onlyCheckLoweringPathEntries;
public int getMaxPathDeltaIncrease() {
return maxPathDeltaIncrease;
}
@Override
@@ -48,6 +48,10 @@ public class BlockConnectionProvider implements Provider {
}
public void unloadChunkSection(UserConnection connection, int chunkX, int chunkY, int chunkZ) {
}
public boolean storesBlocks() {
return false;
}
@@ -47,6 +47,11 @@ public class PacketBlockConnectionProvider extends BlockConnectionProvider {
connection.get(BlockConnectionStorage.class).unloadChunk(x, z);
}
@Override
public void unloadChunkSection(UserConnection connection, int chunkX, int chunkY, int chunkZ) {
connection.get(BlockConnectionStorage.class).unloadSection(chunkX, chunkY, chunkZ);
}
@Override
public boolean storesBlocks() {
return true;
@@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.Particle;
@@ -83,19 +82,16 @@ public class WorldPackets {
map(Type.VAR_INT); // 0 - Entity ID
map(Type.UUID); // 1 - Entity UUID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
PaintingProvider provider = Via.getManager().getProviders().get(PaintingProvider.class);
String motive = wrapper.read(Type.STRING);
handler(wrapper -> {
PaintingProvider provider = Via.getManager().getProviders().get(PaintingProvider.class);
String motive = wrapper.read(Type.STRING);
Optional<Integer> id = provider.getIntByIdentifier(motive);
Optional<Integer> id = provider.getIntByIdentifier(motive);
if (!id.isPresent() && (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug())) {
Via.getPlatform().getLogger().warning("Could not find painting motive: " + motive + " falling back to default (0)");
}
wrapper.write(Type.VAR_INT, id.orElse(0));
if (!id.isPresent() && (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug())) {
Via.getPlatform().getLogger().warning("Could not find painting motive: " + motive + " falling back to default (0)");
}
wrapper.write(Type.VAR_INT, id.orElse(0));
});
}
});
@@ -107,27 +103,24 @@ public class WorldPackets {
map(Type.UNSIGNED_BYTE); // 1 - Action
map(Type.NBT); // 2 - NBT data
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position position = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
handler(wrapper -> {
Position position = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
CompoundTag tag = wrapper.get(Type.NBT, 0);
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
int newId = provider.transform(wrapper.user(), position, tag, true);
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
int newId = provider.transform(wrapper.user(), position, tag, true);
if (newId != -1) {
BlockStorage storage = wrapper.user().get(BlockStorage.class);
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
if (newId != -1) {
BlockStorage storage = wrapper.user().get(BlockStorage.class);
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
}
if (action == 5) { // Set type of flower in flower pot
wrapper.cancel(); // Removed
}
if (action == 5) { // Set type of flower in flower pot
wrapper.cancel(); // Removed
}
});
}
@@ -140,43 +133,40 @@ public class WorldPackets {
map(Type.UNSIGNED_BYTE); // Action Id
map(Type.UNSIGNED_BYTE); // Action param
map(Type.VAR_INT); // Block Id - /!\ NOT BLOCK STATE ID
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position pos = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
short param = wrapper.get(Type.UNSIGNED_BYTE, 1);
int blockId = wrapper.get(Type.VAR_INT, 0);
handler(wrapper -> {
Position pos = wrapper.get(Type.POSITION, 0);
short action = wrapper.get(Type.UNSIGNED_BYTE, 0);
short param = wrapper.get(Type.UNSIGNED_BYTE, 1);
int blockId = wrapper.get(Type.VAR_INT, 0);
if (blockId == 25)
blockId = 73;
else if (blockId == 33)
blockId = 99;
else if (blockId == 29)
blockId = 92;
else if (blockId == 54)
blockId = 142;
else if (blockId == 146)
blockId = 305;
else if (blockId == 130)
blockId = 249;
else if (blockId == 138)
blockId = 257;
else if (blockId == 52)
blockId = 140;
else if (blockId == 209)
blockId = 472;
else if (blockId >= 219 && blockId <= 234)
blockId = blockId - 219 + 483;
if (blockId == 25)
blockId = 73;
else if (blockId == 33)
blockId = 99;
else if (blockId == 29)
blockId = 92;
else if (blockId == 54)
blockId = 142;
else if (blockId == 146)
blockId = 305;
else if (blockId == 130)
blockId = 249;
else if (blockId == 138)
blockId = 257;
else if (blockId == 52)
blockId = 140;
else if (blockId == 209)
blockId = 472;
else if (blockId >= 219 && blockId <= 234)
blockId = blockId - 219 + 483;
if (blockId == 73) { // Note block
PacketWrapper blockChange = wrapper.create(0x0B); // block change
blockChange.write(Type.POSITION, pos);
blockChange.write(Type.VAR_INT, 249 + (action * 24 * 2) + (param * 2));
blockChange.send(Protocol1_13To1_12_2.class);
}
wrapper.set(Type.VAR_INT, 0, blockId);
if (blockId == 73) { // Note block
PacketWrapper blockChange = wrapper.create(0x0B); // block change
blockChange.write(Type.POSITION, pos);
blockChange.write(Type.VAR_INT, 249 + (action * 24 * 2) + (param * 2));
blockChange.send(Protocol1_13To1_12_2.class);
}
wrapper.set(Type.VAR_INT, 0, blockId);
});
}
});
@@ -186,27 +176,24 @@ public class WorldPackets {
public void registerMap() {
map(Type.POSITION);
map(Type.VAR_INT);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
Position position = wrapper.get(Type.POSITION, 0);
int newId = toNewId(wrapper.get(Type.VAR_INT, 0));
handler(wrapper -> {
Position position = wrapper.get(Type.POSITION, 0);
int newId = toNewId(wrapper.get(Type.VAR_INT, 0));
UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
newId = ConnectionData.connect(userConnection, position, newId);
}
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
ConnectionData.update(userConnection, position);
}
UserConnection userConnection = wrapper.user();
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newId);
newId = ConnectionData.connect(userConnection, position, newId);
}
wrapper.set(Type.VAR_INT, 0, checkStorage(wrapper.user(), position, newId));
if (Via.getConfig().isServersideBlockConnections()) {
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
ConnectionData.update(userConnection, position);
}
});
}
});
@@ -217,56 +204,53 @@ public class WorldPackets {
map(Type.INT); // 0 - Chunk X
map(Type.INT); // 1 - Chunk Z
map(Type.BLOCK_CHANGE_RECORD_ARRAY); // 2 - Records
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int chunkX = wrapper.get(Type.INT, 0);
int chunkZ = wrapper.get(Type.INT, 1);
UserConnection userConnection = wrapper.user();
BlockChangeRecord[] records = wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0);
// Convert ids
handler(wrapper -> {
int chunkX = wrapper.get(Type.INT, 0);
int chunkZ = wrapper.get(Type.INT, 1);
UserConnection userConnection = wrapper.user();
BlockChangeRecord[] records = wrapper.get(Type.BLOCK_CHANGE_RECORD_ARRAY, 0);
// Convert ids
for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId());
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
if (Via.getConfig().isServersideBlockConnections()) {
for (BlockChangeRecord record : records) {
int newBlock = toNewId(record.getBlockId());
int blockState = record.getBlockId();
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), newBlock);
}
record.setBlockId(checkStorage(wrapper.user(), position, newBlock));
}
if (Via.getConfig().isServersideBlockConnections()) {
for (BlockChangeRecord record : records) {
int blockState = record.getBlockId();
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionHandler handler = ConnectionData.getConnectionHandler(blockState);
if (handler != null) {
blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState);
}
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (BlockChangeRecord record : records) {
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionData.update(userConnection, position);
ConnectionHandler handler = ConnectionData.getConnectionHandler(blockState);
if (handler != null) {
blockState = handler.connect(userConnection, position, blockState);
record.setBlockId(blockState);
}
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (BlockChangeRecord record : records) {
Position position = new Position(
record.getSectionX() + (chunkX * 16),
record.getY(),
record.getSectionZ() + (chunkZ * 16));
ConnectionData.update(userConnection, position);
}
}
});
}
});
@@ -283,34 +267,31 @@ public class WorldPackets {
map(Type.FLOAT); // Radius
map(Type.INT); // Record Count
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
UserConnection userConnection = wrapper.user();
int x = (int) Math.floor(wrapper.get(Type.FLOAT, 0));
int y = (int) Math.floor(wrapper.get(Type.FLOAT, 1));
int z = (int) Math.floor(wrapper.get(Type.FLOAT, 2));
int recordCount = wrapper.get(Type.INT, 0);
Position[] records = new Position[recordCount];
handler(wrapper -> {
UserConnection userConnection = wrapper.user();
int x = (int) Math.floor(wrapper.get(Type.FLOAT, 0));
int y = (int) Math.floor(wrapper.get(Type.FLOAT, 1));
int z = (int) Math.floor(wrapper.get(Type.FLOAT, 2));
int recordCount = wrapper.get(Type.INT, 0);
Position[] records = new Position[recordCount];
for (int i = 0; i < recordCount; i++) {
Position position = new Position(
x + wrapper.passthrough(Type.BYTE),
(short) (y + wrapper.passthrough(Type.BYTE)),
z + wrapper.passthrough(Type.BYTE));
records[i] = position;
for (int i = 0; i < recordCount; i++) {
Position position = new Position(
x + wrapper.passthrough(Type.BYTE),
(short) (y + wrapper.passthrough(Type.BYTE)),
z + wrapper.passthrough(Type.BYTE));
records[i] = position;
// Set to air
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), 0);
}
// Set to air
ConnectionData.updateBlockStorage(userConnection, position.x(), position.y(), position.z(), 0);
}
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < recordCount; i++) {
ConnectionData.update(userConnection, records[i]);
}
for (int i = 0; i < recordCount; i++) {
ConnectionData.update(userConnection, records[i]);
}
});
}
@@ -320,13 +301,10 @@ public class WorldPackets {
@Override
public void registerMap() {
if (Via.getConfig().isServersideBlockConnections()) {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int x = wrapper.passthrough(Type.INT);
int z = wrapper.passthrough(Type.INT);
ConnectionData.blockConnectionProvider.unloadChunk(wrapper.user(), x, z);
}
handler(wrapper -> {
int x = wrapper.passthrough(Type.INT);
int z = wrapper.passthrough(Type.INT);
ConnectionData.blockConnectionProvider.unloadChunk(wrapper.user(), x, z);
});
}
}
@@ -336,13 +314,10 @@ public class WorldPackets {
@Override
public void registerMap() {
map(Type.STRING);
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
String sound = wrapper.get(Type.STRING, 0).replace("minecraft:", "");
String newSoundId = NamedSoundRewriter.getNewId(sound);
wrapper.set(Type.STRING, 0, newSoundId);
}
handler(wrapper -> {
String sound = wrapper.get(Type.STRING, 0).replace("minecraft:", "");
String newSoundId = NamedSoundRewriter.getNewId(sound);
wrapper.set(Type.STRING, 0, newSoundId);
});
}
});
@@ -350,218 +325,225 @@ public class WorldPackets {
protocol.registerClientbound(ClientboundPackets1_12_1.CHUNK_DATA, new PacketRemapper() {
@Override
public void registerMap() {
handler(new PacketHandler() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
BlockStorage storage = wrapper.user().get(BlockStorage.class);
handler(wrapper -> {
ClientWorld clientWorld = wrapper.user().get(ClientWorld.class);
BlockStorage storage = wrapper.user().get(BlockStorage.class);
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk1_13Type type1_13 = new Chunk1_13Type(clientWorld);
Chunk chunk = wrapper.read(type);
wrapper.write(type1_13, chunk);
Chunk1_9_3_4Type type = new Chunk1_9_3_4Type(clientWorld);
Chunk1_13Type type1_13 = new Chunk1_13Type(clientWorld);
Chunk chunk = wrapper.read(type);
wrapper.write(type1_13, chunk);
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null)
continue;
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null)
continue;
for (int p = 0; p < section.getPaletteSize(); p++) {
int old = section.getPaletteEntry(p);
int newId = toNewId(old);
section.setPaletteEntry(p, newId);
}
for (int p = 0; p < section.getPaletteSize(); p++) {
int old = section.getPaletteEntry(p);
int newId = toNewId(old);
section.setPaletteEntry(p, newId);
}
boolean willSaveToStorage = false;
for (int p = 0; p < section.getPaletteSize(); p++) {
int newId = section.getPaletteEntry(p);
if (storage.isWelcome(newId)) {
willSaveToStorage = true;
break;
}
}
boolean willSaveConnection = false;
if (Via.getConfig().isServersideBlockConnections() && ConnectionData.needStoreBlocks()) {
for (int p = 0; p < section.getPaletteSize(); p++) {
int newId = section.getPaletteEntry(p);
if (ConnectionData.isWelcome(newId)) {
willSaveConnection = true;
storage:
{
if (chunk.isFullChunk()) {
boolean willSave = false;
for (int j = 0; j < section.getPaletteSize(); j++) {
if (storage.isWelcome(section.getPaletteEntry(j))) {
willSave = true;
break;
}
}
if (!willSave) break storage;
}
if (willSaveToStorage) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
if (storage.isWelcome(block)) {
storage.store(new Position(
(x + (chunk.getX() << 4)),
(short) (y + (i << 4)),
(z + (chunk.getZ() << 4))
), block);
}
}
}
}
}
if (willSaveConnection) {
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
if (ConnectionData.isWelcome(block)) {
ConnectionData.blockConnectionProvider.storeBlock(wrapper.user(), x + (chunk.getX() << 4),
y + (i << 4),
z + (chunk.getZ() << 4),
block);
}
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
Position pos = new Position(
(x + (chunk.getX() << 4)),
(y + (i << 4)),
(z + (chunk.getZ() << 4))
);
if (storage.isWelcome(block)) {
storage.store(pos, block);
} else if (!chunk.isFullChunk()) { // Update
storage.remove(pos);
}
}
}
}
}
// Rewrite biome id 255 to plains
if (chunk.isBiomeData()) {
int latestBiomeWarn = Integer.MIN_VALUE;
for (int i = 0; i < 256; i++) {
int biome = chunk.getBiomeData()[i];
if (!VALID_BIOMES.contains(biome)) {
if (biome != 255 // is it generated naturally? *shrug*
&& latestBiomeWarn != biome) {
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
Via.getPlatform().getLogger().warning("Received invalid biome id " + biome);
save_connections:
{
if (!Via.getConfig().isServersideBlockConnections()
|| !ConnectionData.needStoreBlocks()) break save_connections;
if (!chunk.isFullChunk()) { // Update
ConnectionData.blockConnectionProvider.unloadChunkSection(wrapper.user(), chunk.getX(), i, chunk.getZ());
}
boolean willSave = false;
for (int j = 0; j < section.getPaletteSize(); j++) {
if (ConnectionData.isWelcome(section.getPaletteEntry(j))) {
willSave = true;
break;
}
}
if (!willSave) break save_connections;
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int block = section.getFlatBlock(x, y, z);
if (ConnectionData.isWelcome(block)) {
int globalX = x + (chunk.getX() << 4);
int globalY = y + (i << 4);
int globalZ = z + (chunk.getZ() << 4);
ConnectionData.blockConnectionProvider.storeBlock(wrapper.user(), globalX,
globalY, globalZ, block);
}
latestBiomeWarn = biome;
}
chunk.getBiomeData()[i] = 1; // Plains
}
}
}
}
// Rewrite BlockEntities to normal blocks
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
final Iterator<CompoundTag> iterator = chunk.getBlockEntities().iterator();
while (iterator.hasNext()) {
CompoundTag tag = iterator.next();
int newId = provider.transform(wrapper.user(), null, tag, false);
if (newId != -1) {
int x = ((NumberTag) tag.get("x")).asInt();
int y = ((NumberTag) tag.get("y")).asInt();
int z = ((NumberTag) tag.get("z")).asInt();
Position position = new Position(x, (short) y, z);
// Store the replacement blocks for blockupdates
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
chunk.getSections()[y >> 4].setFlatBlock(x & 0xF, y & 0xF, z & 0xF, newId);
}
final Tag idTag = tag.get("id");
if (idTag instanceof StringTag) {
// No longer block entities
final String id = ((StringTag) idTag).getValue();
if (id.equals("minecraft:noteblock") || id.equals("minecraft:flower_pot")) {
iterator.remove();
// Rewrite biome id 255 to plains
if (chunk.isBiomeData()) {
int latestBiomeWarn = Integer.MIN_VALUE;
for (int i = 0; i < 256; i++) {
int biome = chunk.getBiomeData()[i];
if (!VALID_BIOMES.contains(biome)) {
if (biome != 255 // is it generated naturally? *shrug*
&& latestBiomeWarn != biome) {
if (!Via.getConfig().isSuppressConversionWarnings() || Via.getManager().isDebug()) {
Via.getPlatform().getLogger().warning("Received invalid biome id " + biome);
}
latestBiomeWarn = biome;
}
chunk.getBiomeData()[i] = 1; // Plains
}
}
}
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.connectBlocks(wrapper.user(), chunk);
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
ConnectionData.updateChunkSectionNeighbours(wrapper.user(), chunk.getX(), chunk.getZ(), i);
// Rewrite BlockEntities to normal blocks
BlockEntityProvider provider = Via.getManager().getProviders().get(BlockEntityProvider.class);
final Iterator<CompoundTag> iterator = chunk.getBlockEntities().iterator();
while (iterator.hasNext()) {
CompoundTag tag = iterator.next();
int newId = provider.transform(wrapper.user(), null, tag, false);
if (newId != -1) {
int x = ((NumberTag) tag.get("x")).asInt();
int y = ((NumberTag) tag.get("y")).asInt();
int z = ((NumberTag) tag.get("z")).asInt();
Position position = new Position(x, (short) y, z);
// Store the replacement blocks for blockupdates
BlockStorage.ReplacementData replacementData = storage.get(position);
if (replacementData != null) {
replacementData.setReplacement(newId);
}
chunk.getSections()[y >> 4].setFlatBlock(x & 0xF, y & 0xF, z & 0xF, newId);
}
final Tag idTag = tag.get("id");
if (idTag instanceof StringTag) {
// No longer block entities
final String id = ((StringTag) idTag).getValue();
if (id.equals("minecraft:noteblock") || id.equals("minecraft:flower_pot")) {
iterator.remove();
}
}
}
if (Via.getConfig().isServersideBlockConnections()) {
ConnectionData.connectBlocks(wrapper.user(), chunk);
// Workaround for packet order issue
wrapper.send(Protocol1_13To1_12_2.class);
wrapper.cancel();
for (int i = 0; i < chunk.getSections().length; i++) {
ChunkSection section = chunk.getSections()[i];
if (section == null) continue;
ConnectionData.updateChunkSectionNeighbours(wrapper.user(), chunk.getX(), chunk.getZ(), i);
}
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_12_1.SPAWN_PARTICLE, new PacketRemapper() {
@Override
public void registerMap() {
map(Type.INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
map(Type.FLOAT); // 2 - X
map(Type.FLOAT); // 3 - Y
map(Type.FLOAT); // 4 - Z
map(Type.FLOAT); // 5 - Offset X
map(Type.FLOAT); // 6 - Offset Y
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
protocol.registerClientbound(ClientboundPackets1_12_1.SPAWN_PARTICLE, new
handler(new PacketHandler() {
PacketRemapper() {
@Override
public void handle(PacketWrapper wrapper) throws Exception {
int particleId = wrapper.get(Type.INT, 0);
public void registerMap() {
map(Type.INT); // 0 - Particle ID
map(Type.BOOLEAN); // 1 - Long Distance
map(Type.FLOAT); // 2 - X
map(Type.FLOAT); // 3 - Y
map(Type.FLOAT); // 4 - Z
map(Type.FLOAT); // 5 - Offset X
map(Type.FLOAT); // 6 - Offset Y
map(Type.FLOAT); // 7 - Offset Z
map(Type.FLOAT); // 8 - Particle Data
map(Type.INT); // 9 - Particle Count
// Get the data (Arrays are overrated)
int dataCount = 0;
// Particles with 1 data [BlockCrack,BlockDust,FallingDust]
if (particleId == 37 || particleId == 38 || particleId == 46)
dataCount = 1;
// Particles with 2 data [IconCrack]
else if (particleId == 36)
dataCount = 2;
handler(wrapper -> {
int particleId = wrapper.get(Type.INT, 0);
Integer[] data = new Integer[dataCount];
for (int i = 0; i < data.length; i++)
data[i] = wrapper.read(Type.VAR_INT);
// Get the data (Arrays are overrated)
int dataCount = 0;
// Particles with 1 data [BlockCrack,BlockDust,FallingDust]
if (particleId == 37 || particleId == 38 || particleId == 46)
dataCount = 1;
// Particles with 2 data [IconCrack]
else if (particleId == 36)
dataCount = 2;
Particle particle = ParticleRewriter.rewriteParticle(particleId, data);
Integer[] data = new Integer[dataCount];
for (int i = 0; i < data.length; i++)
data[i] = wrapper.read(Type.VAR_INT);
// Cancel if null or completely removed
if (particle == null || particle.getId() == -1) {
wrapper.cancel();
return;
}
Particle particle = ParticleRewriter.rewriteParticle(particleId, data);
//Handle reddust particle color
if (particle.getId() == 11) {
int count = wrapper.get(Type.INT, 1);
float speed = wrapper.get(Type.FLOAT, 6);
// Only handle for count = 0
if (count == 0) {
wrapper.set(Type.INT, 1, 1);
wrapper.set(Type.FLOAT, 6, 0f);
// Cancel if null or completely removed
if (particle == null || particle.getId() == -1) {
wrapper.cancel();
return;
}
List<Particle.ParticleData> arguments = particle.getArguments();
for (int i = 0; i < 3; i++) {
//RGB values are represented by the X/Y/Z offset
float colorValue = wrapper.get(Type.FLOAT, i + 3) * speed;
if (colorValue == 0 && i == 0) {
// https://minecraft.gamepedia.com/User:Alphappy/reddust
colorValue = 1;
//Handle reddust particle color
if (particle.getId() == 11) {
int count = wrapper.get(Type.INT, 1);
float speed = wrapper.get(Type.FLOAT, 6);
// Only handle for count = 0
if (count == 0) {
wrapper.set(Type.INT, 1, 1);
wrapper.set(Type.FLOAT, 6, 0f);
List<Particle.ParticleData> arguments = particle.getArguments();
for (int i = 0; i < 3; i++) {
//RGB values are represented by the X/Y/Z offset
float colorValue = wrapper.get(Type.FLOAT, i + 3) * speed;
if (colorValue == 0 && i == 0) {
// https://minecraft.gamepedia.com/User:Alphappy/reddust
colorValue = 1;
}
arguments.get(i).setValue(colorValue);
wrapper.set(Type.FLOAT, i + 3, 0f);
}
arguments.get(i).setValue(colorValue);
wrapper.set(Type.FLOAT, i + 3, 0f);
}
}
}
wrapper.set(Type.INT, 0, particle.getId());
for (Particle.ParticleData particleData : particle.getArguments())
wrapper.write(particleData.getType(), particleData.getValue());
wrapper.set(Type.INT, 0, particle.getId());
for (Particle.ParticleData particleData : particle.getArguments())
wrapper.write(particleData.getType(), particleData.getValue());
});
}
});
}
});
}
public static int toNewId(int oldId) {
@@ -111,11 +111,15 @@ public class BlockConnectionStorage implements StorableObject {
}
public void unloadChunk(int x, int z) {
for (int y = 0; y < 256; y += 16) {
blockStorage.remove(getChunkSectionIndex(x << 4, y, z << 4));
for (int y = 0; y < 16; y ++) {
unloadSection(x, y, z);
}
}
public void unloadSection(int x, int y, int z) {
blockStorage.remove(getChunkSectionIndex(x << 4, y << 4, z << 4));
}
private Pair<byte[], NibbleArray> getChunkSection(long index, boolean requireNibbleArray) {
Pair<byte[], NibbleArray> map = blockStorage.get(index);
if (map == null) {
@@ -0,0 +1,39 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.protocols.protocol1_19_1to1_19;
import com.google.gson.JsonElement;
public final class ChatDecorationResult {
private final JsonElement content;
private final boolean overlay;
public ChatDecorationResult(final JsonElement content, final boolean overlay) {
this.content = content;
this.overlay = overlay;
}
public JsonElement content() {
return content;
}
public boolean overlay() {
return overlay;
}
}
@@ -17,22 +17,41 @@
*/
package com.viaversion.viaversion.protocols.protocol1_19_1to1_19;
import com.github.steveice10.opennbt.tag.builtin.ByteTag;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.google.common.base.Preconditions;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ProfileKey;
import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.protocol.remapper.PacketRemapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.libs.kyori.adventure.text.Component;
import com.viaversion.viaversion.libs.kyori.adventure.text.TranslatableComponent;
import com.viaversion.viaversion.libs.kyori.adventure.text.format.NamedTextColor;
import com.viaversion.viaversion.libs.kyori.adventure.text.format.Style;
import com.viaversion.viaversion.libs.kyori.adventure.text.format.TextDecoration;
import com.viaversion.viaversion.libs.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage.ChatTypeStorage;
import com.viaversion.viaversion.protocols.protocol1_19_1to1_19.storage.NonceStorage;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ClientboundPackets1_19;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.ServerboundPackets1_19;
import com.viaversion.viaversion.util.CipherUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPackets1_19, ClientboundPackets1_19_1, ServerboundPackets1_19, ServerboundPackets1_19_1> {
@@ -94,18 +113,24 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
@Override
public void registerMap() {
handler(wrapper -> {
// Back to system chat - bye bye chat formats for 1.19.0 servers
// ... not that big of a deal since the majority of modded servers only has Vanilla /say command and the alike sent as proper player chat
// Back to system chat
final JsonElement signedContent = wrapper.read(Type.COMPONENT);
final JsonElement unsignedContent = wrapper.read(Type.OPTIONAL_COMPONENT);
wrapper.write(Type.COMPONENT, unsignedContent != null ? unsignedContent : signedContent);
final int chatTypeId = wrapper.read(Type.VAR_INT);
wrapper.read(Type.UUID); // Sender UUID
final JsonElement senderName = wrapper.read(Type.COMPONENT);
final JsonElement teamName = wrapper.read(Type.OPTIONAL_COMPONENT);
final int type = wrapper.read(Type.VAR_INT);
wrapper.write(Type.BOOLEAN, type == 2); // Overlay, going by the default 1.19 chat type registry
final CompoundTag chatType = wrapper.user().get(ChatTypeStorage.class).chatType(chatTypeId);
final ChatDecorationResult decorationResult = decorateChatMessage(chatType, chatTypeId, senderName, teamName, unsignedContent != null ? unsignedContent : signedContent);
if (decorationResult == null) {
wrapper.cancel();
return;
}
wrapper.write(Type.COMPONENT, decorationResult.content());
wrapper.write(Type.BOOLEAN, decorationResult.overlay());
});
read(Type.UUID); // Sender uuid
read(Type.COMPONENT); // Sender display name
read(Type.OPTIONAL_COMPONENT); // Target display name
read(Type.LONG); // Timestamp
read(Type.LONG); // Salt
read(Type.BYTE_ARRAY_PRIMITIVE); // Signature
@@ -151,11 +176,20 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
map(Type.UNSIGNED_BYTE); // Gamemode
map(Type.BYTE); // Previous Gamemode
map(Type.STRING_ARRAY); // World List
map(Type.NBT); // Registry
handler(wrapper -> {
// Replace chat types - not worth the effort of handling them properly
final CompoundTag tag = wrapper.get(Type.NBT, 0);
tag.put("minecraft:chat_type", CHAT_REGISTRY.clone());
final ChatTypeStorage chatTypeStorage = wrapper.user().get(ChatTypeStorage.class);
chatTypeStorage.clear();
final CompoundTag registry = wrapper.passthrough(Type.NBT);
final ListTag chatTypes = ((CompoundTag) registry.get("minecraft:chat_type")).get("value");
for (final Tag chatType : chatTypes) {
final CompoundTag chatTypeCompound = (CompoundTag) chatType;
final NumberTag idTag = chatTypeCompound.get("id");
chatTypeStorage.addChatType(idTag.asInt(), chatTypeCompound);
}
// Replace chat types - they won't actually be used
registry.put("minecraft:chat_type", CHAT_REGISTRY.clone());
});
}
});
@@ -221,5 +255,110 @@ public final class Protocol1_19_1To1_19 extends AbstractProtocol<ClientboundPack
});
}
});
registerClientbound(State.LOGIN, ClientboundLoginPackets.CUSTOM_QUERY.getId(), ClientboundLoginPackets.CUSTOM_QUERY.getId(), new PacketRemapper() {
@Override
public void registerMap() {
map(Type.VAR_INT);
map(Type.STRING);
handler(wrapper -> {
String identifier = wrapper.get(Type.STRING, 0);
if (identifier.equals("velocity:player_info")) {
byte[] data = wrapper.passthrough(Type.REMAINING_BYTES);
// Velocity modern forwarding version above 1 includes the players public key.
// This is an issue because the server will expect a 1.19 key and receive a 1.19.1 key.
// Velocity modern forwarding versions: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/VelocityConstants.java#L27-L29
// And the version can be specified with a single byte: https://github.com/PaperMC/Velocity/blob/1a3fba4250553702d9dcd05731d04347bfc24c9f/proxy/src/main/java/com/velocitypowered/proxy/connection/backend/LoginSessionHandler.java#L88
if (data.length == 1 && data[0] > 1) {
data[0] = 1;
} else if (data.length == 0) { // Or the version is omitted (default version would be used)
data = new byte[]{1};
wrapper.set(Type.REMAINING_BYTES, 0, data);
} else {
Via.getPlatform().getLogger().warning("Received unexpected data in velocity:player_info (length=" + data.length + ")");
}
}
});
}
});
}
@Override
public void init(final UserConnection connection) {
connection.put(new ChatTypeStorage());
}
public static @Nullable ChatDecorationResult decorateChatMessage(final CompoundTag chatType, final int chatTypeId, final JsonElement senderName, @Nullable final JsonElement teamName, final JsonElement message) {
if (chatType == null) {
Via.getPlatform().getLogger().warning("Chat message has unknown chat type id " + chatTypeId + ". Message: " + message);
return null;
}
CompoundTag chatData = chatType.<CompoundTag>get("element").get("chat");
boolean overlay = false;
if (chatData == null) {
chatData = chatType.<CompoundTag>get("element").get("overlay");
if (chatData == null) {
// Either narration or something we don't know
return null;
}
overlay = true;
}
final CompoundTag decoaration = chatData.get("decoration");
if (decoaration == null) {
return new ChatDecorationResult(message, overlay);
}
final String translationKey = (String) decoaration.get("translation_key").getValue();
final TranslatableComponent.Builder componentBuilder = Component.translatable().key(translationKey);
// Add the style
final CompoundTag style = decoaration.get("style");
if (style != null) {
final Style.Builder styleBuilder = Style.style();
final StringTag color = style.get("color");
if (color != null) {
final NamedTextColor textColor = NamedTextColor.NAMES.value(color.getValue());
if (textColor != null) {
styleBuilder.color(NamedTextColor.NAMES.value(color.getValue()));
}
}
for (final String key : TextDecoration.NAMES.keys()) {
if (style.contains(key)) {
styleBuilder.decoration(TextDecoration.NAMES.value(key), style.<ByteTag>get(key).asByte() == 1);
}
}
componentBuilder.style(styleBuilder.build());
}
// Add the replacements
final ListTag parameters = decoaration.get("parameters");
if (parameters != null) {
final List<Component> arguments = new ArrayList<>();
for (final Tag element : parameters) {
JsonElement argument = null;
switch ((String) element.getValue()) {
case "sender":
argument = senderName;
break;
case "content":
argument = message;
break;
case "team_name":
Preconditions.checkNotNull(teamName, "Team name is null");
argument = teamName;
break;
default:
Via.getPlatform().getLogger().warning("Unknown parameter for chat decoration: " + element.getValue());
}
if (argument != null) {
arguments.add(GsonComponentSerializer.gson().deserializeFromTree(argument));
}
}
componentBuilder.args(arguments);
}
return new ChatDecorationResult(GsonComponentSerializer.gson().serializeToTree(componentBuilder.build()), overlay);
}
}
@@ -0,0 +1,47 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.protocols.protocol1_19_1to1_19.storage;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.Protocol1_19To1_18_2;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class ChatTypeStorage implements StorableObject {
private final Int2ObjectMap<CompoundTag> chatTypes = new Int2ObjectOpenHashMap<>();
public @Nullable CompoundTag chatType(final int id) {
return chatTypes.isEmpty() ? Protocol1_19To1_18_2.MAPPINGS.chatType(id) : chatTypes.get(id);
}
public void addChatType(final int id, final CompoundTag chatType) {
chatTypes.put(id, chatType);
}
public void clear() {
chatTypes.clear();
}
@Override
public boolean clearOnServerSwitch() {
return false;
}
}
@@ -20,8 +20,6 @@ package com.viaversion.viaversion.protocols.protocol1_19to1_18_2;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.minecraft.entities.Entity1_19Types;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
@@ -40,6 +38,7 @@ import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
import com.viaversion.viaversion.protocols.protocol1_17to1_16_4.ServerboundPackets1_17;
import com.viaversion.viaversion.protocols.protocol1_18to1_17_1.ClientboundPackets1_18;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.data.MappingData;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.EntityPackets;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.InventoryPackets;
import com.viaversion.viaversion.protocols.protocol1_19to1_18_2.packets.WorldPackets;
@@ -57,7 +56,7 @@ import java.util.concurrent.ThreadLocalRandom;
public final class Protocol1_19To1_18_2 extends AbstractProtocol<ClientboundPackets1_18, ClientboundPackets1_19, ServerboundPackets1_17, ServerboundPackets1_19> {
public static final MappingData MAPPINGS = new MappingDataBase("1.18", "1.19", true);
public static final MappingData MAPPINGS = new MappingData();
private final EntityPackets entityRewriter = new EntityPackets(this);
private final InventoryPackets itemRewriter = new InventoryPackets(this);
@@ -0,0 +1,59 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2016-2022 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.protocols.protocol1_19to1_18_2.data;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.google.gson.JsonObject;
import com.viaversion.viaversion.api.data.MappingDataBase;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.minecraft.nbt.BinaryTagIO;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
public final class MappingData extends MappingDataBase {
private final Int2ObjectMap<CompoundTag> defaultChatTypes = new Int2ObjectOpenHashMap<>();
public MappingData() {
super("1.18", "1.19", true);
}
@Override
protected void loadExtras(final JsonObject oldMappings, final JsonObject newMappings, @Nullable final JsonObject diffMappings) {
try {
final ListTag chatTypes = BinaryTagIO.readCompressedInputStream(MappingDataLoader.getResource("chat-types-1.19.nbt")).get("values");
for (final Tag chatType : chatTypes) {
final CompoundTag chatTypeCompound = (CompoundTag) chatType;
final NumberTag idTag = chatTypeCompound.get("id");
defaultChatTypes.put(idTag.asInt(), chatTypeCompound);
}
} catch (final IOException e) {
e.printStackTrace();
}
}
public @Nullable CompoundTag chatType(final int id) {
return defaultChatTypes.get(id);
}
}
@@ -75,7 +75,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
" ]\n" +
" }\n" +
"}";
private static final CompoundTag CHAT_REGISTRY;
public static final CompoundTag CHAT_REGISTRY;
static {
try {
@@ -151,7 +151,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
final PacketWrapper metaPacket = wrapper.create(ClientboundPackets1_19.ENTITY_METADATA);
metaPacket.write(Type.VAR_INT, wrapper.get(Type.VAR_INT, 0)); // Entity id
final List<Metadata> metadata = new ArrayList<>();
metadata.add(new Metadata(8, Types1_19.META_TYPES.paintingVariantType, protocol.getMappingData().getPaintingMappings().getNewId(motive)));
metadata.add(new Metadata(8, Types1_19.META_TYPES.paintingVariantType, protocol.getMappingData().getPaintingMappings().getNewIdOrDefault(motive, 0)));
metaPacket.write(Types1_19.METADATA_LIST, metadata);
metaPacket.send(Protocol1_19To1_18_2.class);
});
@@ -218,10 +218,9 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
final CompoundTag dimensionCompound = (CompoundTag) dimension;
final CompoundTag element = dimensionCompound.get("element");
final String name = (String) dimensionCompound.get("name").getValue();
addMonsterSpawnData(element);
dimensionDataMap.put(name, new DimensionDataImpl(element));
dimensionsMap.put(element.clone(), name);
addMonsterSpawnData(element);
}
tracker(wrapper.user()).setDimensions(dimensionDataMap);
@@ -310,6 +309,7 @@ public final class EntityPackets extends EntityRewriter<Protocol1_19To1_18_2> {
private static void writeDimensionKey(final PacketWrapper wrapper, final DimensionRegistryStorage registryStorage) throws Exception {
// Find dimension key by data
final CompoundTag currentDimension = wrapper.read(Type.NBT);
addMonsterSpawnData(currentDimension);
String dimensionKey = registryStorage.dimensionKey(currentDimension);
if (dimensionKey == null) {
if (!Via.getConfig().isSuppressConversionWarnings()) {
@@ -446,10 +446,10 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
public PacketHandler worldDataTrackerHandlerByKey() {
return wrapper -> {
EntityTracker tracker = tracker(wrapper.user());
String key = wrapper.get(Type.STRING, 0);
DimensionData dimensionData = tracker.dimensionData(key);
String dimensionKey = wrapper.get(Type.STRING, 0);
DimensionData dimensionData = tracker.dimensionData(dimensionKey);
if (dimensionData == null) {
Via.getPlatform().getLogger().severe("Dimension data missing for dimension: " + key + ", falling back to overworld");
Via.getPlatform().getLogger().severe("Dimension data missing for dimension: " + dimensionKey + ", falling back to overworld");
dimensionData = tracker.dimensionData("minecraft:overworld");
Preconditions.checkNotNull(dimensionData, "Overworld data missing");
}
@@ -457,7 +457,7 @@ public abstract class EntityRewriter<T extends Protocol> extends RewriterBase<T>
tracker.setCurrentWorldSectionHeight(dimensionData.height() >> 4);
tracker.setCurrentMinY(dimensionData.minY());
String world = wrapper.get(Type.STRING, 0);
String world = wrapper.get(Type.STRING, 1);
if (tracker.currentWorld() != null && !tracker.currentWorld().equals(world)) {
tracker.clearEntities();
tracker.trackClientEntity();
@@ -110,5 +110,6 @@ public final class UnsupportedServerSoftware implements UnsupportedSoftware {
public static final class Reason {
public static final String DANGEROUS_SERVER_SOFTWARE = "You are using server software that - outside of possibly breaking ViaVersion - can also cause severe damage to your server's integrity as a whole.";
public static final String BREAKING_PROXY_SOFTWARE = "You are using proxy software that intentionally breaks ViaVersion. Please use another proxy software or move ViaVersion to each backend server instead of the proxy.";
}
}
@@ -18,8 +18,6 @@
package com.viaversion.viaversion.util;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.io.CharStreams;
import com.google.common.io.Files;
@@ -28,19 +26,23 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
public class CommentStore {
private final Map<String, List<String>> headers = Maps.newConcurrentMap();
private final char pathSeperator;
private final Map<String, List<String>> headers = new HashMap<>();
private final String pathSeparator;
private final String pathSeparatorQuoted;
private final int indents;
private List<String> mainHeader = Lists.newArrayList();
private List<String> mainHeader = new ArrayList<>();
public CommentStore(char pathSeperator, int indents) {
this.pathSeperator = pathSeperator;
public CommentStore(final char pathSeparator, final int indents) {
this.pathSeparator = Character.toString(pathSeparator);
this.pathSeparatorQuoted = Pattern.quote(this.pathSeparator);
this.indents = indents;
}
@@ -49,7 +51,7 @@ public class CommentStore {
*
* @param header header
*/
public void mainHeader(String... header) {
public void mainHeader(final String... header) {
mainHeader = Arrays.asList(header);
}
@@ -68,8 +70,7 @@ public class CommentStore {
* @param key of option (or section)
* @param header of option (or section)
*/
public void header(String key, String... header) {
// String value = Joiner.on('\n').join(header);
public void header(final String key, final String... header) {
headers.put(key, Arrays.asList(header));
}
@@ -79,126 +80,181 @@ public class CommentStore {
* @param key of option (or section)
* @return Header
*/
public List<String> header(String key) {
public List<String> header(final String key) {
return headers.get(key);
}
public void storeComments(InputStream inputStream) throws IOException {
InputStreamReader reader = new InputStreamReader(inputStream);
String contents;
try {
contents = CharStreams.toString(reader);
} finally {
reader.close();
public void storeComments(final InputStream inputStream) throws IOException {
final String data;
try (final InputStreamReader reader = new InputStreamReader(inputStream, StandardCharsets.UTF_8)) {
data = CharStreams.toString(reader);
}
StringBuilder memoryData = new StringBuilder();
// Parse headers
final String pathSeparator = Character.toString(this.pathSeperator);
final List<String> currentComments = new ArrayList<>();
boolean header = true;
boolean multiLineValue = false;
int currentIndents = 0;
String key = "";
List<String> headers = Lists.newArrayList();
for (String line : contents.split("\n")) {
if (line.isEmpty()) continue; // Skip empty lines
int indent = getSuccessiveCharCount(line, ' ');
String subline = indent > 0 ? line.substring(indent) : line;
if (subline.startsWith("#")) {
if (subline.startsWith("#>")) {
String txt = subline.startsWith("#> ") ? subline.substring(3) : subline.substring(2);
mainHeader.add(txt);
continue; // Main header, handled by bukkit
}
// Add header to list
String txt = subline.startsWith("# ") ? subline.substring(2) : subline.substring(1);
headers.add(txt);
for (final String line : data.split("\n")) {
final String s = line.trim();
// It's a comment!
if (s.startsWith("#")) {
currentComments.add(s);
continue;
}
int indents = indent / this.indents;
if (indents <= currentIndents) {
// Remove last section of key
String[] array = key.split(Pattern.quote(pathSeparator));
int backspace = currentIndents - indents + 1;
key = join(array, this.pathSeperator, 0, array.length - backspace);
// Header is over - save it!
if (header) {
if (!currentComments.isEmpty()) {
currentComments.add("");
mainHeader.addAll(currentComments);
currentComments.clear();
}
header = false;
}
// Add new section to key
String separator = key.length() > 0 ? pathSeparator : "";
String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line;
key += separator + lineKey.substring(indent);
// Save empty lines as well
if (s.isEmpty()) {
currentComments.add(s);
continue;
}
// Multi line values?
if (s.startsWith("- |")) {
multiLineValue = true;
continue;
}
final int indent = getIndents(line);
final int indents = indent / this.indents;
// Check if the multi line value is over
if (multiLineValue) {
if (indents > currentIndents) continue;
multiLineValue = false;
}
// Check if this is a level lower
if (indents <= currentIndents) {
final String[] array = key.split(pathSeparatorQuoted);
final int backspace = currentIndents - indents + 1;
final int delta = array.length - backspace;
key = delta >= 0 ? join(array, delta) : key;
}
// Finish current key
final String separator = key.isEmpty() ? "" : this.pathSeparator;
final String lineKey = line.indexOf(':') != -1 ? line.split(Pattern.quote(":"))[0] : line;
key += separator + lineKey.substring(indent);
currentIndents = indents;
memoryData.append(line).append('\n');
if (!headers.isEmpty()) {
this.headers.put(key, headers);
headers = Lists.newArrayList();
if (!currentComments.isEmpty()) {
headers.put(key, new ArrayList<>(currentComments));
currentComments.clear();
}
}
}
public void writeComments(String yaml, File output) throws IOException {
// Custom save
final int indentLength = this.indents;
final String pathSeparator = Character.toString(this.pathSeperator);
StringBuilder fileData = new StringBuilder();
int currentIndents = 0;
String key = "";
for (String h : mainHeader) {
// Append main header to top of file
fileData.append("#> ").append(h).append('\n');
public void writeComments(final String rawYaml, final File output) throws IOException {
final StringBuilder fileData = new StringBuilder();
for (final String mainHeaderLine : mainHeader) {
fileData.append(mainHeaderLine).append('\n');
}
for (String line : yaml.split("\n")) {
if (line.isEmpty() || line.trim().charAt(0) == '-') {
// Remove last new line
fileData.deleteCharAt(fileData.length() - 1);
int currentKeyIndents = 0;
String key = "";
for (final String line : rawYaml.split("\n")) {
if (line.isEmpty()) {
continue;
}
final int indent = getIndents(line);
final int indents = indent / this.indents;
final boolean keyLine;
final String substring = line.substring(indent);
if (substring.trim().isEmpty() || substring.charAt(0) == '-') {
keyLine = false;
} else if (indents <= currentKeyIndents) {
final String[] array = key.split(this.pathSeparatorQuoted);
final int backspace = currentKeyIndents - indents + 1;
key = join(array, array.length - backspace);
keyLine = true;
} else {
keyLine = line.indexOf(':') != -1;
}
if (!keyLine) {
// Nothing to do, go to next line
fileData.append(line).append('\n');
continue;
}
int indent = getSuccessiveCharCount(line, ' ');
int indents = indent / indentLength;
String indentText = indent > 0 ? line.substring(0, indent) : "";
if (indents <= currentIndents) {
// Remove last section of key
String[] array = key.split(Pattern.quote(pathSeparator));
int backspace = currentIndents - indents + 1;
key = join(array, this.pathSeperator, 0, array.length - backspace);
final String newKey = substring.split(Pattern.quote(":"))[0]; // Not sure about the quote thing, so I'll just keep it :aaa:
if (!key.isEmpty()) {
key += this.pathSeparator;
}
key += newKey;
// Add comments
final List<String> strings = headers.get(key);
if (strings != null && !strings.isEmpty()) {
final String indentText = indent > 0 ? line.substring(0, indent) : "";
for (final String comment : strings) {
if (comment.isEmpty()) {
fileData.append('\n');
} else {
fileData.append(indentText).append(comment).append('\n');
}
}
}
// Add new section to key
String separator = !key.isEmpty() ? pathSeparator : "";
String lineKey = line.contains(":") ? line.split(Pattern.quote(":"))[0] : line;
key += separator + lineKey.substring(indent);
currentIndents = indents;
List<String> header = headers.get(key);
String headerText = header != null ? addHeaderTags(header, indentText) : "";
fileData.append(headerText).append(line).append('\n');
currentKeyIndents = indents;
fileData.append(line).append('\n');
}
// Write data to file
Files.write(fileData.toString(), output, StandardCharsets.UTF_8);
}
private String addHeaderTags(List<String> header, String indent) {
StringBuilder builder = new StringBuilder();
for (String line : header) {
private String addHeaderTags(final List<String> header, final String indent) {
final StringBuilder builder = new StringBuilder();
for (final String line : header) {
builder.append(indent).append("# ").append(line).append('\n');
}
return builder.toString();
}
private String join(String[] array, char joinChar, int start, int length) {
String[] copy = new String[length - start];
private String join(final String[] array, final char joinChar, final int start, final int length) {
final String[] copy = new String[length - start];
System.arraycopy(array, start, copy, 0, length - start);
return Joiner.on(joinChar).join(copy);
}
private int getSuccessiveCharCount(String text, char key) {
private int getIndents(final String line) {
int count = 0;
for (int i = 0; i < line.length(); i++) {
if (line.charAt(i) != ' ') {
break;
}
count++;
}
return count;
}
private String join(final String[] array, final int length) {
final String[] copy = new String[length];
System.arraycopy(array, 0, copy, 0, length);
return String.join(this.pathSeparator, copy);
}
private int getSuccessiveSpaces(final String text) {
int count = 0;
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == key) {
if (text.charAt(i) == ' ') {
count += 1;
} else {
break;
@@ -62,9 +62,12 @@ public abstract class Config implements ConfigurationProvider {
return getClass().getClassLoader().getResource("assets/viaversion/config.yml");
}
public synchronized Map<String, Object> loadConfig(File location) {
public Map<String, Object> loadConfig(File location) {
return loadConfig(location, getDefaultConfigURL());
}
public synchronized Map<String, Object> loadConfig(File location, URL jarConfigFile) {
List<String> unsupported = getUnsupportedOptions();
URL jarConfigFile = getDefaultConfigURL();
try {
commentStore.storeComments(jarConfigFile.openStream());
for (String option : unsupported) {
@@ -137,6 +140,10 @@ public abstract class Config implements ConfigurationProvider {
saveConfig(this.configFile, this.config);
}
public void saveConfig(final File file) {
saveConfig(file, this.config);
}
@Override
public void reloadConfig() {
this.configFile.getParentFile().mkdirs();
@@ -164,6 +164,14 @@ resource-pack-1_17-prompt: ''
# Only disable this if you know what you are doing.
cache-1_17-light: true
#
# 1.19 chat type formats used for 1.19.1+ clients.
chat-types-1_19:
chat.type.text: "<%s> %s"
chat.type.announcement: "[%s] %s"
commands.message.display.incoming: "%s whispers to you: %s"
chat.type.team.text: "%s <%s> %s"
chat.type.emote: "* %s %s"
#
#----------------------------------------------------------#
# 1.9+ CLIENTS ON 1.8 SERVERS OPTIONS #
#----------------------------------------------------------#
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=4.4.0
projectVersion=4.4.2
# Gradle properties
org.gradle.daemon=true
+1 -1
View File
@@ -6,7 +6,7 @@ adventure = "4.9.3"
gson = "2.8.8"
fastutil = "8.5.6"
flare = "2.0.0"
openNBT = "2.0"
openNBT = "2.1"
javassist = "3.28.0-GA"
# Common provided
@@ -32,7 +32,7 @@ import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.configuration.ConfigurationProvider;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.platform.PlatformTask;
import com.viaversion.viaversion.api.platform.ViaPlatform;
import com.viaversion.viaversion.api.platform.ViaServerProxyPlatform;
import com.viaversion.viaversion.dump.PluginInfo;
import com.viaversion.viaversion.util.ChatColorUtil;
import com.viaversion.viaversion.util.GsonUtil;
@@ -64,7 +64,7 @@ import java.util.concurrent.TimeUnit;
description = "Allow newer Minecraft versions to connect to an older server version.",
url = "https://viaversion.com"
)
public class VelocityPlugin implements ViaPlatform<Player> {
public class VelocityPlugin implements ViaServerProxyPlatform<Player> {
public static final LegacyComponentSerializer COMPONENT_SERIALIZER = LegacyComponentSerializer.builder().character(ChatColorUtil.COLOR_CHAR).extractUrls().build();
public static ProxyServer PROXY;
@@ -76,6 +76,7 @@ public class VelocityPlugin implements ViaPlatform<Player> {
@DataDirectory
private Path configDir;
private final ProtocolDetectorService protocolDetectorService = new ProtocolDetectorService();
private VelocityViaAPI api;
private java.util.logging.Logger logger;
private VelocityViaConfig conf;
@@ -230,7 +231,7 @@ public class VelocityPlugin implements ViaPlatform<Player> {
));
}
extra.add("plugins", GsonUtil.getGson().toJsonTree(plugins));
extra.add("servers", GsonUtil.getGson().toJsonTree(ProtocolDetectorService.getDetectedIds()));
extra.add("servers", GsonUtil.getGson().toJsonTree(protocolDetectorService.detectedProtocolVersions()));
return extra;
}
@@ -249,6 +250,11 @@ public class VelocityPlugin implements ViaPlatform<Player> {
return logger;
}
@Override
public ProtocolDetectorService protocolDetectorService() {
return protocolDetectorService;
}
private boolean hasConnectionEvent() {
try {
Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent");
@@ -21,7 +21,6 @@ import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.command.ViaCommandSender;
import com.viaversion.viaversion.api.command.ViaSubCommand;
import com.viaversion.viaversion.velocity.platform.VelocityViaConfig;
import com.viaversion.viaversion.velocity.service.ProtocolDetectorService;
public class ProbeSubCmd extends ViaSubCommand {
@Override
@@ -38,7 +37,7 @@ public class ProbeSubCmd extends ViaSubCommand {
@Override
public boolean execute(ViaCommandSender sender, String[] args) {
ProtocolDetectorService.getInstance().run();
Via.proxyPlatform().protocolDetectorService().probeAllServers();
sendMessage(sender, "&6Started searching for protocol versions");
return true;
}
@@ -29,9 +29,9 @@ import com.viaversion.viaversion.velocity.listeners.UpdateListener;
import com.viaversion.viaversion.velocity.providers.VelocityBossBarProvider;
import com.viaversion.viaversion.velocity.providers.VelocityMovementTransmitter;
import com.viaversion.viaversion.velocity.providers.VelocityVersionProvider;
import com.viaversion.viaversion.velocity.service.ProtocolDetectorService;
public class VelocityViaLoader implements ViaPlatformLoader {
@Override
public void load() {
Object plugin = VelocityPlugin.PROXY.getPluginManager()
@@ -51,7 +51,7 @@ public class VelocityViaLoader implements ViaPlatformLoader {
int pingInterval = ((VelocityViaConfig) Via.getPlatform().getConf()).getVelocityPingInterval();
if (pingInterval > 0) {
Via.getPlatform().runRepeatingSync(
new ProtocolDetectorService(),
() -> Via.proxyPlatform().protocolDetectorService().probeAllServers(),
pingInterval * 20L);
}
}
@@ -24,7 +24,6 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.protocols.base.BaseVersionProvider;
import com.viaversion.viaversion.velocity.platform.VelocityViaInjector;
import com.viaversion.viaversion.velocity.service.ProtocolDetectorService;
import io.netty.channel.ChannelHandler;
import java.lang.reflect.Method;
@@ -50,7 +49,7 @@ public class VelocityVersionProvider extends BaseVersionProvider {
private int getBackProtocol(UserConnection user) throws Exception {
//TODO use newly added Velocity netty event
ChannelHandler mcHandler = user.getChannel().pipeline().get("handler");
return ProtocolDetectorService.getProtocolId(
return Via.proxyPlatform().protocolDetectorService().serverProtocolVersion(
((ServerConnection) getAssociation.invoke(mcHandler)).getServerInfo().getName());
}
@@ -21,80 +21,77 @@ import com.velocitypowered.api.proxy.server.RegisteredServer;
import com.viaversion.viaversion.VelocityPlugin;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import com.viaversion.viaversion.platform.AbstractProtocolDetectorService;
import com.viaversion.viaversion.velocity.platform.VelocityViaConfig;
import java.util.HashMap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;
public class ProtocolDetectorService implements Runnable {
private static final Map<String, Integer> detectedProtocolIds = new ConcurrentHashMap<>();
private static ProtocolDetectorService instance;
public ProtocolDetectorService() {
instance = this;
}
public static Integer getProtocolId(String serverName) {
// Step 1. Check Config
Map<String, Integer> servers = ((VelocityViaConfig) Via.getConfig()).getVelocityServerProtocols();
Integer protocol = servers.get(serverName);
if (protocol != null) {
return protocol;
}
// Step 2. Check Detected
Integer detectedProtocol = detectedProtocolIds.get(serverName);
if (detectedProtocol != null) {
return detectedProtocol;
}
// Step 3. Use Default
Integer defaultProtocol = servers.get("default");
if (defaultProtocol != null) {
return defaultProtocol;
}
// Step 4: Use bungee lowest supported... *cries*
try {
return ProtocolVersion.getProtocol(Via.getManager().getInjector().getServerProtocolVersion()).getVersion();
} catch (Exception e) {
e.printStackTrace();
return ProtocolVersion.v1_8.getVersion();
}
}
public final class ProtocolDetectorService extends AbstractProtocolDetectorService {
@Override
public void run() {
for (final RegisteredServer serv : VelocityPlugin.PROXY.getAllServers()) {
probeServer(serv);
public void probeAllServers() {
final Collection<RegisteredServer> servers = VelocityPlugin.PROXY.getAllServers();
final Set<String> serverNames = new HashSet<>(servers.size());
for (final RegisteredServer server : servers) {
probeServer(server);
serverNames.add(server.getServerInfo().getName());
}
// Remove servers that aren't registered anymore
lock.writeLock().lock();
try {
detectedProtocolIds.keySet().retainAll(serverNames);
} finally {
lock.writeLock().unlock();
}
}
public static void probeServer(final RegisteredServer serverInfo) {
final String key = serverInfo.getServerInfo().getName();
serverInfo.ping().thenAccept((serverPing) -> {
if (serverPing != null && serverPing.getVersion() != null) {
detectedProtocolIds.put(key, serverPing.getVersion().getProtocol());
if (((VelocityViaConfig) Via.getConfig()).isVelocityPingSave()) {
Map<String, Integer> servers = ((VelocityViaConfig) Via.getConfig()).getVelocityServerProtocols();
Integer protocol = servers.get(key);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(key, serverPing.getVersion().getProtocol());
}
// Save
Via.getPlatform().getConfigurationProvider().saveConfig();
public void probeServer(final RegisteredServer server) {
final String serverName = server.getServerInfo().getName();
server.ping().thenAccept(serverPing -> {
if (serverPing == null || serverPing.getVersion() == null) {
return;
}
final int oldProtocolVersion = serverProtocolVersion(serverName);
if (oldProtocolVersion != -1 && oldProtocolVersion == serverPing.getVersion().getProtocol()) {
// Same value as previously
return;
}
setProtocolVersion(serverName, serverPing.getVersion().getProtocol());
if (((VelocityViaConfig) Via.getConfig()).isVelocityPingSave()) {
final Map<String, Integer> servers = configuredServers();
final Integer protocol = servers.get(serverName);
if (protocol != null && protocol == serverPing.getVersion().getProtocol()) {
return;
}
// Ensure we're the only ones writing to the config
synchronized (Via.getPlatform().getConfigurationProvider()) {
servers.put(serverName, serverPing.getVersion().getProtocol());
}
Via.getPlatform().getConfigurationProvider().saveConfig();
}
});
}
public static Map<String, Integer> getDetectedIds() {
return new HashMap<>(detectedProtocolIds);
@Override
protected Map<String, Integer> configuredServers() {
return ((VelocityViaConfig) Via.getConfig()).getVelocityServerProtocols();
}
public static ProtocolDetectorService getInstance() {
return instance;
@Override
protected int lowestSupportedProtocolVersion() {
try {
return ProtocolVersion.getProtocol(Via.getManager().getInjector().getServerProtocolVersion()).getVersion();
} catch (final Exception e) {
e.printStackTrace();
return ProtocolVersion.v1_8.getVersion();
}
}
}