3
0
Mirror von https://github.com/GeyserMC/Geyser.git synchronisiert 2024-11-09 01:30:11 +01: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.getQueryPort(),
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
@ -142,7 +142,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
}
initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression()) {
if (bootstrap.config().asPluginConfig().orElseThrow().useDirectConnection()) {
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
new GeyserBungeeCompressionDisabler());
}

Datei anzeigen

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

Datei anzeigen

@ -96,7 +96,7 @@ public class GeyserModInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder");
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());
}
}
@ -125,7 +125,7 @@ public class GeyserModInjector extends GeyserInjector {
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
break;
} 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!");
e.printStackTrace();
}

Datei anzeigen

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

Datei anzeigen

@ -122,7 +122,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
int index = ch.pipeline().names().indexOf("encoder");
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());
}
}
@ -157,7 +157,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
}
break;
} 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!");
e.printStackTrace();
}
@ -176,8 +176,8 @@ public class GeyserSpigotInjector extends GeyserInjector {
*/
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
MinecraftProtocol protocol = new MinecraftProtocol();
LocalSession session = new LocalSession(bootstrap.getGeyserConfig().getRemote().address(),
bootstrap.getGeyserConfig().getRemote().port(), this.serverSocketAddress,
LocalSession session = new LocalSession(bootstrap.config().java().address(),
bootstrap.config().java().port(), this.serverSocketAddress,
InetAddress.getLoopbackAddress().getHostAddress(), protocol, protocol.createHelper());
session.connect();
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",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpigotConfiguration.class);
ConfigLoaderTemp.load(GeyserPluginConfig.class);
ConfigLoaderTemp.load(new File(getDataFolder(), "config.yml"), GeyserPluginConfig.class);
} catch (IOException ex) {
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -25,11 +25,6 @@
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 lombok.Getter;
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.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.ConfigLoaderTemp;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.configuration.GeyserRemoteConfig;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
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.IOException;
@ -61,13 +57,13 @@ import java.lang.reflect.Method;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.Map;
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserCommandManager geyserCommandManager;
private GeyserStandaloneConfiguration geyserConfig;
private GeyserConfig geyserConfig;
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
private IGeyserPingPassthrough geyserPingPassthrough;
private GeyserStandaloneGUI gui;
@ -78,15 +74,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
private GeyserImpl geyser;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
private static final Map<String, String> argsConfigKeys = new HashMap<>();
private static final Map<NodePath, String> argsConfigKeys = new HashMap<>();
public static void main(String[] args) {
if (System.getProperty("io.netty.leakDetection.level") == null) {
ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); // Can eat performance
}
System.setProperty("bstats.relocatecheck", "false");
GeyserStandaloneBootstrap bootstrap = new GeyserStandaloneBootstrap();
// Set defaults
boolean useGuiOpts = bootstrap.useGui;
@ -94,8 +90,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
GeyserLocale.init(bootstrap);
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
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
// 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 =
String[] argParts = arg.substring(2).split("=");
if (argParts.length == 2) {
// Split the config key by . to allow for nested options
String[] configKeyParts = argParts[0].split("\\.");
// 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;
}
argsConfigKeys.put(NodePath.of(argParts[0].split("\\.")), argParts[1]);
break;
}
}
System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
@ -192,18 +160,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
@Override
public void onGeyserEnable() {
try {
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
(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");
}
geyserConfig = ConfigLoaderTemp.load(new File(configFilename), GeyserRemoteConfig.class, this::handleArgsConfigOptions);
} catch (IOException ex) {
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
if (gui == null) {
@ -213,11 +170,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return;
}
}
geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
geyserLogger.setDebug(geyserConfig.debugMode());
// 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);
@ -266,8 +222,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
}
@Override
public GeyserConfiguration getGeyserConfig() {
return geyserConfig;
public GeyserConfig config() {
return this.geyserConfig;
}
@Override
@ -318,100 +274,47 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
return false;
}
/**
* Get the {@link BeanPropertyDefinition}s for the given class
*
* @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());
@Override
public Path getFloodgateKeyPath() {
return Path.of(geyserConfig.floodgateKeyFile());
}
/**
* 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
*/
@SuppressWarnings({"unchecked", "rawtypes"}) // Required for enum usage
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
private static void setConfigOption(CommentedConfigurationNode node, Object value) throws SerializationException {
Object parsedValue = value;
// 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);
} else if (boolean.class.equals(property.getRawPrimaryType())) {
} else if (Boolean.class == clazz) {
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
AnnotatedField field = property.getField();
field.fixAccess(true);
field.setValue(parentObject, parsedValue);
node.set(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() {
// Get the available properties from the class
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
private void handleArgsConfigOptions(CommentedConfigurationNode node) {
for (Map.Entry<NodePath, String> configKey : argsConfigKeys.entrySet()) {
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()) {
String[] configKeyParts = configKey.getKey().split("\\.");
// Loop over the properties looking for any matches against the stored one from the argument
for (BeanPropertyDefinition property : availableProperties) {
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;
}
try {
setConfigOption(subNode, configKey.getValue());
geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
} catch (SerializationException e) {
geyserLogger.error("Failed to set config option: " + path);
}
}
}

Datei anzeigen

@ -27,10 +27,15 @@ package org.geysermc.geyser.platform.velocity;
import com.velocitypowered.api.proxy.ProxyServer;
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 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.LocalServerChannelWrapper;
@ -47,7 +52,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
@Override
@SuppressWarnings("unchecked")
protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception {
protected void initializeLocalChannel0(GeyserPluginBootstrap bootstrap) throws Exception {
Field cm = proxy.getClass().getDeclaredField("cm");
cm.setAccessible(true);
Object connectionManager = cm.get(proxy);
@ -80,7 +85,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(channelInitializer, ch);
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
if (bootstrap.config().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
new GeyserVelocityCompressionDisabler());
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -28,6 +28,7 @@ package org.geysermc.geyser;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfig;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.level.GeyserWorldManager;
@ -72,7 +73,16 @@ public interface GeyserBootstrap {
*
* @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
@ -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.
*/
boolean testFloodgatePluginPresent();
/**
* TEMPORARY - will be removed after The Merge:tm:.
*/
Path getFloodgateKeyPath();
}

Datei anzeigen

@ -25,9 +25,6 @@
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.reflect.TypeToken;
import io.netty.channel.epoll.Epoll;
@ -39,6 +36,11 @@ import lombok.Getter;
import lombok.Setter;
import net.kyori.adventure.text.Component;
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.NonNull;
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.PlatformType;
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.erosion.UnixSocketClientListener;
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.translator.text.MessageTranslator;
import org.geysermc.geyser.util.AssetUtils;
import org.geysermc.geyser.util.CooldownUtils;
import org.geysermc.geyser.util.DimensionUtils;
import org.geysermc.geyser.util.JsonUtils;
import org.geysermc.geyser.util.Metrics;
import org.geysermc.geyser.util.NewsHandler;
import org.geysermc.geyser.util.VersionCheckUtils;
import org.geysermc.geyser.util.WebUtils;
@ -128,13 +128,6 @@ import java.util.regex.Pattern;
@Getter
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 String NAME = "Geyser";
@ -180,7 +173,7 @@ public class GeyserImpl implements GeyserApi {
private final EventBus<EventRegistrar> eventBus;
private final GeyserExtensionManager extensionManager;
private Metrics metrics;
private MetricsBase metrics;
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
@Getter(AccessLevel.NONE)
@ -269,7 +262,7 @@ public class GeyserImpl implements GeyserApi {
startInstance();
GeyserConfiguration config = bootstrap.getGeyserConfig();
GeyserConfig config = bootstrap.config();
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
@ -277,11 +270,11 @@ public class GeyserImpl implements GeyserApi {
logger.info(message);
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
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);
}
@ -296,7 +289,7 @@ public class GeyserImpl implements GeyserApi {
GeyserLocale.finalizeDefaultLocale(this);
}
GeyserLogger logger = bootstrap.getGeyserLogger();
GeyserConfiguration config = bootstrap.getGeyserConfig();
GeyserConfig config = bootstrap.config();
ScoreboardUpdater.init();
@ -314,31 +307,28 @@ public class GeyserImpl implements GeyserApi {
if (platformType != PlatformType.STANDALONE) {
int javaPort = bootstrap.getServerPort();
if (config.getRemote().address().equals("auto")) {
config.setAutoconfiguredRemote(true);
String serverAddress = bootstrap.getServerBindAddress();
if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) {
config.getRemote().setAddress(serverAddress);
} else {
// Set the remote address to localhost since that is where we are always connecting
try {
config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress());
} catch (UnknownHostException ex) {
logger.debug("Unknown host when trying to find localhost.");
if (config.isDebugMode()) {
ex.printStackTrace();
}
config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress());
String serverAddress = bootstrap.getServerBindAddress();
if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) {
config.java().address(serverAddress);
} else {
// Set the remote address to localhost since that is where we are always connecting
try {
config.java().address(InetAddress.getLocalHost().getHostAddress());
} catch (UnknownHostException ex) {
logger.debug("Unknown host when trying to find localhost.");
if (config.debugMode()) {
ex.printStackTrace();
}
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);
if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) {
config.getBedrock().setPort(javaPort);
if ((config.asPluginConfig().map(pluginConfig -> pluginConfig.bedrock().cloneRemotePort()).orElse(false) || forceMatchServerPort) && javaPort != -1) {
config.bedrock().port(javaPort);
if (forceMatchServerPort) {
if (geyserUdpPort.isEmpty()) {
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)) {
String address = bootstrap.getServerBindAddress();
if (!address.isEmpty()) {
config.getBedrock().setAddress(address);
config.bedrock().address(address);
}
} else if (!pluginUdpAddress.isEmpty()) {
config.getBedrock().setAddress(pluginUdpAddress);
config.bedrock().address(pluginUdpAddress);
}
if (!portPropertyApplied && !pluginUdpPort.isEmpty()) {
int port = Integer.parseInt(pluginUdpPort);
config.getBedrock().setPort(port);
config.bedrock().port(port);
if (geyserUdpPort.isEmpty()) {
logger.info("Port set from generic system property: " + port);
} else {
@ -378,42 +368,44 @@ public class GeyserImpl implements GeyserApi {
}
} catch (NumberFormatException e) {
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);
}
if (platformType != PlatformType.VIAPROXY) {
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") + " "
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
return;
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
} else if (floodgatePresent) {
// Floodgate installed means that the user wants Floodgate authentication
logger.debug("Auto-setting to Floodgate authentication.");
config.getRemote().setAuthType(AuthType.FLOODGATE);
config.java().authType(AuthType.FLOODGATE);
}
}
}
String remoteAddress = config.getRemote().address();
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
String[] record = WebUtils.findSrvRecord(this, remoteAddress);
if (record != null) {
int remotePort = Integer.parseInt(record[2]);
config.getRemote().setAddress(remoteAddress = record[3]);
config.getRemote().setPort(remotePort);
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
if (config.asPluginConfig().isEmpty()) {
String remoteAddress = config.java().address();
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
if (!remoteAddress.matches(IP_REGEX) && !remoteAddress.equalsIgnoreCase("localhost")) {
String[] record = WebUtils.findSrvRecord(this, remoteAddress);
if (record != null) {
int remotePort = Integer.parseInt(record[2]);
config.java().address(remoteAddress = record[3]);
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
TcpSession.USE_EVENT_LOOP_FOR_PACKETS = false;
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout());
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout());
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.");
}
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
DimensionUtils.changeBedrockNetherId(config.aboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads");
if (bedrockThreadCount == null) {
@ -436,14 +427,14 @@ public class GeyserImpl implements GeyserApi {
if (shouldStartListener) {
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) -> {
if (throwable == null) {
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.getBedrock().address(),
String.valueOf(config.getBedrock().port())));
logger.info(GeyserLocale.getLocaleStringLog("geyser.core.start", config.bedrock().address(),
String.valueOf(config.bedrock().port())));
} else {
String address = config.getBedrock().address();
int port = config.getBedrock().port();
String address = config.bedrock().address();
int port = config.bedrock().port();
logger.severe(GeyserLocale.getLocaleStringLog("geyser.core.fail", address, String.valueOf(port)));
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));
@ -453,9 +444,9 @@ public class GeyserImpl implements GeyserApi {
}).join();
}
if (config.getRemote().authType() == AuthType.FLOODGATE) {
if (config.java().authType() == AuthType.FLOODGATE) {
try {
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
Key key = new AesKeyProducer().produceFrom(bootstrap.getFloodgateKeyPath());
cipher = new AesCipher(new Base64Topping());
cipher.init(key);
logger.debug("Loaded Floodgate key!");
@ -467,15 +458,39 @@ public class GeyserImpl implements GeyserApi {
}
}
if (config.getMetrics().isEnabled()) {
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
if (config.metrics().enabled()) {
metrics = new MetricsBase(
"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
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT)));
metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::platformName));
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT)));
metrics.addCustomChart(new SimplePie("platform", platformType::platformName));
metrics.addCustomChart(new SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
metrics.addCustomChart(new SimplePie("version", () -> GeyserImpl.VERSION));
metrics.addCustomChart(new AdvancedPie("playerPlatform", () -> {
Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue;
@ -489,7 +504,7 @@ public class GeyserImpl implements GeyserApi {
}
return valueMap;
}));
metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> {
metrics.addCustomChart(new AdvancedPie("playerVersion", () -> {
Map<String, Integer> valueMap = new HashMap<>();
for (GeyserSession session : sessionManager.getAllSessions()) {
if (session == null) continue;
@ -511,7 +526,7 @@ public class GeyserImpl implements GeyserApi {
platformMap.put(platformType.platformName(), 1);
versionMap.put(minecraftVersion, platformMap);
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
metrics.addCustomChart(new DrilldownPie("minecraftServerVersion", () -> {
// By the end, we should return, for example:
// 1.16.5 => (Spigot, 1)
return versionMap;
@ -520,7 +535,7 @@ public class GeyserImpl implements GeyserApi {
// 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
metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> {
metrics.addCustomChart(new DrilldownPie("javaVersion", () -> {
Map<String, Map<String, Integer>> map = new HashMap<>();
String javaVersion = System.getProperty("java.version");
Map<String, Integer> entry = new HashMap<>();
@ -555,7 +570,7 @@ public class GeyserImpl implements GeyserApi {
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
savedRefreshTokens = new ConcurrentHashMap<>();
@ -570,7 +585,7 @@ public class GeyserImpl implements GeyserApi {
logger.error("Cannot load saved user tokens!", e);
}
if (refreshTokenFile != null) {
List<String> validUsers = config.getSavedUserLogins();
List<String> validUsers = config.savedUserLogins();
boolean doWrite = false;
for (Map.Entry<String, String> entry : refreshTokenFile.entrySet()) {
String user = entry.getKey();
@ -598,7 +613,7 @@ public class GeyserImpl implements GeyserApi {
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
}
if (config.isNotifyOnNewBedrockUpdate()) {
if (config.notifyOnNewBedrockUpdate()) {
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
}
}
@ -750,13 +765,13 @@ public class GeyserImpl implements GeyserApi {
@NonNull
public RemoteServer defaultRemoteServer() {
return getConfig().getRemote();
return config().java();
}
@Override
@NonNull
public BedrockListener bedrockListener() {
return getConfig().getBedrock();
return config().bedrock();
}
@Override
@ -830,8 +845,8 @@ public class GeyserImpl implements GeyserApi {
return bootstrap.getGeyserLogger();
}
public GeyserConfiguration getConfig() {
return bootstrap.getGeyserConfig();
public GeyserConfig config() {
return bootstrap.config();
}
public WorldManager getWorldManager() {
@ -844,7 +859,7 @@ public class GeyserImpl implements GeyserApi {
}
public void saveRefreshToken(@NonNull String bedrockName, @NonNull String refreshToken) {
if (!getConfig().getSavedUserLogins().contains(bedrockName)) {
if (!config().savedUserLogins().contains(bedrockName)) {
// Do not save this login
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.command.GeyserCommand;
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.text.GeyserLocale;
import org.geysermc.geyser.util.LoopbackUtil;
@ -86,7 +86,7 @@ public class ConnectionTestCommand extends GeyserCommand {
return;
}
} else {
port = geyser.getConfig().getBedrock().broadcastPort();
port = geyser.config().bedrock().broadcastPort();
}
String ip = fullAddress[0];
@ -114,41 +114,41 @@ public class ConnectionTestCommand extends GeyserCommand {
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
if (config.getBedrock().broadcastPort() == config.getBedrock().port()) {
if (port != config.getBedrock().port()) {
if (config.bedrock().broadcastPort() == config.bedrock().port()) {
if (port != config.bedrock().port()) {
if (fullAddress.length == 2) {
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.");
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.");
}
} else {
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 ("
+ 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`.");
}
}
} 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 ("
+ 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("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?
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.");
}
// 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. " +
"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;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.dump.DumpInfo;
@ -49,7 +50,6 @@ import java.util.List;
public class DumpCommand extends GeyserCommand {
private final GeyserImpl geyser;
private static final ObjectMapper MAPPER = new ObjectMapper();
private static final String DUMP_URL = "https://dump.geysermc.org/";
public DumpCommand(GeyserImpl geyser, String name, String description, String permission) {
@ -81,18 +81,13 @@ public class DumpCommand extends GeyserCommand {
AsteriskSerializer.showSensitive = showSensitive;
Gson gson = new GsonBuilder().setPrettyPrinting().create();
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", sender.locale()));
String dumpData;
try {
if (offlineDump) {
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
// 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) {
dumpData = gson.toJson(new DumpInfo(addLog));
} catch (JsonSyntaxException e) {
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);
return;
@ -118,10 +113,10 @@ public class DumpCommand extends GeyserCommand {
sender.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", sender.locale()));
String response;
JsonNode responseNode;
JsonObject responseNode;
try {
response = WebUtils.post(DUMP_URL + "documents", dumpData);
responseNode = MAPPER.readTree(response);
responseNode = (JsonObject) new JsonParser().parse(response);
} catch (IOException e) {
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);
@ -129,11 +124,11 @@ public class DumpCommand extends GeyserCommand {
}
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;
}
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);

Datei anzeigen

@ -25,18 +25,21 @@
package org.geysermc.geyser.configuration;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.NodePath;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions;
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.Consumer;
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 = """
--------------------------------
Geyser Configuration File
@ -51,35 +54,81 @@ public class ConfigLoaderTemp {
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 {
var loader = YamlConfigurationLoader.builder()
.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"));
public static <T extends GeyserConfig> T load(File file, Class<T> configClass) throws IOException {
return load(file, configClass, null);
}
// 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()
.versionKey("config-version")
// Pre-Configurate
.addVersion(5, ConfigurationTransformation.builder()
.addAction(NodePath.path("legacyPingPassthrough"), (path, value) -> {
// Invert value
value.set(Boolean.FALSE.equals(value.get(boolean.class)));
return new Object[]{"integratedPingPassthrough"};
})
.addAction(NodePath.path("remote"), (path, value) ->
new Object[]{"java"})
.build())
.addAction(path("legacy-ping-passthrough"), configClass == GeyserRemoteConfig.class ? remove() : (path, value) -> {
// Invert value
value.set(!value.getBoolean());
return new Object[]{"integrated-ping-passthrough"};
})
.addAction(path("remote"), rename("java"))
.addAction(path("floodgate-key-file"), (path, value) -> {
// 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();
int currentVersion = migrations.version(node);
migrations.apply(node);
int newVersion = migrations.version(node);
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;
}

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.RemoteServer;
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.defaults.DefaultBoolean;
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.meta.Comment;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ConfigSerializable
@ -49,7 +51,9 @@ public interface GeyserConfig {
JavaConfig java();
Path floodgateKeyPath();
// Cannot be type File yet because we want to hide it in plugin instances.
@DefaultString("key.pem")
String floodgateKeyFile();
@Comment("""
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
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\"""")
@DefaultString("title")
String showCooldown();
default CooldownUtils.CooldownType showCooldown() {
return CooldownUtils.CooldownType.TITLE;
}
@Comment("Controls if coordinates are shown to players.")
@DefaultBoolean(true)
@ -118,9 +123,9 @@ public interface GeyserConfig {
@Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.")
boolean disableBedrockScaffolding();
//@DefaultString("disabled")
EmoteOffhandWorkaroundOption emoteOffhandWorkaround();
@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.")
@NonNull
@DefaultString("system")
String defaultLocale();
@Comment("""
@ -207,13 +212,15 @@ public interface GeyserConfig {
@Override
@Comment("The port that will listen for connections")
@DefaultNumeric(19132)
@NumericRange(from = 0, to = 65535)
int port();
@Override
@Comment("""
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)
@NumericRange(from = 0, to = 65535)
int broadcastPort();
void address(String address);
@ -223,13 +230,21 @@ public interface GeyserConfig {
void broadcastPort(int broadcastPort);
@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")
String primaryMotd();
@Override
@DefaultString("Another Geyser server.") // TODO migrate or change name
@DefaultString("Another Geyser server.")
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("""
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.""")
@ -237,6 +252,9 @@ public interface GeyserConfig {
@NumericRange(from = -1, to = 9)
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
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.).
Keeping this list empty means there is no IP address whitelist.
IP addresses, subnets, and links to plain text files are supported.""")
List<String> proxyProtocolWhitelistedIPs();
// /**
// * @return Unmodifiable list of {@link CIDRMatcher}s from {@link #proxyProtocolWhitelistedIPs()}
// */
// @Exclude
// List<CIDRMatcher> whitelistedIPsMatchers();
default List<String> proxyProtocolWhitelistedIps() {
return Collections.emptyList();
}
}
@ConfigSerializable
@ -270,6 +284,8 @@ public interface GeyserConfig {
return AuthType.ONLINE;
}
void authType(AuthType authType);
@Comment("""
Whether to enable PROXY protocol or not while connecting to the server.
This is useful only when:
@ -297,17 +313,16 @@ public interface GeyserConfig {
default boolean resolveSrv() {
return false;
}
void authType(AuthType authType);
}
@ConfigSerializable
interface MetricsInfo {
@Comment("If metrics should be enabled")
@DefaultBoolean(true)
boolean enabled();
default UUID uniqueId() { //TODO rename?
@Comment("UUID of server. Don't change!")
default UUID uuid() { //TODO rename?
return UUID.randomUUID();
}
}
@ -333,7 +348,15 @@ public interface GeyserConfig {
int mtu();
@Comment("Do not change!")
default int version() {
default int configVersion() {
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.meta.Comment;
import java.io.File;
@ConfigSerializable
public interface GeyserPluginConfig extends GeyserConfig {
@Override
@ -89,6 +91,10 @@ public interface GeyserPluginConfig extends GeyserConfig {
}
}
@Override
@Hidden
String floodgateKeyFile();
@Comment("""
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.""")

Datei anzeigen

@ -25,10 +25,9 @@
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.DefaultString;
import org.spongepowered.configurate.interfaces.meta.range.NumericRange;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
@ -37,12 +36,17 @@ import org.spongepowered.configurate.objectmapping.meta.Comment;
*/
@ConfigSerializable
public interface GeyserRemoteConfig extends GeyserConfig {
// @Override // For config placement
// BedrockConfig bedrock();
@Override
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
interface RemoteConfig extends JavaConfig {
@Override
@ -53,13 +57,9 @@ public interface GeyserRemoteConfig extends GeyserConfig {
@Override
@Comment("The port of the Java Edition server.")
@DefaultNumeric(25565)
@NumericRange(from = 0, to = 65535)
int port();
@Override // For config placement
default @NonNull AuthType authType() {
return JavaConfig.super.authType();
}
@Override
@Comment("""
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;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
import com.google.common.io.Files;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.annotations.SerializedName;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import lombok.Getter;
@ -41,7 +40,7 @@ import org.geysermc.floodgate.util.FloodgateInfoHolder;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.GeyserApi;
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.session.GeyserSession;
import org.geysermc.geyser.text.AsteriskSerializer;
@ -57,12 +56,16 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
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;
@Getter
public class DumpInfo {
@JsonIgnore
private static final long MEGABYTE = 1024L * 1024L;
private final DumpInfo.VersionInfo versionInfo;
@ -71,7 +74,7 @@ public class DumpInfo {
private final Locale systemLocale;
private final String systemEncoding;
private final GitInfo gitInfo;
private final GeyserConfiguration config;
private final GeyserConfig config;
private final Floodgate floodgate;
private final Object2IntMap<DeviceOs> userPlatforms;
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.config = GeyserImpl.getInstance().getConfig();
this.config = GeyserImpl.getInstance().config();
this.floodgate = new Floodgate();
String md5Hash = "unknown";
@ -108,7 +111,7 @@ public class DumpInfo {
//noinspection UnstableApiUsage
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
} catch (Exception e) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
if (GeyserImpl.getInstance().config().debugMode()) {
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 GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash,
@JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) {
public record GitInfo(String buildNumber, @SerializedName("git.commit.id.abbrev") String commitHashAbbrev, @SerializedName("git.commit.id") String commitHash,
@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()) {
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());
}
return;

Datei anzeigen

@ -185,7 +185,7 @@ public class Entity implements GeyserEntity {
flagsDirty = false;
if (session.getGeyser().getConfig().isDebugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
if (session.getGeyser().config().debugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
EntityType type = definition.entityType();
String name = type != null ? type.name() : getClass().getSimpleName();
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) {
stateIdIncrements = 1;
} 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);
}
stateIdIncrements = 1;

Datei anzeigen

@ -90,7 +90,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
super(geyser, session);
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);
}
@ -211,7 +211,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
header.uuid().toString(), header.version().toString(), codec.size(), pack.contentKey(),
"", header.uuid().toString(), false, false));
}
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().config().forceResourcePacks());
session.sendUpstreamPacket(resourcePacksInfo);
GeyserLocale.loadGeyserLocale(session.locale());
@ -222,7 +222,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
public PacketSignal handle(ResourcePackClientResponsePacket packet) {
switch (packet.getStatus()) {
case COMPLETED:
if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) {
if (geyser.config().java().authType() != AuthType.ONLINE) {
session.authenticate(session.getAuthData().name());
} else if (!couldLoginUserByName(session.getAuthData().name())) {
// 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(), ""));
}
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
if (GeyserImpl.getInstance().config().addNonBedrockItems()) {
// Allow custom items to work
stackPacket.getExperiments().add(new ExperimentData("data_driven_items", true));
}
@ -273,7 +273,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
}
private boolean couldLoginUserByName(String bedrockUsername) {
if (geyser.getConfig().getSavedUserLogins().contains(bedrockUsername)) {
if (geyser.config().savedUserLogins().contains(bedrockUsername)) {
String refreshToken = geyser.refreshTokenFor(bedrockUsername);
if (refreshToken != null) {
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 lombok.Getter;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserPluginBootstrap;
import java.net.SocketAddress;
@ -49,8 +49,8 @@ public abstract class GeyserInjector {
/**
* @param bootstrap the bootstrap of the Geyser instance.
*/
public void initializeLocalChannel(GeyserBootstrap bootstrap) {
if (!bootstrap.getGeyserConfig().isUseDirectConnection()) {
public void initializeLocalChannel(GeyserPluginBootstrap bootstrap) {
if (!bootstrap.config().useDirectConnection()) {
bootstrap.getGeyserLogger().debug("Disabling direct injection!");
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() {
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.api.event.connection.ConnectionRequestEvent;
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.network.CIDRMatcher;
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.text.GeyserLocale;
import org.geysermc.geyser.translator.text.MessageTranslator;
import org.geysermc.geyser.util.WebUtils;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction;
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_PACKET_LIMIT;
@ -135,7 +139,7 @@ public final class GeyserServer {
this.listenCount = 1;
}
if (this.geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
if (this.geyser.config().bedrock().enableProxyProtocol()) {
this.proxiedAddresses = ExpiringMap.builder()
.expiration(30 + 1, TimeUnit.MINUTES)
.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
if (geyser.getConfig().getBedrock().broadcastPort() == 0) {
geyser.getConfig().getBedrock().setBroadcastPort(geyser.getConfig().getBedrock().port());
if (geyser.config().bedrock().broadcastPort() == 0) {
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) {
@ -170,12 +174,12 @@ public final class GeyserServer {
.addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this));
// Add proxy handler
boolean isProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol();
boolean isProxyProtocol = this.geyser.config().bedrock().enableProxyProtocol();
if (isProxyProtocol) {
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) {
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
channel.pipeline().remove(RakServerRateLimiter.NAME);
@ -205,7 +209,7 @@ public final class GeyserServer {
}
private ServerBootstrap createBootstrap() {
if (this.geyser.getConfig().isDebugMode()) {
if (this.geyser.config().debugMode()) {
this.geyser.getLogger().debug("EventLoop type: " + TRANSPORT.datagramChannel());
if (TRANSPORT.datagramChannel() == NioDatagramChannel.class) {
if (System.getProperties().contains("disableNativeEventLoop")) {
@ -220,7 +224,7 @@ public final class GeyserServer {
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
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);
this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit);
@ -235,7 +239,7 @@ public final class GeyserServer {
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannel()))
.group(group, childGroup)
.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_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
.option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie)
@ -243,10 +247,10 @@ public final class GeyserServer {
}
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
List<String> allowedProxyIPs = geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs();
if (geyser.getConfig().getBedrock().isEnableProxyProtocol() && !allowedProxyIPs.isEmpty()) {
List<String> allowedProxyIPs = geyser.config().bedrock().proxyProtocolWhitelistedIps();
if (geyser.config().bedrock().enableProxyProtocol() && !allowedProxyIPs.isEmpty()) {
boolean isWhitelistedIP = false;
for (CIDRMatcher matcher : geyser.getConfig().getBedrock().getWhitelistedIPsMatchers()) {
for (CIDRMatcher matcher : getWhitelistedIPsMatchers()) {
if (matcher.matches(inetSocketAddress.getAddress())) {
isWhitelistedIP = true;
break;
@ -260,8 +264,8 @@ public final class GeyserServer {
}
String ip;
if (geyser.getConfig().isLogPlayerIpAddresses()) {
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
if (geyser.config().logPlayerIpAddresses()) {
if (geyser.config().bedrock().enableProxyProtocol()) {
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
} else {
ip = inetSocketAddress.toString();
@ -287,10 +291,10 @@ public final class GeyserServer {
}
public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) {
if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) {
if (geyser.config().debugMode() && PRINT_DEBUG_PINGS) {
String ip;
if (geyser.getConfig().isLogPlayerIpAddresses()) {
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
if (geyser.config().logPlayerIpAddresses()) {
if (geyser.config().bedrock().enableProxyProtocol()) {
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
} else {
ip = inetSocketAddress.toString();
@ -301,10 +305,10 @@ public final class GeyserServer {
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
}
GeyserConfiguration config = geyser.getConfig();
GeyserConfig config = geyser.config();
GeyserPingInfo pingInfo = null;
if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) {
if (config.passthroughMotd() || config.passthroughPlayerCounts()) {
IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough();
if (pingPassthrough != null) {
pingInfo = pingPassthrough.getPingInformation(inetSocketAddress);
@ -321,25 +325,25 @@ public final class GeyserServer {
.ipv6Port(this.broadcastPort)
.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 mainMotd = (motd.length > 0) ? motd[0] : config.getBedrock().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 mainMotd = (motd.length > 0) ? motd[0] : config.bedrock().primaryMotd(); // First line of the motd.
String subMotd = (motd.length > 1) ? motd[1] : config.bedrock().secondaryMotd(); // Second line of the motd if present, otherwise default.
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.
} else {
pong.motd(config.getBedrock().primaryMotd());
pong.subMotd(config.getBedrock().secondaryMotd());
pong.motd(config.bedrock().primaryMotd());
pong.subMotd(config.bedrock().secondaryMotd());
}
// 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.maximumPlayerCount(pingInfo.getPlayers().getMax());
} else {
pong.playerCount(geyser.getSessionManager().getSessions().size());
pong.maximumPlayerCount(config.getMaxPlayers());
pong.maximumPlayerCount(config.maxPlayers());
}
this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress));
@ -390,6 +394,35 @@ public final class GeyserServer {
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.
*/

Datei anzeigen

@ -78,7 +78,7 @@ public class SkullResourcePackManager {
Path packPath = cachePath.resolve("player_skulls.mcpack");
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
return null;
}
@ -161,7 +161,7 @@ public class SkullResourcePackManager {
}
} catch (IOException e) {
GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache.");
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace();
}
}

Datei anzeigen

@ -65,10 +65,10 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
* @return GeyserPingPassthrough, or null if not initialized
*/
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);
// 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.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
return pingPassthrough;
@ -84,8 +84,8 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
@Override
public void run() {
try (Socket socket = new Socket()) {
String address = geyser.getConfig().getRemote().address();
int port = geyser.getConfig().getRemote().port();
String address = geyser.config().java().address();
int port = geyser.config().java().port();
InetSocketAddress endpoint = new InetSocketAddress(address, port);
socket.connect(endpoint, 5000);
@ -102,7 +102,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
byte[] buffer;
try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) {
if (geyser.getConfig().getRemote().isUseProxyProtocol()) {
if (geyser.config().java().useProxyProtocol()) {
// HAProxy support
// 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);

Datei anzeigen

@ -70,7 +70,7 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
}
return true;
} else {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
if (GeyserImpl.getInstance().config().debugMode()) {
if (!IGNORED_PACKETS.contains(clazz)) {
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
*/
public static void populate(Stage stage) {
if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
if (!GeyserImpl.getInstance().config().addNonBedrockItems()) {
return;
}

Datei anzeigen

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

Datei anzeigen

@ -129,7 +129,7 @@ public class ItemRegistryPopulator {
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
// (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.RequiredArgsConstructor;
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.cache.WorldCache;
import org.geysermc.geyser.text.GeyserLocale;
@ -46,9 +46,9 @@ public final class ScoreboardUpdater extends Thread {
private static final boolean DEBUG_ENABLED;
static {
GeyserConfiguration config = GeyserImpl.getInstance().getConfig();
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
DEBUG_ENABLED = config.isDebugMode();
GeyserConfig config = GeyserImpl.getInstance().config();
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
DEBUG_ENABLED = config.debugMode();
}
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.Nullable;
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.protocol.bedrock.BedrockDisconnectReasons;
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.CommandPermission;
import org.cloudburstmc.protocol.bedrock.data.command.SoftEnumUpdateType;
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
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.util.OptionalBoolean;
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.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.entity.EntityDefinitions;
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.session.auth.AuthData;
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.text.GeyserLocale;
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.mcprotocollib.network.BuiltinFlags;
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.tcp.TcpClientSession;
import org.geysermc.mcprotocollib.network.tcp.TcpSession;
@ -154,7 +207,16 @@ import java.net.ConnectException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
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.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;
@ -600,12 +662,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
this.spawned = false;
this.loggedIn = false;
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
this.emotes = new HashSet<>();
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
} else {
this.emotes = null;
}
this.emotes = new HashSet<>();
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
this.remoteServer = geyser.defaultRemoteServer();
}
@ -618,7 +676,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
sentSpawnPacket = true;
syncEntityProperties();
if (GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
if (GeyserImpl.getInstance().config().addNonBedrockItems()) {
ItemComponentPacket componentPacket = new ItemComponentPacket();
componentPacket.getItems().addAll(itemMappings.getComponentItemData());
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.
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.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
}
if (geyser.getConfig().isForwardPlayerPing()) {
if (geyser.config().forwardPlayerPing()) {
// Let Geyser handle sending the keep alive
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
}
@ -943,7 +1001,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
ClientIntentionPacket intentionPacket = event.getPacket();
String address;
if (geyser.getConfig().getRemote().isForwardHost()) {
if (geyser.config().java().forwardHostname()) {
address = clientData.getServerAddress().split(":")[0];
} else {
address = intentionPacket.getHostname();
@ -1035,7 +1093,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} else {
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
}
if (geyser.getConfig().isDebugMode()) {
if (geyser.config().debugMode()) {
cause.printStackTrace();
}
}
@ -1058,7 +1116,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
@Override
public void packetError(PacketErrorEvent event) {
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.downstream_error", event.getCause().getMessage()));
if (geyser.getConfig().isDebugMode())
if (geyser.config().debugMode())
event.getCause().printStackTrace();
event.setSuppress(true);
}
@ -1090,7 +1148,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
} else {
// Downstream's disconnect will fire an event that prints a log message
// 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));
}
@ -1482,7 +1540,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setLevelGameType(GameType.SURVIVAL);
startGamePacket.setDifficulty(1);
startGamePacket.setDefaultSpawn(Vector3i.ZERO);
startGamePacket.setAchievementsDisabled(!geyser.getConfig().isXboxAchievementsEnabled());
startGamePacket.setAchievementsDisabled(!geyser.config().xboxAchievementsEnabled());
startGamePacket.setCurrentTick(-1);
startGamePacket.setEduEditionOffers(0);
startGamePacket.setEduFeaturesEnabled(false);
@ -1492,7 +1550,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setBroadcastingToLan(true);
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
startGamePacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled());
startGamePacket.setCommandsEnabled(!geyser.config().xboxAchievementsEnabled());
startGamePacket.setTexturePacksRequired(false);
startGamePacket.setBonusChestEnabled(false);
startGamePacket.setStartingWithMap(false);
@ -1510,7 +1568,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
startGamePacket.setEducationProductionId("");
startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty());
String serverName = geyser.getConfig().getBedrock().serverName();
String serverName = geyser.config().bedrock().serverName();
startGamePacket.setLevelId(serverName);
startGamePacket.setLevelName(serverName);
@ -1634,7 +1692,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) {
// protocol can be null when we're not yet logged in (online auth)
if (protocol == null) {
if (geyser.getConfig().isDebugMode()) {
if (geyser.config().debugMode()) {
geyser.getLogger().debug("Tried to send downstream packet with no downstream session!");
Thread.dumpStack();
}
@ -1660,7 +1718,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
if (channel == null) {
// Channel is only null before the connection has initialized
geyser.getLogger().warning("Tried to send a packet to the Java server too early!");
if (geyser.getConfig().isDebugMode()) {
if (geyser.config().debugMode()) {
Thread.dumpStack();
}
return;
@ -2051,7 +2109,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
// There is no need to send command enums if command suggestions are disabled
if (!this.geyser.getConfig().isCommandSuggestions()) {
if (!this.geyser.config().commandSuggestions()) {
return;
}
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();

Datei anzeigen

@ -25,93 +25,97 @@
package org.geysermc.geyser.session.auth;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
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.Setter;
import org.geysermc.floodgate.util.DeviceOs;
import org.geysermc.floodgate.util.InputMode;
import org.geysermc.floodgate.util.UiProfile;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
public final class BedrockClientData {
@JsonProperty(value = "GameVersion")
@SerializedName(value = "GameVersion")
private String gameVersion;
@JsonProperty(value = "ServerAddress")
@SerializedName(value = "ServerAddress")
private String serverAddress;
@JsonProperty(value = "ThirdPartyName")
@SerializedName(value = "ThirdPartyName")
private String username;
@JsonProperty(value = "LanguageCode")
@SerializedName(value = "LanguageCode")
private String languageCode;
@JsonProperty(value = "SkinId")
@SerializedName(value = "SkinId")
private String skinId;
@JsonProperty(value = "SkinData")
@SerializedName(value = "SkinData")
private String skinData;
@JsonProperty(value = "SkinImageHeight")
@SerializedName(value = "SkinImageHeight")
private int skinImageHeight;
@JsonProperty(value = "SkinImageWidth")
@SerializedName(value = "SkinImageWidth")
private int skinImageWidth;
@JsonProperty(value = "CapeId")
@SerializedName(value = "CapeId")
private String capeId;
@JsonProperty(value = "CapeData")
@SerializedName(value = "CapeData")
@JsonAdapter(value = StringToByteDeserializer.class)
private byte[] capeData;
@JsonProperty(value = "CapeImageHeight")
@SerializedName(value = "CapeImageHeight")
private int capeImageHeight;
@JsonProperty(value = "CapeImageWidth")
@SerializedName(value = "CapeImageWidth")
private int capeImageWidth;
@JsonProperty(value = "CapeOnClassicSkin")
@SerializedName(value = "CapeOnClassicSkin")
private boolean capeOnClassicSkin;
@JsonProperty(value = "SkinResourcePatch")
@SerializedName(value = "SkinResourcePatch")
private String geometryName;
@JsonProperty(value = "SkinGeometryData")
@SerializedName(value = "SkinGeometryData")
private String geometryData;
@JsonProperty(value = "PersonaSkin")
@SerializedName(value = "PersonaSkin")
private boolean personaSkin;
@JsonProperty(value = "PremiumSkin")
@SerializedName(value = "PremiumSkin")
private boolean premiumSkin;
@JsonProperty(value = "DeviceId")
@SerializedName(value = "DeviceId")
private String deviceId;
@JsonProperty(value = "DeviceModel")
@SerializedName(value = "DeviceModel")
private String deviceModel;
@JsonProperty(value = "DeviceOS")
@SerializedName(value = "DeviceOS")
private DeviceOs deviceOs;
@JsonProperty(value = "UIProfile")
@SerializedName(value = "UIProfile")
private UiProfile uiProfile;
@JsonProperty(value = "GuiScale")
@SerializedName(value = "GuiScale")
private int guiScale;
@JsonProperty(value = "CurrentInputMode")
@SerializedName(value = "CurrentInputMode")
private InputMode currentInputMode;
@JsonProperty(value = "DefaultInputMode")
@SerializedName(value = "DefaultInputMode")
private InputMode defaultInputMode;
@JsonProperty("PlatformOnlineId")
@SerializedName("PlatformOnlineId")
private String platformOnlineId;
@JsonProperty(value = "PlatformOfflineId")
@SerializedName(value = "PlatformOfflineId")
private String platformOfflineId;
@JsonProperty(value = "SelfSignedId")
@SerializedName(value = "SelfSignedId")
private UUID selfSignedId;
@JsonProperty(value = "ClientRandomId")
@SerializedName(value = "ClientRandomId")
private long clientRandomId;
@JsonProperty(value = "ArmSize")
@SerializedName(value = "ArmSize")
private String armSize;
@JsonProperty(value = "SkinAnimationData")
@SerializedName(value = "SkinAnimationData")
private String skinAnimationData;
@JsonProperty(value = "SkinColor")
@SerializedName(value = "SkinColor")
private String skinColor;
@JsonProperty(value = "ThirdPartyNameOnly")
@SerializedName(value = "ThirdPartyNameOnly")
private boolean thirdPartyNameOnly;
@JsonProperty(value = "PlayFabId")
@SerializedName(value = "PlayFabId")
private String playFabId;
@JsonIgnore
@Setter
private String originalString = null;
private transient String originalString = null;
public DeviceOs getDeviceOs() {
return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN;
@ -128,4 +132,11 @@ public final class BedrockClientData {
public UiProfile getUiProfile() {
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;
/**
* Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}.
* Which CooldownType the client prefers. Initially set to the config default.
*/
@Setter
private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown();
private CooldownUtils.CooldownType cooldownPreference;
public PreferencesCache(GeyserSession 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
*/
public void updateShowCoordinates() {
allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().getConfig().isShowCoordinates();
allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().config().showCoordinates();
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.
*/
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) {
this.session = session;
this.maxVisibleSkulls = session.getGeyser().getConfig().getMaxVisibleCustomSkulls();
this.maxVisibleSkulls = session.getGeyser().config().maxVisibleCustomSkulls();
this.cullingEnabled = this.maxVisibleSkulls != -1;
// 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;
}
@ -98,7 +98,7 @@ public class SkullCache {
}
} catch (IOException e) {
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();
}
}

Datei anzeigen

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

Datei anzeigen

@ -25,7 +25,8 @@
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.cloudburstmc.nbt.NbtMap;
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.auth.BedrockClientData;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.JsonUtils;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@ -194,7 +196,7 @@ public class SkinManager {
public static void handleBedrockSkin(PlayerEntity playerEntity, BedrockClientData clientData) {
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()));
}
@ -208,7 +210,7 @@ public class SkinManager {
if (skinBytes.length <= (128 * 128 * 4) && !clientData.isPersonaSkin()) {
SkinProvider.storeBedrockSkin(playerEntity.getUuid(), clientData.getSkinId(), skinBytes);
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().debug("The size of '" + playerEntity.getUsername() + "' skin is: " + clientData.getSkinImageWidth() + "x" + clientData.getSkinImageHeight());
}
@ -246,7 +248,7 @@ public class SkinManager {
return loadFromJson(skinDataValue);
} catch (IOException e) {
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();
}
return null;
@ -274,7 +276,7 @@ public class SkinManager {
} else {
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();
}
}
@ -282,30 +284,25 @@ public class SkinManager {
}
public static @Nullable GameProfileData loadFromJson(String encodedJson) throws IOException, IllegalArgumentException {
// TODO use GameProfile method.
JsonNode skinObject;
JsonObject skinObject;
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) {
GeyserImpl.getInstance().getLogger().debug("Invalid base64 encoded skin entry: " + encodedJson);
return null;
}
JsonNode textures = skinObject.get("textures");
if (textures == null) {
if (!(skinObject.get("textures") instanceof JsonObject textures)) {
return null;
}
JsonNode skinTexture = textures.get("SKIN");
if (skinTexture == null) {
if (!(textures.get("SKIN") instanceof JsonObject skinTexture)) {
return null;
}
String skinUrl;
JsonNode skinUrlNode = skinTexture.get("url");
if (skinUrlNode != null && skinUrlNode.isTextual()) {
skinUrl = skinUrlNode.asText().replace("http://", "https://");
if (skinTexture.get("url") instanceof JsonPrimitive skinUrlNode && skinUrlNode.isString()) {
skinUrl = skinUrlNode.getAsString().replace("http://", "https://");
} else {
return null;
}
@ -322,11 +319,9 @@ public class SkinManager {
boolean isAlex = skinTexture.has("metadata");
String capeUrl = null;
JsonNode capeTexture = textures.get("CAPE");
if (capeTexture != null) {
JsonNode capeUrlNode = capeTexture.get("url");
if (capeUrlNode != null && capeUrlNode.isTextual()) {
capeUrl = capeUrlNode.asText().replace("http://", "https://");
if (textures.get("CAPE") instanceof JsonObject capeTexture) {
if (capeTexture.get("url") instanceof JsonPrimitive capeUrlNode && capeUrlNode.isString()) {
capeUrl = capeUrlNode.getAsString().replace("http://", "https://");
}
}

Datei anzeigen

@ -126,11 +126,6 @@ public class SkinProvider {
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);
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() {
@ -149,7 +144,7 @@ public class SkinProvider {
public static void registerCacheImageTask(GeyserImpl geyser) {
// Schedule Daily Image Expiry if we are caching them
if (geyser.getConfig().getCacheImages() > 0) {
if (geyser.config().cacheImages() > 0) {
geyser.getScheduledThread().scheduleAtFixedRate(() -> {
File cacheFolder = GeyserImpl.getInstance().getBootstrap().getConfigFolder().resolve("cache").resolve("images").toFile();
if (!cacheFolder.exists()) {
@ -157,7 +152,7 @@ public class SkinProvider {
}
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())) {
if (imageFile.lastModified() < System.currentTimeMillis() - expireTime) {
//noinspection ResultOfMethodCallIgnored
@ -188,7 +183,7 @@ public class SkinProvider {
Cape cape = null;
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.
GeyserSession session = GeyserImpl.getInstance().connectionByUuid(uuid);
if (session != null) {
@ -427,7 +422,7 @@ public class SkinProvider {
GeyserImpl.getInstance().getLogger().debug("Downloaded " + imageUrl);
// Write to cache if we are allowed
if (GeyserImpl.getInstance().getConfig().getCacheImages() > 0) {
if (GeyserImpl.getInstance().config().cacheImages() > 0) {
imageFile.getParentFile().mkdirs();
try {
ImageIO.write(image, "png", imageFile);
@ -496,7 +491,7 @@ public class SkinProvider {
return properties.get(0).getAsJsonObject().get("value").getAsString();
} catch (Exception e) {
GeyserImpl.getInstance().getLogger().debug("Unable to request textures for " + uuid);
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace();
}
return null;
@ -515,7 +510,6 @@ public class SkinProvider {
try {
// Offline skin, or no present UUID
JsonObject node = WebUtils.getJson("https://api.mojang.com/users/profiles/minecraft/" + username);
System.out.println(node);
JsonElement id = node.get("id");
if (id == null) {
GeyserImpl.getInstance().getLogger().debug("No UUID found in Mojang response for " + username);
@ -523,7 +517,7 @@ public class SkinProvider {
}
return id.getAsString();
} catch (Exception e) {
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
if (GeyserImpl.getInstance().config().debugMode()) {
e.printStackTrace();
}
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.
*/
public static void finalizeDefaultLocale(GeyserImpl geyser) {
String newDefaultLocale = geyser.getConfig().getDefaultLocale();
if (newDefaultLocale == null) {
String newDefaultLocale = geyser.config().defaultLocale();
if ("system".equals(newDefaultLocale)) {
// We want to use the system locale which is already loaded
return;
}

Datei anzeigen

@ -207,7 +207,7 @@ public abstract class InventoryTranslator {
case PLACE: {
TransferItemStackRequestAction transferAction = (TransferItemStackRequestAction) action;
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());
dumpStackRequestDetails(session, inventory, transferAction.getSource(), transferAction.getDestination());
}
@ -312,7 +312,7 @@ public abstract class InventoryTranslator {
ItemStackRequestSlotData destination = swapAction.getDestination();
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());
dumpStackRequestDetails(session, inventory, source, destination);
}
@ -807,7 +807,7 @@ public abstract class InventoryTranslator {
* as bad (false).
*/
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();
}
return new ItemStackResponse(ItemStackResponseStatus.ERROR, request.getRequestId(), Collections.emptyList());
@ -952,4 +952,4 @@ public abstract class InventoryTranslator {
TRANSFER,
DONE
}
}
}

Datei anzeigen

@ -120,7 +120,7 @@ public class SkullBlockEntityTranslator extends BlockEntityTranslator implements
return skull.getBlockDefinition();
} catch (InterruptedException | ExecutionException e) {
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();
}
}

Datei anzeigen

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

Datei anzeigen

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

Datei anzeigen

@ -47,7 +47,7 @@ public class BedrockNetworkStackLatencyTranslator extends PacketTranslator<Netwo
public void translate(GeyserSession session, NetworkStackLatencyPacket packet) {
// negative timestamps are used as hack to fix the url image loading bug
if (packet.getTimestamp() >= 0) {
if (session.getGeyser().getConfig().isForwardPlayerPing()) {
if (session.getGeyser().config().forwardPlayerPing()) {
// use our cached value because
// a) bedrock can be inaccurate with the value returned
// 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.isLoggedIn()) {
if (session.getGeyser().getConfig().getSavedUserLogins().contains(session.bedrockUsername())) {
if (session.getGeyser().config().savedUserLogins().contains(session.bedrockUsername())) {
if (session.getGeyser().refreshTokenFor(session.bedrockUsername()) == null) {
LoginEncryptionUtils.buildAndShowConsentWindow(session);
} 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.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.player.PlayerEntity;
import org.geysermc.geyser.session.GeyserSession;
@ -39,15 +38,6 @@ public class BedrockEmoteTranslator extends PacketTranslator<EmotePacket> {
@Override
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
ClientEmoteEvent event = new ClientEmoteEvent(session, packet.getEmoteId());
session.getGeyser().eventBus().fire(event);

Datei anzeigen

@ -113,7 +113,7 @@ public class JavaCommandsTranslator extends PacketTranslator<ClientboundCommands
@Override
public void translate(GeyserSession session, ClientboundCommandsPacket packet) {
// 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.");
// 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
public void translate(GeyserSession session, ClientboundKeepAlivePacket packet) {
if (!session.getGeyser().getConfig().isForwardPlayerPing()) {
if (!session.getGeyser().config().forwardPlayerPing()) {
return;
}
// We use this once the client replies (see BedrockNetworkStackLatencyTranslator)

Datei anzeigen

@ -132,7 +132,7 @@ public class JavaUpdateRecipesTranslator extends PacketTranslator<ClientboundUpd
case STONECUTTING -> {
StoneCuttingRecipeData stoneCuttingData = (StoneCuttingRecipeData) recipe.getData();
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 + " " +
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();
for (EntityMetadata<?, ?> metadata : packet.getMetadata()) {
if (metadata.getId() >= definition.translators().size()) {
if (session.getGeyser().getConfig().isDebugMode()) {
if (session.getGeyser().config().debugMode()) {
// 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().debug(metadata.toString());

Datei anzeigen

@ -91,7 +91,7 @@ public class JavaPlayerPositionTranslator extends PacketTranslator<ClientboundPl
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());
}
return;

Datei anzeigen

@ -357,7 +357,7 @@ public class MessageTranslator {
textPacket.setMessage(MessageTranslator.convertMessage(withDecoration.build(), session.locale()));
} else {
session.getGeyser().getLogger().debug("Likely illegal chat type detection found.");
if (session.getGeyser().getConfig().isDebugMode()) {
if (session.getGeyser().config().debugMode()) {
Thread.dumpStack();
}
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>
*/
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}
* @param session GeyserSession
*/
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();
if (sessionPreference == CooldownType.DISABLED) return;
@ -161,10 +151,6 @@ public class CooldownUtils {
* @return The converted CooldownType
*/
public static CooldownType getByName(String name) {
if (name.equalsIgnoreCase("true")) { // Backwards config compatibility
return CooldownType.TITLE;
}
for (CooldownType type : VALUES) {
if (type.name().equalsIgnoreCase(name)) {
return type;

Datei anzeigen

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

Datei anzeigen

@ -48,6 +48,10 @@ public final class JsonUtils {
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) {
return GeyserImpl.GSON.fromJson(new InputStreamReader(stream), type);
}

Datei anzeigen

@ -25,9 +25,6 @@
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 org.cloudburstmc.protocol.bedrock.packet.LoginPacket;
import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket;
@ -53,8 +50,6 @@ import java.util.List;
import java.util.function.BiConsumer;
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;
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()));
if (!result.signed() && !session.getGeyser().getConfig().isEnableProxyConnections()) {
if (!result.signed() && !session.getGeyser().config().enableProxyConnections()) {
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.network.remote.invalid_xbox_account"));
return;
}
@ -85,8 +80,7 @@ public class LoginEncryptionUtils {
throw new IllegalStateException("Client data isn't signed by the given chain data");
}
JsonNode clientDataJson = JSON_MAPPER.readTree(clientDataPayload);
BedrockClientData data = JSON_MAPPER.convertValue(clientDataJson, BedrockClientData.class);
BedrockClientData data = JsonUtils.fromJson(clientDataPayload, BedrockClientData.class);
data.setOriginalString(clientData);
session.setClientData(data);
@ -94,7 +88,7 @@ public class LoginEncryptionUtils {
startEncryptionHandshake(session, identityPublicKey);
} catch (Throwable e) {
// An error can be thrown on older Java 8 versions about an invalid key
if (geyser.getConfig().isDebugMode()) {
if (geyser.config().debugMode()) {
e.printStackTrace();
}
@ -213,7 +207,7 @@ public class LoginEncryptionUtils {
.append("\n%xbox.signin.enterCode\n")
.append(ChatColor.GREEN)
.append(msCode.user_code);
int timeout = session.getGeyser().getConfig().getPendingAuthenticationTimeout();
int timeout = session.getGeyser().config().pendingAuthenticationTimeout();
if (timeout != 0) {
message.append("\n\n")
.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
boolean showCoordinates = session.getPreferencesCache().isAllowShowCoordinates();
boolean cooldownShown = CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED;
boolean customSkulls = session.getGeyser().getConfig().isAllowCustomSkulls();
boolean cooldownShown = session.getGeyser().config().showCooldown() != CooldownUtils.CooldownType.DISABLED;
boolean customSkulls = session.getGeyser().config().allowCustomSkulls();
// Only show the client title if any of the client settings are available
boolean showClientSettings = showCoordinates || cooldownShown || customSkulls;

Datei anzeigen

@ -199,7 +199,7 @@ public class WebUtils {
return ((String) attr.get(0)).split(" ");
}
} 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.");
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]
base-api = "1.0.0-SNAPSHOT"
bstats = "3.0.2"
cumulus = "1.1.2"
configurate = "4.2.0-GeyserMC-SNAPSHOT"
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" }
bstats = { group = "org.bstats", name = "bstats-base", version.ref = "bstats"}
# plugins
indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
shadow = { group = "com.github.johnrengelman", name = "shadow", version.ref = "shadow" }