Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-26 08:10:11 +01:00
Implement update notifications for Geyser
Geyser installations will now get notified when a new Bedrock release is out and Geyser must be updated. The system works similarly to ViaVersion where OPs get a notification of an update when they join. The permission node for players to see update notifications is `geyser.update` and the backing JSON that controls this can be found at https://github.com/GeyserMC/GeyserSite/blob/gh-pages/versions.json. There is also a config option to disable update checking. This update also fixes modern Paper installations not being able to see colored text logged from Geyser in the console.
Dieser Commit ist enthalten in:
Ursprung
a3b1cf61ad
Commit
67a65c45d3
@ -24,6 +24,12 @@
|
||||
<version>a7c6ede</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-bungeecord</artifactId>
|
||||
<version>${adventure-platform.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${outputName}-BungeeCord</finalName>
|
||||
|
@ -149,6 +149,8 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||
}
|
||||
|
||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(geyser));
|
||||
|
||||
this.getProxy().getPluginManager().registerListener(this, new GeyserBungeeUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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 net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.event.EventHandler;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSender;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserBungeeUpdateListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(final PostLoginEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final ProxiedPlayer player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSender(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -25,11 +25,15 @@
|
||||
|
||||
package org.geysermc.geyser.platform.bungeecord.command;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class BungeeCommandSender implements CommandSender {
|
||||
|
||||
private final net.md_5.bungee.api.CommandSender handle;
|
||||
@ -50,6 +54,18 @@ public class BungeeCommandSender implements CommandSender {
|
||||
handle.sendMessage(TextComponent.fromLegacyText(message));
|
||||
}
|
||||
|
||||
private static final int PROTOCOL_HEX_COLOR = 713; // Added 20w17a
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
if (handle instanceof ProxiedPlayer player && player.getPendingConnection().getVersion() >= PROTOCOL_HEX_COLOR) {
|
||||
// Include hex colors
|
||||
handle.sendMessage(BungeeComponentSerializer.get().serialize(message));
|
||||
return;
|
||||
}
|
||||
handle.sendMessage(BungeeComponentSerializer.legacy().serialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return !(handle instanceof ProxiedPlayer);
|
||||
@ -58,8 +74,11 @@ public class BungeeCommandSender implements CommandSender {
|
||||
@Override
|
||||
public String getLocale() {
|
||||
if (handle instanceof ProxiedPlayer player) {
|
||||
String locale = player.getLocale().getLanguage() + "_" + player.getLocale().getCountry();
|
||||
return GeyserLocale.formatLocale(locale);
|
||||
Locale locale = player.getLocale();
|
||||
if (locale != null) {
|
||||
// Locale can be null early on in the conneciton
|
||||
return GeyserLocale.formatLocale(locale.getLanguage() + "_" + locale.getCountry());
|
||||
}
|
||||
}
|
||||
return GeyserLocale.getDefaultLocale();
|
||||
}
|
||||
|
@ -11,6 +11,10 @@
|
||||
<artifactId>bootstrap-parent</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<adventure-platform.version>4.1.2</adventure-platform.version>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spigot-public</id>
|
||||
|
@ -59,7 +59,13 @@
|
||||
<dependency>
|
||||
<groupId>me.lucko</groupId>
|
||||
<artifactId>commodore</artifactId>
|
||||
<version>1.13</version>
|
||||
<version>2.2</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-text-serializer-bungeecord</artifactId>
|
||||
<version>${adventure-platform.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@ -107,6 +113,9 @@
|
||||
<relocation>
|
||||
<pattern>net.kyori</pattern>
|
||||
<shadedPattern>org.geysermc.geyser.platform.spigot.shaded.kyori</shadedPattern>
|
||||
<excludes>
|
||||
<exclude>net.kyori.adventure.text.logger.slf4j.ComponentLogger</exclude>
|
||||
</excludes>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>org.objectweb.asm</pattern>
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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 net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.logger.slf4j.ComponentLogger;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public final class GeyserPaperLogger extends GeyserSpigotLogger {
|
||||
private final ComponentLogger componentLogger;
|
||||
|
||||
public GeyserPaperLogger(Plugin plugin, Logger logger, boolean debug) {
|
||||
super(logger, debug);
|
||||
componentLogger = plugin.getComponentLogger();
|
||||
}
|
||||
|
||||
/**
|
||||
* Since 1.18.2 this is required so legacy format symbols don't show up in the console for colors
|
||||
*/
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
// Done like this so the native component object field isn't relocated
|
||||
componentLogger.info("{}", PaperAdventure.toNativeComponent(message));
|
||||
}
|
||||
|
||||
static boolean supported() {
|
||||
try {
|
||||
Plugin.class.getMethod("getComponentLogger");
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -123,6 +123,22 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.chat.ComponentSerializer");
|
||||
} catch (ClassNotFoundException e) {
|
||||
if (!PaperAdventure.canSendMessageUsingComponent()) { // Prepare for Paper eventually removing Bungee chat
|
||||
getLogger().severe("*********************************************");
|
||||
getLogger().severe("");
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.header", getServer().getName()));
|
||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||
getLogger().severe("");
|
||||
getLogger().severe("*********************************************");
|
||||
|
||||
Bukkit.getPluginManager().disablePlugin(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// By default this should be localhost but may need to be changed in some circumstances
|
||||
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
|
||||
geyserConfig.setAutoconfiguredRemote(true);
|
||||
@ -137,7 +153,8 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
geyserConfig.getBedrock().setPort(Bukkit.getPort());
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
||||
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||
|
||||
// Remove this in like a year
|
||||
@ -266,12 +283,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||
GeyserLocale.getLocaleStringLog(command.getDescription()),
|
||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||
}
|
||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||
|
||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||
|
||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||
}
|
||||
|
||||
boolean brigadierSupported = CommodoreProvider.isSupported();
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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 org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.platform.spigot.command.SpigotCommandSender;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserSpigotUpdateListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSender(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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 com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Utility class for converting our shaded Adventure into the Adventure bundled in Paper.
|
||||
*
|
||||
* Code mostly taken from https://github.com/KyoriPowered/adventure-platform/blob/94d5821f2e755170f42bd8a5fe1d5bf6f66d04ad/platform-bukkit/src/main/java/net/kyori/adventure/platform/bukkit/PaperFacet.java#L46
|
||||
* and the MinecraftReflection class.
|
||||
*/
|
||||
public final class PaperAdventure {
|
||||
private static final MethodHandle NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND;
|
||||
private static final Method SEND_MESSAGE_COMPONENT;
|
||||
|
||||
static {
|
||||
final MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
MethodHandle nativeGsonComponentSerializerDeserializeMethodBound = null;
|
||||
|
||||
// String.join because otherwise the class name will be relocated
|
||||
final Class<?> nativeGsonComponentSerializerClass = findClass(String.join(".",
|
||||
"net", "kyori", "adventure", "text", "serializer", "gson", "GsonComponentSerializer"));
|
||||
final Class<?> nativeGsonComponentSerializerImplClass = findClass(String.join(".",
|
||||
"net", "kyori", "adventure", "text", "serializer", "gson", "GsonComponentSerializerImpl"));
|
||||
if (nativeGsonComponentSerializerClass != null && nativeGsonComponentSerializerImplClass != null) {
|
||||
MethodHandle nativeGsonComponentSerializerGsonGetter = null;
|
||||
try {
|
||||
nativeGsonComponentSerializerGsonGetter = lookup.findStatic(nativeGsonComponentSerializerClass,
|
||||
"gson", MethodType.methodType(nativeGsonComponentSerializerClass));
|
||||
} catch (final NoSuchMethodException | IllegalAccessException ignored) {
|
||||
}
|
||||
|
||||
MethodHandle nativeGsonComponentSerializerDeserializeMethod = null;
|
||||
try {
|
||||
final Method method = nativeGsonComponentSerializerImplClass.getDeclaredMethod("deserialize", String.class);
|
||||
method.setAccessible(true);
|
||||
nativeGsonComponentSerializerDeserializeMethod = lookup.unreflect(method);
|
||||
} catch (final NoSuchMethodException | IllegalAccessException ignored) {
|
||||
}
|
||||
|
||||
if (nativeGsonComponentSerializerGsonGetter != null) {
|
||||
if (nativeGsonComponentSerializerDeserializeMethod != null) {
|
||||
try {
|
||||
nativeGsonComponentSerializerDeserializeMethodBound = nativeGsonComponentSerializerDeserializeMethod
|
||||
.bindTo(nativeGsonComponentSerializerGsonGetter.invoke());
|
||||
} catch (final Throwable throwable) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to access native GsonComponentSerializer", throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND = nativeGsonComponentSerializerDeserializeMethodBound;
|
||||
|
||||
Method playerComponentSendMessage = null;
|
||||
final Class<?> nativeComponentClass = findClass(String.join(".",
|
||||
"net", "kyori", "adventure", "text", "Component"));
|
||||
if (nativeComponentClass != null) {
|
||||
try {
|
||||
playerComponentSendMessage = CommandSender.class.getMethod("sendMessage", nativeComponentClass);
|
||||
} catch (final NoSuchMethodException e) {
|
||||
if (GeyserImpl.getInstance().getLogger().isDebug()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
SEND_MESSAGE_COMPONENT = playerComponentSendMessage;
|
||||
}
|
||||
|
||||
public static Object toNativeComponent(final Component component) {
|
||||
if (NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND == null) {
|
||||
GeyserImpl.getInstance().getLogger().error("Illegal state where Component serialization was called when it wasn't available!");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return NATIVE_GSON_COMPONENT_SERIALIZER_DESERIALIZE_METHOD_BOUND.invoke(DefaultComponentSerializer.get().serialize(component));
|
||||
} catch (final Throwable throwable) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to create native Component message", throwable);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMessage(final CommandSender sender, final Component component) {
|
||||
if (SEND_MESSAGE_COMPONENT == null) {
|
||||
GeyserImpl.getInstance().getLogger().error("Illegal state where Component sendMessage was called when it wasn't available!");
|
||||
return;
|
||||
}
|
||||
|
||||
final Object nativeComponent = toNativeComponent(component);
|
||||
if (nativeComponent != null) {
|
||||
try {
|
||||
SEND_MESSAGE_COMPONENT.invoke(sender, nativeComponent);
|
||||
} catch (final InvocationTargetException | IllegalAccessException e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Failed to send native Component message", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canSendMessageUsingComponent() {
|
||||
return SEND_MESSAGE_COMPONENT != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a class by the first name available.
|
||||
*
|
||||
* @return a class or {@code null} if not found
|
||||
*/
|
||||
private static @Nullable Class<?> findClass(final String className) {
|
||||
try {
|
||||
return Class.forName(className);
|
||||
} catch (final ClassNotFoundException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private PaperAdventure() {
|
||||
}
|
||||
}
|
@ -25,10 +25,13 @@
|
||||
|
||||
package org.geysermc.geyser.platform.spigot.command;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.bungeecord.BungeeComponentSerializer;
|
||||
import org.bukkit.command.ConsoleCommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
import org.geysermc.geyser.platform.spigot.PaperAdventure;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -63,6 +66,16 @@ public class SpigotCommandSender implements CommandSender {
|
||||
handle.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
if (PaperAdventure.canSendMessageUsingComponent()) {
|
||||
PaperAdventure.sendMessage(handle, message);
|
||||
return;
|
||||
}
|
||||
|
||||
handle.sendMessage(BungeeComponentSerializer.get().serialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return handle instanceof ConsoleCommandSender;
|
||||
|
@ -38,14 +38,14 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.BookMeta;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.geysermc.geyser.network.MinecraftProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.level.GameRule;
|
||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||
import org.geysermc.geyser.network.MinecraftProtocol;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||
import org.geysermc.geyser.level.GameRule;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -31,11 +31,10 @@ import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
|
||||
@Log4j2
|
||||
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, CommandSender {
|
||||
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger {
|
||||
|
||||
@Override
|
||||
protected boolean isRunning() {
|
||||
@ -95,24 +94,4 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
||||
public boolean isDebug() {
|
||||
return log.isDebugEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "CONSOLE";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String message) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +161,8 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||
} else {
|
||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||
}
|
||||
|
||||
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2022 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 com.velocitypowered.api.event.Subscribe;
|
||||
import com.velocitypowered.api.event.connection.PostLoginEvent;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSender;
|
||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||
|
||||
public final class GeyserVelocityUpdateListener {
|
||||
|
||||
@Subscribe
|
||||
public void onPlayerJoin(PostLoginEvent event) {
|
||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
||||
final Player player = event.getPlayer();
|
||||
if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSender(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ package org.geysermc.geyser.platform.velocity.command;
|
||||
import com.velocitypowered.api.command.CommandSource;
|
||||
import com.velocitypowered.api.proxy.ConsoleCommandSource;
|
||||
import com.velocitypowered.api.proxy.Player;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
@ -59,6 +60,12 @@ public class VelocityCommandSender implements CommandSender {
|
||||
handle.sendMessage(LegacyComponentSerializer.legacy('§').deserialize(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(Component message) {
|
||||
// Be careful that we don't shade in Adventure!!
|
||||
handle.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConsole() {
|
||||
return handle instanceof ConsoleCommandSource;
|
||||
|
@ -37,6 +37,9 @@ public final class Constants {
|
||||
|
||||
public static final String FLOODGATE_DOWNLOAD_LOCATION = "https://ci.opencollab.dev/job/GeyserMC/job/Floodgate/job/master/";
|
||||
|
||||
public static final String GEYSER_DOWNLOAD_LOCATION = "https://ci.geysermc.org";
|
||||
public static final String UPDATE_PERMISSION = "geyser.update";
|
||||
|
||||
static final String SAVED_REFRESH_TOKEN_FILE = "saved-refresh-tokens.json";
|
||||
|
||||
static {
|
||||
|
@ -41,6 +41,8 @@ import io.netty.util.internal.SystemPropertyUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.geysermc.api.Geyser;
|
||||
@ -66,7 +68,6 @@ import org.geysermc.geyser.session.SessionManager;
|
||||
import org.geysermc.geyser.session.auth.AuthType;
|
||||
import org.geysermc.geyser.skin.FloodgateSkinUploader;
|
||||
import org.geysermc.geyser.skin.SkinProvider;
|
||||
import org.geysermc.geyser.text.ChatColor;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
import org.geysermc.geyser.text.MinecraftLocale;
|
||||
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
||||
@ -303,8 +304,8 @@ public class GeyserImpl implements GeyserApi {
|
||||
int port = config.getBedrock().getPort();
|
||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port)));
|
||||
if (!"0.0.0.0".equals(address)) {
|
||||
logger.info(ChatColor.GREEN + "Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0");
|
||||
logger.info(ChatColor.GREEN + "Then, restart this server.");
|
||||
logger.info(Component.text("Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0", NamedTextColor.GREEN));
|
||||
logger.info(Component.text("Then, restart this server.", NamedTextColor.GREEN));
|
||||
}
|
||||
}
|
||||
}).join();
|
||||
@ -454,6 +455,9 @@ public class GeyserImpl implements GeyserApi {
|
||||
}
|
||||
|
||||
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
|
||||
if (config.isNotifyOnNewBedrockUpdate()) {
|
||||
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,9 +25,12 @@
|
||||
|
||||
package org.geysermc.geyser;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface GeyserLogger {
|
||||
public interface GeyserLogger extends CommandSender {
|
||||
|
||||
/**
|
||||
* Logs a severe message to console
|
||||
@ -73,6 +76,15 @@ public interface GeyserLogger {
|
||||
*/
|
||||
void info(String message);
|
||||
|
||||
/**
|
||||
* Logs an info component to console
|
||||
*
|
||||
* @param message the message to log
|
||||
*/
|
||||
default void info(Component message) {
|
||||
sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a debug message to console
|
||||
*
|
||||
@ -100,4 +112,24 @@ public interface GeyserLogger {
|
||||
* If debug is enabled for this logger
|
||||
*/
|
||||
boolean isDebug();
|
||||
|
||||
@Override
|
||||
default String name() {
|
||||
return "CONSOLE";
|
||||
}
|
||||
|
||||
@Override
|
||||
default void sendMessage(String message) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean isConsole() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean hasPermission(String permission) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.command;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
/**
|
||||
@ -43,6 +45,10 @@ public interface CommandSender {
|
||||
|
||||
void sendMessage(String message);
|
||||
|
||||
default void sendMessage(Component message) {
|
||||
sendMessage(LegacyComponentSerializer.legacySection().serialize(message));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the specified sender is from the console.
|
||||
*/
|
||||
|
@ -105,6 +105,8 @@ public interface GeyserConfiguration {
|
||||
|
||||
int getCustomSkullRenderDistance();
|
||||
|
||||
boolean isNotifyOnNewBedrockUpdate();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
||||
int getPendingAuthenticationTimeout();
|
||||
|
@ -148,6 +148,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
||||
@JsonProperty("xbox-achievements-enabled")
|
||||
private boolean xboxAchievementsEnabled = false;
|
||||
|
||||
@JsonProperty("notify-on-new-bedrock-update")
|
||||
private boolean notifyOnNewBedrockUpdate = true;
|
||||
|
||||
private MetricsInfo metrics = new MetricsInfo();
|
||||
|
||||
@JsonProperty("pending-authentication-timeout")
|
||||
|
@ -31,6 +31,7 @@ import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.geyser.registry.BlockRegistries;
|
||||
import org.geysermc.geyser.scoreboard.Scoreboard;
|
||||
import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
@ -172,6 +173,7 @@ public final class WorldCache {
|
||||
if (serverVerifiedState.sequence <= sequence) {
|
||||
// This block may be out of sync with the server
|
||||
// In 1.19.0 Java, you can verify this by trying to mine in spawn protection
|
||||
System.out.println("Resetting " + entry.getKey() + " to " + BlockRegistries.JAVA_BLOCKS.get(serverVerifiedState.blockState).getJavaIdentifier());
|
||||
ChunkUtils.updateBlockClientSide(session, serverVerifiedState.blockState, entry.getKey());
|
||||
it.remove();
|
||||
}
|
||||
|
@ -25,10 +25,22 @@
|
||||
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.TextReplacementConfig;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.geysermc.geyser.Constants;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.command.CommandSender;
|
||||
import org.geysermc.geyser.network.MinecraftProtocol;
|
||||
import org.geysermc.geyser.text.GeyserLocale;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class VersionCheckUtils {
|
||||
|
||||
public static void checkForOutdatedFloodgate(GeyserLogger logger) {
|
||||
@ -42,6 +54,41 @@ public final class VersionCheckUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkForGeyserUpdate(Supplier<CommandSender> recipient) {
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
JsonNode json = WebUtils.getJson("https://api.geysermc.org/v2/versions/geyser");
|
||||
JsonNode bedrock = json.get("bedrock").get("protocol");
|
||||
int protocolVersion = bedrock.get("id").asInt();
|
||||
if (MinecraftProtocol.getBedrockCodec(protocolVersion) != null) {
|
||||
// We support the latest version! No need to print a message.
|
||||
return;
|
||||
}
|
||||
|
||||
final String newBedrockVersion = bedrock.get("name").asText();
|
||||
|
||||
// Delayed for two reasons: save unnecessary processing, and wait to load locale if this is on join.
|
||||
CommandSender sender = recipient.get();
|
||||
|
||||
// Overarching component is green - geyser.version.new component cannot be green or else the link blue is overshadowed
|
||||
Component message = Component.text().color(NamedTextColor.GREEN)
|
||||
.append(Component.text(GeyserLocale.getPlayerLocaleString("geyser.version.new", sender.getLocale(), newBedrockVersion))
|
||||
.replaceText(TextReplacementConfig.builder()
|
||||
.match("\\{1\\}") // Replace "Download here: {1}" so we can use fancy text component yesyes
|
||||
.replacement(Component.text()
|
||||
.content(Constants.GEYSER_DOWNLOAD_LOCATION)
|
||||
.color(NamedTextColor.BLUE)
|
||||
.decoration(TextDecoration.UNDERLINED, TextDecoration.State.TRUE)
|
||||
.clickEvent(ClickEvent.openUrl(Constants.GEYSER_DOWNLOAD_LOCATION)))
|
||||
.build()))
|
||||
.build();
|
||||
sender.sendMessage(message);
|
||||
} catch (Exception e) {
|
||||
GeyserImpl.getInstance().getLogger().error("Error whilst checking for Geyser update!", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private VersionCheckUtils() {
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ public class WebUtils {
|
||||
public static JsonNode getJson(String reqURL) throws IOException {
|
||||
HttpURLConnection con = (HttpURLConnection) new URL(reqURL).openConnection();
|
||||
con.setRequestProperty("User-Agent", "Geyser-" + GeyserImpl.getInstance().getPlatformType().toString() + "/" + GeyserImpl.VERSION);
|
||||
con.setConnectTimeout(10000);
|
||||
con.setReadTimeout(10000);
|
||||
return GeyserImpl.JSON_MAPPER.readTree(con.getInputStream());
|
||||
}
|
||||
|
||||
|
@ -175,6 +175,11 @@ force-resource-packs: true
|
||||
# THIS DISABLES ALL COMMANDS FROM SUCCESSFULLY RUNNING FOR BEDROCK IN-GAME, as otherwise Bedrock thinks you are cheating.
|
||||
xbox-achievements-enabled: false
|
||||
|
||||
# Whether to alert the console and operators that a new Geyser version is available that supports a Bedrock version
|
||||
# that this Geyser version does not support. It's recommended to keep this option enabled, as many Bedrock platforms
|
||||
# auto-update.
|
||||
notify-on-new-bedrock-update: true
|
||||
|
||||
# bStats is a stat tracker that is entirely anonymous and tracks only basic information
|
||||
# about Geyser, such as how many people are online, how many servers are using Geyser,
|
||||
# what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren