mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'master' into feature/protocol-3.0
This commit is contained in:
commit
1769f2a85c
117 changed files with 12195 additions and 1369 deletions
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.bungeecord;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import net.md_5.bungee.protocol.packet.LoginSuccess;
|
||||
import net.md_5.bungee.protocol.packet.SetCompression;
|
||||
|
||||
public class GeyserBungeeCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
if (!(msg instanceof SetCompression)) {
|
||||
if (msg instanceof LoginSuccess) {
|
||||
// We're past the point that compression can be enabled
|
||||
if (ctx.pipeline().get("compress") != null) {
|
||||
ctx.pipeline().remove("compress");
|
||||
}
|
||||
if (ctx.pipeline().get("decompress") != null) {
|
||||
ctx.pipeline().remove("decompress");
|
||||
}
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
super.write(ctx, msg, promise);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -140,6 +140,11 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||
channelInitializer = PipelineUtils.SERVER_CHILD;
|
||||
}
|
||||
initChannel.invoke(channelInitializer, ch);
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
||||
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
|
||||
new GeyserBungeeCompressionDisabler());
|
||||
}
|
||||
}
|
||||
})
|
||||
.childAttr(listener, listenerInfo)
|
||||
|
@ -163,7 +168,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||
// If native compression is enabled, then this line is tripped up if a heap buffer is sent over in such a situation
|
||||
// as a new direct buffer is not created with that patch (HeapByteBufs throw an UnsupportedOperationException here):
|
||||
// https://github.com/SpigotMC/BungeeCord/blob/a283aaf724d4c9a815540cd32f3aafaa72df9e05/native/src/main/java/net/md_5/bungee/jni/zlib/NativeZlib.java#L43
|
||||
// This issue could be mitigated down the line by preventing Bungee from setting compression
|
||||
// If disable compression is enabled, this can probably be disabled now, but BungeeCord (not Waterfall) complains
|
||||
LocalSession.createDirectByteBufAllocator();
|
||||
}
|
||||
|
||||
|
|
|
@ -34,12 +34,13 @@ import org.cloudburstmc.nbt.NbtType;
|
|||
>>>>>>> d1febe0b3904d52cdc6301711950f22d1caf09b5
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.WritableBookItem;
|
||||
import net.minecraft.world.item.WrittenBookItem;
|
||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
|
@ -50,8 +51,10 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||
|
@ -133,7 +136,127 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|||
return Permissions.check(player, permission);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<com.github.steveice10.opennbt.tag.builtin.CompoundTag> future = new CompletableFuture<>();
|
||||
server.execute(() -> {
|
||||
ServerPlayer player = getPlayer(session);
|
||||
if (player == null) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
// Don't create a new block entity if invalid
|
||||
BlockEntity blockEntity = player.level.getChunkAt(pos).getBlockEntity(pos);
|
||||
if (blockEntity instanceof BannerBlockEntity banner) {
|
||||
// Potentially exposes other NBT data? But we need to get the NBT data for the banner patterns *and*
|
||||
// the banner might have a custom name, both of which a Java client knows and caches
|
||||
ItemStack itemStack = banner.getItem();
|
||||
var tag = OpenNbtTagVisitor.convert("", itemStack.getOrCreateTag());
|
||||
|
||||
future.complete(tag);
|
||||
return;
|
||||
}
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private ServerPlayer getPlayer(GeyserSession session) {
|
||||
return server.getPlayerList().getPlayer(session.getPlayerEntity().getUuid());
|
||||
}
|
||||
|
||||
// Future considerations: option to clone; would affect arrays
|
||||
private static class OpenNbtTagVisitor implements TagVisitor {
|
||||
private String currentKey;
|
||||
private final com.github.steveice10.opennbt.tag.builtin.CompoundTag root;
|
||||
private com.github.steveice10.opennbt.tag.builtin.Tag currentTag;
|
||||
|
||||
OpenNbtTagVisitor(String key) {
|
||||
root = new com.github.steveice10.opennbt.tag.builtin.CompoundTag(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitString(StringTag stringTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.StringTag(currentKey, stringTag.getAsString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByte(ByteTag byteTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteTag(currentKey, byteTag.getAsByte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitShort(ShortTag shortTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ShortTag(currentKey, shortTag.getAsShort());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInt(IntTag intTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntTag(currentKey, intTag.getAsInt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLong(LongTag longTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongTag(currentKey, longTag.getAsLong());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFloat(FloatTag floatTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.FloatTag(currentKey, floatTag.getAsFloat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitDouble(DoubleTag doubleTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.DoubleTag(currentKey, doubleTag.getAsDouble());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitByteArray(ByteArrayTag byteArrayTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.ByteArrayTag(currentKey, byteArrayTag.getAsByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitIntArray(IntArrayTag intArrayTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.IntArrayTag(currentKey, intArrayTag.getAsIntArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLongArray(LongArrayTag longArrayTag) {
|
||||
currentTag = new com.github.steveice10.opennbt.tag.builtin.LongArrayTag(currentKey, longArrayTag.getAsLongArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitList(ListTag listTag) {
|
||||
var newList = new com.github.steveice10.opennbt.tag.builtin.ListTag(currentKey);
|
||||
for (Tag tag : listTag) {
|
||||
currentKey = "";
|
||||
tag.accept(this);
|
||||
newList.add(currentTag);
|
||||
}
|
||||
currentTag = newList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCompound(CompoundTag compoundTag) {
|
||||
currentTag = convert(currentKey, compoundTag);
|
||||
}
|
||||
|
||||
private static com.github.steveice10.opennbt.tag.builtin.CompoundTag convert(String name, CompoundTag compoundTag) {
|
||||
OpenNbtTagVisitor visitor = new OpenNbtTagVisitor(name);
|
||||
for (String key : compoundTag.getAllKeys()) {
|
||||
visitor.currentKey = key;
|
||||
Tag tag = compoundTag.get(key);
|
||||
tag.accept(visitor);
|
||||
visitor.root.put(visitor.currentTag);
|
||||
}
|
||||
return visitor.root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd(EndTag endTag) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.spigot;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
||||
/**
|
||||
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
|
||||
* that won't be receiving the data over the network.
|
||||
*
|
||||
* As of 1.8 - 1.17.1, compression is enabled in the Netty pipeline by adding a listener after a packet is written.
|
||||
* If we simply "cancel" or don't forward the packet, then the listener is never called.
|
||||
*/
|
||||
public class GeyserSpigotCompressionDisabler extends ChannelOutboundHandlerAdapter {
|
||||
static final boolean ENABLED;
|
||||
|
||||
private static final Class<?> COMPRESSION_PACKET_CLASS;
|
||||
private static final Class<?> LOGIN_SUCCESS_PACKET_CLASS;
|
||||
private static final boolean PROTOCOL_SUPPORT_INSTALLED;
|
||||
|
||||
static {
|
||||
PROTOCOL_SUPPORT_INSTALLED = Bukkit.getPluginManager().getPlugin("ProtocolSupport") != null;
|
||||
|
||||
Class<?> compressionPacketClass = null;
|
||||
Class<?> loginSuccessPacketClass = null;
|
||||
boolean enabled = false;
|
||||
try {
|
||||
compressionPacketClass = findCompressionPacket();
|
||||
loginSuccessPacketClass = findLoginSuccessPacket();
|
||||
enabled = true;
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not initialize compression disabler!", e);
|
||||
}
|
||||
COMPRESSION_PACKET_CLASS = compressionPacketClass;
|
||||
LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass;
|
||||
ENABLED = enabled;
|
||||
}
|
||||
|
||||
public GeyserSpigotCompressionDisabler() {
|
||||
if (!ENABLED) {
|
||||
throw new RuntimeException("Geyser compression disabler cannot be initialized in its current state!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
Class<?> msgClass = msg.getClass();
|
||||
// Don't let any compression packet get through
|
||||
if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) {
|
||||
if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) {
|
||||
if (PROTOCOL_SUPPORT_INSTALLED) {
|
||||
// ProtocolSupport must send the compression packet, so let's remove what it did before it does damage
|
||||
if (ctx.pipeline().get("compress") != null) {
|
||||
ctx.pipeline().remove("compress");
|
||||
}
|
||||
if (ctx.pipeline().get("decompress") != null) {
|
||||
ctx.pipeline().remove("decompress");
|
||||
}
|
||||
}
|
||||
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
|
||||
ctx.channel().pipeline().remove(this);
|
||||
}
|
||||
super.write(ctx, msg, promise);
|
||||
} else if (PROTOCOL_SUPPORT_INSTALLED) {
|
||||
// We must indicate it "succeeded" or ProtocolSupport will time us out
|
||||
promise.setSuccess();
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> findCompressionPacket() throws ClassNotFoundException {
|
||||
try {
|
||||
return Class.forName("net.minecraft.network.protocol.login.PacketLoginOutSetCompression");
|
||||
} catch (ClassNotFoundException e) {
|
||||
String prefix = Bukkit.getServer().getClass().getPackage().getName().replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
return Class.forName(prefix + ".PacketLoginOutSetCompression");
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> findLoginSuccessPacket() throws ClassNotFoundException {
|
||||
try {
|
||||
return Class.forName("net.minecraft.network.protocol.login.PacketLoginOutSuccess");
|
||||
} catch (ClassNotFoundException e) {
|
||||
String prefix = Bukkit.getServer().getClass().getPackage().getName().replace("org.bukkit.craftbukkit", "net.minecraft.server");
|
||||
return Class.forName(prefix + ".PacketLoginOutSuccess");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -115,10 +115,14 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||
|
||||
ChannelFuture channelFuture = (new ServerBootstrap()
|
||||
.channel(LocalServerChannelWrapper.class)
|
||||
.childHandler(new ChannelInitializer<Channel>() {
|
||||
.childHandler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
initChannel.invoke(childHandler, ch);
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
||||
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
||||
}
|
||||
}
|
||||
})
|
||||
// Set to MAX_PRIORITY as MultithreadEventLoopGroup#newDefaultThreadFactory which DefaultEventLoopGroup implements does by default
|
||||
|
|
|
@ -195,6 +195,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
|
||||
geyserConfig.loadFloodgate(this);
|
||||
|
||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
if (!INITIALIZED) {
|
||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||
|
@ -206,9 +209,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||
}
|
||||
}, this);
|
||||
|
||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||
this.geyserCommandManager.init();
|
||||
|
||||
// Because Bukkit locks its command map upon startup, we need to
|
||||
// add our plugin commands in onEnable, but populating the executor
|
||||
// can happen at any time
|
||||
|
|
|
@ -25,21 +25,18 @@
|
|||
|
||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
<<<<<<< HEAD
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
=======
|
||||
>>>>>>> d1febe0b3904d52cdc6301711950f22d1caf09b5
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.Lectern;
|
||||
import org.bukkit.block.*;
|
||||
import org.bukkit.block.banner.Pattern;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.cloudburstmc.math.vector.Vector3i;
|
||||
import org.cloudburstmc.nbt.NbtMap;
|
||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||
import org.cloudburstmc.nbt.NbtType;
|
||||
|
@ -49,10 +46,14 @@ import org.geysermc.geyser.level.block.BlockStateValues;
|
|||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* The base world manager to use when there is no supported NMS revision
|
||||
|
@ -179,6 +180,46 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||
return Bukkit.getPlayer(session.getPlayerEntity().getUsername()).hasPermission(permission);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
|
||||
// Paper 1.19.3 complains about async access otherwise.
|
||||
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||
Player bukkitPlayer;
|
||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||
future.complete(null);
|
||||
return;
|
||||
}
|
||||
|
||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||
BlockState state = block.getState();
|
||||
if (state instanceof Banner banner) {
|
||||
ListTag list = new ListTag("Patterns");
|
||||
for (int i = 0; i < banner.numberOfPatterns(); i++) {
|
||||
Pattern pattern = banner.getPattern(i);
|
||||
list.add(BannerTranslator.getJavaPatternTag(pattern.getPattern().getIdentifier(), pattern.getColor().ordinal()));
|
||||
}
|
||||
|
||||
CompoundTag root = addToBlockEntityTag(list);
|
||||
|
||||
future.complete(root);
|
||||
return;
|
||||
}
|
||||
future.complete(null);
|
||||
});
|
||||
return future;
|
||||
}
|
||||
|
||||
private CompoundTag addToBlockEntityTag(Tag tag) {
|
||||
CompoundTag compoundTag = new CompoundTag("");
|
||||
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
||||
blockEntityTag.put(tag);
|
||||
compoundTag.put(blockEntityTag);
|
||||
return compoundTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be set to true if we are post-1.13 but before the latest version, and we should convert the old block state id
|
||||
* to the current one.
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.platform.velocity;
|
||||
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class GeyserVelocityCompressionDisabler extends ChannelDuplexHandler {
|
||||
static final boolean ENABLED;
|
||||
private static final Class<?> COMPRESSION_PACKET_CLASS;
|
||||
private static final Class<?> LOGIN_SUCCESS_PACKET_CLASS;
|
||||
private static final Object COMPRESSION_ENABLED_EVENT;
|
||||
private static final Method SET_COMPRESSION_METHOD;
|
||||
|
||||
static {
|
||||
boolean enabled = false;
|
||||
Class<?> compressionPacketClass = null;
|
||||
Class<?> loginSuccessPacketClass = null;
|
||||
Object compressionEnabledEvent = null;
|
||||
Method setCompressionMethod = null;
|
||||
|
||||
try {
|
||||
compressionPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.SetCompression");
|
||||
loginSuccessPacketClass = Class.forName("com.velocitypowered.proxy.protocol.packet.ServerLoginSuccess");
|
||||
compressionEnabledEvent = Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent")
|
||||
.getDeclaredField("COMPRESSION_ENABLED").get(null);
|
||||
setCompressionMethod = Class.forName("com.velocitypowered.proxy.connection.MinecraftConnection")
|
||||
.getMethod("setCompressionThreshold", int.class);
|
||||
enabled = true;
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Could not initialize compression disabler!", e);
|
||||
}
|
||||
|
||||
ENABLED = enabled;
|
||||
COMPRESSION_PACKET_CLASS = compressionPacketClass;
|
||||
LOGIN_SUCCESS_PACKET_CLASS = loginSuccessPacketClass;
|
||||
COMPRESSION_ENABLED_EVENT = compressionEnabledEvent;
|
||||
SET_COMPRESSION_METHOD = setCompressionMethod;
|
||||
}
|
||||
|
||||
public GeyserVelocityCompressionDisabler() {
|
||||
if (!ENABLED) {
|
||||
throw new RuntimeException("Geyser compression disabler cannot be initialized in its current state!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
Class<?> msgClass = msg.getClass();
|
||||
if (!COMPRESSION_PACKET_CLASS.isAssignableFrom(msgClass)) {
|
||||
if (LOGIN_SUCCESS_PACKET_CLASS.isAssignableFrom(msgClass)) {
|
||||
// We're past the point that compression can be enabled
|
||||
|
||||
ctx.pipeline().remove(this);
|
||||
}
|
||||
super.write(ctx, msg, promise);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
|
||||
if (evt != COMPRESSION_ENABLED_EVENT) {
|
||||
super.userEventTriggered(ctx, evt);
|
||||
return;
|
||||
}
|
||||
|
||||
// Invoke the method as it calls a Netty event and handles removing cleaner than we could
|
||||
Object minecraftConnection = ctx.pipeline().get("handler");
|
||||
SET_COMPRESSION_METHOD.invoke(minecraftConnection, -1);
|
||||
// Do not call super and let the new compression enabled event continue firing
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ import org.geysermc.geyser.network.netty.GeyserInjector;
|
|||
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class GeyserVelocityInjector extends GeyserInjector {
|
||||
|
@ -67,9 +68,23 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
|||
workerGroupField.setAccessible(true);
|
||||
EventLoopGroup workerGroup = (EventLoopGroup) workerGroupField.get(connectionManager);
|
||||
|
||||
// This method is what initializes the connection in Java Edition, after Netty is all set.
|
||||
Method initChannel = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||
initChannel.setAccessible(true);
|
||||
|
||||
ChannelFuture channelFuture = (new ServerBootstrap()
|
||||
.channel(LocalServerChannelWrapper.class)
|
||||
.childHandler(channelInitializer)
|
||||
.childHandler(new ChannelInitializer<>() {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
initChannel.invoke(channelInitializer, ch);
|
||||
|
||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
|
||||
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
|
||||
new GeyserVelocityCompressionDisabler());
|
||||
}
|
||||
}
|
||||
})
|
||||
.group(bossGroup, workerGroup) // Cannot be DefaultEventLoopGroup
|
||||
.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, serverWriteMark) // Required or else rare network freezes can occur
|
||||
.localAddress(LocalAddress.ANY))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue