3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-10-08 10:50:11 +02:00

New config used in core

Dieser Commit ist enthalten in:
Camotoy 2024-07-06 19:16:08 -04:00
Ursprung db9b951352
Commit 29f8e294ad
Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
GPG-Schlüssel-ID: 7EEFB66FE798081F
66 geänderte Dateien mit 758 neuen und 1045 gelöschten Zeilen

Datei anzeigen

@ -108,7 +108,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
listenerInfo.isPingPassthrough(), listenerInfo.isPingPassthrough(),
listenerInfo.getQueryPort(), listenerInfo.getQueryPort(),
listenerInfo.isQueryEnabled(), listenerInfo.isQueryEnabled(),
bootstrap.getGeyserConfig().getRemote().isUseProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end bootstrap.config().java().useProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end
); );
// The field that stores all listeners in BungeeCord // The field that stores all listeners in BungeeCord
@ -142,7 +142,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
} }
initChannel.invoke(channelInitializer, ch); initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression()) { if (bootstrap.config().asPluginConfig().orElseThrow().useDirectConnection()) {
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler", ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
new GeyserBungeeCompressionDisabler()); new GeyserBungeeCompressionDisabler());
} }

Datei anzeigen

@ -38,7 +38,7 @@ public final class GeyserBungeeUpdateListener implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(final PostLoginEvent event) { public void onPlayerJoin(final PostLoginEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
final ProxiedPlayer player = event.getPlayer(); final ProxiedPlayer player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) { if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player)); VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));

Datei anzeigen

@ -96,7 +96,7 @@ public class GeyserModInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder"); int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config"; String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression()) { if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression()) {
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler()); ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
} }
} }
@ -125,7 +125,7 @@ public class GeyserModInjector extends GeyserInjector {
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler); childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
break; break;
} catch (Exception e) { } catch (Exception e) {
if (bootstrap.getGeyserConfig().isDebugMode()) { if (bootstrap.config().debugMode()) {
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!"); bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
e.printStackTrace(); e.printStackTrace();
} }

Datei anzeigen

@ -56,7 +56,6 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
// If the LAN is opened, starts Geyser. // If the LAN is opened, starts Geyser.
GeyserModBootstrap instance = GeyserModBootstrap.getInstance(); GeyserModBootstrap instance = GeyserModBootstrap.getInstance();
instance.setServer((MinecraftServer) (Object) this); instance.setServer((MinecraftServer) (Object) this);
instance.getGeyserConfig().getRemote().setPort(port);
instance.onGeyserEnable(); instance.onGeyserEnable();
// Ensure player locale has been loaded, in case it's different from Java system language // Ensure player locale has been loaded, in case it's different from Java system language
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode); GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);

Datei anzeigen

@ -122,7 +122,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder"); int index = ch.pipeline().names().indexOf("encoder");
String baseName = index != -1 ? "encoder" : "outbound_config"; String baseName = index != -1 ? "encoder" : "outbound_config";
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) { if (bootstrap.config().asPluginConfig().orElseThrow().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler()); ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
} }
} }
@ -157,7 +157,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
} }
break; break;
} catch (Exception e) { } catch (Exception e) {
if (bootstrap.getGeyserConfig().isDebugMode()) { if (bootstrap.config().debugMode()) {
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!"); bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
e.printStackTrace(); e.printStackTrace();
} }
@ -176,8 +176,8 @@ public class GeyserSpigotInjector extends GeyserInjector {
*/ */
private void workAroundWeirdBug(GeyserBootstrap bootstrap) { private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
MinecraftProtocol protocol = new MinecraftProtocol(); MinecraftProtocol protocol = new MinecraftProtocol();
LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(), LocalSession session = new LocalSession(bootstrap.config().java().address(),
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress, bootstrap.config().java().port(), this.serverSocketAddress,
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper()); InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
session.connect(); session.connect();
session.disconnect(""); session.disconnect("");

Datei anzeigen

@ -487,7 +487,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this); (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class); this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
ConfigLoaderTemp.load(GeyserPluginConfig.class); ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class);
} catch (IOException ex) { } catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace(); ex.printStackTrace();

Datei anzeigen

@ -38,7 +38,7 @@ public final class GeyserSpigotUpdateListener implements Listener {
@EventHandler @EventHandler
public void onPlayerJoin(final PlayerJoinEvent event) { public void onPlayerJoin(final PlayerJoinEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) { if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player)); VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));

Datei anzeigen

@ -76,7 +76,7 @@ public final class GeyserPaperCommandListener implements Listener {
return false; return false;
} }
if (GeyserImpl.getInstance().getConfig().isUseDirectConnection()) { if (GeyserImpl.getInstance().config().asPluginConfig().orElseThrow().useDirectConnection()) {
InetSocketAddress address = player.getAddress(); InetSocketAddress address = player.getAddress();
if (address != null) { if (address != null) {
return address.getPort() != 0; return address.getPort() != 0;

Datei anzeigen

@ -25,11 +25,6 @@
package org.geysermc.geyser.platform.standalone; package org.geysermc.geyser.platform.standalone;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import io.netty.util.ResourceLeakDetector; import io.netty.util.ResourceLeakDetector;
import lombok.Getter; import lombok.Getter;
import net.minecrell.terminalconsole.TerminalConsoleAppender; import net.minecrell.terminalconsole.TerminalConsoleAppender;
@ -44,16 +39,17 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.ConfigLoaderTemp; import org.geysermc.geyser.configuration.ConfigLoaderTemp;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import org.geysermc.geyser.configuration.GeyserRemoteConfig; import org.geysermc.geyser.configuration.GeyserRemoteConfig;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough; import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough; import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI; import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
import org.geysermc.geyser.util.LoopbackUtil; import org.geysermc.geyser.util.LoopbackUtil;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.NodePath;
import org.spongepowered.configurate.serialize.SerializationException;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -61,13 +57,13 @@ import java.lang.reflect.Method;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.HashMap;
import java.util.stream.Collectors; import java.util.Map;
public class GeyserStandaloneBootstrap implements GeyserBootstrap { public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager; private GeyserCommandManager geyserCommandManager;
private GeyserStandaloneConfiguration geyserConfig; private GeyserConfig geyserConfig;
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger(); private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
private IGeyserPingPassthrough geyserPingPassthrough; private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserStandaloneGUI gui; private GeyserStandaloneGUI gui;
@ -78,15 +74,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserImpl geyser; private GeyserImpl geyser;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Map<NodePath, String> argsConfigKeys = new HashMap<>();
private static final Map<String, String> argsConfigKeys = new HashMap<>();
public static void main(String[] args) { public static void main(String[] args) {
if (System.getProperty("io.netty.leakDetection.level") == null) { if (System.getProperty("io.netty.leakDetection.level") == null) {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance
} }
System.setProperty("bstats.relocatecheck", "false");
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap(); GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
// Set defaults // Set defaults
boolean useGuiOpts = bootstrap.useGui; boolean useGuiOpts = bootstrap.useGui;
@ -94,8 +90,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
GeyserLocale.init(bootstrap); GeyserLocale.init(bootstrap);
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null // By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
// Optionally, you can force the use of a GUI or no GUI by specifying args // Optionally, you can force the use of a GUI or no GUI by specifying args
@ -127,34 +121,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
// Split the argument by an = // Split the argument by an =
String[] argParts = arg.substring(2).split("="); String[] argParts = arg.substring(2).split("=");
if (argParts.length == 2) { if (argParts.length == 2) {
// Split the config key by . to allow for nested options argsConfigKeys.put(NodePath.of(argParts[0].split("\\.")), argParts[1]);
String[] configKeyParts = argParts[0].split("\\."); break;
// Loop the possible config options to check the passed key is valid
boolean found = false;
for (BeanPropertyDefinition property : availableProperties) {
if (configKeyParts[0].equals(property.getName())) {
if (configKeyParts.length > 1) {
// Loop sub-section options to check the passed key is valid
for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
if (configKeyParts[1].equals(subProperty.getName())) {
found = true;
break;
}
}
} else {
found = true;
}
break;
}
}
// Add the found key to the stored list for later usage
if (found) {
argsConfigKeys.put(argParts[0], argParts[1]);
break;
}
} }
} }
System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg)); System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
@ -192,18 +160,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@Override @Override
public void onGeyserEnable() { public void onGeyserEnable() {
try { try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml", geyserConfig = ConfigLoaderTemp.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions);
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
ConfigLoaderTemp.load(GeyserRemoteConfig.class);
handleArgsConfigOptions();
if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) {
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
geyserConfig.getRemote().setAddress("127.0.0.1");
}
} catch (IOException ex) { } catch (IOException ex) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex); geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
if (gui == null) { if (gui == null) {
@ -213,11 +170,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return; return;
} }
} }
geyserLogger.setDebug(geyserConfig.isDebugMode()); geyserLogger.setDebug(geyserConfig.debugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
// Allow libraries like Protocol to have their debug information passthrough // Allow libraries like Protocol to have their debug information passthrough
log4jLogger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO); log4jLogger.get().setLevel(geyserConfig.debugMode() ? Level.DEBUG : Level.INFO);
geyser = GeyserImpl.load(PlatformType.STANDALONE, this); geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
@ -266,8 +222,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
} }
@Override @Override
public GeyserConfiguration getGeyserConfig() { public GeyserConfig config() {
return geyserConfig; return this.geyserConfig;
} }
@Override @Override
@ -318,100 +274,47 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return false; return false;
} }
/** @Override
* Get the {@link BeanPropertyDefinition}s for the given class public Path getFloodgateKeyPath() {
* return Path.of(geyserConfig.floodgateKeyFile());
* @param clazz The class to get the definitions for
* @return A list of {@link BeanPropertyDefinition} for the given class
*/
public static List<BeanPropertyDefinition> getPOJOForClass(Class<?> clazz) {
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
// Introspect the given type
BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
// Find properties
List<BeanPropertyDefinition> properties = beanDescription.findProperties();
// Get the ignored properties
Set<String> ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
.findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig() ,beanDescription.getClassInfo()).getIgnored();
// Filter properties removing the ignored ones
return properties.stream()
.filter(property -> !ignoredProperties.contains(property.getName()))
.collect(Collectors.toList());
} }
/** /**
* Set a POJO property value on an object * Set a POJO property value on an object
* *
* @param property The {@link BeanPropertyDefinition} to set
* @param parentObject The object to alter
* @param value The new value of the property * @param value The new value of the property
*/ */
@SuppressWarnings({"unchecked", "rawtypes"}) // Required for enum usage private static void setConfigOption(CommentedConfigurationNode node, Object value) throws SerializationException {
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
Object parsedValue = value; Object parsedValue = value;
// Change the values type if needed // Change the values type if needed
if (int.class.equals(property.getRawPrimaryType())) { Class<?> clazz = node.raw().getClass();
if (Integer.class == clazz) {
parsedValue = Integer.valueOf((String) parsedValue); parsedValue = Integer.valueOf((String) parsedValue);
} else if (boolean.class.equals(property.getRawPrimaryType())) { } else if (Boolean.class == clazz) {
parsedValue = Boolean.valueOf((String) parsedValue); parsedValue = Boolean.valueOf((String) parsedValue);
} else if (Enum.class.isAssignableFrom(property.getRawPrimaryType())) {
parsedValue = Enum.valueOf((Class<? extends Enum>) property.getRawPrimaryType(), ((String) parsedValue).toUpperCase(Locale.ROOT));
} }
// Force the value to be set node.set(parsedValue);
AnnotatedField field = property.getField();
field.fixAccess(true);
field.setValue(parentObject, parsedValue);
} }
/** /**
* Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments * Update the loaded config with any values passed in the command line arguments
*/ */
private void handleArgsConfigOptions() { private void handleArgsConfigOptions(CommentedConfigurationNode node) {
// Get the available properties from the class for (Map.Entry<NodePath, String> configKey : argsConfigKeys.entrySet()) {
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class); NodePath path = configKey.getKey();
CommentedConfigurationNode subNode = node.node(path);
if (subNode.virtual()) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", path));
continue;
}
for (Map.Entry<String, String> configKey : argsConfigKeys.entrySet()) { try {
String[] configKeyParts = configKey.getKey().split("\\."); setConfigOption(subNode, configKey.getValue());
geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
// Loop over the properties looking for any matches against the stored one from the argument } catch (SerializationException e) {
for (BeanPropertyDefinition property : availableProperties) { geyserLogger.error("Failed to set config option: " + path);
if (configKeyParts[0].equals(property.getName())) {
if (configKeyParts.length > 1) {
// Loop through the sub property if the first part matches
for (BeanPropertyDefinition subProperty : getPOJOForClass(property.getRawPrimaryType())) {
if (configKeyParts[1].equals(subProperty.getName())) {
geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
// Set the sub property value on the config
try {
Object subConfig = property.getGetter().callOn(geyserConfig);
setConfigOption(subProperty, subConfig, configKey.getValue());
} catch (Exception e) {
geyserLogger.error("Failed to set config option: " + property.getFullName());
}
break;
}
}
} else {
geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
// Set the property value on the config
try {
setConfigOption(property, geyserConfig, configKey.getValue());
} catch (Exception e) {
geyserLogger.error("Failed to set config option: " + property.getFullName());
}
}
break;
}
} }
} }
} }

Datei anzeigen

@ -27,10 +27,15 @@ package org.geysermc.geyser.platform.velocity;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserPluginBootstrap;
import org.geysermc.geyser.network.netty.GeyserInjector; import org.geysermc.geyser.network.netty.GeyserInjector;
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper; import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
@ -47,7 +52,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception { protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception {
Field cm = proxy.getClass().getDeclaredField("cm"); Field cm = proxy.getClass().getDeclaredField("cm");
cm.setAccessible(true); cm.setAccessible(true);
Object connectionManager = cm.get(proxy); Object connectionManager = cm.get(proxy);
@ -80,7 +85,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
protected void initChannel(@NonNull Channel ch) throws Exception { protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(channelInitializer, ch); initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) { if (bootstrap.config().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler", ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
new GeyserVelocityCompressionDisabler()); new GeyserVelocityCompressionDisabler());
} }

Datei anzeigen

