mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-12-24 01:17:40 +01:00
Merge remote-tracking branch 'origin/master' into dev
This commit is contained in:
commit
b19ec959b3
4
.github/workflows/gradle.yml
vendored
4
.github/workflows/gradle.yml
vendored
@ -7,7 +7,7 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
# Only run on PRs if the source branch is on a different repo. We do not need run everything twice.
|
# Only run on PRs if the source branch is on a different repo. We do not need to run everything twice.
|
||||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@ -18,7 +18,7 @@ jobs:
|
|||||||
- name: Set up JDK 11
|
- name: Set up JDK 11
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: 'adopt'
|
distribution: 'temurin'
|
||||||
java-version: 11
|
java-version: 11
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
@ -17,46 +17,58 @@
|
|||||||
*/
|
*/
|
||||||
package com.viaversion.viaversion.bungee.platform;
|
package com.viaversion.viaversion.bungee.platform;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.viaversion.viaversion.api.Via;
|
import com.viaversion.viaversion.api.Via;
|
||||||
import com.viaversion.viaversion.api.platform.ViaInjector;
|
import com.viaversion.viaversion.api.platform.ViaInjector;
|
||||||
import com.viaversion.viaversion.bungee.handlers.BungeeChannelInitializer;
|
import com.viaversion.viaversion.bungee.handlers.BungeeChannelInitializer;
|
||||||
import com.viaversion.viaversion.compatibility.ForcefulFieldModifier;
|
|
||||||
import com.viaversion.viaversion.compatibility.unsafe.UnsafeBackedForcefulFieldModifier;
|
|
||||||
import com.viaversion.viaversion.util.ReflectionUtil;
|
import com.viaversion.viaversion.util.ReflectionUtil;
|
||||||
|
import com.viaversion.viaversion.util.SetWrapper;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
import it.unimi.dsi.fastutil.ints.IntLinkedOpenHashSet;
|
||||||
import it.unimi.dsi.fastutil.ints.IntSortedSet;
|
import it.unimi.dsi.fastutil.ints.IntSortedSet;
|
||||||
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class BungeeViaInjector implements ViaInjector {
|
public class BungeeViaInjector implements ViaInjector {
|
||||||
|
|
||||||
private final ForcefulFieldModifier forcefulFieldModifier;
|
private static final Field LISTENERS_FIELD;
|
||||||
|
private final List<Channel> injectedChannels = new ArrayList<>();
|
||||||
|
|
||||||
public BungeeViaInjector() {
|
static {
|
||||||
try {
|
try {
|
||||||
this.forcefulFieldModifier = new UnsafeBackedForcefulFieldModifier();
|
LISTENERS_FIELD = ProxyServer.getInstance().getClass().getDeclaredField("listeners");
|
||||||
} catch (final ReflectiveOperationException ex) {
|
LISTENERS_FIELD.setAccessible(true);
|
||||||
throw new IllegalStateException("Cannot create a modifier accessor", ex);
|
} catch (ReflectiveOperationException e) {
|
||||||
|
throw new RuntimeException("Unable to access listeners field.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inject() throws Exception {
|
@SuppressWarnings("unchecked")
|
||||||
try {
|
public void inject() throws ReflectiveOperationException {
|
||||||
Class<?> pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils");
|
Set<Channel> listeners = (Set<Channel>) LISTENERS_FIELD.get(ProxyServer.getInstance());
|
||||||
Field field = pipelineUtils.getDeclaredField("SERVER_CHILD");
|
|
||||||
field.setAccessible(true);
|
|
||||||
|
|
||||||
BungeeChannelInitializer newInit = new BungeeChannelInitializer((ChannelInitializer<Channel>) field.get(null));
|
// Inject the list
|
||||||
|
Set<Channel> wrapper = new SetWrapper<>(listeners, channel -> {
|
||||||
|
try {
|
||||||
|
injectChannel(channel);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.forcefulFieldModifier.setField(field, null, newInit);
|
LISTENERS_FIELD.set(ProxyServer.getInstance(), wrapper);
|
||||||
} catch (Exception e) {
|
|
||||||
Via.getPlatform().getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version.");
|
// Iterate through current list
|
||||||
throw e;
|
for (Channel channel : listeners) {
|
||||||
|
injectChannel(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +77,40 @@ public class BungeeViaInjector implements ViaInjector {
|
|||||||
Via.getPlatform().getLogger().severe("ViaVersion cannot remove itself from Bungee without a reboot!");
|
Via.getPlatform().getLogger().severe("ViaVersion cannot remove itself from Bungee without a reboot!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void injectChannel(Channel channel) throws ReflectiveOperationException {
|
||||||
|
List<String> names = channel.pipeline().names();
|
||||||
|
ChannelHandler bootstrapAcceptor = null;
|
||||||
|
|
||||||
|
for (String name : names) {
|
||||||
|
ChannelHandler handler = channel.pipeline().get(name);
|
||||||
|
try {
|
||||||
|
ReflectionUtil.get(handler, "childHandler", ChannelInitializer.class);
|
||||||
|
bootstrapAcceptor = handler;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Not this one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default to first
|
||||||
|
if (bootstrapAcceptor == null) {
|
||||||
|
bootstrapAcceptor = channel.pipeline().first();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bootstrapAcceptor.getClass().getName().equals("net.md_5.bungee.query.QueryHandler")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ChannelInitializer<Channel> oldInit = ReflectionUtil.get(bootstrapAcceptor, "childHandler", ChannelInitializer.class);
|
||||||
|
ChannelInitializer<Channel> newInit = new BungeeChannelInitializer(oldInit);
|
||||||
|
|
||||||
|
ReflectionUtil.set(bootstrapAcceptor, "childHandler", newInit);
|
||||||
|
this.injectedChannels.add(channel);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. issue: " + bootstrapAcceptor.getClass().getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getServerProtocolVersion() throws Exception {
|
public int getServerProtocolVersion() throws Exception {
|
||||||
@ -76,29 +122,64 @@ public class BungeeViaInjector implements ViaInjector {
|
|||||||
return new IntLinkedOpenHashSet(getBungeeSupportedVersions());
|
return new IntLinkedOpenHashSet(getBungeeSupportedVersions());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private List<Integer> getBungeeSupportedVersions() throws Exception {
|
private List<Integer> getBungeeSupportedVersions() throws Exception {
|
||||||
return ReflectionUtil.getStatic(Class.forName("net.md_5.bungee.protocol.ProtocolConstants"), "SUPPORTED_VERSION_IDS", List.class);
|
return ReflectionUtil.getStatic(Class.forName("net.md_5.bungee.protocol.ProtocolConstants"), "SUPPORTED_VERSION_IDS", List.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelInitializer<Channel> getChannelInitializer() throws Exception {
|
|
||||||
Class<?> pipelineUtils = Class.forName("net.md_5.bungee.netty.PipelineUtils");
|
|
||||||
Field field = pipelineUtils.getDeclaredField("SERVER_CHILD");
|
|
||||||
field.setAccessible(true);
|
|
||||||
return (ChannelInitializer<Channel>) field.get(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JsonObject getDump() {
|
public JsonObject getDump() {
|
||||||
JsonObject data = new JsonObject();
|
JsonObject data = new JsonObject();
|
||||||
try {
|
|
||||||
ChannelInitializer<Channel> initializer = getChannelInitializer();
|
// Generate information about current injections
|
||||||
data.addProperty("currentInitializer", initializer.getClass().getName());
|
JsonArray injectedChannelInitializers = new JsonArray();
|
||||||
if (initializer instanceof BungeeChannelInitializer) {
|
for (Channel channel : this.injectedChannels) {
|
||||||
data.addProperty("originalInitializer", ((BungeeChannelInitializer) initializer).getOriginal().getClass().getName());
|
JsonObject channelInfo = new JsonObject();
|
||||||
|
channelInfo.addProperty("channelClass", channel.getClass().getName());
|
||||||
|
|
||||||
|
// Get information about the pipes for this channel
|
||||||
|
JsonArray pipeline = new JsonArray();
|
||||||
|
for (String pipeName : channel.pipeline().names()) {
|
||||||
|
JsonObject handlerInfo = new JsonObject();
|
||||||
|
handlerInfo.addProperty("name", pipeName);
|
||||||
|
|
||||||
|
ChannelHandler channelHandler = channel.pipeline().get(pipeName);
|
||||||
|
if (channelHandler == null) {
|
||||||
|
handlerInfo.addProperty("status", "INVALID");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
handlerInfo.addProperty("class", channelHandler.getClass().getName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object child = ReflectionUtil.get(channelHandler, "childHandler", ChannelInitializer.class);
|
||||||
|
handlerInfo.addProperty("childClass", child.getClass().getName());
|
||||||
|
if (child instanceof BungeeChannelInitializer) {
|
||||||
|
handlerInfo.addProperty("oldInit", ((BungeeChannelInitializer) child).getOriginal().getClass().getName());
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException e) {
|
||||||
|
// Don't display
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.add(handlerInfo);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
channelInfo.add("pipeline", pipeline);
|
||||||
// Ignored, not printed in the dump
|
|
||||||
|
injectedChannelInitializers.add(channelInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data.add("injectedChannelInitializers", injectedChannelInitializers);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Object list = LISTENERS_FIELD.get(ProxyServer.getInstance());
|
||||||
|
data.addProperty("currentList", list.getClass().getName());
|
||||||
|
if (list instanceof SetWrapper) {
|
||||||
|
data.addProperty("wrappedList", ((SetWrapper<?>) list).originalSet().getClass().getName());
|
||||||
|
}
|
||||||
|
} catch (ReflectiveOperationException ignored) {
|
||||||
|
// Ignored
|
||||||
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.viaversion.viaversion.util;
|
||||||
|
|
||||||
|
import com.google.common.collect.ForwardingSet;
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public class SetWrapper<E> extends ForwardingSet<E> {
|
||||||
|
|
||||||
|
private final Set<E> set;
|
||||||
|
private final Consumer<E> addListener;
|
||||||
|
|
||||||
|
public SetWrapper(Set<E> set, Consumer<E> addListener) {
|
||||||
|
this.set = set;
|
||||||
|
this.addListener = addListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(@NonNull E element) {
|
||||||
|
addListener.accept(element);
|
||||||
|
|
||||||
|
return super.add(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends E> collection) {
|
||||||
|
for (E element : collection) {
|
||||||
|
addListener.accept(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.addAll(collection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Set<E> delegate() {
|
||||||
|
return originalSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<E> originalSet() {
|
||||||
|
return this.set;
|
||||||
|
}
|
||||||
|
}
|
@ -22,10 +22,14 @@ import com.viaversion.viaversion.compatibility.ForcefulFieldModifier;
|
|||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated usage of sun.misc.Unsafe is discouraged and can stop working in future Java releases.
|
||||||
|
*/
|
||||||
@SuppressWarnings({
|
@SuppressWarnings({
|
||||||
"java:S1191", // SonarLint/-Qube/-Cloud: We need Unsafe for the modifier implementation.
|
"java:S1191", // SonarLint/-Qube/-Cloud: We need Unsafe for the modifier implementation.
|
||||||
"java:S3011", // ^: We need to circumvent the access restrictions of fields.
|
"java:S3011", // ^: We need to circumvent the access restrictions of fields.
|
||||||
})
|
})
|
||||||
|
@Deprecated
|
||||||
public final class UnsafeBackedForcefulFieldModifier implements ForcefulFieldModifier {
|
public final class UnsafeBackedForcefulFieldModifier implements ForcefulFieldModifier {
|
||||||
private final sun.misc.Unsafe unsafe;
|
private final sun.misc.Unsafe unsafe;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user