@ -37,7 +37,7 @@ public final class GeyserVelocityUpdateListener {
@Subscribe @Subscribe
public void onPlayerJoin(PostLoginEvent event) { public void onPlayerJoin(PostLoginEvent event) {
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) { if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
final Player player = event.getPlayer(); final Player player = event.getPlayer();
if (player.hasPermission(Constants.UPDATE_PERMISSION)) { if (player.hasPermission(Constants.UPDATE_PERMISSION)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player)); VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));

Datei anzeigen

@ -73,6 +73,8 @@ dependencies {
annotationProcessor(projects.ap) annotationProcessor(projects.ap)
api(libs.events) api(libs.events)
api(libs.bstats)
} }
tasks.processResources { tasks.processResources {

Datei anzeigen

@ -47,6 +47,8 @@ public final class Constants {
public static final int CONFIG_VERSION = 5; public static final int CONFIG_VERSION = 5;
public static final int BSTATS_ID = 5273;
static { static {
URI wsUri = null; URI wsUri = null;
try { try {

Datei anzeigen

@ -28,6 +28,7 @@ package org.geysermc.geyser;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo; import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.level.GeyserWorldManager; import org.geysermc.geyser.level.GeyserWorldManager;
@ -72,7 +73,16 @@ public interface GeyserBootstrap {
* *
* @return The current GeyserConfiguration * @return The current GeyserConfiguration
*/ */
GeyserConfiguration getGeyserConfig(); default GeyserConfiguration getGeyserConfig() {
throw new UnsupportedOperationException();
}
/**
* Returns the current GeyserConfig
*
* @return The current GeyserConfig
*/
GeyserConfig config();
/** /**
* Returns the current GeyserLogger * Returns the current GeyserLogger
@ -189,4 +199,9 @@ public interface GeyserBootstrap {
* Tests if Floodgate is installed, loads the Floodgate key if so, and returns the result of Floodgate installed. * Tests if Floodgate is installed, loads the Floodgate key if so, and returns the result of Floodgate installed.
*/ */
boolean testFloodgatePluginPresent(); boolean testFloodgatePluginPresent();
/**
* TEMPORARY - will be removed after The Merge:tm:.
*/
Path getFloodgateKeyPath();
} }

Datei anzeigen

@ -25,9 +25,6 @@
package org.geysermc.geyser; package org.geysermc.geyser;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import io.netty.channel.epoll.Epoll; import io.netty.channel.epoll.Epoll;
@ -39,6 +36,11 @@ import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bstats.MetricsBase;
import org.bstats.charts.AdvancedPie;
import org.bstats.charts.DrilldownPie;
import org.bstats.charts.SimplePie;
import org.bstats.charts.SingleLineChart;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull; import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
@ -67,7 +69,7 @@ import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.api.util.MinecraftVersion; import org.geysermc.geyser.api.util.MinecraftVersion;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandManager; import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.erosion.UnixSocketClientListener; import org.geysermc.geyser.erosion.UnixSocketClientListener;
import org.geysermc.geyser.event.GeyserEventBus; import org.geysermc.geyser.event.GeyserEventBus;
@ -91,10 +93,8 @@ import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.text.MinecraftLocale;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.AssetUtils; import org.geysermc.geyser.util.AssetUtils;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.DimensionUtils; import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.JsonUtils; import org.geysermc.geyser.util.JsonUtils;
import org.geysermc.geyser.util.Metrics;
import org.geysermc.geyser.util.NewsHandler; import org.geysermc.geyser.util.NewsHandler;
import org.geysermc.geyser.util.VersionCheckUtils; import org.geysermc.geyser.util.VersionCheckUtils;
import org.geysermc.geyser.util.WebUtils; import org.geysermc.geyser.util.WebUtils;
@ -128,13 +128,6 @@ import java.util.regex.Pattern;
@Getter @Getter
public class GeyserImpl implements GeyserApi { public class GeyserImpl implements GeyserApi {
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
.enable(JsonParser.Feature.IGNORE_UNDEFINED)
.enable(JsonParser.Feature.ALLOW_COMMENTS)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.enable(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES)
.enable(JsonParser.Feature.ALLOW_SINGLE_QUOTES);
public static final Gson GSON = JsonUtils.createGson(); public static final Gson GSON = JsonUtils.createGson();
public static final String NAME = "Geyser"; public static final String NAME = "Geyser";
@ -180,7 +173,7 @@ public class GeyserImpl implements GeyserApi {
private final EventBus<EventRegistrar> eventBus; private final EventBus<EventRegistrar> eventBus;
private final GeyserExtensionManager extensionManager; private final GeyserExtensionManager extensionManager;
private Metrics metrics; private MetricsBase metrics;
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication; private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
@Getter(AccessLevel.NONE) @Getter(AccessLevel.NONE)
@ -269,7 +262,7 @@ public class GeyserImpl implements GeyserApi {
startInstance(); startInstance();
GeyserConfiguration config = bootstrap.getGeyserConfig(); GeyserConfig config = bootstrap.config();
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D; double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime)); String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
@ -277,11 +270,11 @@ public class GeyserImpl implements GeyserApi {
logger.info(message); logger.info(message);
if (platformType == PlatformType.STANDALONE) { if (platformType == PlatformType.STANDALONE) {
if (config.getRemote().authType() != AuthType.FLOODGATE) { if (config.java().authType() != AuthType.FLOODGATE) {
// If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server // If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server
logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn")); logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn"));
} }
} else if (config.getRemote().authType() == AuthType.FLOODGATE) { } else if (config.java().authType() == AuthType.FLOODGATE) {
VersionCheckUtils.checkForOutdatedFloodgate(logger); VersionCheckUtils.checkForOutdatedFloodgate(logger);
} }
@ -296,7 +289,7 @@ public class GeyserImpl implements GeyserApi {
GeyserLocale.finalizeDefaultLocale(this); GeyserLocale.finalizeDefaultLocale(this);
} }
GeyserLogger logger = bootstrap.getGeyserLogger(); GeyserLogger logger = bootstrap.getGeyserLogger();
GeyserConfiguration config = bootstrap.getGeyserConfig(); GeyserConfig config = bootstrap.config();
ScoreboardUpdater.init(); ScoreboardUpdater.init();
@ -314,31 +307,28 @@ public class GeyserImpl implements GeyserApi {
if (platformType != PlatformType.STANDALONE) { if (platformType != PlatformType.STANDALONE) {
int javaPort = bootstrap.getServerPort(); int javaPort = bootstrap.getServerPort();
if (config.getRemote().address().equals("auto")) { String serverAddress = bootstrap.getServerBindAddress();
config.setAutoconfiguredRemote(true); if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) {
String serverAddress = bootstrap.getServerBindAddress(); config.java().address(serverAddress);
if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) { } else {
config.getRemote().setAddress(serverAddress); // Set the remote address to localhost since that is where we are always connecting
} else { try {
// Set the remote address to localhost since that is where we are always connecting config.java().address(InetAddress.getLocalHost().getHostAddress());
try { } catch (UnknownHostException ex) {
config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress()); logger.debug("Unknown host when trying to find localhost.");
} catch (UnknownHostException ex) { if (config.debugMode()) {
logger.debug("Unknown host when trying to find localhost."); ex.printStackTrace();
if (config.isDebugMode()) {
ex.printStackTrace();
}
config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress());
} }
config.java().address(InetAddress.getLoopbackAddress().getHostAddress());
} }
if (javaPort != -1) { }
config.getRemote().setPort(javaPort); if (javaPort != -1 && config.asPluginConfig().isEmpty()) {
} config.java().port(javaPort);
} }
boolean forceMatchServerPort = "server".equals(pluginUdpPort); boolean forceMatchServerPort = "server".equals(pluginUdpPort);
if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) { if ((config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false) || forceMatchServerPort) && javaPort != -1) {
config.getBedrock().setPort(javaPort); config.bedrock().port(javaPort);
if (forceMatchServerPort) { if (forceMatchServerPort) {
if (geyserUdpPort.isEmpty()) { if (geyserUdpPort.isEmpty()) {
logger.info("Port set from system generic property to match Java server."); logger.info("Port set from system generic property to match Java server.");
@ -352,15 +342,15 @@ public class GeyserImpl implements GeyserApi {
if ("server".equals(pluginUdpAddress)) { if ("server".equals(pluginUdpAddress)) {
String address = bootstrap.getServerBindAddress(); String address = bootstrap.getServerBindAddress();
if (!address.isEmpty()) { if (!address.isEmpty()) {
config.getBedrock().setAddress(address); config.bedrock().address(address);
} }
} else if (!pluginUdpAddress.isEmpty()) { } else if (!pluginUdpAddress.isEmpty()) {
config.getBedrock().setAddress(pluginUdpAddress); config.bedrock().address(pluginUdpAddress);
} }
if (!portPropertyApplied && !pluginUdpPort.isEmpty()) { if (!portPropertyApplied && !pluginUdpPort.isEmpty()) {
int port = Integer.parseInt(pluginUdpPort); int port = Integer.parseInt(pluginUdpPort);
config.getBedrock().setPort(port); config.bedrock().port(port);
if (geyserUdpPort.isEmpty()) { if (geyserUdpPort.isEmpty()) {
logger.info("Port set from generic system property: " + port); logger.info("Port set from generic system property: " + port);
} else { } else {
@ -378,42 +368,44 @@ public class GeyserImpl implements GeyserApi {
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")")); logger.error(String.format("Invalid broadcast port: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
parsedPort = config.getBedrock().port(); parsedPort = config.bedrock().port();
} }
config.getBedrock().setBroadcastPort(parsedPort); config.bedrock().broadcastPort(parsedPort);
logger.info("Broadcast port set from system property: " + parsedPort); logger.info("Broadcast port set from system property: " + parsedPort);
} }
if (platformType != PlatformType.VIAPROXY) { if (platformType != PlatformType.VIAPROXY) {
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent(); boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) { if (config.java().authType() == AuthType.FLOODGATE && !floodgatePresent) {
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " " logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling")); + GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return; return;
} else if (config.isAutoconfiguredRemote() && floodgatePresent) { } else if (floodgatePresent) {
// Floodgate installed means that the user wants Floodgate authentication // Floodgate installed means that the user wants Floodgate authentication
logger.debug("Auto-setting to Floodgate authentication."); logger.debug("Auto-setting to Floodgate authentication.");
config.getRemote().setAuthType(AuthType.FLOODGATE); config.java().authType(AuthType.FLOODGATE);
} }
} }
} }
String remoteAddress = config.getRemote().address(); if (config.asPluginConfig().isEmpty()) {
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry. String remoteAddress = config.java().address();
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) { // Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
String[] record = WebUtils.findSrvRecord(this, remoteAddress); if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
if (record != null) { String[] record = WebUtils.findSrvRecord(this, remoteAddress);
int remotePort = Integer.parseInt(record[2]); if (record != null) {
config.getRemote().setAddress(remoteAddress = record[3]); int remotePort = Integer.parseInt(record[2]);
config.getRemote().setPort(remotePort); config.java().address(remoteAddress = record[3]);
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\""); config.java().port(remotePort);
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
}
} }
} }
// Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves // Ensure that PacketLib does not create an event loop for handling packets; we'll do that ourselves
TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false; TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false;
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout()); pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout());
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber()); this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
@ -425,8 +417,7 @@ public class GeyserImpl implements GeyserApi {
logger.debug("Epoll is not available; Erosion's Unix socket handling will not work."); logger.debug("Epoll is not available; Erosion's Unix socket handling will not work.");
} }
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown()); DimensionUtils.changeBedrockNetherId(config.aboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads"); Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads");
if (bedrockThreadCount == null) { if (bedrockThreadCount == null) {
@ -436,14 +427,14 @@ public class GeyserImpl implements GeyserApi {
if (shouldStartListener) { if (shouldStartListener) {
this.geyserServer = new GeyserServer(this, bedrockThreadCount); this.geyserServer = new GeyserServer(this, bedrockThreadCount);
this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port())) this.geyserServer.bind(new InetSocketAddress(config.bedrock().address(), config.bedrock().port()))
.whenComplete((avoid, throwable) -> { .whenComplete((avoid, throwable) -> {
if (throwable == null) { if (throwable == null) {
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(), logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.bedrock().address(),
String.valueOf(config.getBedrock().port()))); String.valueOf(config.bedrock().port())));
} else { } else {
String address = config.getBedrock().address(); String address = config.bedrock().address();
int port = config.getBedrock().port(); int port = config.bedrock().port();
logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port))); logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port)));
if (!"0.0.0.0".equals(address)) { if (!"0.0.0.0".equals(address)) {
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("Suggestion: try setting `address` under `bedrock` in the Geyser config back to 0.0.0.0", NamedTextColor.GREEN));
@ -453,9 +444,9 @@ public class GeyserImpl implements GeyserApi {
}).join(); }).join();
} }
if (config.getRemote().authType() == AuthType.FLOODGATE) { if (config.java().authType() == AuthType.FLOODGATE) {
try { try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath()); Key key = new AesKeyProducer().produceFrom(bootstrap.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping()); cipher = new AesCipher(new Base64Topping());
cipher.init(key); cipher.init(key);
logger.debug("Loaded Floodgate key!"); logger.debug("Loaded Floodgate key!");
@ -467,15 +458,39 @@ public class GeyserImpl implements GeyserApi {
} }
} }
if (config.getMetrics().isEnabled()) { if (config.metrics().enabled()) {
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); metrics = new MetricsBase(
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); "server-implementation",
config.metrics().uuid().toString(),
Constants.BSTATS_ID,
true, // Already checked above.
builder -> {
// OS specific data
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
builder.appendField("osName", osName);
builder.appendField("osArch", osArch);
builder.appendField("osVersion", osVersion);
builder.appendField("coreCount", coreCount);
},
builder -> {},
null,
() -> true,
logger::error,
logger::info,
config.debugMode(),
config.debugMode(),
config.debugMode());
metrics.addCustomChart(new SingleLineChart("players", sessionManager::size));
// Prevent unwanted words best we can // Prevent unwanted words best we can
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT))); metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT)));
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::platformName)); metrics.addCustomChart(new SimplePie("platform", platformType::platformName));
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale)); metrics.addCustomChart(new SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION)); metrics.addCustomChart(new SimplePie("version", () -> GeyserImpl.VERSION));
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { metrics.addCustomChart(new AdvancedPie("playerPlatform", () -> {
Map<String, Integer> valueMap = new HashMap<>(); Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) { for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue; if (session == null) continue;
@ -489,7 +504,7 @@ public class GeyserImpl implements GeyserApi {
} }
return valueMap; return valueMap;
})); }));
metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { metrics.addCustomChart(new AdvancedPie("playerVersion", () -> {
Map<String, Integer> valueMap = new HashMap<>(); Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) { for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue; if (session == null) continue;
@ -511,7 +526,7 @@ public class GeyserImpl implements GeyserApi {
platformMap.put(platformType.platformName(), 1); platformMap.put(platformType.platformName(), 1);
versionMap.put(minecraftVersion, platformMap); versionMap.put(minecraftVersion, platformMap);
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> { metrics.addCustomChart(new DrilldownPie("minecraftServerVersion", () -> {
// By the end, we should return, for example: // By the end, we should return, for example:
// 1.16.5 => (Spigot, 1) // 1.16.5 => (Spigot, 1)
return versionMap; return versionMap;
@ -520,7 +535,7 @@ public class GeyserImpl implements GeyserApi {
// The following code can be attributed to the PaperMC project // The following code can be attributed to the PaperMC project
// https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614 // https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614
metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> { metrics.addCustomChart(new DrilldownPie("javaVersion", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>(); Map<String, Map<String, Integer>> map = new HashMap<>();
String javaVersion = System.getProperty("java.version"); String javaVersion = System.getProperty("java.version");
Map<String, Integer> entry = new HashMap<>(); Map<String, Integer> entry = new HashMap<>();
@ -555,7 +570,7 @@ public class GeyserImpl implements GeyserApi {
metrics = null; metrics = null;
} }
if (config.getRemote().authType() == AuthType.ONLINE) { if (config.java().authType() == AuthType.ONLINE) {
// May be written/read to on multiple threads from each GeyserSession as well as writing the config // May be written/read to on multiple threads from each GeyserSession as well as writing the config
savedRefreshTokens = new ConcurrentHashMap<>(); savedRefreshTokens = new ConcurrentHashMap<>();
@ -570,7 +585,7 @@ public class GeyserImpl implements GeyserApi {
logger.error("Cannot load saved user tokens!", e); logger.error("Cannot load saved user tokens!", e);
} }
if (refreshTokenFile != null) { if (refreshTokenFile != null) {
List<String> validUsers = config.getSavedUserLogins(); List<String> validUsers = config.savedUserLogins();
boolean doWrite = false; boolean doWrite = false;
for (Map.Entry<String, String> entry : refreshTokenFile.entrySet()) { for (Map.Entry<String, String> entry : refreshTokenFile.entrySet()) {
String user = entry.getKey(); String user = entry.getKey();
@ -598,7 +613,7 @@ public class GeyserImpl implements GeyserApi {
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus)); this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
} }
if (config.isNotifyOnNewBedrockUpdate()) { if (config.notifyOnNewBedrockUpdate()) {
VersionCheckUtils.checkForGeyserUpdate(this::getLogger); VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
} }
} }
@ -750,13 +765,13 @@ public class GeyserImpl implements GeyserApi {
@NonNull @NonNull
public RemoteServer defaultRemoteServer() { public RemoteServer defaultRemoteServer() {
return getConfig().getRemote(); return config().java();
} }
@Override @Override
@NonNull @NonNull
public BedrockListener bedrockListener() { public BedrockListener bedrockListener() {
return getConfig().getBedrock(); return config().bedrock();
} }
@Override @Override
@ -830,8 +845,8 @@ public class GeyserImpl implements GeyserApi {
return bootstrap.getGeyserLogger(); return bootstrap.getGeyserLogger();
} }
public GeyserConfiguration getConfig() { public GeyserConfig config() {
return bootstrap.getGeyserConfig(); return bootstrap.config();
} }
public WorldManager getWorldManager() { public WorldManager getWorldManager() {
@ -844,7 +859,7 @@ public class GeyserImpl implements GeyserApi {
} }
public void saveRefreshToken(@NonNull String bedrockName, @NonNull String refreshToken) { public void saveRefreshToken(@NonNull String bedrockName, @NonNull String refreshToken) {
if (!getConfig().getSavedUserLogins().contains(bedrockName)) { if (!config().savedUserLogins().contains(bedrockName)) {
// Do not save this login // Do not save this login
return; return;
} }

Datei anzeigen

@ -0,0 +1,36 @@
/*
* Copyright (c) 2024 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;
import org.geysermc.geyser.configuration.GeyserPluginConfig;
/**
* Used in any instance where Geyser is directly attached to a server instance of some sort.
*/
public interface GeyserPluginBootstrap extends GeyserBootstrap {
@Override
GeyserPluginConfig config();
}

Datei anzeigen

@ -32,7 +32,7 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.LoopbackUtil; import org.geysermc.geyser.util.LoopbackUtil;
@ -86,7 +86,7 @@ public class ConnectionTestCommand extends GeyserCommand {
return; return;
} }
} else { } else {
port = geyser.getConfig().getBedrock().broadcastPort(); port = geyser.config().bedrock().broadcastPort();
} }
String ip = fullAddress[0]; String ip = fullAddress[0];
@ -114,41 +114,41 @@ public class ConnectionTestCommand extends GeyserCommand {
return; return;
} }
GeyserConfiguration config = geyser.getConfig(); GeyserConfig config = geyser.config();
// Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing // Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing
if (config.getBedrock().broadcastPort() == config.getBedrock().port()) { if (config.bedrock().broadcastPort() == config.bedrock().port()) {
if (port != config.getBedrock().port()) { if (port != config.bedrock().port()) {
if (fullAddress.length == 2) { if (fullAddress.length == 2) {
sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration (" sender.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
+ config.getBedrock().port() + ")"); + config.bedrock().port() + ")");
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config."); sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
if (config.getBedrock().isCloneRemotePort()) { if (config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false)) {
sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead."); sender.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
} }
} else { } else {
sender.sendMessage("You did not specify the port to check (add it with \":<port>\"), " + sender.sendMessage("You did not specify the port to check (add it with \":<port>\"), " +
"and the default port 19132 does not match the port in your Geyser configuration (" "and the default port 19132 does not match the port in your Geyser configuration ("
+ config.getBedrock().port() + ")!"); + config.bedrock().port() + ")!");
sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`."); sender.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
} }
} }
} else { } else {
if (config.getBedrock().broadcastPort() != port) { if (config.bedrock().broadcastPort() != port) {
sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration (" sender.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration ("
+ config.getBedrock().broadcastPort() + "). "); + config.bedrock().broadcastPort() + "). ");
sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on."); sender.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config."); sender.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config.");
} }
} }
// Issue: is the `bedrock` `address` in the config different? // Issue: is the `bedrock` `address` in the config different?
if (!config.getBedrock().address().equals("0.0.0.0")) { if (!config.bedrock().address().equals("0.0.0.0")) {
sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional."); sender.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
} }
// Issue: did someone turn on enable-proxy-protocol, and they didn't mean it? // Issue: did someone turn on enable-proxy-protocol, and they didn't mean it?
if (config.getBedrock().isEnableProxyProtocol()) { if (config.bedrock().enableProxyProtocol()) {
sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " + sender.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled."); "Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
} }

Datei anzeigen

@ -25,13 +25,14 @@
package org.geysermc.geyser.command.defaults; package org.geysermc.geyser.command.defaults;
import com.fasterxml.jackson.core.util.DefaultIndenter; import com.google.gson.Gson;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.google.gson.GsonBuilder;
import com.fasterxml.jackson.databind.JsonNode; import com.google.gson.JsonObject;
import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommand; import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.dump.DumpInfo; import org.geysermc.geyser.dump.DumpInfo;
@ -49,7 +50,6 @@ import java.util.List;
public class DumpCommand extends GeyserCommand { public class DumpCommand extends GeyserCommand {
private final GeyserImpl geyser; private final GeyserImpl geyser;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DUMP_URL = "https://dump.geysermc.org/"; private static final String DUMP_URL = "https://dump.geysermc.org/";
public DumpCommand(GeyserImpl geyser, String name, String description, String permission) { public DumpCommand(GeyserImpl geyser, String name, String description, String permission) {
@ -81,18 +81,13 @@ public class DumpCommand extends GeyserCommand {
AsteriskSerializer.showSensitive = showSensitive; AsteriskSerializer.showSensitive = showSensitive;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale())); sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale()));
String dumpData; String dumpData;
try { try {
if (offlineDump) { dumpData = gson.toJson(new DumpInfo(addLog));
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); } catch (JsonSyntaxException e) {
// Make arrays easier to read
prettyPrinter.indentArraysWith(new DefaultIndenter(" ", "\n"));
dumpData = MAPPER.writer(prettyPrinter).writeValueAsString(new DumpInfo(addLog));
} else {
dumpData = MAPPER.writeValueAsString(new DumpInfo(addLog));
}
} catch (IOException e) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale())); sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", sender.locale()));
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
return; return;
@ -118,10 +113,10 @@ public class DumpCommand extends GeyserCommand {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale())); sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale()));
String response; String response;
JsonNode responseNode; JsonObject responseNode;
try { try {
response = WebUtils.post(DUMP_URL + "documents", dumpData); response = WebUtils.post(DUMP_URL + "documents", dumpData);
responseNode = MAPPER.readTree(response); responseNode = (JsonObject) new JsonParser().parse(response);
} catch (IOException e) { } catch (IOException e) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale())); sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", sender.locale()));
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e); geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
@ -129,11 +124,11 @@ public class DumpCommand extends GeyserCommand {
} }
if (!responseNode.has("key")) { if (!responseNode.has("key")) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response)); sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", sender.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").getAsString() : response));
return; return;
} }
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText(); uploadedDumpUrl = DUMP_URL + responseNode.get("key").getAsString();
} }
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl); sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", sender.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);

Datei anzeigen

@ -25,18 +25,21 @@
package org.geysermc.geyser.configuration; package org.geysermc.geyser.configuration;
import org.spongepowered.configurate.ConfigurationNode; import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.NodePath; import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions; import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions;
import org.spongepowered.configurate.transformation.ConfigurationTransformation; import org.spongepowered.configurate.transformation.ConfigurationTransformation;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.util.function.Consumer;
import java.nio.file.Path;
public class ConfigLoaderTemp { import static org.spongepowered.configurate.NodePath.path;
import static org.spongepowered.configurate.transformation.TransformAction.remove;
import static org.spongepowered.configurate.transformation.TransformAction.rename;
public final class ConfigLoaderTemp {
private static final String HEADER = """ private static final String HEADER = """
-------------------------------- --------------------------------
Geyser Configuration File Geyser Configuration File
@ -51,35 +54,81 @@ public class ConfigLoaderTemp {
In most cases, especially with server hosting providers, further hosting-specific configuration is required. In most cases, especially with server hosting providers, further hosting-specific configuration is required.
--------------------------------"""; --------------------------------""";
public static <T extends GeyserConfig> T load(Class<T> configClass) throws IOException { public static <T extends GeyserConfig> T load(File file, Class<T> configClass) throws IOException {
var loader = YamlConfigurationLoader.builder() return load(file, configClass, null);
.file(new File("newconfig.yml")) }
.defaultOptions(options -> InterfaceDefaultOptions.addTo(options.header(HEADER)))
.build();
ConfigurationNode node = loader.load();
// temp fix for node.virtual() being broken
var virtual = !Files.exists(Path.of("newconfig.yml"));
// TODO needed or else Configurate breaks public static <T extends GeyserConfig> T load(File file, Class<T> configClass, @Nullable Consumer<CommentedConfigurationNode> transformer) throws IOException {
var loader = YamlConfigurationLoader.builder()
.file(file)
.defaultOptions(options -> InterfaceDefaultOptions.addTo(options
.header(HEADER)
.serializers(builder -> builder.register(new LowercaseEnumSerializer()))))
.build();
var node = loader.load();
// temp fix for node.virtual() being broken
boolean virtual = file.exists();
// Note for Tim? Needed or else Configurate breaks.
var migrations = ConfigurationTransformation.versionedBuilder() var migrations = ConfigurationTransformation.versionedBuilder()
.versionKey("config-version")
// Pre-Configurate // Pre-Configurate
.addVersion(5, ConfigurationTransformation.builder() .addVersion(5, ConfigurationTransformation.builder()
.addAction(NodePath.path("legacyPingPassthrough"), (path, value) -> { .addAction(path("legacy-ping-passthrough"), configClass == GeyserRemoteConfig.class ? remove() : (path, value) -> {
// Invert value // Invert value
value.set(Boolean.FALSE.equals(value.get(boolean.class))); value.set(!value.getBoolean());
return new Object[]{"integratedPingPassthrough"}; return new Object[]{"integrated-ping-passthrough"};
}) })
.addAction(NodePath.path("remote"), (path, value) -> .addAction(path("remote"), rename("java"))
new Object[]{"java"}) .addAction(path("floodgate-key-file"), (path, value) -> {
.build()) // Elimate any legacy config values
if ("public-key.pem".equals(value.getString())) {
value.set("key.pem");
}
return null;
})
.addAction(path("default-locale"), (path, value) -> {
if (value.getString() == null) {
value.set("system");
}
return null;
})
.addAction(path("show-cooldown"), (path, value) -> {
String s = value.getString();
if (s != null) {
switch (s) {
case "true" -> value.set("title");
case "false" -> value.set("disabled");
}
}
return null;
})
.addAction(path("bedrock", "motd1"), rename("primary-motd"))
.addAction(path("bedrock", "motd2"), rename("secondary-motd"))
// Legacy config values
.addAction(path("emote-offhand-workaround"), remove())
.addAction(path("allow-third-party-capes"), remove())
.addAction(path("allow-third-party-ears"), remove())
.addAction(path("general-thread-pool"), remove())
.addAction(path("cache-chunks"), remove())
.build())
.build(); .build();
int currentVersion = migrations.version(node);
migrations.apply(node); migrations.apply(node);
int newVersion = migrations.version(node);
T config = node.get(configClass); T config = node.get(configClass);
System.out.println(config);
loader.save(node); if (virtual || currentVersion != newVersion) {
loader.save(node);
}
if (transformer != null) {
// Do not let the transformer change the node.
transformer.accept(node);
config = node.get(configClass);
}
return config; return config;
} }

Datei anzeigen

@ -31,6 +31,7 @@ import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.network.BedrockListener; import org.geysermc.geyser.api.network.BedrockListener;
import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.util.CooldownUtils;
import org.spongepowered.configurate.interfaces.meta.Exclude; import org.spongepowered.configurate.interfaces.meta.Exclude;
import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean;
import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric;
@ -39,8 +40,9 @@ import org.spongepowered.configurate.interfaces.meta.range.NumericRange;
import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment; import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.nio.file.Path; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.UUID; import java.util.UUID;
@ConfigSerializable @ConfigSerializable
@ -49,7 +51,9 @@ public interface GeyserConfig {
JavaConfig java(); JavaConfig java();
Path floodgateKeyPath(); // Cannot be type File yet because we want to hide it in plugin instances.
@DefaultString("key.pem")
String floodgateKeyFile();
@Comment(""" @Comment("""
For online mode authentication type only. For online mode authentication type only.
@ -108,8 +112,9 @@ public interface GeyserConfig {
https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png https://cdn.discordapp.com/attachments/613170125696270357/957075682230419466/Screenshot_from_2022-03-25_20-35-08.png
This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0 This can be disabled by going into Bedrock settings under the accessibility tab and setting "Text Background Opacity" to 0
This setting can be set to "title", "actionbar" or "false\"""") This setting can be set to "title", "actionbar" or "false\"""")
@DefaultString("title") default CooldownUtils.CooldownType showCooldown() {
String showCooldown(); return CooldownUtils.CooldownType.TITLE;
}
@Comment("Controls if coordinates are shown to players.") @Comment("Controls if coordinates are shown to players.")
@DefaultBoolean(true) @DefaultBoolean(true)
@ -118,9 +123,9 @@ public interface GeyserConfig {
@Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.") @Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.")
boolean disableBedrockScaffolding(); boolean disableBedrockScaffolding();
//@DefaultString("disabled") @Comment("The default locale if we don't have the one the client requested. If set to \"system\", the system's language will be used.")
EmoteOffhandWorkaroundOption emoteOffhandWorkaround(); @NonNull
@DefaultString("system")
String defaultLocale(); String defaultLocale();
@Comment(""" @Comment("""
@ -207,13 +212,15 @@ public interface GeyserConfig {
@Override @Override
@Comment("The port that will listen for connections") @Comment("The port that will listen for connections")
@DefaultNumeric(19132) @DefaultNumeric(19132)
@NumericRange(from = 0, to = 65535)
int port(); int port();
@Override @Override
@Comment(""" @Comment("""
The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server. The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server.
DO NOT uncomment and change this unless Geyser runs on a different internal port than the one that is used to connect.""") DO NOT change this unless Geyser runs on a different internal port than the one that is used to connect.""")
@DefaultNumeric(19132) @DefaultNumeric(19132)
@NumericRange(from = 0, to = 65535)
int broadcastPort(); int broadcastPort();
void address(String address); void address(String address);
@ -223,13 +230,21 @@ public interface GeyserConfig {
void broadcastPort(int broadcastPort); void broadcastPort(int broadcastPort);
@Override @Override
@Comment("""
The MOTD that will be broadcasted to Minecraft: Bedrock Edition clients. This is irrelevant if "passthrough-motd" is set to true.
If either of these are empty, the respective string will default to "Geyser\"""")
@DefaultString("Geyser") @DefaultString("Geyser")
String primaryMotd(); String primaryMotd();
@Override @Override
@DefaultString("Another Geyser server.") // TODO migrate or change name @DefaultString("Another Geyser server.")
String secondaryMotd(); String secondaryMotd();
@Override
@Comment("The Server Name that will be sent to Minecraft: Bedrock Edition clients. This is visible in both the pause menu and the settings menu.")
@DefaultString("Geyser")
String serverName();
@Comment(""" @Comment("""
How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but How much to compress network traffic to the Bedrock client. The higher the number, the more CPU usage used, but
the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable.""") the smaller the bandwidth used. Does not have any effect below -1 or above 9. Set to -1 to disable.""")
@ -237,6 +252,9 @@ public interface GeyserConfig {
@NumericRange(from = -1, to = 9) @NumericRange(from = -1, to = 9)
int compressionLevel(); int compressionLevel();
@Comment("""
Whether to enable PROXY protocol or not for clients. You DO NOT WANT this feature unless you run UDP reverse proxy
in front of your Geyser instance.""")
@DefaultBoolean @DefaultBoolean
boolean enableProxyProtocol(); boolean enableProxyProtocol();
@ -245,13 +263,9 @@ public interface GeyserConfig {
should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.). should really only be used when you are not able to use a proper firewall (usually true with shared hosting providers etc.).
Keeping this list empty means there is no IP address whitelist. Keeping this list empty means there is no IP address whitelist.
IP addresses, subnets, and links to plain text files are supported.""") IP addresses, subnets, and links to plain text files are supported.""")
List<String> proxyProtocolWhitelistedIPs(); default List<String> proxyProtocolWhitelistedIps() {
return Collections.emptyList();
// /** }
// * @return Unmodifiable list of {@link CIDRMatcher}s from {@link #proxyProtocolWhitelistedIPs()}
// */
// @Exclude
// List<CIDRMatcher> whitelistedIPsMatchers();
} }
@ConfigSerializable @ConfigSerializable
@ -270,6 +284,8 @@ public interface GeyserConfig {
return AuthType.ONLINE; return AuthType.ONLINE;
} }
void authType(AuthType authType);
@Comment(""" @Comment("""
Whether to enable PROXY protocol or not while connecting to the server. Whether to enable PROXY protocol or not while connecting to the server.
This is useful only when: This is useful only when:
@ -297,17 +313,16 @@ public interface GeyserConfig {
default boolean resolveSrv() { default boolean resolveSrv() {
return false; return false;
} }
void authType(AuthType authType);
} }
@ConfigSerializable @ConfigSerializable
interface MetricsInfo { interface MetricsInfo {
@Comment("If metrics should be enabled")
@DefaultBoolean(true) @DefaultBoolean(true)
boolean enabled(); boolean enabled();
default UUID uniqueId() { //TODO rename? @Comment("UUID of server. Don't change!")
default UUID uuid() { //TODO rename?
return UUID.randomUUID(); return UUID.randomUUID();
} }
} }
@ -333,7 +348,15 @@ public interface GeyserConfig {
int mtu(); int mtu();
@Comment("Do not change!") @Comment("Do not change!")
default int version() { default int configVersion() {
return Constants.CONFIG_VERSION; return Constants.CONFIG_VERSION;
} }
@Exclude
default Optional<GeyserPluginConfig> asPluginConfig() {
if (this instanceof GeyserPluginConfig config) {
return Optional.of(config);
}
return Optional.empty();
}
} }

Datei anzeigen

@ -32,6 +32,8 @@ import org.spongepowered.configurate.interfaces.meta.defaults.DefaultBoolean;
import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment; import org.spongepowered.configurate.objectmapping.meta.Comment;
import java.io.File;
@ConfigSerializable @ConfigSerializable
public interface GeyserPluginConfig extends GeyserConfig { public interface GeyserPluginConfig extends GeyserConfig {
@Override @Override
@ -89,6 +91,10 @@ public interface GeyserPluginConfig extends GeyserConfig {
} }
} }
@Override
@Hidden
String floodgateKeyFile();
@Comment(""" @Comment("""
Use server API methods to determine the Java server's MOTD and ping passthrough. Use server API methods to determine the Java server's MOTD and ping passthrough.
There is no need to disable this unless your MOTD or player count does not appear properly.""") There is no need to disable this unless your MOTD or player count does not appear properly.""")

Datei anzeigen

@ -25,10 +25,9 @@
package org.geysermc.geyser.configuration; package org.geysermc.geyser.configuration;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.network.AuthType;
import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultNumeric;
import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString; import org.spongepowered.configurate.interfaces.meta.defaults.DefaultString;
import org.spongepowered.configurate.interfaces.meta.range.NumericRange;
import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment; import org.spongepowered.configurate.objectmapping.meta.Comment;
@ -37,12 +36,17 @@ import org.spongepowered.configurate.objectmapping.meta.Comment;
*/ */
@ConfigSerializable @ConfigSerializable
public interface GeyserRemoteConfig extends GeyserConfig { public interface GeyserRemoteConfig extends GeyserConfig {
// @Override // For config placement
// BedrockConfig bedrock();
@Override @Override
RemoteConfig java(); RemoteConfig java();
@Override
@Comment("""
Floodgate uses encryption to ensure use from authorized sources.
This should point to the public key generated by Floodgate (BungeeCord, Spigot or Velocity)
You can ignore this when not using Floodgate.
If you're using a plugin version of Floodgate on the same server, the key will automatically be picked up from Floodgate.""")
String floodgateKeyFile();
@ConfigSerializable @ConfigSerializable
interface RemoteConfig extends JavaConfig { interface RemoteConfig extends JavaConfig {
@Override @Override
@ -53,13 +57,9 @@ public interface GeyserRemoteConfig extends GeyserConfig {
@Override @Override
@Comment("The port of the Java Edition server.") @Comment("The port of the Java Edition server.")
@DefaultNumeric(25565) @DefaultNumeric(25565)
@NumericRange(from = 0, to = 65535)
int port(); int port();
@Override // For config placement
default @NonNull AuthType authType() {
return JavaConfig.super.authType();
}
@Override @Override
@Comment(""" @Comment("""
Forward the hostname that the Bedrock client used to connect over to the Java server Forward the hostname that the Bedrock client used to connect over to the Java server

Datei anzeigen

@ -0,0 +1,55 @@
/*
* Copyright (c) 2024 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.configuration;
import io.leangen.geantyref.TypeToken;
import org.spongepowered.configurate.serialize.ScalarSerializer;
import org.spongepowered.configurate.serialize.Scalars;
import org.spongepowered.configurate.serialize.SerializationException;
import java.lang.reflect.Type;
import java.util.Locale;
import java.util.function.Predicate;
/**
* Ensures enum values are written to lowercase. {@link Scalars#ENUM} will read enum values
* in any case.
*/
final class LowercaseEnumSerializer extends ScalarSerializer<Enum<?>> {
LowercaseEnumSerializer() {
super(new TypeToken<Enum<?>>() {});
}
@Override
public Enum<?> deserialize(Type type, Object obj) throws SerializationException {
return Scalars.ENUM.deserialize(type, obj);
}
@Override
protected Object serialize(Enum<?> item, Predicate<Class<?>> typeSupported) {
return item.name().toLowerCase(Locale.ROOT);
}
}

Datei anzeigen

@ -25,13 +25,12 @@
package org.geysermc.geyser.dump; package org.geysermc.geyser.dump;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource; import com.google.common.io.ByteSource;
import com.google.common.io.Files; import com.google.common.io.Files;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import com.google.gson.annotations.SerializedName;
import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter; import lombok.Getter;
@ -41,7 +40,7 @@ import org.geysermc.floodgate.util.FloodgateInfoHolder;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.GeyserApi; import org.geysermc.geyser.api.GeyserApi;
import org.geysermc.geyser.api.extension.Extension; import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.AsteriskSerializer; import org.geysermc.geyser.text.AsteriskSerializer;
@ -57,12 +56,16 @@ import java.net.InetSocketAddress;
import java.net.Socket; import java.net.Socket;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Getter @Getter
public class DumpInfo { public class DumpInfo {
@JsonIgnore
private static final long MEGABYTE = 1024L * 1024L; private static final long MEGABYTE = 1024L * 1024L;
private final DumpInfo.VersionInfo versionInfo; private final DumpInfo.VersionInfo versionInfo;
@ -71,7 +74,7 @@ public class DumpInfo {
private final Locale systemLocale; private final Locale systemLocale;
private final String systemEncoding; private final String systemEncoding;
private final GitInfo gitInfo; private final GitInfo gitInfo;
private final GeyserConfiguration config; private final GeyserConfig config;
private final Floodgate floodgate; private final Floodgate floodgate;
private final Object2IntMap<DeviceOs> userPlatforms; private final Object2IntMap<DeviceOs> userPlatforms;
private final int connectionAttempts; private final int connectionAttempts;
@ -92,7 +95,7 @@ public class DumpInfo {
this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY); this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY);
this.config = GeyserImpl.getInstance().getConfig(); this.config = GeyserImpl.getInstance().config();
this.floodgate = new Floodgate(); this.floodgate = new Floodgate();
String md5Hash = "unknown"; String md5Hash = "unknown";
@ -108,7 +111,7 @@ public class DumpInfo {
//noinspection UnstableApiUsage //noinspection UnstableApiUsage
sha256Hash = byteSource.hash(Hashing.sha256()).toString(); sha256Hash = byteSource.hash(Hashing.sha256()).toString();
} catch (Exception e) { } catch (Exception e) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
} }
@ -280,7 +283,7 @@ public class DumpInfo {
public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) { public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) {
} }
public record GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash, public record GitInfo(String buildNumber, @SerializedName("git.commit.id.abbrev") String commitHashAbbrev, @SerializedName("git.commit.id") String commitHash,
@JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) { @SerializedName("git.branch") String branchName, @SerializedName("git.remote.origin.url") String originUrl) {
} }
} }

Datei anzeigen

@ -71,7 +71,7 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
if (translator.acceptedType() != metadata.getType()) { if (translator.acceptedType() != metadata.getType()) {
GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType()); GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
GeyserImpl.getInstance().getLogger().debug(metadata.toString()); GeyserImpl.getInstance().getLogger().debug(metadata.toString());
} }
return; return;

Datei anzeigen

@ -185,7 +185,7 @@ public class Entity implements GeyserEntity {
flagsDirty = false; flagsDirty = false;
if (session.getGeyser().getConfig().isDebugMode() && PRINT_ENTITY_SPAWN_DEBUG) { if (session.getGeyser().config().debugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
EntityType type = definition.entityType(); EntityType type = definition.entityType();
String name = type != null ? type.name() : getClass().getSimpleName(); String name = type != null ? type.name() : getClass().getSimpleName();
session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")"); session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");

Datei anzeigen

@ -342,7 +342,7 @@ public final class ClickPlan {
} else if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) { } else if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) {
stateIdIncrements = 1; stateIdIncrements = 1;
} else { } else {
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
session.getGeyser().getLogger().debug("Not sure how to handle state ID hack in crafting table: " + plan); session.getGeyser().getLogger().debug("Not sure how to handle state ID hack in crafting table: " + plan);
} }
stateIdIncrements = 1; stateIdIncrements = 1;

Datei anzeigen

@ -90,7 +90,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
super(geyser, session); super(geyser, session);
ZlibCompression compression = new ZlibCompression(Zlib.RAW); ZlibCompression compression = new ZlibCompression(Zlib.RAW);
compression.setLevel(this.geyser.getConfig().getBedrock().getCompressionLevel()); compression.setLevel(this.geyser.config().bedrock().compressionLevel());
this.compressionStrategy = new SimpleCompressionStrategy(compression); this.compressionStrategy = new SimpleCompressionStrategy(compression);
} }
@ -211,7 +211,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(), header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(),
"", header.uuid().toString(), false, false)); "", header.uuid().toString(), false, false));
} }
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks()); resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().config().forceResourcePacks());
session.sendUpstreamPacket(resourcePacksInfo); session.sendUpstreamPacket(resourcePacksInfo);
GeyserLocale.loadGeyserLocale(session.locale()); GeyserLocale.loadGeyserLocale(session.locale());
@ -222,7 +222,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
public PacketSignal handle(ResourcePackClientResponsePacket packet) { public PacketSignal handle(ResourcePackClientResponsePacket packet) {
switch (packet.getStatus()) { switch (packet.getStatus()) {
case COMPLETED: case COMPLETED:
if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) { if (geyser.config().java().authType() != AuthType.ONLINE) {
session.authenticate(session.getAuthData().name()); session.authenticate(session.getAuthData().name());
} else if (!couldLoginUserByName(session.getAuthData().name())) { } else if (!couldLoginUserByName(session.getAuthData().name())) {
// We must spawn the white world // We must spawn the white world
@ -247,7 +247,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), "")); stackPacket.getResourcePacks().add(new ResourcePackStackPacket.Entry(header.uuid().toString(), header.version().toString(), ""));
} }
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { if (GeyserImpl.getInstance().config().addNonBedrockItems()) {
// Allow custom items to work // Allow custom items to work
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true)); stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
} }
@ -273,7 +273,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
} }
private boolean couldLoginUserByName(String bedrockUsername) { private boolean couldLoginUserByName(String bedrockUsername) {
if (geyser.getConfig().getSavedUserLogins().contains(bedrockUsername)) { if (geyser.config().savedUserLogins().contains(bedrockUsername)) {
String refreshToken = geyser.refreshTokenFor(bedrockUsername); String refreshToken = geyser.refreshTokenFor(bedrockUsername);
if (refreshToken != null) { if (refreshToken != null) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name())); geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name()));

Datei anzeigen

@ -27,7 +27,7 @@ package org.geysermc.geyser.network.netty;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import lombok.Getter; import lombok.Getter;
import org.geysermc.geyser.GeyserBootstrap; import org.geysermc.geyser.GeyserPluginBootstrap;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -49,8 +49,8 @@ public abstract class GeyserInjector {
/** /**
* @param bootstrap the bootstrap of the Geyser instance. * @param bootstrap the bootstrap of the Geyser instance.
*/ */
public void initializeLocalChannel(GeyserBootstrap bootstrap) { public void initializeLocalChannel(GeyserPluginBootstrap bootstrap) {
if (!bootstrap.getGeyserConfig().isUseDirectConnection()) { if (!bootstrap.config().useDirectConnection()) {
bootstrap.getGeyserLogger().debug("Disabling direct injection!"); bootstrap.getGeyserLogger().debug("Disabling direct injection!");
return; return;
} }
@ -71,9 +71,9 @@ public abstract class GeyserInjector {
} }
/** /**
* The method to implement that is called by {@link #initializeLocalChannel(GeyserBootstrap)} wrapped around a try/catch. * The method to implement that is called by {@link #initializeLocalChannel(GeyserPluginBootstrap)} wrapped around a try/catch.
*/ */
protected abstract void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception; protected abstract void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception;
public void shutdown() { public void shutdown() {
if (localChannel != null && localChannel.channel().isOpen()) { if (localChannel != null && localChannel.channel().isOpen()) {

Datei anzeigen

@ -53,7 +53,7 @@ import org.cloudburstmc.protocol.bedrock.BedrockPong;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.event.connection.ConnectionRequestEvent; import org.geysermc.geyser.api.event.connection.ConnectionRequestEvent;
import org.geysermc.geyser.command.defaults.ConnectionTestCommand; import org.geysermc.geyser.command.defaults.ConnectionTestCommand;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.event.type.GeyserBedrockPingEventImpl; import org.geysermc.geyser.event.type.GeyserBedrockPingEventImpl;
import org.geysermc.geyser.network.CIDRMatcher; import org.geysermc.geyser.network.CIDRMatcher;
import org.geysermc.geyser.network.GameProtocol; import org.geysermc.geyser.network.GameProtocol;
@ -66,14 +66,18 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.skin.SkinProvider; import org.geysermc.geyser.skin.SkinProvider;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.WebUtils;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction; import java.util.function.IntFunction;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_GLOBAL_PACKET_LIMIT; import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_GLOBAL_PACKET_LIMIT;
import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_PACKET_LIMIT; import static org.cloudburstmc.netty.channel.raknet.RakConstants.DEFAULT_PACKET_LIMIT;
@ -135,7 +139,7 @@ public final class GeyserServer {
this.listenCount = 1; this.listenCount = 1;
} }
if (this.geyser.getConfig().getBedrock().isEnableProxyProtocol()) { if (this.geyser.config().bedrock().enableProxyProtocol()) {
this.proxiedAddresses = ExpiringMap.builder() this.proxiedAddresses = ExpiringMap.builder()
.expiration(30 + 1, TimeUnit.MINUTES) .expiration(30 + 1, TimeUnit.MINUTES)
.expirationPolicy(ExpirationPolicy.ACCESSED).build(); .expirationPolicy(ExpirationPolicy.ACCESSED).build();
@ -144,11 +148,11 @@ public final class GeyserServer {
} }
// It's set to 0 only if no system property or manual config value was set // It's set to 0 only if no system property or manual config value was set
if (geyser.getConfig().getBedrock().broadcastPort() == 0) { if (geyser.config().bedrock().broadcastPort() == 0) {
geyser.getConfig().getBedrock().setBroadcastPort(geyser.getConfig().getBedrock().port()); geyser.config().bedrock().broadcastPort(geyser.config().bedrock().port());
} }
this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort(); this.broadcastPort = geyser.config().bedrock().broadcastPort();
} }
public CompletableFuture<Void> bind(InetSocketAddress address) { public CompletableFuture<Void> bind(InetSocketAddress address) {
@ -170,12 +174,12 @@ public final class GeyserServer {
.addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this)); .addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this));
// Add proxy handler // Add proxy handler
boolean isProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol(); boolean isProxyProtocol = this.geyser.config().bedrock().enableProxyProtocol();
if (isProxyProtocol) { if (isProxyProtocol) {
channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler()); channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler());
} }
boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty(); boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.config().bedrock().proxyProtocolWhitelistedIps().isEmpty();
if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) { if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) {
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter // We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
channel.pipeline().remove(RakServerRateLimiter.NAME); channel.pipeline().remove(RakServerRateLimiter.NAME);
@ -205,7 +209,7 @@ public final class GeyserServer {
} }
private ServerBootstrap createBootstrap() { private ServerBootstrap createBootstrap() {
if (this.geyser.getConfig().isDebugMode()) { if (this.geyser.config().debugMode()) {
this.geyser.getLogger().debug("EventLoop type: " + TRANSPORT.datagramChannel()); this.geyser.getLogger().debug("EventLoop type: " + TRANSPORT.datagramChannel());
if (TRANSPORT.datagramChannel() == NioDatagramChannel.class) { if (TRANSPORT.datagramChannel() == NioDatagramChannel.class) {
if (System.getProperties().contains("disableNativeEventLoop")) { if (System.getProperties().contains("disableNativeEventLoop")) {
@ -220,7 +224,7 @@ public final class GeyserServer {
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser); GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
playerGroup = serverInitializer.getEventLoopGroup(); playerGroup = serverInitializer.getEventLoopGroup();
this.geyser.getLogger().debug("Setting MTU to " + this.geyser.getConfig().getMtu()); this.geyser.getLogger().debug("Setting MTU to " + this.geyser.config().mtu());
int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT); int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT);
this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit); this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit);
@ -235,7 +239,7 @@ public final class GeyserServer {
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel())) .channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel()))
.group(group, childGroup) .group(group, childGroup)
.option(RakChannelOption.RAK_HANDLE_PING, true) .option(RakChannelOption.RAK_HANDLE_PING, true)
.option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu()) .option(RakChannelOption.RAK_MAX_MTU, this.geyser.config().mtu())
.option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit) .option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit)
.option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit) .option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
.option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie) .option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie)
@ -243,10 +247,10 @@ public final class GeyserServer {
} }
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) { public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
List<String> allowedProxyIPs = geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs(); List<String> allowedProxyIPs = geyser.config().bedrock().proxyProtocolWhitelistedIps();
if (geyser.getConfig().getBedrock().isEnableProxyProtocol() && !allowedProxyIPs.isEmpty()) { if (geyser.config().bedrock().enableProxyProtocol() && !allowedProxyIPs.isEmpty()) {
boolean isWhitelistedIP = false; boolean isWhitelistedIP = false;
for (CIDRMatcher matcher : geyser.getConfig().getBedrock().getWhitelistedIPsMatchers()) { for (CIDRMatcher matcher : getWhitelistedIPsMatchers()) {
if (matcher.matches(inetSocketAddress.getAddress())) { if (matcher.matches(inetSocketAddress.getAddress())) {
isWhitelistedIP = true; isWhitelistedIP = true;
break; break;
@ -260,8 +264,8 @@ public final class GeyserServer {
} }
String ip; String ip;
if (geyser.getConfig().isLogPlayerIpAddresses()) { if (geyser.config().logPlayerIpAddresses()) {
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) { if (geyser.config().bedrock().enableProxyProtocol()) {
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString(); ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
} else { } else {
ip = inetSocketAddress.toString(); ip = inetSocketAddress.toString();
@ -287,10 +291,10 @@ public final class GeyserServer {
} }
public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) { public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) {
if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) { if (geyser.config().debugMode() && PRINT_DEBUG_PINGS) {
String ip; String ip;
if (geyser.getConfig().isLogPlayerIpAddresses()) { if (geyser.config().logPlayerIpAddresses()) {
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) { if (geyser.config().bedrock().enableProxyProtocol()) {
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString(); ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
} else { } else {
ip = inetSocketAddress.toString(); ip = inetSocketAddress.toString();
@ -301,10 +305,10 @@ public final class GeyserServer {
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip)); geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
} }
GeyserConfiguration config = geyser.getConfig(); GeyserConfig config = geyser.config();
GeyserPingInfo pingInfo = null; GeyserPingInfo pingInfo = null;
if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) { if (config.passthroughMotd() || config.passthroughPlayerCounts()) {
IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough(); IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough();
if (pingPassthrough != null) { if (pingPassthrough != null) {
pingInfo = pingPassthrough.getPingInformation(inetSocketAddress); pingInfo = pingPassthrough.getPingInformation(inetSocketAddress);
@ -321,25 +325,25 @@ public final class GeyserServer {
.ipv6Port(this.broadcastPort) .ipv6Port(this.broadcastPort)
.serverId(channel.config().getOption(RakChannelOption.RAK_GUID)); .serverId(channel.config().getOption(RakChannelOption.RAK_GUID));
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) { if (config.passthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n"); String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
String mainMotd = (motd.length > 0) ? motd[0] : config.getBedrock().primaryMotd(); // First line of the motd. String mainMotd = (motd.length > 0) ? motd[0] : config.bedrock().primaryMotd(); // First line of the motd.
String subMotd = (motd.length > 1) ? motd[1] : config.getBedrock().secondaryMotd(); // Second line of the motd if present, otherwise default. String subMotd = (motd.length > 1) ? motd[1] : config.bedrock().secondaryMotd(); // Second line of the motd if present, otherwise default.
pong.motd(mainMotd.trim()); pong.motd(mainMotd.trim());
pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit. pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
} else { } else {
pong.motd(config.getBedrock().primaryMotd()); pong.motd(config.bedrock().primaryMotd());
pong.subMotd(config.getBedrock().secondaryMotd()); pong.subMotd(config.bedrock().secondaryMotd());
} }
// Placed here to prevent overriding values set in the ping event. // Placed here to prevent overriding values set in the ping event.
if (config.isPassthroughPlayerCounts() && pingInfo != null) { if (config.passthroughPlayerCounts() && pingInfo != null) {
pong.playerCount(pingInfo.getPlayers().getOnline()); pong.playerCount(pingInfo.getPlayers().getOnline());
pong.maximumPlayerCount(pingInfo.getPlayers().getMax()); pong.maximumPlayerCount(pingInfo.getPlayers().getMax());
} else { } else {
pong.playerCount(geyser.getSessionManager().getSessions().size()); pong.playerCount(geyser.getSessionManager().getSessions().size());
pong.maximumPlayerCount(config.getMaxPlayers()); pong.maximumPlayerCount(config.maxPlayers());
} }
this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress)); this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress));
@ -390,6 +394,35 @@ public final class GeyserServer {
return pong; return pong;
} }
private List<CIDRMatcher> whitelistedIPsMatchers = null;
/**
* @return Unmodifiable list of {@link CIDRMatcher}s from {@link GeyserConfig.BedrockConfig#proxyProtocolWhitelistedIps()}
*/
public List<CIDRMatcher> getWhitelistedIPsMatchers() {
// Effective Java, Third Edition; Item 83: Use lazy initialization judiciously
List<CIDRMatcher> matchers = this.whitelistedIPsMatchers;
if (matchers == null) {
synchronized (this) {
// Check if proxyProtocolWhitelistedIPs contains URLs we need to fetch and parse by line
List<String> whitelistedCIDRs = new ArrayList<>();
for (String ip: geyser.config().bedrock().proxyProtocolWhitelistedIps()) {
if (!ip.startsWith("http")) {
whitelistedCIDRs.add(ip);
continue;
}
WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add);
}
this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream()
.map(CIDRMatcher::new)
.collect(Collectors.toList());
}
}
return Collections.unmodifiableList(matchers);
}
/** /**
* @return the throwable from the given supplier, or the throwable caught while calling the supplier. * @return the throwable from the given supplier, or the throwable caught while calling the supplier.
*/ */

Datei anzeigen

@ -78,7 +78,7 @@ public class SkullResourcePackManager {
Path packPath = cachePath.resolve("player_skulls.mcpack"); Path packPath = cachePath.resolve("player_skulls.mcpack");
File packFile = packPath.toFile(); File packFile = packPath.toFile();
if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().config().addNonBedrockItems()) {
packFile.delete(); // No need to keep resource pack packFile.delete(); // No need to keep resource pack
return null; return null;
} }
@ -161,7 +161,7 @@ public class SkullResourcePackManager {
} }
} catch (IOException e) { } catch (IOException e) {
GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache."); GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache.");
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
} }

Datei anzeigen

@ -65,10 +65,10 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
* @return GeyserPingPassthrough, or null if not initialized * @return GeyserPingPassthrough, or null if not initialized
*/ */
public static @Nullable IGeyserPingPassthrough init(GeyserImpl geyser) { public static @Nullable IGeyserPingPassthrough init(GeyserImpl geyser) {
if (geyser.getConfig().isPassthroughMotd() || geyser.getConfig().isPassthroughPlayerCounts()) { if (geyser.config().passthroughMotd() || geyser.config().passthroughPlayerCounts()) {
GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(geyser); GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(geyser);
// Ensure delay is not zero // Ensure delay is not zero
int interval = (geyser.getConfig().getPingPassthroughInterval() == 0) ? 1 : geyser.getConfig().getPingPassthroughInterval(); int interval = (geyser.config().pingPassthroughInterval() == 0) ? 1 : geyser.config().pingPassthroughInterval();
geyser.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s)."); geyser.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s).");
geyser.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS); geyser.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
return pingPassthrough; return pingPassthrough;
@ -84,8 +84,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
@Override @Override
public void run() { public void run() {
try (Socket socket = new Socket()) { try (Socket socket = new Socket()) {
String address = geyser.getConfig().getRemote().address(); String address = geyser.config().java().address();
int port = geyser.getConfig().getRemote().port(); int port = geyser.config().java().port();
InetSocketAddress endpoint = new InetSocketAddress(address, port); InetSocketAddress endpoint = new InetSocketAddress(address, port);
socket.connect(endpoint, 5000); socket.connect(endpoint, 5000);
@ -102,7 +102,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
byte[] buffer; byte[] buffer;
try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) { try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) {
if (geyser.getConfig().getRemote().isUseProxyProtocol()) { if (geyser.config().java().useProxyProtocol()) {
// HAProxy support // HAProxy support
// Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78 // Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78
dataOutputStream.write(HAPROXY_BINARY_PREFIX); dataOutputStream.write(HAPROXY_BINARY_PREFIX);

Datei anzeigen

@ -70,7 +70,7 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
} }
return true; return true;
} else { } else {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
if (!IGNORED_PACKETS.contains(clazz)) { if (!IGNORED_PACKETS.contains(clazz)) {
GeyserImpl.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet)); GeyserImpl.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
} }

Datei anzeigen

@ -90,7 +90,7 @@ public class CustomBlockRegistryPopulator {
* @param stage the stage to populate * @param stage the stage to populate
*/ */
public static void populate(Stage stage) { public static void populate(Stage stage) {
if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { if (!GeyserImpl.getInstance().config().addNonBedrockItems()) {
return; return;
} }

Datei anzeigen

@ -55,7 +55,7 @@ public class CustomSkullRegistryPopulator {
SkullResourcePackManager.SKULL_SKINS.clear(); // Remove skins after reloading SkullResourcePackManager.SKULL_SKINS.clear(); // Remove skins after reloading
BlockRegistries.CUSTOM_SKULLS.set(Object2ObjectMaps.emptyMap()); BlockRegistries.CUSTOM_SKULLS.set(Object2ObjectMaps.emptyMap());
if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { if (!GeyserImpl.getInstance().config().addNonBedrockItems()) {
return; return;
} }
@ -65,7 +65,6 @@ public class CustomSkullRegistryPopulator {
Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml"); Path skullConfigPath = bootstrap.getConfigFolder().resolve("custom-skulls.yml");
File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap); File skullConfigFile = FileUtils.fileOrCopiedFromResource(skullConfigPath.toFile(), "custom-skulls.yml", Function.identity(), bootstrap);
skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class); skullConfig = FileUtils.loadConfigNew(skullConfigFile, GeyserCustomSkullConfiguration.class);
System.out.println(skullConfig);
} catch (IOException e) { } catch (IOException e) {
GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e); GeyserImpl.getInstance().getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e);
return; return;

Datei anzeigen

@ -129,7 +129,7 @@ public class ItemRegistryPopulator {
throw new AssertionError("Unable to load Bedrock item components", e); throw new AssertionError("Unable to load Bedrock item components", e);
} }
boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems(); boolean customItemsAllowed = GeyserImpl.getInstance().config().addNonBedrockItems();
// List values here is important compared to HashSet - we need to preserve the order of what's given to us // List values here is important compared to HashSet - we need to preserve the order of what's given to us
// (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom // (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom

Datei anzeigen

@ -28,7 +28,7 @@ package org.geysermc.geyser.scoreboard;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.cache.WorldCache; import org.geysermc.geyser.session.cache.WorldCache;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
@ -46,9 +46,9 @@ public final class ScoreboardUpdater extends Thread {
private static final boolean DEBUG_ENABLED; private static final boolean DEBUG_ENABLED;
static { static {
GeyserConfiguration config = GeyserImpl.getInstance().getConfig(); GeyserConfig config = GeyserImpl.getInstance().config();
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD); FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
DEBUG_ENABLED = config.isDebugMode(); DEBUG_ENABLED = config.debugMode();
} }
private final GeyserImpl geyser = GeyserImpl.getInstance(); private final GeyserImpl geyser = GeyserImpl.getInstance();

Datei anzeigen

@ -46,17 +46,52 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.common.value.qual.IntRange; import org.checkerframework.common.value.qual.IntRange;
import org.cloudburstmc.math.vector.*; import org.cloudburstmc.math.vector.Vector2f;
import org.cloudburstmc.math.vector.Vector2i;
import org.cloudburstmc.math.vector.Vector3d;
import org.cloudburstmc.math.vector.Vector3f;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons; import org.cloudburstmc.protocol.bedrock.BedrockDisconnectReasons;
import org.cloudburstmc.protocol.bedrock.BedrockServerSession; import org.cloudburstmc.protocol.bedrock.BedrockServerSession;
import org.cloudburstmc.protocol.bedrock.data.*; import org.cloudburstmc.protocol.bedrock.data.Ability;
import org.cloudburstmc.protocol.bedrock.data.AbilityLayer;
import org.cloudburstmc.protocol.bedrock.data.AuthoritativeMovementMode;
import org.cloudburstmc.protocol.bedrock.data.ChatRestrictionLevel;
import org.cloudburstmc.protocol.bedrock.data.ExperimentData;
import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting;
import org.cloudburstmc.protocol.bedrock.data.GameRuleData;
import org.cloudburstmc.protocol.bedrock.data.GameType;
import org.cloudburstmc.protocol.bedrock.data.PlayerPermission;
import org.cloudburstmc.protocol.bedrock.data.SoundEvent;
import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType;
import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData; import org.cloudburstmc.protocol.bedrock.data.command.CommandEnumData;
import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType; import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData;
import org.cloudburstmc.protocol.bedrock.packet.*; import org.cloudburstmc.protocol.bedrock.packet.AvailableEntityIdentifiersPacket;
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket;
import org.cloudburstmc.protocol.bedrock.packet.CameraPresetsPacket;
import org.cloudburstmc.protocol.bedrock.packet.ChunkRadiusUpdatedPacket;
import org.cloudburstmc.protocol.bedrock.packet.CraftingDataPacket;
import org.cloudburstmc.protocol.bedrock.packet.CreativeContentPacket;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.cloudburstmc.protocol.bedrock.packet.GameRulesChangedPacket;
import org.cloudburstmc.protocol.bedrock.packet.ItemComponentPacket;
import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEvent2Packet;
import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket;
import org.cloudburstmc.protocol.bedrock.packet.SetTimePacket;
import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket;
import org.cloudburstmc.protocol.bedrock.packet.SyncEntityPropertyPacket;
import org.cloudburstmc.protocol.bedrock.packet.TextPacket;
import org.cloudburstmc.protocol.bedrock.packet.TransferPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAbilitiesPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAdventureSettingsPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateClientInputLocksPacket;
import org.cloudburstmc.protocol.bedrock.packet.UpdateSoftEnumPacket;
import org.cloudburstmc.protocol.common.DefinitionRegistry; import org.cloudburstmc.protocol.common.DefinitionRegistry;
import org.cloudburstmc.protocol.common.util.OptionalBoolean; import org.cloudburstmc.protocol.common.util.OptionalBoolean;
import org.geysermc.api.util.BedrockPlatform; import org.geysermc.api.util.BedrockPlatform;
@ -80,7 +115,6 @@ import org.geysermc.geyser.api.network.AuthType;
import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.api.network.RemoteServer;
import org.geysermc.geyser.api.util.PlatformType; import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandSource; import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.configuration.GeyserConfiguration; import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.EntityDefinitions;
import org.geysermc.geyser.entity.GeyserEntityData; import org.geysermc.geyser.entity.GeyserEntityData;
@ -107,7 +141,22 @@ import org.geysermc.geyser.registry.type.BlockMappings;
import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.registry.type.ItemMappings;
import org.geysermc.geyser.session.auth.AuthData; import org.geysermc.geyser.session.auth.AuthData;
import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.session.auth.BedrockClientData;
import org.geysermc.geyser.session.cache.*; import org.geysermc.geyser.session.cache.AdvancementsCache;
import org.geysermc.geyser.session.cache.BookEditCache;
import org.geysermc.geyser.session.cache.ChunkCache;
import org.geysermc.geyser.session.cache.EntityCache;
import org.geysermc.geyser.session.cache.EntityEffectCache;
import org.geysermc.geyser.session.cache.FormCache;
import org.geysermc.geyser.session.cache.LodestoneCache;
import org.geysermc.geyser.session.cache.PistonCache;
import org.geysermc.geyser.session.cache.PreferencesCache;
import org.geysermc.geyser.session.cache.RegistryCache;
import org.geysermc.geyser.session.cache.SkullCache;
import org.geysermc.geyser.session.cache.StructureBlockCache;
import org.geysermc.geyser.session.cache.TagCache;
import org.geysermc.geyser.session.cache.TeleportCache;
import org.geysermc.geyser.session.cache.WorldBorder;
import org.geysermc.geyser.session.cache.WorldCache;
import org.geysermc.geyser.skin.FloodgateSkinUploader; import org.geysermc.geyser.skin.FloodgateSkinUploader;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.text.MinecraftLocale;
@ -119,7 +168,11 @@ import org.geysermc.geyser.util.EntityUtils;
import org.geysermc.geyser.util.LoginEncryptionUtils; import org.geysermc.geyser.util.LoginEncryptionUtils;
import org.geysermc.mcprotocollib.network.BuiltinFlags; import org.geysermc.mcprotocollib.network.BuiltinFlags;
import org.geysermc.mcprotocollib.network.Session; import org.geysermc.mcprotocollib.network.Session;
import org.geysermc.mcprotocollib.network.event.session.*; import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent;
import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent;
import org.geysermc.mcprotocollib.network.event.session.PacketErrorEvent;
import org.geysermc.mcprotocollib.network.event.session.PacketSendingEvent;
import org.geysermc.mcprotocollib.network.event.session.SessionAdapter;
import org.geysermc.mcprotocollib.network.packet.Packet; import org.geysermc.mcprotocollib.network.packet.Packet;
import org.geysermc.mcprotocollib.network.tcp.TcpClientSession; import org.geysermc.mcprotocollib.network.tcp.TcpClientSession;
import org.geysermc.mcprotocollib.network.tcp.TcpSession; import org.geysermc.mcprotocollib.network.tcp.TcpSession;
@ -154,7 +207,16 @@ import java.net.ConnectException;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledFuture;
@ -600,12 +662,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.spawned = false; this.spawned = false;
this.loggedIn = false; this.loggedIn = false;
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) { this.emotes = new HashSet<>();
this.emotes = new HashSet<>(); geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
} else {
this.emotes = null;
}
this.remoteServer = geyser.defaultRemoteServer(); this.remoteServer = geyser.defaultRemoteServer();
} }
@ -618,7 +676,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
sentSpawnPacket = true; sentSpawnPacket = true;
syncEntityProperties(); syncEntityProperties();
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) { if (GeyserImpl.getInstance().config().addNonBedrockItems()) {
ItemComponentPacket componentPacket = new ItemComponentPacket(); ItemComponentPacket componentPacket = new ItemComponentPacket();
componentPacket.getItems().addAll(itemMappings.getComponentItemData()); componentPacket.getItems().addAll(itemMappings.getComponentItemData());
upstream.sendPacket(componentPacket); upstream.sendPacket(componentPacket);
@ -870,11 +928,11 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
// Disable automatic creation of a new TcpClientSession when transferring - we don't use that functionality. // Disable automatic creation of a new TcpClientSession when transferring - we don't use that functionality.
this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false); this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false);
if (geyser.getConfig().getRemote().isUseProxyProtocol()) { if (geyser.config().java().useProxyProtocol()) {
downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true); downstream.setFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, true);
downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress()); downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
} }
if (geyser.getConfig().isForwardPlayerPing()) { if (geyser.config().forwardPlayerPing()) {
// Let Geyser handle sending the keep alive // Let Geyser handle sending the keep alive
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false); downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
} }
@ -943,7 +1001,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
ClientIntentionPacket intentionPacket = event.getPacket(); ClientIntentionPacket intentionPacket = event.getPacket();
String address; String address;
if (geyser.getConfig().getRemote().isForwardHost()) { if (geyser.config().java().forwardHostname()) {
address = clientData.getServerAddress().split(":")[0]; address = clientData.getServerAddress().split(":")[0];
} else { } else {
address = intentionPacket.getHostname(); address = intentionPacket.getHostname();
@ -1035,7 +1093,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} else { } else {
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause); GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
} }
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
cause.printStackTrace(); cause.printStackTrace();
} }
} }
@ -1058,7 +1116,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Override @Override
public void packetError(PacketErrorEvent event) { public void packetError(PacketErrorEvent event) {
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage())); geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage()));
if (geyser.getConfig().isDebugMode()) if (geyser.config().debugMode())
event.getCause().printStackTrace(); event.getCause().printStackTrace();
event.setSuppress(true); event.setSuppress(true);
} }
@ -1090,7 +1148,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} else { } else {
// Downstream's disconnect will fire an event that prints a log message // Downstream's disconnect will fire an event that prints a log message
// Otherwise, we print a message here // Otherwise, we print a message here
String address = geyser.getConfig().isLogPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : "<IP address withheld>"; String address = geyser.config().logPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : "<IP address withheld>";
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, reason)); geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, reason));
} }
@ -1482,7 +1540,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setLevelGameType(GameType.SURVIVAL); startGamePacket.setLevelGameType(GameType.SURVIVAL);
startGamePacket.setDifficulty(1); startGamePacket.setDifficulty(1);
startGamePacket.setDefaultSpawn(Vector3i.ZERO); startGamePacket.setDefaultSpawn(Vector3i.ZERO);
startGamePacket.setAchievementsDisabled(!geyser.getConfig().isXboxAchievementsEnabled()); startGamePacket.setAchievementsDisabled(!geyser.config().xboxAchievementsEnabled());
startGamePacket.setCurrentTick(-1); startGamePacket.setCurrentTick(-1);
startGamePacket.setEduEditionOffers(0); startGamePacket.setEduEditionOffers(0);
startGamePacket.setEduFeaturesEnabled(false); startGamePacket.setEduFeaturesEnabled(false);
@ -1492,7 +1550,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setBroadcastingToLan(true); startGamePacket.setBroadcastingToLan(true);
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled()); startGamePacket.setCommandsEnabled(!geyser.config().xboxAchievementsEnabled());
startGamePacket.setTexturePacksRequired(false); startGamePacket.setTexturePacksRequired(false);
startGamePacket.setBonusChestEnabled(false); startGamePacket.setBonusChestEnabled(false);
startGamePacket.setStartingWithMap(false); startGamePacket.setStartingWithMap(false);
@ -1510,7 +1568,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setEducationProductionId(""); startGamePacket.setEducationProductionId("");
startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty()); startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty());
String serverName = geyser.getConfig().getBedrock().serverName(); String serverName = geyser.config().bedrock().serverName();
startGamePacket.setLevelId(serverName); startGamePacket.setLevelId(serverName);
startGamePacket.setLevelName(serverName); startGamePacket.setLevelName(serverName);
@ -1634,7 +1692,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) { public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) {
// protocol can be null when we're not yet logged in (online auth) // protocol can be null when we're not yet logged in (online auth)
if (protocol == null) { if (protocol == null) {
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
geyser.getLogger().debug("Tried to send downstream packet with no downstream session!"); geyser.getLogger().debug("Tried to send downstream packet with no downstream session!");
Thread.dumpStack(); Thread.dumpStack();
} }
@ -1660,7 +1718,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (channel == null) { if (channel == null) {
// Channel is only null before the connection has initialized // Channel is only null before the connection has initialized
geyser.getLogger().warning("Tried to send a packet to the Java server too early!"); geyser.getLogger().warning("Tried to send a packet to the Java server too early!");
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
Thread.dumpStack(); Thread.dumpStack();
} }
return; return;
@ -2051,7 +2109,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) { private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
// There is no need to send command enums if command suggestions are disabled // There is no need to send command enums if command suggestions are disabled
if (!this.geyser.getConfig().isCommandSuggestions()) { if (!this.geyser.config().commandSuggestions()) {
return; return;
} }
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket(); UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();

Datei anzeigen

@ -25,93 +25,97 @@
package org.geysermc.geyser.session.auth; package org.geysermc.geyser.session.auth;
import com.fasterxml.jackson.annotation.JsonIgnore; import com.google.gson.JsonDeserializationContext;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.google.gson.JsonDeserializer;
import com.fasterxml.jackson.annotation.JsonProperty; import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.annotations.JsonAdapter;
import com.google.gson.annotations.SerializedName;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.geysermc.floodgate.util.DeviceOs; import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.InputMode; import org.geysermc.floodgate.util.InputMode;
import org.geysermc.floodgate.util.UiProfile; import org.geysermc.floodgate.util.UiProfile;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.UUID; import java.util.UUID;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter @Getter
public final class BedrockClientData { public final class BedrockClientData {
@JsonProperty(value = "GameVersion") @SerializedName(value = "GameVersion")
private String gameVersion; private String gameVersion;
@JsonProperty(value = "ServerAddress") @SerializedName(value = "ServerAddress")
private String serverAddress; private String serverAddress;
@JsonProperty(value = "ThirdPartyName") @SerializedName(value = "ThirdPartyName")
private String username; private String username;
@JsonProperty(value = "LanguageCode") @SerializedName(value = "LanguageCode")
private String languageCode; private String languageCode;
@JsonProperty(value = "SkinId") @SerializedName(value = "SkinId")
private String skinId; private String skinId;
@JsonProperty(value = "SkinData") @SerializedName(value = "SkinData")
private String skinData; private String skinData;
@JsonProperty(value = "SkinImageHeight") @SerializedName(value = "SkinImageHeight")
private int skinImageHeight; private int skinImageHeight;
@JsonProperty(value = "SkinImageWidth") @SerializedName(value = "SkinImageWidth")
private int skinImageWidth; private int skinImageWidth;
@JsonProperty(value = "CapeId") @SerializedName(value = "CapeId")
private String capeId; private String capeId;
@JsonProperty(value = "CapeData") @SerializedName(value = "CapeData")
@JsonAdapter(value = StringToByteDeserializer.class)
private byte[] capeData; private byte[] capeData;
@JsonProperty(value = "CapeImageHeight") @SerializedName(value = "CapeImageHeight")
private int capeImageHeight; private int capeImageHeight;
@JsonProperty(value = "CapeImageWidth") @SerializedName(value = "CapeImageWidth")
private int capeImageWidth; private int capeImageWidth;
@JsonProperty(value = "CapeOnClassicSkin") @SerializedName(value = "CapeOnClassicSkin")
private boolean capeOnClassicSkin; private boolean capeOnClassicSkin;
@JsonProperty(value = "SkinResourcePatch") @SerializedName(value = "SkinResourcePatch")
private String geometryName; private String geometryName;
@JsonProperty(value = "SkinGeometryData") @SerializedName(value = "SkinGeometryData")
private String geometryData; private String geometryData;
@JsonProperty(value = "PersonaSkin") @SerializedName(value = "PersonaSkin")
private boolean personaSkin; private boolean personaSkin;
@JsonProperty(value = "PremiumSkin") @SerializedName(value = "PremiumSkin")
private boolean premiumSkin; private boolean premiumSkin;
@JsonProperty(value = "DeviceId") @SerializedName(value = "DeviceId")
private String deviceId; private String deviceId;
@JsonProperty(value = "DeviceModel") @SerializedName(value = "DeviceModel")
private String deviceModel; private String deviceModel;
@JsonProperty(value = "DeviceOS") @SerializedName(value = "DeviceOS")
private DeviceOs deviceOs; private DeviceOs deviceOs;
@JsonProperty(value = "UIProfile") @SerializedName(value = "UIProfile")
private UiProfile uiProfile; private UiProfile uiProfile;
@JsonProperty(value = "GuiScale") @SerializedName(value = "GuiScale")
private int guiScale; private int guiScale;
@JsonProperty(value = "CurrentInputMode") @SerializedName(value = "CurrentInputMode")
private InputMode currentInputMode; private InputMode currentInputMode;
@JsonProperty(value = "DefaultInputMode") @SerializedName(value = "DefaultInputMode")
private InputMode defaultInputMode; private InputMode defaultInputMode;
@JsonProperty("PlatformOnlineId") @SerializedName("PlatformOnlineId")
private String platformOnlineId; private String platformOnlineId;
@JsonProperty(value = "PlatformOfflineId") @SerializedName(value = "PlatformOfflineId")
private String platformOfflineId; private String platformOfflineId;
@JsonProperty(value = "SelfSignedId") @SerializedName(value = "SelfSignedId")
private UUID selfSignedId; private UUID selfSignedId;
@JsonProperty(value = "ClientRandomId") @SerializedName(value = "ClientRandomId")
private long clientRandomId; private long clientRandomId;
@JsonProperty(value = "ArmSize") @SerializedName(value = "ArmSize")
private String armSize; private String armSize;
@JsonProperty(value = "SkinAnimationData") @SerializedName(value = "SkinAnimationData")
private String skinAnimationData; private String skinAnimationData;
@JsonProperty(value = "SkinColor") @SerializedName(value = "SkinColor")
private String skinColor; private String skinColor;
@JsonProperty(value = "ThirdPartyNameOnly") @SerializedName(value = "ThirdPartyNameOnly")
private boolean thirdPartyNameOnly; private boolean thirdPartyNameOnly;
@JsonProperty(value = "PlayFabId") @SerializedName(value = "PlayFabId")
private String playFabId; private String playFabId;
@JsonIgnore
@Setter @Setter
private String originalString = null; private transient String originalString = null;
public DeviceOs getDeviceOs() { public DeviceOs getDeviceOs() {
return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN; return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN;
@ -128,4 +132,11 @@ public final class BedrockClientData {
public UiProfile getUiProfile() { public UiProfile getUiProfile() {
return uiProfile != null ? uiProfile : UiProfile.CLASSIC; return uiProfile != null ? uiProfile : UiProfile.CLASSIC;
} }
private static final class StringToByteDeserializer implements JsonDeserializer<byte[]> {
@Override
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return json.getAsString().getBytes(StandardCharsets.UTF_8);
}
}
} }

Datei anzeigen

@ -54,15 +54,16 @@ public class PreferencesCache {
private boolean prefersCustomSkulls; private boolean prefersCustomSkulls;
/** /**
* Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}. * Which CooldownType the client prefers. Initially set to the config default.
*/ */
@Setter @Setter
private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown(); private CooldownUtils.CooldownType cooldownPreference;
public PreferencesCache(GeyserSession session) { public PreferencesCache(GeyserSession session) {
this.session = session; this.session = session;
prefersCustomSkulls = session.getGeyser().getConfig().isAllowCustomSkulls(); prefersCustomSkulls = session.getGeyser().config().allowCustomSkulls();
cooldownPreference = session.getGeyser().config().showCooldown();
} }
/** /**
@ -74,7 +75,7 @@ public class PreferencesCache {
* {@link GeyserConfiguration#isShowCoordinates()} is disabled * {@link GeyserConfiguration#isShowCoordinates()} is disabled
*/ */
public void updateShowCoordinates() { public void updateShowCoordinates() {
allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().getConfig().isShowCoordinates(); allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().config().showCoordinates();
session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates);
} }
@ -82,6 +83,6 @@ public class PreferencesCache {
* @return true if the session prefers custom skulls, and the config allows them. * @return true if the session prefers custom skulls, and the config allows them.
*/ */
public boolean showCustomSkulls() { public boolean showCustomSkulls() {
return prefersCustomSkulls && session.getGeyser().getConfig().isAllowCustomSkulls(); return prefersCustomSkulls && session.getGeyser().config().allowCustomSkulls();
} }
} }

Datei anzeigen

@ -74,11 +74,11 @@ public class SkullCache {
public SkullCache(GeyserSession session) { public SkullCache(GeyserSession session) {
this.session = session; this.session = session;
this.maxVisibleSkulls = session.getGeyser().getConfig().getMaxVisibleCustomSkulls(); this.maxVisibleSkulls = session.getGeyser().config().maxVisibleCustomSkulls();
this.cullingEnabled = this.maxVisibleSkulls != -1; this.cullingEnabled = this.maxVisibleSkulls != -1;
// Normal skulls are not rendered beyond 64 blocks // Normal skulls are not rendered beyond 64 blocks
int distance = Math.min(session.getGeyser().getConfig().getCustomSkullRenderDistance(), 64); int distance = Math.min(session.getGeyser().config().customSkullRenderDistance(), 64);
this.skullRenderDistanceSquared = distance * distance; this.skullRenderDistanceSquared = distance * distance;
} }
@ -98,7 +98,7 @@ public class SkullCache {
} }
} catch (IOException e) { } catch (IOException e) {
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty); session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
} }

Datei anzeigen

@ -25,11 +25,9 @@
package org.geysermc.geyser.skin; package org.geysermc.geyser.skin;
import com.fasterxml.jackson.core.JsonProcessingException; import com.google.gson.JsonArray;
import com.fasterxml.jackson.databind.JsonNode; import com.google.gson.JsonObject;
import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.JsonSyntaxException;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Getter; import lombok.Getter;
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels; import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
import org.geysermc.floodgate.util.WebsocketEventType; import org.geysermc.floodgate.util.WebsocketEventType;
@ -37,6 +35,7 @@ import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.JsonUtils;
import org.geysermc.geyser.util.PluginMessageUtils; import org.geysermc.geyser.util.PluginMessageUtils;
import org.java_websocket.client.WebSocketClient; import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake; import org.java_websocket.handshake.ServerHandshake;
@ -52,7 +51,6 @@ import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
public final class FloodgateSkinUploader { public final class FloodgateSkinUploader {
private final ObjectMapper JACKSON = new ObjectMapper();
private final List<String> skinQueue = new ArrayList<>(); private final List<String> skinQueue = new ArrayList<>();
private final GeyserLogger logger; private final GeyserLogger logger;
@ -79,15 +77,14 @@ public final class FloodgateSkinUploader {
@Override @Override
public void onMessage(String message) { public void onMessage(String message) {
// The reason why I don't like Jackson
try { try {
JsonNode node = JACKSON.readTree(message); JsonObject node = JsonUtils.parseJson(message);
if (node.has("error")) { if (node.has("error")) {
logger.error("Got an error: " + node.get("error").asText()); logger.error("Got an error: " + node.get("error").getAsString());
return; return;
} }
int typeId = node.get("event_id").asInt(); int typeId = node.get("event_id").getAsInt();
WebsocketEventType type = WebsocketEventType.fromId(typeId); WebsocketEventType type = WebsocketEventType.fromId(typeId);
if (type == null) { if (type == null) {
logger.warning(String.format( logger.warning(String.format(
@ -98,11 +95,11 @@ public final class FloodgateSkinUploader {
switch (type) { switch (type) {
case SUBSCRIBER_CREATED: case SUBSCRIBER_CREATED:
id = node.get("id").asInt(); id = node.get("id").getAsInt();
verifyCode = node.get("verify_code").asText(); verifyCode = node.get("verify_code").getAsString();
break; break;
case SUBSCRIBER_COUNT: case SUBSCRIBER_COUNT:
subscribersCount = node.get("subscribers_count").asInt(); subscribersCount = node.get("subscribers_count").getAsInt();
break; break;
case SKIN_UPLOADED: case SKIN_UPLOADED:
// if Geyser is the only subscriber we have send it to the server manually // if Geyser is the only subscriber we have send it to the server manually
@ -111,19 +108,19 @@ public final class FloodgateSkinUploader {
break; break;
} }
String xuid = node.get("xuid").asText(); String xuid = node.get("xuid").getAsString();
GeyserSession session = geyser.connectionByXuid(xuid); GeyserSession session = geyser.connectionByXuid(xuid);
if (session != null) { if (session != null) {
if (!node.get("success").asBoolean()) { if (!node.get("success").getAsBoolean()) {
logger.info("Failed to upload skin for " + session.bedrockUsername()); logger.info("Failed to upload skin for " + session.bedrockUsername());
return; return;
} }
JsonNode data = node.get("data"); JsonObject data = node.getAsJsonObject("data");
String value = data.get("value").asText(); String value = data.get("value").getAsString();
String signature = data.get("signature").asText(); String signature = data.get("signature").getAsString();
byte[] bytes = (value + '\0' + signature) byte[] bytes = (value + '\0' + signature)
.getBytes(StandardCharsets.UTF_8); .getBytes(StandardCharsets.UTF_8);
@ -131,8 +128,8 @@ public final class FloodgateSkinUploader {
} }
break; break;
case LOG_MESSAGE: case LOG_MESSAGE:
String logMessage = node.get("message").asText(); String logMessage = node.get("message").getAsString();
switch (node.get("priority").asInt()) { switch (node.get("priority").getAsInt()) {
case -1 -> logger.debug("Got a message from skin uploader: " + logMessage); case -1 -> logger.debug("Got a message from skin uploader: " + logMessage);
case 0 -> logger.info("Got a message from skin uploader: " + logMessage); case 0 -> logger.info("Got a message from skin uploader: " + logMessage);
case 1 -> logger.error("Got a message from skin uploader: " + logMessage); case 1 -> logger.error("Got a message from skin uploader: " + logMessage);
@ -150,20 +147,19 @@ public final class FloodgateSkinUploader {
@Override @Override
public void onClose(int code, String reason, boolean remote) { public void onClose(int code, String reason, boolean remote) {
if (reason != null && !reason.isEmpty()) { if (reason != null && !reason.isEmpty()) {
// The reason why I don't like Jackson
try { try {
JsonNode node = JACKSON.readTree(reason); JsonObject node = JsonUtils.parseJson(reason);
// info means that the uploader itself did nothing wrong // info means that the uploader itself did nothing wrong
if (node.has("info")) { if (node.has("info")) {
String info = node.get("info").asText(); String info = node.get("info").getAsString();
logger.debug("Got disconnected from the skin uploader: " + info); logger.debug("Got disconnected from the skin uploader: " + info);
} }
// error means that the uploader did something wrong // error means that the uploader did something wrong
if (node.has("error")) { if (node.has("error")) {
String error = node.get("error").asText(); String error = node.get("error").getAsString();
logger.info("Got disconnected from the skin uploader: " + error); logger.info("Got disconnected from the skin uploader: " + error);
} }
} catch (JsonProcessingException ignored) { } catch (JsonSyntaxException ignored) {
// ignore invalid json // ignore invalid json
} catch (Exception e) { } catch (Exception e) {
logger.error("Error while handling onClose", e); logger.error("Error while handling onClose", e);
@ -195,20 +191,13 @@ public final class FloodgateSkinUploader {
return; return;
} }
ObjectNode node = JACKSON.createObjectNode(); JsonObject node = new JsonObject();
ArrayNode chainDataNode = JACKSON.createArrayNode(); JsonArray chainDataNode = new JsonArray();
chainData.forEach(chainDataNode::add); chainData.forEach(chainDataNode::add);
node.set("chain_data", chainDataNode); node.add("chain_data", chainDataNode);
node.put("client_data", clientData); node.addProperty("client_data", clientData);
// The reason why I don't like Jackson String jsonString = node.toString();
String jsonString;
try {
jsonString = JACKSON.writeValueAsString(node);
} catch (Exception e) {
logger.error("Failed to upload skin", e);
return;
}
if (client.isOpen()) { if (client.isOpen()) {
client.send(jsonString); client.send(jsonString);

Datei anzeigen

@ -25,7 +25,8 @@
package org.geysermc.geyser.skin; package org.geysermc.geyser.skin;
import com.fasterxml.jackson.databind.JsonNode; import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.checker.nullness.qual.Nullable;
import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMap;
import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.nbt.NbtType;
@ -43,6 +44,7 @@ import org.geysermc.geyser.entity.type.player.SkullPlayerEntity;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.session.auth.BedrockClientData; import org.geysermc.geyser.session.auth.BedrockClientData;
import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.JsonUtils;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -194,7 +196,7 @@ public class SkinManager {
public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) { public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) {
GeyserImpl geyser = GeyserImpl.getInstance(); GeyserImpl geyser = GeyserImpl.getInstance();
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid())); geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.register", playerEntity.getUsername(), playerEntity.getUuid()));
} }
@ -208,7 +210,7 @@ public class SkinManager {
if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) { if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) {
SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes); SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes);
SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes); SkinProvider.storeBedrockGeometry(playerEntity.getUuid(), geometryNameBytes, geometryBytes);
} else if (geyser.getConfig().isDebugMode()) { } else if (geyser.config().debugMode()) {
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername())); geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.skin.bedrock.fail", playerEntity.getUsername()));
geyser.getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight()); geyser.getLogger().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight());
} }
@ -246,7 +248,7 @@ public class SkinManager {
return loadFromJson(skinDataValue); return loadFromJson(skinDataValue);
} catch (IOException e) { } catch (IOException e) {
GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for tag " + tag); GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for tag " + tag);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
@ -274,7 +276,7 @@ public class SkinManager {
} else { } else {
GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername() + " with Value: " + texturesProperty); GeyserImpl.getInstance().getLogger().debug("Something went wrong while processing skin for " + entity.getUsername() + " with Value: " + texturesProperty);
} }
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
exception.printStackTrace(); exception.printStackTrace();
} }
} }
@ -282,30 +284,25 @@ public class SkinManager {
} }
public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException { public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException {
// TODO use GameProfile method. JsonObject skinObject;
JsonNode skinObject;
try { try {
skinObject = GeyserImpl.JSON_MAPPER.readTree(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8)); skinObject = JsonUtils.parseJson(new String(Base64.getDecoder().decode(encodedJson), StandardCharsets.UTF_8));
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
GeyserImpl.getInstance().getLogger().debug("Invalid base64 encoded skin entry: " + encodedJson); GeyserImpl.getInstance().getLogger().debug("Invalid base64 encoded skin entry: " + encodedJson);
return null; return null;
} }
JsonNode textures = skinObject.get("textures"); if (!(skinObject.get("textures") instanceof JsonObject textures)) {
if (textures == null) {
return null; return null;
} }
JsonNode skinTexture = textures.get("SKIN"); if (!(textures.get("SKIN") instanceof JsonObject skinTexture)) {
if (skinTexture == null) {
return null; return null;
} }
String skinUrl; String skinUrl;
JsonNode skinUrlNode = skinTexture.get("url"); if (skinTexture.get("url") instanceof JsonPrimitive skinUrlNode && skinUrlNode.isString()) {
if (skinUrlNode != null && skinUrlNode.isTextual()) { skinUrl = skinUrlNode.getAsString().replace("http://", "https://");
skinUrl = skinUrlNode.asText().replace("http://", "https://");
} else { } else {
return null; return null;
} }
@ -322,11 +319,9 @@ public class SkinManager {
boolean isAlex = skinTexture.has("metadata"); boolean isAlex = skinTexture.has("metadata");
String capeUrl = null; String capeUrl = null;
JsonNode capeTexture = textures.get("CAPE"); if (textures.get("CAPE") instanceof JsonObject capeTexture) {
if (capeTexture != null) { if (capeTexture.get("url") instanceof JsonPrimitive capeUrlNode && capeUrlNode.isString()) {
JsonNode capeUrlNode = capeTexture.get("url"); capeUrl = capeUrlNode.getAsString().replace("http://", "https://");
if (capeUrlNode != null && capeUrlNode.isTextual()) {
capeUrl = capeUrlNode.asText().replace("http://", "https://");
} }
} }

Datei anzeigen

@ -126,11 +126,6 @@ public class SkinProvider {
WEARING_CUSTOM_SKULL = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkull\"}}", wearingCustomSkull); WEARING_CUSTOM_SKULL = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkull\"}}", wearingCustomSkull);
String wearingCustomSkullSlim = new String(FileUtils.readAllBytes("bedrock/skin/geometry.humanoid.wearingCustomSkullSlim.json"), StandardCharsets.UTF_8); String wearingCustomSkullSlim = new String(FileUtils.readAllBytes("bedrock/skin/geometry.humanoid.wearingCustomSkullSlim.json"), StandardCharsets.UTF_8);
WEARING_CUSTOM_SKULL_SLIM = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkullSlim\"}}", wearingCustomSkullSlim); WEARING_CUSTOM_SKULL_SLIM = new SkinGeometry("{\"geometry\" :{\"default\" :\"geometry.humanoid.wearingCustomSkullSlim\"}}", wearingCustomSkullSlim);
GeyserImpl geyser = GeyserImpl.getInstance();
if (geyser.getConfig().isAllowThirdPartyEars() || geyser.getConfig().isAllowThirdPartyCapes()) {
geyser.getLogger().warning("Third-party ears/capes have been removed from Geyser, if you still wish to have this functionality please use the extension: https://github.com/GeyserMC/ThirdPartyCosmetics");
}
} }
public static ExecutorService getExecutorService() { public static ExecutorService getExecutorService() {
@ -149,7 +144,7 @@ public class SkinProvider {
public static void registerCacheImageTask(GeyserImpl geyser) { public static void registerCacheImageTask(GeyserImpl geyser) {
// Schedule Daily Image Expiry if we are caching them // Schedule Daily Image Expiry if we are caching them
if (geyser.getConfig().getCacheImages() > 0) { if (geyser.config().cacheImages() > 0) {
geyser.getScheduledThread().scheduleAtFixedRate(() -> { geyser.getScheduledThread().scheduleAtFixedRate(() -> {
File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile(); File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile();
if (!cacheFolder.exists()) { if (!cacheFolder.exists()) {
@ -157,7 +152,7 @@ public class SkinProvider {
} }
int count = 0; int count = 0;
final long expireTime = ((long) GeyserImpl.getInstance().getConfig().getCacheImages()) * ((long)1000 * 60 * 60 * 24); final long expireTime = ((long) GeyserImpl.getInstance().config().cacheImages()) * ((long)1000 * 60 * 60 * 24);
for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) { for (File imageFile : Objects.requireNonNull(cacheFolder.listFiles())) {
if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) { if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) {
//noinspection ResultOfMethodCallIgnored //noinspection ResultOfMethodCallIgnored
@ -188,7 +183,7 @@ public class SkinProvider {
Cape cape = null; Cape cape = null;
SkinGeometry geometry = SkinGeometry.WIDE; SkinGeometry geometry = SkinGeometry.WIDE;
if (GeyserImpl.getInstance().getConfig().getRemote().authType() != AuthType.ONLINE) { if (GeyserImpl.getInstance().config().java().authType() != AuthType.ONLINE) {
// Let's see if this player is a Bedrock player, and if so, let's pull their skin. // Let's see if this player is a Bedrock player, and if so, let's pull their skin.
GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid); GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid);
if (session != null) { if (session != null) {
@ -427,7 +422,7 @@ public class SkinProvider {
GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl); GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl);
// Write to cache if we are allowed // Write to cache if we are allowed
if (GeyserImpl.getInstance().getConfig().getCacheImages() > 0) { if (GeyserImpl.getInstance().config().cacheImages() > 0) {
imageFile.getParentFile().mkdirs(); imageFile.getParentFile().mkdirs();
try { try {
ImageIO.write(image, "png", imageFile); ImageIO.write(image, "png", imageFile);
@ -496,7 +491,7 @@ public class SkinProvider {
return properties.get(0).getAsJsonObject().get("value").getAsString(); return properties.get(0).getAsJsonObject().get("value").getAsString();
} catch (Exception e) { } catch (Exception e) {
GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid); GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;
@ -515,7 +510,6 @@ public class SkinProvider {
try { try {
// Offline skin, or no present UUID // Offline skin, or no present UUID
JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username); JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username);
System.out.println(node);
JsonElement id = node.get("id"); JsonElement id = node.get("id");
if (id == null) { if (id == null) {
GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username); GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username);
@ -523,7 +517,7 @@ public class SkinProvider {
} }
return id.getAsString(); return id.getAsString();
} catch (Exception e) { } catch (Exception e) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
return null; return null;

Datei anzeigen

@ -80,8 +80,8 @@ public class GeyserLocale {
* Finalize the default locale, now that we know what the default locale should be. * Finalize the default locale, now that we know what the default locale should be.
*/ */
public static void finalizeDefaultLocale(GeyserImpl geyser) { public static void finalizeDefaultLocale(GeyserImpl geyser) {
String newDefaultLocale = geyser.getConfig().getDefaultLocale(); String newDefaultLocale = geyser.config().defaultLocale();
if (newDefaultLocale == null) { if ("system".equals(newDefaultLocale)) {
// We want to use the system locale which is already loaded // We want to use the system locale which is already loaded
return; return;
} }

Datei anzeigen

@ -207,7 +207,7 @@ public abstract class InventoryTranslator {
case PLACE: { case PLACE: {
TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action; TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action;
if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) { if (!(checkNetId(session, inventory, transferAction.getSource()) && checkNetId(session, inventory, transferAction.getDestination()))) {
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.bedrockUsername()); session.getGeyser().getLogger().error("DEBUG: About to reject TAKE/PLACE request made by " + session.bedrockUsername());
dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination()); dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination());
} }
@ -312,7 +312,7 @@ public abstract class InventoryTranslator {
ItemStackRequestSlotData destination = swapAction.getDestination(); ItemStackRequestSlotData destination = swapAction.getDestination();
if (!(checkNetId(session, inventory, source) && checkNetId(session, inventory, destination))) { if (!(checkNetId(session, inventory, source) && checkNetId(session, inventory, destination))) {
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.bedrockUsername()); session.getGeyser().getLogger().error("DEBUG: About to reject SWAP request made by " + session.bedrockUsername());
dumpStackRequestDetails(session, inventory, source, destination); dumpStackRequestDetails(session, inventory, source, destination);
} }
@ -807,7 +807,7 @@ public abstract class InventoryTranslator {
* as bad (false). * as bad (false).
*/ */
protected static ItemStackResponse rejectRequest(ItemStackRequest request, boolean throwError) { protected static ItemStackResponse rejectRequest(ItemStackRequest request, boolean throwError) {
if (throwError && GeyserImpl.getInstance().getConfig().isDebugMode()) { if (throwError && GeyserImpl.getInstance().config().debugMode()) {
new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace(); new Throwable("DEBUGGING: ItemStackRequest rejected " + request.toString()).printStackTrace();
} }
return new ItemStackResponse(ItemStackResponseStatus.ERROR, request.getRequestId(), Collections.emptyList()); return new ItemStackResponse(ItemStackResponseStatus.ERROR, request.getRequestId(), Collections.emptyList());

Datei anzeigen

@ -120,7 +120,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
return skull.getBlockDefinition(); return skull.getBlockDefinition();
} catch (InterruptedException | ExecutionException e) { } catch (InterruptedException | ExecutionException e) {
session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + javaNbt); session.getGeyser().getLogger().debug("Failed to acquire textures for custom skull: " + blockPosition + " " + javaNbt);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
} }

Datei anzeigen

@ -26,7 +26,6 @@
package org.geysermc.geyser.translator.protocol.bedrock; package org.geysermc.geyser.translator.protocol.bedrock;
import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket; import org.cloudburstmc.protocol.bedrock.packet.EmoteListPacket;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.PacketTranslator;
import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.protocol.Translator;
@ -36,10 +35,6 @@ public class BedrockEmoteListTranslator extends PacketTranslator<EmoteListPacket
@Override @Override
public void translate(GeyserSession session, EmoteListPacket packet) { public void translate(GeyserSession session, EmoteListPacket packet) {
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
return;
}
session.refreshEmotes(packet.getPieceIds()); session.refreshEmotes(packet.getPieceIds());
} }
} }

Datei anzeigen

@ -169,7 +169,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
final Vector3i packetBlockPosition = packet.getBlockPosition(); final Vector3i packetBlockPosition = packet.getBlockPosition();
Vector3i blockPos = BlockUtils.getBlockPosition(packetBlockPosition, packet.getBlockFace()); Vector3i blockPos = BlockUtils.getBlockPosition(packetBlockPosition, packet.getBlockFace());
if (session.getGeyser().getConfig().isDisableBedrockScaffolding()) { if (session.getGeyser().config().disableBedrockScaffolding()) {
float yaw = session.getPlayerEntity().getYaw(); float yaw = session.getPlayerEntity().getYaw();
boolean isGodBridging = switch (packet.getBlockFace()) { boolean isGodBridging = switch (packet.getBlockFace()) {
case 2 -> yaw <= -135f || yaw > 135f; case 2 -> yaw <= -135f || yaw > 135f;

Datei anzeigen

@ -47,7 +47,7 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
public void translate(GeyserSession session, NetworkStackLatencyPacket packet) { public void translate(GeyserSession session, NetworkStackLatencyPacket packet) {
// negative timestamps are used as hack to fix the url image loading bug // negative timestamps are used as hack to fix the url image loading bug
if (packet.getTimestamp() >= 0) { if (packet.getTimestamp() >= 0) {
if (session.getGeyser().getConfig().isForwardPlayerPing()) { if (session.getGeyser().config().forwardPlayerPing()) {
// use our cached value because // use our cached value because
// a) bedrock can be inaccurate with the value returned // a) bedrock can be inaccurate with the value returned
// b) playstation replies with a different magnitude than other platforms // b) playstation replies with a different magnitude than other platforms

Datei anzeigen

@ -45,7 +45,7 @@ public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslat
if (session.remoteServer().authType() == AuthType.ONLINE) { if (session.remoteServer().authType() == AuthType.ONLINE) {
if (!session.isLoggedIn()) { if (!session.isLoggedIn()) {
if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) { if (session.getGeyser().config().savedUserLogins().contains(session.bedrockUsername())) {
if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) { if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) {
LoginEncryptionUtils.buildAndShowConsentWindow(session); LoginEncryptionUtils.buildAndShowConsentWindow(session);
} else { } else {

Datei anzeigen

@ -27,7 +27,6 @@ package org.geysermc.geyser.translator.protocol.bedrock.entity.player;
import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; import org.cloudburstmc.protocol.bedrock.packet.EmotePacket;
import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.Entity;
import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.entity.type.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.GeyserSession;
@ -39,15 +38,6 @@ public class BedrockEmoteTranslator extends PacketTranslator<EmotePacket> {
@Override @Override
public void translate(GeyserSession session, EmotePacket packet) { public void translate(GeyserSession session, EmotePacket packet) {
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) {
// Activate the workaround - we should trigger the offhand now
session.requestOffhandSwap();
if (session.getGeyser().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) {
return;
}
}
// For the future: could have a method that exposes which players will see the emote // For the future: could have a method that exposes which players will see the emote
ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId()); ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId());
session.getGeyser().eventBus().fire(event); session.getGeyser().eventBus().fire(event);

Datei anzeigen

@ -113,7 +113,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
@Override @Override
public void translate(GeyserSession session, ClientboundCommandsPacket packet) { public void translate(GeyserSession session, ClientboundCommandsPacket packet) {
// Don't send command suggestions if they are disabled // Don't send command suggestions if they are disabled
if (!session.getGeyser().getConfig().isCommandSuggestions()) { if (!session.getGeyser().config().commandSuggestions()) {
session.getGeyser().getLogger().debug("Not sending translated command suggestions as they are disabled."); session.getGeyser().getLogger().debug("Not sending translated command suggestions as they are disabled.");
// Send an empty packet so Bedrock doesn't override /help with its own, built-in help command. // Send an empty packet so Bedrock doesn't override /help with its own, built-in help command.

Datei anzeigen

@ -39,7 +39,7 @@ public class JavaKeepAliveTranslator extends PacketTranslator<ClientboundKeepAli
@Override @Override
public void translate(GeyserSession session, ClientboundKeepAlivePacket packet) { public void translate(GeyserSession session, ClientboundKeepAlivePacket packet) {
if (!session.getGeyser().getConfig().isForwardPlayerPing()) { if (!session.getGeyser().config().forwardPlayerPing()) {
return; return;
} }
// We use this once the client replies (see BedrockNetworkStackLatencyTranslator) // We use this once the client replies (see BedrockNetworkStackLatencyTranslator)

Datei anzeigen

@ -132,7 +132,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
case STONECUTTING -> { case STONECUTTING -> {
StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData(); StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData();
if (stoneCuttingData.getIngredient().getOptions().length == 0) { if (stoneCuttingData.getIngredient().getOptions().length == 0) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) { if (GeyserImpl.getInstance().config().debugMode()) {
GeyserImpl.getInstance().getLogger().debug("Received broken stone cutter recipe: " + stoneCuttingData + " " + GeyserImpl.getInstance().getLogger().debug("Received broken stone cutter recipe: " + stoneCuttingData + " " +
recipe.getIdentifier() + " " + Registries.JAVA_ITEMS.get().get(stoneCuttingData.getResult().getId()).javaIdentifier()); recipe.getIdentifier() + " " + Registries.JAVA_ITEMS.get().get(stoneCuttingData.getResult().getId()).javaIdentifier());
} }

Datei anzeigen

@ -45,7 +45,7 @@ public class JavaSetEntityDataTranslator extends PacketTranslator<ClientboundSet
EntityDefinition<?> definition = entity.getDefinition(); EntityDefinition<?> definition = entity.getDefinition();
for (EntityMetadata<?, ?> metadata : packet.getMetadata()) { for (EntityMetadata<?, ?> metadata : packet.getMetadata()) {
if (metadata.getId() >= definition.translators().size()) { if (metadata.getId() >= definition.translators().size()) {
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
// Minecraft client just ignores these // Minecraft client just ignores these
session.getGeyser().getLogger().warning("Metadata ID " + metadata.getId() + " is out of bounds of known entity metadata size " + definition.translators().size() + " for entity type " + entity.getDefinition().entityType()); session.getGeyser().getLogger().warning("Metadata ID " + metadata.getId() + " is out of bounds of known entity metadata size " + definition.translators().size() + " for entity type " + entity.getDefinition().entityType());
session.getGeyser().getLogger().debug(metadata.toString()); session.getGeyser().getLogger().debug(metadata.toString());

Datei anzeigen

@ -91,7 +91,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
ChunkUtils.updateChunkPosition(session, pos.toInt()); ChunkUtils.updateChunkPosition(session, pos.toInt());
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
session.getGeyser().getLogger().debug("Spawned player at " + packet.getX() + " " + packet.getY() + " " + packet.getZ()); session.getGeyser().getLogger().debug("Spawned player at " + packet.getX() + " " + packet.getY() + " " + packet.getZ());
} }
return; return;

Datei anzeigen

@ -357,7 +357,7 @@ public class MessageTranslator {
textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale())); textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale()));
} else { } else {
session.getGeyser().getLogger().debug("Likely illegal chat type detection found."); session.getGeyser().getLogger().debug("Likely illegal chat type detection found.");
if (session.getGeyser().getConfig().isDebugMode()) { if (session.getGeyser().config().debugMode()) {
Thread.dumpStack(); Thread.dumpStack();
} }
textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale())); textPacket.setMessage(MessageTranslator.convertMessage(message, session.locale()));

Datei anzeigen

@ -38,22 +38,12 @@ import java.util.concurrent.TimeUnit;
* Much of the work here is from the wonderful folks from <a href="https://github.com/ViaVersion/ViaRewind">ViaRewind</a> * Much of the work here is from the wonderful folks from <a href="https://github.com/ViaVersion/ViaRewind">ViaRewind</a>
*/ */
public class CooldownUtils { public class CooldownUtils {
private static CooldownType DEFAULT_SHOW_COOLDOWN;
public static void setDefaultShowCooldown(String showCooldown) {
DEFAULT_SHOW_COOLDOWN = CooldownType.getByName(showCooldown);
}
public static CooldownType getDefaultShowCooldown() {
return DEFAULT_SHOW_COOLDOWN;
}
/** /**
* Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is the cooldownPreference in {@link PreferencesCache} * Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is the cooldownPreference in {@link PreferencesCache}
* @param session GeyserSession * @param session GeyserSession
*/ */
public static void sendCooldown(GeyserSession session) { public static void sendCooldown(GeyserSession session) {
if (DEFAULT_SHOW_COOLDOWN == CooldownType.DISABLED) return; if (session.getGeyser().config().showCooldown() == CooldownType.DISABLED) return;
CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference(); CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference();
if (sessionPreference == CooldownType.DISABLED) return; if (sessionPreference == CooldownType.DISABLED) return;
@ -161,10 +151,6 @@ public class CooldownUtils {
* @return The converted CooldownType * @return The converted CooldownType
*/ */
public static CooldownType getByName(String name) { public static CooldownType getByName(String name) {
if (name.equalsIgnoreCase("true")) { // Backwards config compatibility
return CooldownType.TITLE;
}
for (CooldownType type : VALUES) { for (CooldownType type : VALUES) {
if (type.name().equalsIgnoreCase(name)) { if (type.name().equalsIgnoreCase(name)) {
return type; return type;

Datei anzeigen

@ -179,7 +179,7 @@ public class InventoryUtils {
} }
public static boolean canStack(GeyserItemStack item1, GeyserItemStack item2) { public static boolean canStack(GeyserItemStack item1, GeyserItemStack item2) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) if (GeyserImpl.getInstance().config().debugMode())
canStackDebug(item1, item2); canStackDebug(item1, item2);
if (item1.isEmpty() || item2.isEmpty()) if (item1.isEmpty() || item2.isEmpty())
return false; return false;
@ -231,7 +231,7 @@ public class InventoryUtils {
private static ItemDefinition getUnusableSpaceBlockDefinition(int protocolVersion) { private static ItemDefinition getUnusableSpaceBlockDefinition(int protocolVersion) {
ItemMappings mappings = Registries.ITEMS.forVersion(protocolVersion); ItemMappings mappings = Registries.ITEMS.forVersion(protocolVersion);
String unusableSpaceBlock = GeyserImpl.getInstance().getConfig().getUnusableSpaceBlock(); String unusableSpaceBlock = GeyserImpl.getInstance().config().unusableSpaceBlock();
ItemDefinition itemDefinition = mappings.getDefinition(unusableSpaceBlock); ItemDefinition itemDefinition = mappings.getDefinition(unusableSpaceBlock);
if (itemDefinition == null) { if (itemDefinition == null) {

Datei anzeigen

@ -48,6 +48,10 @@ public final class JsonUtils {
return (JsonObject) new JsonParser().parse(new InputStreamReader(stream)); return (JsonObject) new JsonParser().parse(new InputStreamReader(stream));
} }
public static JsonObject parseJson(String s) {
return (JsonObject) new JsonParser().parse(s);
}
public static <T> T fromJson(InputStream stream, Type type) { public static <T> T fromJson(InputStream stream, Type type) {
return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type); return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type);
} }

Datei anzeigen

@ -25,9 +25,6 @@
package org.geysermc.geyser.util; package org.geysermc.geyser.util;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.steveice10.mc.auth.service.MsaAuthenticationService; import com.github.steveice10.mc.auth.service.MsaAuthenticationService;
import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; import org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket; import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket;
@ -53,8 +50,6 @@ import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
public class LoginEncryptionUtils { public class LoginEncryptionUtils {
private static final ObjectMapper JSON_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
private static boolean HAS_SENT_ENCRYPTION_MESSAGE = false; private static boolean HAS_SENT_ENCRYPTION_MESSAGE = false;
public static void encryptPlayerConnection(GeyserSession session, LoginPacket loginPacket) { public static void encryptPlayerConnection(GeyserSession session, LoginPacket loginPacket) {
@ -69,7 +64,7 @@ public class LoginEncryptionUtils {
geyser.getLogger().debug(String.format("Is player data signed? %s", result.signed())); geyser.getLogger().debug(String.format("Is player data signed? %s", result.signed()));
if (!result.signed() && !session.getGeyser().getConfig().isEnableProxyConnections()) { if (!result.signed() && !session.getGeyser().config().enableProxyConnections()) {
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.remote.invalid_xbox_account")); session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.remote.invalid_xbox_account"));
return; return;
} }
@ -85,8 +80,7 @@ public class LoginEncryptionUtils {
throw new IllegalStateException("Client data isn't signed by the given chain data"); throw new IllegalStateException("Client data isn't signed by the given chain data");
} }
JsonNode clientDataJson = JSON_MAPPER.readTree(clientDataPayload); BedrockClientData data = JsonUtils.fromJson(clientDataPayload, BedrockClientData.class);
BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class);
data.setOriginalString(clientData); data.setOriginalString(clientData);
session.setClientData(data); session.setClientData(data);
@ -94,7 +88,7 @@ public class LoginEncryptionUtils {
startEncryptionHandshake(session, identityPublicKey); startEncryptionHandshake(session, identityPublicKey);
} catch (Throwable e) { } catch (Throwable e) {
// An error can be thrown on older Java 8 versions about an invalid key // An error can be thrown on older Java 8 versions about an invalid key
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
e.printStackTrace(); e.printStackTrace();
} }
@ -213,7 +207,7 @@ public class LoginEncryptionUtils {
.append("\n%xbox.signin.enterCode\n") .append("\n%xbox.signin.enterCode\n")
.append(ChatColor.GREEN) .append(ChatColor.GREEN)
.append(msCode.user_code); .append(msCode.user_code);
int timeout = session.getGeyser().getConfig().getPendingAuthenticationTimeout(); int timeout = session.getGeyser().config().pendingAuthenticationTimeout();
if (timeout != 0) { if (timeout != 0) {
message.append("\n\n") message.append("\n\n")
.append(ChatColor.RESET) .append(ChatColor.RESET)

Datei anzeigen

@ -1,447 +0,0 @@
/*
* 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.util;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserImpl;
import javax.net.ssl.HttpsURLConnection;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out <a href="https://bStats.org/">bStats</a> to learn more about bStats!
*/
public class Metrics {
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/server-implementation";
// Should failed requests be logged?
private static boolean logFailedRequests = false;
// The logger for the failed requests
private static Logger logger = Logger.getLogger("bStats");
// The name of the server software
private final String name;
// The uuid of the server
private final String serverUUID;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
private final static ObjectMapper mapper = new ObjectMapper();
private final GeyserImpl geyser;
/**
* Class constructor.
*
* @param geyser The Geyser instance
* @param name The name of the server software.
* @param serverUUID The uuid of the server.
* @param logFailedRequests Whether failed requests should be logged or not.
* @param logger The logger for the failed requests.
*/
public Metrics(GeyserImpl geyser, String name, String serverUUID, boolean logFailedRequests, Logger logger) {
this.geyser = geyser;
this.name = name;
this.serverUUID = serverUUID;
Metrics.logFailedRequests = logFailedRequests;
Metrics.logger = logger;
// Start submitting the data
startSubmitting();
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
if (chart == null) {
throw new IllegalArgumentException("Chart cannot be null!");
}
charts.add(chart);
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
geyser.getScheduledThread().scheduleAtFixedRate(this::submitData, 1, 30, TimeUnit.MINUTES);
// Submit the data every 30 minutes, first time after 1 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
*
* @return The plugin specific data.
*/
private ObjectNode getPluginData() {
ObjectNode data = mapper.createObjectNode();
data.put("pluginName", name); // Append the name of the server software
data.put("pluginVersion", GeyserImpl.VERSION); // Append the name of the server software
ArrayNode customCharts = mapper.createArrayNode();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
JsonNode chart = customChart.getRequestJsonNode();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.set("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private ObjectNode getServerData() {
// OS specific data
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
ObjectNode data = mapper.createObjectNode();
data.put("serverUUID", serverUUID);
data.put("osName", osName);
data.put("osArch", osArch);
data.put("osVersion", osVersion);
data.put("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final ObjectNode data = getServerData();
ArrayNode pluginData = mapper.createArrayNode();
pluginData.add(getPluginData());
data.putPOJO("plugins", pluginData);
new Thread(() -> {
try {
// We are still in the Thread of the timer, so nothing get blocked :)
sendData(data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
logger.log(Level.WARNING, "Could not submit stats of " + name, e);
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(ObjectNode data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
connection.getInputStream().close(); // We don't care about the response - Just send our data :)
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte @NonNull [] compress(final @NonNull String str) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
private @Nullable ObjectNode getRequestJsonNode() {
ObjectNode chart = new ObjectMapper().createObjectNode();
chart.put("chartId", chartId);
try {
ObjectNode data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
chart.putPOJO("data", data);
} catch (Throwable t) {
if (logFailedRequests) {
logger.log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract ObjectNode getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected @Nullable ObjectNode getChartData() throws Exception {
ObjectNode data = mapper.createObjectNode();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
data.put("value", value);
return data;
}
}
/**
* Represents a custom advanced pie.
*/
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected @Nullable ObjectNode getChartData() throws Exception {
ObjectNode data = mapper.createObjectNode();
ObjectNode values = mapper.createObjectNode();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.put(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.putPOJO("values", values);
return data;
}
}
/**
* Represents a custom drilldown pie.
*/
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public @Nullable ObjectNode getChartData() throws Exception {
ObjectNode data = mapper.createObjectNode();
ObjectNode values = mapper.createObjectNode();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
ObjectNode value = mapper.createObjectNode();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
value.put(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
values.putPOJO(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
data.putPOJO("values", values);
return data;
}
}
/**
* Represents a custom single line chart.
*/
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected @Nullable ObjectNode getChartData() throws Exception {
ObjectNode data = mapper.createObjectNode();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
data.put("value", value);
return data;
}
}
}

Datei anzeigen

@ -51,8 +51,8 @@ public class SettingsUtils {
// Let's store these to avoid issues // Let's store these to avoid issues
boolean showCoordinates = session.getPreferencesCache().isAllowShowCoordinates(); boolean showCoordinates = session.getPreferencesCache().isAllowShowCoordinates();
boolean cooldownShown = CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED; boolean cooldownShown = session.getGeyser().config().showCooldown() != CooldownUtils.CooldownType.DISABLED;
boolean customSkulls = session.getGeyser().getConfig().isAllowCustomSkulls(); boolean customSkulls = session.getGeyser().config().allowCustomSkulls();
// Only show the client title if any of the client settings are available // Only show the client title if any of the client settings are available
boolean showClientSettings = showCoordinates || cooldownShown || customSkulls; boolean showClientSettings = showCoordinates || cooldownShown || customSkulls;

Datei anzeigen

@ -199,7 +199,7 @@ public class WebUtils {
return ((String) attr.get(0)).split(" "); return ((String) attr.get(0)).split(" ");
} }
} catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes } catch (Exception | NoClassDefFoundError ex) { // Check for a NoClassDefFoundError to prevent Android crashes
if (geyser.getConfig().isDebugMode()) { if (geyser.config().debugMode()) {
geyser.getLogger().debug("Exception while trying to find an SRV record for the remote host."); geyser.getLogger().debug("Exception while trying to find an SRV record for the remote host.");
ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record ex.printStackTrace(); // Otherwise we can get a stack trace for any domain that doesn't have an SRV record
} }

Datei anzeigen

@ -1,5 +1,6 @@
[versions] [versions]
base-api = "1.0.0-SNAPSHOT" base-api = "1.0.0-SNAPSHOT"
bstats = "3.0.2"
cumulus = "1.1.2" cumulus = "1.1.2"
configurate = "4.2.0-GeyserMC-SNAPSHOT" configurate = "4.2.0-GeyserMC-SNAPSHOT"
erosion = "1.1-20240515.191456-1" erosion = "1.1-20240515.191456-1"
@ -130,6 +131,8 @@ protocol-connection = { group = "org.cloudburstmc.protocol", name = "bedrock-con
math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" } math = { group = "org.cloudburstmc.math", name = "immutable", version = "2.0" }
bstats = { group = "org.bstats", name = "bstats-base", version.ref = "bstats"}
# plugins # plugins
indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" } indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" } shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }