mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Switch config system to Configurate (#5010)
* Start implementing Configurate config system * More development * Start migrating to Gson only * Progress * Update usage of WebUtils * Most things now use Gson for JSON * Allow tests to succeed by using new Gson version * Use slightly cleaner version for Version deserializer * Work around older Gson versions without record support * GeyserCustomSkullConfiguration uses Configurate * Fix regression in properties get * New config used in core * The configuration is gone. Long live the config. * More changes and migrations away from Jackson * Improve node ordering when updating configs * typo * Better check for ignoring non-configurate configs for considering comment moving * Ensure metrics UUID is valid * Initial advanced config * Remove Jackson; finish config value placements * Remove duplicate relocate declarations * Let annotations work * Renaming to PluginSpecific * Use global bStats config where possible * Fix test * Re-introduce asterisk behavior in configs * Remove GeyserPluginBootstrap as it's no longer necessary * Remove old config.yml file * Update Xbox achievement comment * Apply suggestions from code review Co-authored-by: chris <github@onechris.mozmail.com> * No need to remove values anymore * Fix: disable bstats relocation on platforms where it is not needed * ensure it builds * Update custom unavailable slot comment Co-authored-by: chris <github@onechris.mozmail.com> * Update cooldown image * Logger message for direct-compression still being enabled * oops * More explicit RuntimeException message Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> * Constant for 'system' locale * Better config JSON encoding (something is broken with Cloudflare; we'll figure it out * Fix broadcast port default * Add this file too * Update configurate branch * fix build * Fix: Allow using custom config file on Standalone, add relocation comment * Move config loading to GeyserBootstrap interface * Add and rename some config options, add section notes (#5390) * Add and rename some config options, add section notes * adjust message * Update core/src/main/java/org/geysermc/geyser/command/defaults/ConnectionTestCommand.java Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz> * Update core/src/main/java/org/geysermc/geyser/configuration/GeyserConfig.java Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz> * Update ConfigLoader.java * Update AdvancedConfig.java * clarify that we're talking about the HAProxy protocol * rename config option to use-haproxy-protocol * remove ominous warning sign on xbox auth warning * adjust wording --------- Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz> * Back to one config file * Some minor touchups * Configurate: Sectionification (#5904) * Init: config sections * Start on adding tests, move migrations over to ConfigMigrations class * Get rid of auth section again, rename that one config option, fix mtu migration * Move custom skulls config options to the bottom of the gameplay settings * Add more tests * Rename and migrate proxy-protocol-whitelisted-ips to haproxy-protocol-whitelisted-ips * Add automatic downloading of the GeyserOptionalPack * Revert "Add automatic downloading of the GeyserOptionalPack" This reverts commit65b96208fb. * Add more invalid config tests * Warn about emote-offhand-workaround removal * Add automatic loading of the GeyserOptionalPack (feature/configurate) (#5964) * Add automatic downloading of the GeyserOptionalPack * Warn about including the OptionalPack from extensions when Geyser is already including it instead of throwing. * Copy optional pack instead of downloading --------- Co-authored-by: onebeastchris <github@onechris.mozmail.com> * Remove unused variable * Start warning users not running Java 21 * Update tests, temporarily remove NumericRanges test * Remove duplicate advanced section from Geyser dump * Address some "reviews" * yeet md5 hash from geyser dump * Add info about number of resource packs / amount of mappings into Geyser dump * Re-enable invalid config loading test * Fix: allow-custom-skulls migration * Fix test * Add "enable-emotes" configuration option * Rename "emotes-enabled" to "show-emotes" * Only enable integrated pack when optional pack isn't present * Update integrated pack * Exclude jackson annotations, remove leftover debug print * Remove one-time config migration warnings as we don't have access to the logger at that stage * Throw more detailed descriptive error when loading resource packs from the "packs" folder, add another legacy config test * Fix NeoForge's fun module conflict * Re-add warning about moved functionality, fix Geyser-ViaProxy This reverts commitfbadfa574a. * oops * Move GeyserLegacyPingPassthrough to separate thread to avoid Standalone command locking issues --------- Co-authored-by: Konicai <71294714+Konicai@users.noreply.github.com> Co-authored-by: chris <github@onechris.mozmail.com> Co-authored-by: Eclipse <eclipse@eclipseisoffline.xyz> Co-authored-by: Aurora <auroranova8756@gmail.com>
This commit is contained in:
@@ -15,11 +15,12 @@ dependencies {
|
|||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("net.md_5.bungee.jni")
|
platformRelocate("net.md_5.bungee.jni")
|
||||||
platformRelocate("com.fasterxml.jackson")
|
|
||||||
platformRelocate("net.kyori")
|
platformRelocate("net.kyori")
|
||||||
platformRelocate("org.incendo")
|
platformRelocate("org.incendo")
|
||||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated
|
||||||
platformRelocate("org.yaml") // Broken as of 1.20
|
platformRelocate("org.yaml") // Broken as of 1.20
|
||||||
|
platformRelocate("org.spongepowered")
|
||||||
|
platformRelocate("org.bstats")
|
||||||
|
|
||||||
// These dependencies are already present on the platform
|
// These dependencies are already present on the platform
|
||||||
provided(libs.bungeecord.proxy)
|
provided(libs.bungeecord.proxy)
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.platform.bungeecord;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.plugin.Plugin;
|
||||||
|
import net.md_5.bungee.config.Configuration;
|
||||||
|
import net.md_5.bungee.config.ConfigurationProvider;
|
||||||
|
import net.md_5.bungee.config.YamlConfiguration;
|
||||||
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class BungeeMetrics implements MetricsPlatform {
|
||||||
|
private final Configuration configuration;
|
||||||
|
|
||||||
|
public BungeeMetrics(Plugin plugin) throws IOException {
|
||||||
|
// https://github.com/Bastian/bstats-metrics/blob/master/bungeecord/src/main/java/org/bstats/bungeecord/Metrics.java
|
||||||
|
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
bStatsFolder.mkdirs();
|
||||||
|
File configFile = new File(bStatsFolder, "config.yml");
|
||||||
|
if (!configFile.exists()) {
|
||||||
|
writeFile(configFile,
|
||||||
|
"# bStats (https://bStats.org) collects some basic information for plugin authors, like how",
|
||||||
|
"# many people use their plugin and their total player count. It's recommended to keep bStats",
|
||||||
|
"# enabled, but if you're not comfortable with this, you can turn this setting off. There is no",
|
||||||
|
"# performance penalty associated with having metrics enabled, and data sent to bStats is fully",
|
||||||
|
"# anonymous.",
|
||||||
|
"enabled: true",
|
||||||
|
"serverUuid: \"" + UUID.randomUUID() + "\"",
|
||||||
|
"logFailedRequests: false",
|
||||||
|
"logSentData: false",
|
||||||
|
"logResponseStatusText: false");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enabled() {
|
||||||
|
return configuration.getBoolean("enabled", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverUuid() {
|
||||||
|
return configuration.getString("serverUuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logFailedRequests() {
|
||||||
|
return configuration.getBoolean("logFailedRequests", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logSentData() {
|
||||||
|
return configuration.getBoolean("logSentData", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logResponseStatusText() {
|
||||||
|
return configuration.getBoolean("logResponseStatusText", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeFile(File file, String... lines) throws IOException {
|
||||||
|
try (BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file))) {
|
||||||
|
for (String line : lines) {
|
||||||
|
bufferedWriter.write(line);
|
||||||
|
bufferedWriter.newLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -157,7 +157,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||||||
listenerInfo.isPingPassthrough(),
|
listenerInfo.isPingPassthrough(),
|
||||||
listenerInfo.getQueryPort(),
|
listenerInfo.getQueryPort(),
|
||||||
listenerInfo.isQueryEnabled(),
|
listenerInfo.isQueryEnabled(),
|
||||||
bootstrap.getGeyserConfig().getRemote().isUseProxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end
|
bootstrap.config().advanced().java().useHaproxyProtocol() // If Geyser is expecting HAProxy, so should the Bungee end
|
||||||
);
|
);
|
||||||
|
|
||||||
// The field that stores all listeners in BungeeCord
|
// The field that stores all listeners in BungeeCord
|
||||||
@@ -191,7 +191,7 @@ public class GeyserBungeeInjector extends GeyserInjector implements Listener {
|
|||||||
}
|
}
|
||||||
initChannel.invoke(channelInitializer, ch);
|
initChannel.invoke(channelInitializer, ch);
|
||||||
|
|
||||||
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
if (bootstrap.config().advanced().java().disableCompression()) {
|
||||||
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
|
ch.pipeline().addAfter(PipelineUtils.PACKET_ENCODER, "geyser-compression-disabler",
|
||||||
new GeyserBungeeCompressionDisabler());
|
new GeyserBungeeCompressionDisabler());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class GeyserBungeePingPassthrough implements IGeyserPingPassthrough, List
|
|||||||
try {
|
try {
|
||||||
event = future.get(100, TimeUnit.MILLISECONDS);
|
event = future.get(100, TimeUnit.MILLISECONDS);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
String address = GeyserImpl.getInstance().getConfig().isLogPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
|
String address = GeyserImpl.getInstance().config().logPlayerIpAddresses() ? inetSocketAddress.toString() : "<IP address withheld>";
|
||||||
GeyserImpl.getInstance().getLogger().error("Failed to get ping information for " + address, cause);
|
GeyserImpl.getInstance().getLogger().error("Failed to get ping information for " + address, cause);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,25 +33,25 @@ import net.md_5.bungee.api.plugin.Plugin;
|
|||||||
import net.md_5.bungee.protocol.ProtocolConstants;
|
import net.md_5.bungee.protocol.ProtocolConstants;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.FloodgateKeyLoader;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
import org.geysermc.geyser.platform.bungeecord.command.BungeeCommandSource;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
import org.incendo.cloud.CommandManager;
|
import org.incendo.cloud.CommandManager;
|
||||||
import org.incendo.cloud.bungee.BungeeCommandManager;
|
import org.incendo.cloud.bungee.BungeeCommandManager;
|
||||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@@ -61,13 +61,12 @@ import java.nio.file.Paths;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
|
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
private GeyserBungeeConfiguration geyserConfig;
|
private GeyserPluginConfig geyserConfig;
|
||||||
private GeyserBungeeInjector geyserInjector;
|
private GeyserBungeeInjector geyserInjector;
|
||||||
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
private final GeyserBungeeLogger geyserLogger = new GeyserBungeeLogger(getLogger());
|
||||||
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
private IGeyserPingPassthrough geyserBungeePingPassthrough;
|
||||||
@@ -102,12 +101,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
System.setProperty("Mcpl.io_uring", "true");
|
System.setProperty("Mcpl.io_uring", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
this.geyser = GeyserImpl.load(this);
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
|
||||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||||
|
|
||||||
// Registration of listeners occurs only once
|
// Registration of listeners occurs only once
|
||||||
@@ -168,16 +166,15 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
public void onGeyserEnable() {
|
public void onGeyserEnable() {
|
||||||
if (GeyserImpl.getInstance().isReloading()) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force-disable query if enabled, or else Geyser won't enable
|
// Force-disable query if enabled, or else Geyser won't enable
|
||||||
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
|
||||||
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
|
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.bedrock().port()) {
|
||||||
try {
|
try {
|
||||||
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
|
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
|
||||||
queryField.setAccessible(true);
|
queryField.setAccessible(true);
|
||||||
@@ -195,7 +192,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
GeyserImpl.start();
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (!geyserConfig.motd().integratedPingPassthrough()) {
|
||||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
} else {
|
} else {
|
||||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||||
@@ -232,8 +229,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserBungeeConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
return geyserConfig;
|
return PlatformType.BUNGEECORD;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserPluginConfig config() {
|
||||||
|
return this.geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -290,11 +292,29 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean testFloodgatePluginPresent() {
|
public boolean testFloodgatePluginPresent() {
|
||||||
if (getProxy().getPluginManager().getPlugin("floodgate") != null) {
|
return getProxy().getPluginManager().getPlugin("floodgate") != null;
|
||||||
geyserConfig.loadFloodgate(this);
|
}
|
||||||
return true;
|
|
||||||
|
@Override
|
||||||
|
public Path getFloodgateKeyPath() {
|
||||||
|
Plugin floodgate = getProxy().getPluginManager().getPlugin("floodgate");
|
||||||
|
Path geyserDataFolder = getDataFolder().toPath();
|
||||||
|
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
|
||||||
|
|
||||||
|
return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MetricsPlatform createMetricsPlatform() {
|
||||||
|
try {
|
||||||
|
return new BungeeMetrics(this);
|
||||||
|
} catch (IOException e) {
|
||||||
|
this.geyserLogger.debug("Integrated bStats support failed to load.");
|
||||||
|
if (this.config().debugMode()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<InetSocketAddress> findCompatibleListener() {
|
private Optional<InetSocketAddress> findCompatibleListener() {
|
||||||
@@ -303,20 +323,4 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||||||
.map(info -> (InetSocketAddress) info.getSocketAddress())
|
.map(info -> (InetSocketAddress) info.getSocketAddress())
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
private boolean loadConfig() {
|
|
||||||
try {
|
|
||||||
if (!getDataFolder().exists()) //noinspection ResultOfMethodCallIgnored
|
|
||||||
getDataFolder().mkdir();
|
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"),
|
|
||||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class GeyserBungeeUpdateListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(final PostLoginEvent event) {
|
public void onPlayerJoin(final PostLoginEvent event) {
|
||||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
|
||||||
final ProxiedPlayer player = event.getPlayer();
|
final ProxiedPlayer player = event.getPlayer();
|
||||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||||
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));
|
VersionCheckUtils.checkForGeyserUpdate(() -> new BungeeCommandSource(player));
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ dependencies {
|
|||||||
shadowBundle(projects.core)
|
shadowBundle(projects.core)
|
||||||
includeTransitive(projects.core)
|
includeTransitive(projects.core)
|
||||||
|
|
||||||
// These are NOT transitively included, and instead shadowed + relocated.
|
// These are NOT transitively included, and instead shadowed (+ relocated, if not under the org.geyser namespace).
|
||||||
// Avoids fabric complaining about non-SemVer versioning
|
// Avoids fabric complaining about non-SemVer versioning
|
||||||
shadowBundle(libs.protocol.connection)
|
shadowBundle(libs.protocol.connection)
|
||||||
shadowBundle(libs.protocol.common)
|
shadowBundle(libs.protocol.common)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric;
|
package org.geysermc.geyser.platform.fabric;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
@@ -48,7 +49,7 @@ public class GeyserFabricDumpInfo extends BootstrapDumpInfo {
|
|||||||
private final String minecraftVersion;
|
private final String minecraftVersion;
|
||||||
private final EnvType environmentType;
|
private final EnvType environmentType;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
private final boolean onlineMode;
|
private final boolean onlineMode;
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class GeyserFabricPlatform implements GeyserModPlatform {
|
|||||||
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
|
||||||
if (floodgate.isPresent()) {
|
if (floodgate.isPresent()) {
|
||||||
Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate");
|
Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate");
|
||||||
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
bootstrap.loadFloodgate(floodgateDataFolder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ architectury {
|
|||||||
provided("org.cloudburstmc.math", "api")
|
provided("org.cloudburstmc.math", "api")
|
||||||
provided("com.google.errorprone", "error_prone_annotations")
|
provided("com.google.errorprone", "error_prone_annotations")
|
||||||
|
|
||||||
// Jackson shipped by Minecraft is too old, so we shade & relocate our newer version
|
|
||||||
relocate("com.fasterxml.jackson")
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// See https://github.com/google/guava/issues/6618
|
// See https://github.com/google/guava/issues/6618
|
||||||
modules {
|
modules {
|
||||||
@@ -30,15 +27,12 @@ dependencies {
|
|||||||
shadowBundle(project(path = ":mod", configuration = "transformProductionNeoForge"))
|
shadowBundle(project(path = ":mod", configuration = "transformProductionNeoForge"))
|
||||||
shadowBundle(projects.core)
|
shadowBundle(projects.core)
|
||||||
|
|
||||||
// Minecraft (1.21.2+) includes jackson. But an old version!
|
|
||||||
shadowBundle(libs.jackson.core)
|
|
||||||
shadowBundle(libs.jackson.databind)
|
|
||||||
shadowBundle(libs.jackson.dataformat.yaml)
|
|
||||||
shadowBundle(libs.jackson.annotations)
|
|
||||||
|
|
||||||
// Let's shade in our own api
|
// Let's shade in our own api
|
||||||
shadowBundle(projects.api)
|
shadowBundle(projects.api)
|
||||||
|
|
||||||
|
// this one is particularly dumb
|
||||||
|
shadowBundle(libs.configurate.`interface`)
|
||||||
|
|
||||||
// cannot be shaded, since neoforge will complain if floodgate-neoforge tries to provide this
|
// cannot be shaded, since neoforge will complain if floodgate-neoforge tries to provide this
|
||||||
include(projects.common)
|
include(projects.common)
|
||||||
|
|
||||||
@@ -61,11 +55,6 @@ tasks {
|
|||||||
remapModrinthJar {
|
remapModrinthJar {
|
||||||
archiveBaseName.set("geyser-neoforge")
|
archiveBaseName.set("geyser-neoforge")
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
// Without this, jackson's service files are not relocated
|
|
||||||
mergeServiceFiles()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modrinth {
|
modrinth {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.neoforge;
|
package org.geysermc.geyser.platform.neoforge;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
@@ -47,7 +48,7 @@ public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo {
|
|||||||
private final String minecraftVersion;
|
private final String minecraftVersion;
|
||||||
private final Dist dist;
|
private final Dist dist;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
private final boolean onlineMode;
|
private final boolean onlineMode;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public class GeyserNeoForgePlatform implements GeyserModPlatform {
|
|||||||
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
|
||||||
if (ModList.get().isLoaded("floodgate")) {
|
if (ModList.get().isLoaded("floodgate")) {
|
||||||
Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate");
|
Path floodgateDataFolder = FMLPaths.CONFIGDIR.get().resolve("floodgate");
|
||||||
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
|
bootstrap.loadFloodgate(floodgateDataFolder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ import lombok.Setter;
|
|||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.FloodgateKeyLoader;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
@@ -43,14 +45,10 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
|||||||
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
|
||||||
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
|
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
||||||
@@ -69,7 +67,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
private GeyserModConfiguration geyserConfig;
|
private GeyserPluginConfig geyserConfig;
|
||||||
private GeyserModInjector geyserInjector;
|
private GeyserModInjector geyserInjector;
|
||||||
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
private final GeyserModLogger geyserLogger = new GeyserModLogger();
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
@@ -80,12 +78,11 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
instance = this;
|
instance = this;
|
||||||
dataFolder = this.platform.dataFolder(this.platform.configPath());
|
dataFolder = this.platform.dataFolder(this.platform.configPath());
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
this.geyser = GeyserImpl.load(this);
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onGeyserEnable() {
|
public void onGeyserEnable() {
|
||||||
@@ -95,16 +92,15 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (GeyserImpl.getInstance().isReloading()) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserImpl.start();
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (!geyserConfig.motd().integratedPingPassthrough()) {
|
||||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
} else {
|
} else {
|
||||||
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
||||||
@@ -145,7 +141,12 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserModConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
|
return this.platform.platformType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserPluginConfig config() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,12 +200,7 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getServerPort() {
|
public int getServerPort() {
|
||||||
if (isServer()) {
|
|
||||||
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
return ((GeyserServerPortGetter) server).geyser$getServerPort();
|
||||||
} else {
|
|
||||||
// Set in the IntegratedServerMixin
|
|
||||||
return geyserConfig.getRemote().port();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean isServer();
|
public abstract boolean isServer();
|
||||||
@@ -214,31 +210,23 @@ public abstract class GeyserModBootstrap implements GeyserBootstrap {
|
|||||||
return this.platform.testFloodgatePluginPresent(this);
|
return this.platform.testFloodgatePluginPresent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Path floodgateKeyPath;
|
||||||
|
|
||||||
|
public void loadFloodgate(Path floodgateDataFolder) {
|
||||||
|
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, dataFolder, geyserLogger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Path getFloodgateKeyPath() {
|
||||||
|
return floodgateKeyPath;
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public InputStream getResourceOrNull(String resource) {
|
public InputStream getResourceOrNull(String resource) {
|
||||||
return this.platform.resolveResource(resource);
|
return this.platform.resolveResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
private boolean loadConfig() {
|
|
||||||
try {
|
|
||||||
if (!dataFolder.toFile().exists()) {
|
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
dataFolder.toFile().mkdir();
|
|
||||||
}
|
|
||||||
|
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
|
|
||||||
return true;
|
|
||||||
} catch (IOException ex) {
|
|
||||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull String getServerPlatform() {
|
public @NonNull String getServerPlatform() {
|
||||||
return server.getServerModName();
|
return server.getServerModName();
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ public class GeyserModInjector extends GeyserInjector {
|
|||||||
int index = ch.pipeline().names().indexOf("encoder");
|
int index = ch.pipeline().names().indexOf("encoder");
|
||||||
String baseName = index != -1 ? "encoder" : "outbound_config";
|
String baseName = index != -1 ? "encoder" : "outbound_config";
|
||||||
|
|
||||||
if (bootstrap.getGeyserConfig().isDisableCompression()) {
|
if (bootstrap.config().advanced().java().disableCompression()) {
|
||||||
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
|
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserModCompressionDisabler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ public class GeyserModInjector extends GeyserInjector {
|
|||||||
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
|
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
|
||||||
break;
|
break;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (bootstrap.getGeyserConfig().isDebugMode()) {
|
if (bootstrap.config().debugMode()) {
|
||||||
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
|
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import org.geysermc.geyser.util.VersionCheckUtils;
|
|||||||
public final class GeyserModUpdateListener {
|
public final class GeyserModUpdateListener {
|
||||||
public static void onPlayReady(ServerPlayer player) {
|
public static void onPlayReady(ServerPlayer player) {
|
||||||
// We could just not register the listener, but, this allows config reloading
|
// We could just not register the listener, but, this allows config reloading
|
||||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
|
||||||
// Should be creating this in the supplier, but we need it for the permission check.
|
// Should be creating this in the supplier, but we need it for the permission check.
|
||||||
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
|
// Not a big deal currently because ModCommandSource doesn't load locale, so don't need to try to wait for it.
|
||||||
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
|
ModCommandSource source = new ModCommandSource(player.createCommandSourceStack());
|
||||||
|
|||||||
@@ -56,14 +56,13 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
|
|||||||
// If the LAN is opened, starts Geyser.
|
// If the LAN is opened, starts Geyser.
|
||||||
GeyserModBootstrap instance = GeyserModBootstrap.getInstance();
|
GeyserModBootstrap instance = GeyserModBootstrap.getInstance();
|
||||||
instance.setServer((MinecraftServer) (Object) this);
|
instance.setServer((MinecraftServer) (Object) this);
|
||||||
instance.getGeyserConfig().getRemote().setPort(port);
|
|
||||||
instance.onGeyserEnable();
|
instance.onGeyserEnable();
|
||||||
// Ensure player locale has been loaded, in case it's different from Java system language
|
// Ensure player locale has been loaded, in case it's different from Java system language
|
||||||
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
|
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
|
||||||
// Give indication that Geyser is loaded
|
// Give indication that Geyser is loaded
|
||||||
Objects.requireNonNull(this.minecraft.player);
|
Objects.requireNonNull(this.minecraft.player);
|
||||||
this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start",
|
this.minecraft.player.displayClientMessage(Component.literal(GeyserLocale.getPlayerLocaleString("geyser.core.start.ip_suppressed",
|
||||||
this.minecraft.options.languageCode, "localhost", String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false);
|
this.minecraft.options.languageCode, String.valueOf(GeyserImpl.getInstance().bedrockListener().port()))), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,17 +31,25 @@ dependencies {
|
|||||||
compileOnly(libs.folia.api)
|
compileOnly(libs.folia.api)
|
||||||
|
|
||||||
compileOnlyApi(libs.viaversion)
|
compileOnlyApi(libs.viaversion)
|
||||||
|
|
||||||
|
// For 1.16.5/1.17.1
|
||||||
|
implementation(libs.gson.record.factory) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("it.unimi.dsi.fastutil")
|
platformRelocate("it.unimi.dsi.fastutil")
|
||||||
platformRelocate("com.fasterxml.jackson")
|
|
||||||
// Relocate net.kyori but exclude the component logger
|
// Relocate net.kyori but exclude the component logger
|
||||||
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
|
platformRelocate("net.kyori", "net.kyori.adventure.text.logger.slf4j.ComponentLogger")
|
||||||
platformRelocate("org.objectweb.asm")
|
platformRelocate("org.objectweb.asm")
|
||||||
platformRelocate("me.lucko.commodore")
|
platformRelocate("me.lucko.commodore")
|
||||||
platformRelocate("org.incendo")
|
platformRelocate("org.incendo")
|
||||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated
|
||||||
platformRelocate("org.yaml") // Broken as of 1.20
|
platformRelocate("org.yaml") // Broken as of 1.20
|
||||||
|
platformRelocate("org.spongepowered")
|
||||||
|
platformRelocate("marcono1234.gson")
|
||||||
|
platformRelocate("org.bstats")
|
||||||
|
|
||||||
provided(libs.viaversion)
|
provided(libs.viaversion)
|
||||||
|
|
||||||
tasks.withType<Jar> {
|
tasks.withType<Jar> {
|
||||||
|
|||||||
@@ -1,51 +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.platform.spigot;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.geysermc.geyser.FloodgateKeyLoader;
|
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration {
|
|
||||||
@JsonIgnore
|
|
||||||
private Path floodgateKeyPath;
|
|
||||||
|
|
||||||
public void loadFloodgate(GeyserSpigotPlugin plugin) {
|
|
||||||
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate");
|
|
||||||
Path geyserDataFolder = plugin.getDataFolder().toPath();
|
|
||||||
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
|
|
||||||
|
|
||||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot;
|
package org.geysermc.geyser.platform.spigot;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
@@ -42,7 +43,7 @@ public class GeyserSpigotDumpInfo extends BootstrapDumpInfo {
|
|||||||
private final String platformAPIVersion;
|
private final String platformAPIVersion;
|
||||||
private final boolean onlineMode;
|
private final boolean onlineMode;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
private final List<PluginInfo> plugins;
|
private final List<PluginInfo> plugins;
|
||||||
|
|||||||
@@ -25,11 +25,13 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot;
|
package org.geysermc.geyser.platform.spigot;
|
||||||
|
|
||||||
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
|
|
||||||
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
|
||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitChannelInitializer;
|
||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.channel.*;
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelFuture;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.DefaultEventLoopGroup;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
@@ -38,6 +40,8 @@ import org.geysermc.geyser.GeyserBootstrap;
|
|||||||
import org.geysermc.geyser.network.netty.GeyserInjector;
|
import org.geysermc.geyser.network.netty.GeyserInjector;
|
||||||
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
|
||||||
import org.geysermc.geyser.network.netty.LocalSession;
|
import org.geysermc.geyser.network.netty.LocalSession;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.MinecraftConstants;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.MinecraftProtocol;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@@ -123,7 +127,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||||||
int index = ch.pipeline().names().indexOf("encoder");
|
int index = ch.pipeline().names().indexOf("encoder");
|
||||||
String baseName = index != -1 ? "encoder" : "outbound_config";
|
String baseName = index != -1 ? "encoder" : "outbound_config";
|
||||||
|
|
||||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
if (bootstrap.config().advanced().java().disableCompression() && GeyserSpigotCompressionDisabler.ENABLED) {
|
||||||
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
ch.pipeline().addAfter(baseName, "geyser-compression-disabler", new GeyserSpigotCompressionDisabler());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +162,7 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (bootstrap.getGeyserConfig().isDebugMode()) {
|
if (bootstrap.config().debugMode()) {
|
||||||
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
|
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -178,8 +182,8 @@ public class GeyserSpigotInjector extends GeyserInjector {
|
|||||||
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
|
private void workAroundWeirdBug(GeyserBootstrap bootstrap) {
|
||||||
MinecraftProtocol protocol = new MinecraftProtocol();
|
MinecraftProtocol protocol = new MinecraftProtocol();
|
||||||
LocalSession session = new LocalSession(this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
|
LocalSession session = new LocalSession(this.serverSocketAddress, InetAddress.getLoopbackAddress().getHostAddress(), protocol, Runnable::run);
|
||||||
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.getGeyserConfig().getRemote().address());
|
session.setFlag(MinecraftConstants.CLIENT_HOST, bootstrap.config().java().address());
|
||||||
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.getGeyserConfig().getRemote().port());
|
session.setFlag(MinecraftConstants.CLIENT_PORT, bootstrap.config().java().port());
|
||||||
session.connect();
|
session.connect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ import org.bukkit.event.Listener;
|
|||||||
import org.bukkit.event.server.ServerLoadEvent;
|
import org.bukkit.event.server.ServerLoadEvent;
|
||||||
import org.bukkit.permissions.Permission;
|
import org.bukkit.permissions.Permission;
|
||||||
import org.bukkit.permissions.PermissionDefault;
|
import org.bukkit.permissions.PermissionDefault;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
import org.bukkit.plugin.PluginManager;
|
import org.bukkit.plugin.PluginManager;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.FloodgateKeyLoader;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
import org.geysermc.geyser.adapters.paper.PaperAdapters;
|
||||||
@@ -50,7 +52,7 @@ import org.geysermc.geyser.api.util.PlatformType;
|
|||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
@@ -64,23 +66,20 @@ import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotLegacyNativ
|
|||||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager;
|
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotNativeWorldManager;
|
||||||
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
import org.geysermc.geyser.platform.spigot.world.manager.GeyserSpigotWorldManager;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
import org.incendo.cloud.bukkit.BukkitCommandManager;
|
import org.incendo.cloud.bukkit.BukkitCommandManager;
|
||||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||||
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
import org.incendo.cloud.paper.LegacyPaperCommandManager;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
private GeyserSpigotConfiguration geyserConfig;
|
private GeyserPluginConfig geyserConfig;
|
||||||
private GeyserSpigotInjector geyserInjector;
|
private GeyserSpigotInjector geyserInjector;
|
||||||
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
|
private final GeyserSpigotLogger geyserLogger = GeyserPaperLogger.supported() ?
|
||||||
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
|
new GeyserPaperLogger(this, getLogger()) : new GeyserSpigotLogger(getLogger());
|
||||||
@@ -161,16 +160,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
|
// We'll disable ourselves later
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
|
|
||||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||||
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
|
this.geyser = GeyserImpl.load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -226,16 +225,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
// Configs are loaded once early - so we can create the logger, then load extensions and finally register
|
// Configs are loaded once early - so we can create the logger, then load extensions and finally register
|
||||||
// extension commands in #onEnable. To ensure reloading geyser also reloads the geyser config, this exists
|
// extension commands in #onEnable. To ensure reloading geyser also reloads the geyser config, this exists
|
||||||
if (GeyserImpl.getInstance().isReloading()) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(this.geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserImpl.start();
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (!geyserConfig.motd().integratedPingPassthrough()) {
|
||||||
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
} else {
|
} else {
|
||||||
if (ReflectedNames.checkPaperPingEvent()) {
|
if (ReflectedNames.checkPaperPingEvent()) {
|
||||||
@@ -289,7 +288,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
|
geyserLogger.debug("Using world manager of type: " + this.geyserWorldManager.getClass().getSimpleName());
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if (geyserConfig.isDebugMode()) {
|
if (geyserConfig.debugMode()) {
|
||||||
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
geyserLogger.debug("Error while attempting to find NMS adapter. Most likely, this can be safely ignored. :)");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@@ -363,8 +362,13 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
return geyserConfig;
|
return PlatformType.SPIGOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserPluginConfig config() {
|
||||||
|
return this.geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -455,32 +459,21 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean testFloodgatePluginPresent() {
|
public boolean testFloodgatePluginPresent() {
|
||||||
if (Bukkit.getPluginManager().getPlugin("floodgate") != null) {
|
return Bukkit.getPluginManager().getPlugin("floodgate") != null;
|
||||||
geyserConfig.loadFloodgate(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
@Override
|
||||||
private boolean loadConfig() {
|
public Path getFloodgateKeyPath() {
|
||||||
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate");
|
||||||
try {
|
Path geyserDataFolder = getDataFolder().toPath();
|
||||||
if (!getDataFolder().exists()) {
|
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
|
||||||
//noinspection ResultOfMethodCallIgnored
|
|
||||||
getDataFolder().mkdir();
|
return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataFolder, geyserDataFolder, geyserLogger);
|
||||||
}
|
|
||||||
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);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
@Override
|
||||||
|
public MetricsPlatform createMetricsPlatform() {
|
||||||
|
return new SpigotMetrics(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void warnInvalidProxySetups(String platform) {
|
private void warnInvalidProxySetups(String platform) {
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public final class GeyserSpigotUpdateListener implements Listener {
|
|||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerJoin(final PlayerJoinEvent event) {
|
public void onPlayerJoin(final PlayerJoinEvent event) {
|
||||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||||
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));
|
VersionCheckUtils.checkForGeyserUpdate(() -> new SpigotCommandSource(player));
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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.platform.spigot;
|
||||||
|
|
||||||
|
import org.bukkit.configuration.file.YamlConfiguration;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public final class SpigotMetrics implements MetricsPlatform {
|
||||||
|
private final YamlConfiguration config;
|
||||||
|
|
||||||
|
public SpigotMetrics(Plugin plugin) {
|
||||||
|
// https://github.com/Bastian/bstats-metrics/blob/master/bukkit/src/main/java/org/bstats/bukkit/Metrics.java
|
||||||
|
// Get the config file
|
||||||
|
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
|
||||||
|
File configFile = new File(bStatsFolder, "config.yml");
|
||||||
|
config = YamlConfiguration.loadConfiguration(configFile);
|
||||||
|
|
||||||
|
if (!config.isSet("serverUuid")) {
|
||||||
|
config.addDefault("enabled", true);
|
||||||
|
config.addDefault("serverUuid", UUID.randomUUID().toString());
|
||||||
|
config.addDefault("logFailedRequests", false);
|
||||||
|
config.addDefault("logSentData", false);
|
||||||
|
config.addDefault("logResponseStatusText", false);
|
||||||
|
|
||||||
|
// Inform the server owners about bStats
|
||||||
|
config.options().header(
|
||||||
|
"bStats (https://bStats.org) collects some basic information for plugin authors, like how\n" +
|
||||||
|
"many people use their plugin and their total player count. It's recommended to keep bStats\n" +
|
||||||
|
"enabled, but if you're not comfortable with this, you can turn this setting off. There is no\n" +
|
||||||
|
"performance penalty associated with having metrics enabled, and data sent to bStats is fully\n" +
|
||||||
|
"anonymous."
|
||||||
|
).copyDefaults(true);
|
||||||
|
try {
|
||||||
|
config.save(configFile);
|
||||||
|
} catch (IOException ignored) { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enabled() {
|
||||||
|
return config.getBoolean("enabled", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverUuid() {
|
||||||
|
return config.getString("serverUuid");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logFailedRequests() {
|
||||||
|
return config.getBoolean("logFailedRequests", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logSentData() {
|
||||||
|
return config.getBoolean("logSentData", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logResponseStatusText() {
|
||||||
|
return config.getBoolean("logResponseStatusText", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,6 +15,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.bundles.jline)
|
implementation(libs.bundles.jline)
|
||||||
implementation(libs.bundles.log4j)
|
implementation(libs.bundles.log4j)
|
||||||
|
|
||||||
|
implementation(libs.gson.runtime)
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
@@ -25,11 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.standalone;
|
package org.geysermc.geyser.platform.standalone;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.BeanDescription;
|
|
||||||
import com.fasterxml.jackson.databind.JavaType;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
|
|
||||||
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
|
|
||||||
import io.netty.util.ResourceLeakDetector;
|
import io.netty.util.ResourceLeakDetector;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.apache.logging.log4j.Level;
|
import org.apache.logging.log4j.Level;
|
||||||
@@ -41,35 +36,32 @@ import org.geysermc.geyser.GeyserImpl;
|
|||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.ConfigLoader;
|
||||||
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.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
|
import org.geysermc.geyser.platform.standalone.gui.GeyserStandaloneGUI;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
|
||||||
import org.geysermc.geyser.util.LoopbackUtil;
|
import org.geysermc.geyser.util.LoopbackUtil;
|
||||||
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.NodePath;
|
||||||
|
import org.spongepowered.configurate.serialize.SerializationException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
|
|
||||||
private StandaloneCloudCommandManager cloud;
|
private StandaloneCloudCommandManager cloud;
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
private GeyserStandaloneConfiguration geyserConfig;
|
private GeyserConfig geyserConfig;
|
||||||
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
|
private final GeyserStandaloneLogger geyserLogger = new GeyserStandaloneLogger();
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
private GeyserStandaloneGUI gui;
|
private GeyserStandaloneGUI gui;
|
||||||
@@ -80,9 +72,7 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
|
|
||||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
private static final Map<NodePath, String> argsConfigKeys = new HashMap<>();
|
||||||
|
|
||||||
private static final Map<String, String> argsConfigKeys = new HashMap<>();
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
if (System.getProperty("io.netty.leakDetection.level") == null) {
|
if (System.getProperty("io.netty.leakDetection.level") == null) {
|
||||||
@@ -99,8 +89,6 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
|
|
||||||
GeyserLocale.init(bootstrap);
|
GeyserLocale.init(bootstrap);
|
||||||
|
|
||||||
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
|
|
||||||
|
|
||||||
for (int i = 0; i < args.length; i++) {
|
for (int i = 0; i < args.length; i++) {
|
||||||
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
|
// By default, standalone Geyser will check if it should open the GUI based on if the GUI is null
|
||||||
// Optionally, you can force the use of a GUI or no GUI by specifying args
|
// Optionally, you can force the use of a GUI or no GUI by specifying args
|
||||||
@@ -132,36 +120,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
// Split the argument by an =
|
// Split the argument by an =
|
||||||
String[] argParts = arg.substring(2).split("=");
|
String[] argParts = arg.substring(2).split("=");
|
||||||
if (argParts.length == 2) {
|
if (argParts.length == 2) {
|
||||||
// Split the config key by . to allow for nested options
|
argsConfigKeys.put(NodePath.of(argParts[0].split("\\.")), argParts[1]);
|
||||||
String[] configKeyParts = argParts[0].split("\\.");
|
|
||||||
|
|
||||||
// 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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the found key to the stored list for later usage
|
|
||||||
if (found) {
|
|
||||||
argsConfigKeys.put(argParts[0], argParts[1]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
|
System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", arg));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -189,19 +151,8 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGeyserEnable() {
|
public void onGeyserEnable() {
|
||||||
try {
|
this.geyserConfig = loadConfig(GeyserRemoteConfig.class);
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
if (this.geyserConfig == null) {
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
geyserConfig = FileUtils.loadConfig(configFile, GeyserStandaloneConfiguration.class);
|
|
||||||
|
|
||||||
handleArgsConfigOptions();
|
|
||||||
|
|
||||||
if (this.geyserConfig.getRemote().address().equalsIgnoreCase("auto")) {
|
|
||||||
geyserConfig.setAutoconfiguredRemote(true); // Doesn't really need to be set but /shrug
|
|
||||||
geyserConfig.getRemote().setAddress("127.0.0.1");
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
|
||||||
geyserLogger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
if (gui == null) {
|
if (gui == null) {
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
} else {
|
} else {
|
||||||
@@ -209,13 +160,11 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
|
|
||||||
// Allow libraries like Protocol to have their debug information passthrough
|
// Allow libraries like Protocol to have their debug information passthrough
|
||||||
log4jLogger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
log4jLogger.get().setLevel(geyserConfig.debugMode() ? Level.DEBUG : Level.INFO);
|
||||||
|
|
||||||
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
geyser = GeyserImpl.load(this);
|
||||||
|
|
||||||
boolean reloading = geyser.isReloading();
|
boolean reloading = geyser.isReloading();
|
||||||
if (!reloading) {
|
if (!reloading) {
|
||||||
@@ -245,6 +194,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
geyserLogger.start();
|
geyserLogger.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends GeyserConfig> T loadConfig(Class<T> configClass) {
|
||||||
|
return new ConfigLoader(this)
|
||||||
|
.configFile(new File(getConfigFolder().toFile(), configFilename))
|
||||||
|
.transformer(this::handleArgsConfigOptions)
|
||||||
|
.load(configClass);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check using {@link java.awt.GraphicsEnvironment} that we are a headless client
|
* Check using {@link java.awt.GraphicsEnvironment} that we are a headless client
|
||||||
*
|
*
|
||||||
@@ -273,8 +230,13 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
return geyserConfig;
|
return PlatformType.STANDALONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserConfig config() {
|
||||||
|
return this.geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -330,100 +292,47 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Get the {@link BeanPropertyDefinition}s for the given class
|
public Path getFloodgateKeyPath() {
|
||||||
*
|
return Path.of(geyserConfig.advanced().floodgateKeyFile());
|
||||||
* @param clazz The class to get the definitions for
|
|
||||||
* @return A list of {@link BeanPropertyDefinition} for the given class
|
|
||||||
*/
|
|
||||||
public static List<BeanPropertyDefinition> getPOJOForClass(Class<?> clazz) {
|
|
||||||
JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
|
|
||||||
|
|
||||||
// Introspect the given type
|
|
||||||
BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
|
|
||||||
|
|
||||||
// Find properties
|
|
||||||
List<BeanPropertyDefinition> properties = beanDescription.findProperties();
|
|
||||||
|
|
||||||
// Get the ignored properties
|
|
||||||
Set<String> ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
|
|
||||||
.findPropertyIgnoralByName(OBJECT_MAPPER.getSerializationConfig(), beanDescription.getClassInfo()).getIgnored();
|
|
||||||
|
|
||||||
// Filter properties removing the ignored ones
|
|
||||||
return properties.stream()
|
|
||||||
.filter(property -> !ignoredProperties.contains(property.getName()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set a POJO property value on an object
|
* Set a POJO property value on an object
|
||||||
*
|
*
|
||||||
* @param property The {@link BeanPropertyDefinition} to set
|
|
||||||
* @param parentObject The object to alter
|
|
||||||
* @param value The new value of the property
|
* @param value The new value of the property
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"}) // Required for enum usage
|
private static void setConfigOption(CommentedConfigurationNode node, Object value) throws SerializationException {
|
||||||
private static void setConfigOption(BeanPropertyDefinition property, Object parentObject, Object value) {
|
|
||||||
Object parsedValue = value;
|
Object parsedValue = value;
|
||||||
|
|
||||||
// Change the values type if needed
|
// Change the values type if needed
|
||||||
if (int.class.equals(property.getRawPrimaryType())) {
|
Class<?> clazz = node.raw().getClass();
|
||||||
|
if (Integer.class == clazz) {
|
||||||
parsedValue = Integer.valueOf((String) parsedValue);
|
parsedValue = Integer.valueOf((String) parsedValue);
|
||||||
} else if (boolean.class.equals(property.getRawPrimaryType())) {
|
} else if (Boolean.class == clazz) {
|
||||||
parsedValue = Boolean.valueOf((String) parsedValue);
|
parsedValue = Boolean.valueOf((String) parsedValue);
|
||||||
} else if (Enum.class.isAssignableFrom(property.getRawPrimaryType())) {
|
|
||||||
parsedValue = Enum.valueOf((Class<? extends Enum>) property.getRawPrimaryType(), ((String) parsedValue).toUpperCase(Locale.ROOT));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force the value to be set
|
node.set(parsedValue);
|
||||||
AnnotatedField field = property.getField();
|
|
||||||
field.fixAccess(true);
|
|
||||||
field.setValue(parentObject, parsedValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the loaded {@link GeyserStandaloneConfiguration} with any values passed in the command line arguments
|
* Update the loaded config with any values passed in the command line arguments
|
||||||
*/
|
*/
|
||||||
private void handleArgsConfigOptions() {
|
private void handleArgsConfigOptions(CommentedConfigurationNode node) {
|
||||||
// Get the available properties from the class
|
for (Map.Entry<NodePath, String> configKey : argsConfigKeys.entrySet()) {
|
||||||
List<BeanPropertyDefinition> availableProperties = getPOJOForClass(GeyserJacksonConfiguration.class);
|
NodePath path = configKey.getKey();
|
||||||
|
CommentedConfigurationNode subNode = node.node(path);
|
||||||
|
if (subNode.virtual()) {
|
||||||
|
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.unrecognised", path));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
for (Map.Entry<String, String> configKey : argsConfigKeys.entrySet()) {
|
|
||||||
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 {
|
try {
|
||||||
Object subConfig = property.getGetter().callOn(geyserConfig);
|
setConfigOption(subNode, configKey.getValue());
|
||||||
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()));
|
geyserLogger.info(GeyserLocale.getLocaleStringLog("geyser.bootstrap.args.set_config_option", configKey.getKey(), configKey.getValue()));
|
||||||
|
} catch (SerializationException e) {
|
||||||
// Set the property value on the config
|
geyserLogger.error("Failed to set config option: " + path);
|
||||||
try {
|
|
||||||
setConfigOption(property, geyserConfig, configKey.getValue());
|
|
||||||
} catch (Exception e) {
|
|
||||||
geyserLogger.error("Failed to set config option: " + property.getFullName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.apache.logging.log4j.LogManager;
|
|||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.apache.logging.log4j.core.config.Configurator;
|
import org.apache.logging.log4j.core.config.Configurator;
|
||||||
import org.apache.logging.log4j.io.IoBuilder;
|
import org.apache.logging.log4j.io.IoBuilder;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
@@ -112,7 +113,12 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(String message) {
|
public void debug(String message) {
|
||||||
log.debug(ChatColor.GRAY + message);
|
log.debug(ChatColor.GRAY + "{}", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(@Nullable Object object) {
|
||||||
|
log.debug("{}", object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,12 +16,13 @@ dependencies {
|
|||||||
api(libs.cloud.velocity)
|
api(libs.cloud.velocity)
|
||||||
}
|
}
|
||||||
|
|
||||||
platformRelocate("com.fasterxml.jackson")
|
|
||||||
platformRelocate("it.unimi.dsi.fastutil")
|
platformRelocate("it.unimi.dsi.fastutil")
|
||||||
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
|
platformRelocate("net.kyori.adventure.text.serializer.gson.legacyimpl")
|
||||||
platformRelocate("org.yaml")
|
platformRelocate("org.yaml")
|
||||||
|
platformRelocate("org.spongepowered")
|
||||||
|
platformRelocate("org.bstats")
|
||||||
platformRelocate("org.incendo")
|
platformRelocate("org.incendo")
|
||||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated
|
||||||
|
|
||||||
// These dependencies are already present on the platform
|
// These dependencies are already present on the platform
|
||||||
provided(libs.velocity.api)
|
provided(libs.velocity.api)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.velocity;
|
package org.geysermc.geyser.platform.velocity;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -42,7 +43,7 @@ public class GeyserVelocityDumpInfo extends BootstrapDumpInfo {
|
|||||||
private final String platformVendor;
|
private final String platformVendor;
|
||||||
private final boolean onlineMode;
|
private final boolean onlineMode;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
private final List<PluginInfo> plugins;
|
private final List<PluginInfo> plugins;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ public class GeyserVelocityInjector extends GeyserInjector {
|
|||||||
protected void initChannel(@NonNull Channel ch) throws Exception {
|
protected void initChannel(@NonNull Channel ch) throws Exception {
|
||||||
initChannel.invoke(channelInitializer, ch);
|
initChannel.invoke(channelInitializer, ch);
|
||||||
|
|
||||||
if (bootstrap.getGeyserConfig().isDisableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
|
if (bootstrap.config().advanced().java().disableCompression() && GeyserVelocityCompressionDisabler.ENABLED) {
|
||||||
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
|
ch.pipeline().addAfter("minecraft-encoder", "geyser-compression-disabler",
|
||||||
new GeyserVelocityCompressionDisabler());
|
new GeyserVelocityCompressionDisabler());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,31 +39,31 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.FloodgateKeyLoader;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.CommandSourceConverter;
|
import org.geysermc.geyser.command.CommandSourceConverter;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
|
import org.geysermc.geyser.platform.velocity.command.VelocityCommandSource;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
import org.incendo.cloud.CommandManager;
|
import org.incendo.cloud.CommandManager;
|
||||||
import org.incendo.cloud.execution.ExecutionCoordinator;
|
import org.incendo.cloud.execution.ExecutionCoordinator;
|
||||||
import org.incendo.cloud.velocity.VelocityCommandManager;
|
import org.incendo.cloud.velocity.VelocityCommandManager;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.UUID;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
@Plugin(id = "geyser", name = GeyserImpl.NAME + "-Velocity", version = GeyserImpl.VERSION, url = "https://geysermc.org", authors = "GeyserMC")
|
||||||
public class GeyserVelocityPlugin implements GeyserBootstrap {
|
public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||||
@@ -71,7 +71,7 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||||||
private final ProxyServer proxyServer;
|
private final ProxyServer proxyServer;
|
||||||
private final PluginContainer container;
|
private final PluginContainer container;
|
||||||
private final GeyserVelocityLogger geyserLogger;
|
private final GeyserVelocityLogger geyserLogger;
|
||||||
private GeyserVelocityConfiguration geyserConfig;
|
private GeyserPluginConfig geyserConfig;
|
||||||
private GeyserVelocityInjector geyserInjector;
|
private GeyserVelocityInjector geyserInjector;
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
@@ -106,13 +106,12 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||||||
System.setProperty("Mcpl.io_uring", "true");
|
System.setProperty("Mcpl.io_uring", "true");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
|
this.geyser = GeyserImpl.load(this);
|
||||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||||
|
|
||||||
// We need to register commands here, rather than in onGeyserEnable which is invoked during the appropriate ListenerBoundEvent.
|
// We need to register commands here, rather than in onGeyserEnable which is invoked during the appropriate ListenerBoundEvent.
|
||||||
@@ -139,16 +138,15 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (GeyserImpl.getInstance().isReloading()) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
if (!loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserImpl.start();
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (!geyserConfig.motd().integratedPingPassthrough()) {
|
||||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
} else {
|
} else {
|
||||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||||
@@ -178,7 +176,12 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserVelocityConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
|
return PlatformType.VELOCITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserPluginConfig config() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,27 +253,26 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
|||||||
@Override
|
@Override
|
||||||
public boolean testFloodgatePluginPresent() {
|
public boolean testFloodgatePluginPresent() {
|
||||||
var floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
|
var floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
|
||||||
if (floodgate.isPresent()) {
|
return floodgate.isPresent();
|
||||||
geyserConfig.loadFloodgate(this, proxyServer, configFolder.toFile());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
@Override
|
||||||
private boolean loadConfig() {
|
public Path getFloodgateKeyPath() {
|
||||||
try {
|
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
|
||||||
if (!configFolder.toFile().exists())
|
Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null;
|
||||||
//noinspection ResultOfMethodCallIgnored
|
return FloodgateKeyLoader.getKeyPath(geyserConfig, floodgateDataPath, configFolder, geyserLogger);
|
||||||
configFolder.toFile().mkdirs();
|
}
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(),
|
|
||||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
@Override
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
public MetricsPlatform createMetricsPlatform() {
|
||||||
} catch (IOException ex) {
|
try {
|
||||||
geyserLogger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
return new VelocityMetrics(this.configFolder);
|
||||||
ex.printStackTrace();
|
} catch (IOException e) {
|
||||||
return false;
|
this.geyserLogger.debug("Integrated bStats support failed to load.");
|
||||||
|
if (this.config().debugMode()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public final class GeyserVelocityUpdateListener {
|
|||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onPlayerJoin(PostLoginEvent event) {
|
public void onPlayerJoin(PostLoginEvent event) {
|
||||||
if (GeyserImpl.getInstance().getConfig().isNotifyOnNewBedrockUpdate()) {
|
if (GeyserImpl.getInstance().config().notifyOnNewBedrockUpdate()) {
|
||||||
final Player player = event.getPlayer();
|
final Player player = event.getPlayer();
|
||||||
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
if (player.hasPermission(Permissions.CHECK_UPDATE)) {
|
||||||
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));
|
VersionCheckUtils.checkForGeyserUpdate(() -> new VelocityCommandSource(player));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,28 +25,45 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.velocity;
|
package org.geysermc.geyser.platform.velocity;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import org.bstats.config.MetricsConfig;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
|
||||||
import lombok.Getter;
|
|
||||||
import org.geysermc.geyser.FloodgateKeyLoader;
|
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
@Getter
|
public final class VelocityMetrics implements MetricsPlatform {
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
private final MetricsConfig config;
|
||||||
public final class GeyserVelocityConfiguration extends GeyserJacksonConfiguration {
|
|
||||||
@JsonIgnore
|
|
||||||
private Path floodgateKeyPath;
|
|
||||||
|
|
||||||
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
|
public VelocityMetrics(Path dataDirectory) throws IOException {
|
||||||
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
|
// https://github.com/Bastian/bstats-metrics/blob/master/velocity/src/main/java/org/bstats/velocity/Metrics.java
|
||||||
Path floodgateDataPath = floodgate.isPresent() ? Paths.get("plugins/floodgate/") : null;
|
File configFile = dataDirectory.getParent().resolve("bStats").resolve("config.txt").toFile();
|
||||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataPath, dataFolder.toPath(), plugin.getGeyserLogger());
|
this.config = new MetricsConfig(configFile, true);
|
||||||
|
// No logger message is implemented as Velocity should print its own before we do.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean enabled() {
|
||||||
|
return config.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String serverUuid() {
|
||||||
|
return config.getServerUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logFailedRequests() {
|
||||||
|
return config.isLogErrorsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logSentData() {
|
||||||
|
return config.isLogSentDataEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean logResponseStatusText() {
|
||||||
|
return config.isLogResponseStatusTextEnabled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,8 +12,9 @@ platformRelocate("net.kyori")
|
|||||||
platformRelocate("org.yaml")
|
platformRelocate("org.yaml")
|
||||||
platformRelocate("it.unimi.dsi.fastutil")
|
platformRelocate("it.unimi.dsi.fastutil")
|
||||||
platformRelocate("org.cloudburstmc.netty")
|
platformRelocate("org.cloudburstmc.netty")
|
||||||
|
platformRelocate("org.bstats")
|
||||||
platformRelocate("org.incendo")
|
platformRelocate("org.incendo")
|
||||||
platformRelocate("io.leangen.geantyref") // provided by cloud, should also be relocated
|
platformRelocate("io.leangen.geantyref") // provided by cloud and Configurate, should also be relocated
|
||||||
|
|
||||||
// These dependencies are already present on the platform
|
// These dependencies are already present on the platform
|
||||||
provided(libs.viaproxy)
|
provided(libs.viaproxy)
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
|
|
||||||
*
|
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
|
||||||
* in the Software without restriction, including without limitation the rights
|
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
|
||||||
* furnished to do so, subject to the following conditions:
|
|
||||||
*
|
|
||||||
* The above copyright notice and this permission notice shall be included in
|
|
||||||
* all copies or substantial portions of the Software.
|
|
||||||
*
|
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
* @author GeyserMC
|
|
||||||
* @link https://github.com/GeyserMC/Geyser
|
|
||||||
*/
|
|
||||||
package org.geysermc.geyser.platform.viaproxy;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import net.raphimc.vialegacy.api.LegacyProtocolVersion;
|
|
||||||
import net.raphimc.viaproxy.ViaProxy;
|
|
||||||
import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig;
|
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
|
||||||
public class GeyserViaProxyConfiguration extends GeyserJacksonConfiguration {
|
|
||||||
|
|
||||||
private RemoteConfiguration remote = new RemoteConfiguration() {
|
|
||||||
@Override
|
|
||||||
public boolean isForwardHost() {
|
|
||||||
return super.isForwardHost() || !ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Path getFloodgateKeyPath() {
|
|
||||||
return new File(GeyserViaProxyPlugin.ROOT_FOLDER, this.getFloodgateKeyFile()).toPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPingPassthroughInterval() {
|
|
||||||
int interval = super.getPingPassthroughInterval();
|
|
||||||
if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) {
|
|
||||||
// <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made
|
|
||||||
interval = 15;
|
|
||||||
}
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteConfiguration getRemote() {
|
|
||||||
return this.remote;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.geysermc.geyser.platform.viaproxy;
|
package org.geysermc.geyser.platform.viaproxy;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.raphimc.viaproxy.ViaProxy;
|
import net.raphimc.viaproxy.ViaProxy;
|
||||||
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
|
import net.raphimc.viaproxy.plugins.ViaProxyPlugin;
|
||||||
@@ -41,7 +42,7 @@ public class GeyserViaProxyDumpInfo extends BootstrapDumpInfo {
|
|||||||
private final String platformVersion;
|
private final String platformVersion;
|
||||||
private final boolean onlineMode;
|
private final boolean onlineMode;
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
private final String serverIP;
|
private final String serverIP;
|
||||||
private final int serverPort;
|
private final int serverPort;
|
||||||
private final List<PluginInfo> plugins;
|
private final List<PluginInfo> plugins;
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ package org.geysermc.geyser.platform.viaproxy;
|
|||||||
|
|
||||||
import net.raphimc.viaproxy.cli.ConsoleFormatter;
|
import net.raphimc.viaproxy.cli.ConsoleFormatter;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
|
|
||||||
@@ -75,6 +76,13 @@ public class GeyserViaProxyLogger implements GeyserLogger, GeyserCommandSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(@Nullable Object object) {
|
||||||
|
if (this.debug) {
|
||||||
|
this.logger.debug(ConsoleFormatter.convert(String.valueOf(object)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void debug(String message, Object... arguments) {
|
public void debug(String message, Object... arguments) {
|
||||||
if (this.debug) {
|
if (this.debug) {
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ import net.raphimc.viaproxy.plugins.events.ConsoleCommandEvent;
|
|||||||
import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
import net.raphimc.viaproxy.plugins.events.ProxyStartEvent;
|
||||||
import net.raphimc.viaproxy.plugins.events.ProxyStopEvent;
|
import net.raphimc.viaproxy.plugins.events.ProxyStopEvent;
|
||||||
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
|
import net.raphimc.viaproxy.plugins.events.ShouldVerifyOnlineModeEvent;
|
||||||
|
import net.raphimc.viaproxy.plugins.events.ViaProxyLoadedEvent;
|
||||||
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
import net.raphimc.viaproxy.plugins.events.types.ITyped;
|
||||||
|
import net.raphimc.viaproxy.protocoltranslator.viaproxy.ViaProxyConfig;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
@@ -47,18 +49,19 @@ import org.geysermc.geyser.api.network.AuthType;
|
|||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
import org.geysermc.geyser.command.standalone.StandaloneCloudCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.ConfigLoader;
|
||||||
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
import org.geysermc.geyser.platform.viaproxy.listener.GeyserServerTransferListener;
|
import org.geysermc.geyser.platform.viaproxy.listener.GeyserServerTransferListener;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
|
||||||
import org.geysermc.geyser.util.LoopbackUtil;
|
import org.geysermc.geyser.util.LoopbackUtil;
|
||||||
|
import org.spongepowered.configurate.serialize.SerializationException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@@ -67,10 +70,10 @@ import java.util.UUID;
|
|||||||
|
|
||||||
public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, EventRegistrar {
|
public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootstrap, EventRegistrar {
|
||||||
|
|
||||||
public static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser");
|
private static final File ROOT_FOLDER = new File(PluginManager.PLUGINS_DIR, "Geyser");
|
||||||
|
|
||||||
private final GeyserViaProxyLogger logger = new GeyserViaProxyLogger(LogManager.getLogger("Geyser"));
|
private final GeyserViaProxyLogger logger = new GeyserViaProxyLogger(LogManager.getLogger("Geyser"));
|
||||||
private GeyserViaProxyConfiguration config;
|
private GeyserPluginConfig geyserConfig;
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
private StandaloneCloudCommandManager cloud;
|
private StandaloneCloudCommandManager cloud;
|
||||||
private CommandRegistry commandRegistry;
|
private CommandRegistry commandRegistry;
|
||||||
@@ -79,10 +82,6 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
ROOT_FOLDER.mkdirs();
|
ROOT_FOLDER.mkdirs();
|
||||||
|
|
||||||
GeyserLocale.init(this);
|
|
||||||
this.onGeyserInitialize();
|
|
||||||
|
|
||||||
ViaProxy.EVENT_MANAGER.register(this);
|
ViaProxy.EVENT_MANAGER.register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,6 +90,12 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
this.onGeyserShutdown();
|
this.onGeyserShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
private void onViaProxyLoaded(ViaProxyLoadedEvent event) {
|
||||||
|
GeyserLocale.init(this);
|
||||||
|
this.onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
private void onConsoleCommand(final ConsoleCommandEvent event) {
|
private void onConsoleCommand(final ConsoleCommandEvent event) {
|
||||||
final String command = event.getCommand().startsWith("/") ? event.getCommand().substring(1) : event.getCommand();
|
final String command = event.getCommand().startsWith("/") ? event.getCommand().substring(1) : event.getCommand();
|
||||||
@@ -119,6 +124,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
if (event.getType() != ITyped.Type.POST || event.isLegacyPassthrough()) {
|
if (event.getType() != ITyped.Type.POST || event.isLegacyPassthrough()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// TODO remove
|
||||||
if (System.getProperty("geyser.viaproxy.disableIpPassthrough") != null) { // Temporary until Configurate branch is merged
|
if (System.getProperty("geyser.viaproxy.disableIpPassthrough") != null) { // Temporary until Configurate branch is merged
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -147,11 +153,12 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGeyserInitialize() {
|
public void onGeyserInitialize() {
|
||||||
if (!this.loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.VIAPROXY, this);
|
this.geyser = GeyserImpl.load(this);
|
||||||
this.geyser.eventBus().register(this, new GeyserServerTransferListener());
|
this.geyser.eventBus().register(this, new GeyserServerTransferListener());
|
||||||
LoopbackUtil.checkAndApplyLoopback(this.logger);
|
LoopbackUtil.checkAndApplyLoopback(this.logger);
|
||||||
}
|
}
|
||||||
@@ -164,7 +171,8 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
}
|
}
|
||||||
boolean reloading = geyser.isReloading();
|
boolean reloading = geyser.isReloading();
|
||||||
if (reloading) {
|
if (reloading) {
|
||||||
if (!this.loadConfig()) {
|
geyserConfig = loadConfig(GeyserPluginConfig.class);
|
||||||
|
if (geyserConfig == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -185,7 +193,7 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
|
// Only initialize the ping passthrough if the protocol version is above beta 1.7.3, as that's when the status protocol was added
|
||||||
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
|
this.pingPassthrough = GeyserLegacyPingPassthrough.init(this.geyser);
|
||||||
}
|
}
|
||||||
if (this.config.getRemote().authType() == AuthType.FLOODGATE) {
|
if (this.geyserConfig.java().authType() == AuthType.FLOODGATE) {
|
||||||
ViaProxy.getConfig().setPassthroughBungeecordPlayerInfo(true);
|
ViaProxy.getConfig().setPassthroughBungeecordPlayerInfo(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -201,8 +209,13 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserConfiguration getGeyserConfig() {
|
public @NonNull PlatformType platformType() {
|
||||||
return this.config;
|
return PlatformType.VIAPROXY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserPluginConfig config() {
|
||||||
|
return this.geyserConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -259,19 +272,36 @@ public class GeyserViaProxyPlugin extends ViaProxyPlugin implements GeyserBootst
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
@Override
|
||||||
private boolean loadConfig() {
|
public Path getFloodgateKeyPath() {
|
||||||
try {
|
return new File(ROOT_FOLDER, geyserConfig.advanced().floodgateKeyFile()).toPath();
|
||||||
final File configFile = FileUtils.fileOrCopiedFromResource(new File(ROOT_FOLDER, "config.yml"), "config.yml", s -> s.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
|
||||||
this.config = FileUtils.loadConfig(configFile, GeyserViaProxyConfiguration.class);
|
|
||||||
} catch (IOException e) {
|
|
||||||
this.logger.severe(GeyserLocale.getLocaleStringLog("geyser.config.failed"), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.config.getRemote().setAuthType(Files.isRegularFile(this.config.getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE);
|
|
||||||
this.logger.setDebug(this.config.isDebugMode());
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(this.config, this.logger);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends GeyserConfig> T loadConfig(Class<T> configClass) {
|
||||||
|
T config = new ConfigLoader(this)
|
||||||
|
.transformer(node -> {
|
||||||
|
try {
|
||||||
|
if (!ViaProxy.getConfig().getWildcardDomainHandling().equals(ViaProxyConfig.WildcardDomainHandling.NONE)) {
|
||||||
|
node.node("java", "forward-host").set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pingPassthroughInterval = node.node("ping-passthrough-interval");
|
||||||
|
int interval = pingPassthroughInterval.getInt();
|
||||||
|
if (interval < 15 && ViaProxy.getConfig().getTargetVersion() != null && ViaProxy.getConfig().getTargetVersion().olderThanOrEqualTo(LegacyProtocolVersion.r1_6_4)) {
|
||||||
|
// <= 1.6.4 servers sometimes block incoming connections from an IP address if too many connections are made
|
||||||
|
pingPassthroughInterval.set(15);
|
||||||
|
}
|
||||||
|
} catch (SerializationException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.configFile(new File(ROOT_FOLDER, "config.yml"))
|
||||||
|
.load(configClass);
|
||||||
|
if (config != null) {
|
||||||
|
this.geyserConfig = (GeyserPluginConfig) config;
|
||||||
|
config.java().authType(Files.isRegularFile(getFloodgateKeyPath()) ? AuthType.FLOODGATE : AuthType.OFFLINE);
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,19 +14,29 @@ dependencies {
|
|||||||
api(projects.common)
|
api(projects.common)
|
||||||
api(projects.api)
|
api(projects.api)
|
||||||
|
|
||||||
// Jackson JSON and YAML serialization
|
api(libs.yaml) // Used for extensions
|
||||||
api(libs.bundles.jackson)
|
annotationProcessor(libs.configurate.`interface`.ap)
|
||||||
|
api(libs.configurate.`interface`)
|
||||||
|
implementation(libs.configurate.yaml)
|
||||||
api(libs.guava)
|
api(libs.guava)
|
||||||
|
|
||||||
|
compileOnly(libs.gson.record.factory) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
|
||||||
// Fastutil Maps
|
// Fastutil Maps
|
||||||
implementation(libs.bundles.fastutil)
|
implementation(libs.bundles.fastutil)
|
||||||
|
|
||||||
// Network libraries
|
// Network libraries
|
||||||
implementation(libs.websocket)
|
implementation(libs.websocket)
|
||||||
|
|
||||||
api(libs.bundles.protocol)
|
api(libs.bundles.protocol) {
|
||||||
|
exclude("com.fasterxml.jackson.core", "jackson-annotations")
|
||||||
|
}
|
||||||
|
|
||||||
api(libs.minecraftauth)
|
api(libs.minecraftauth) {
|
||||||
|
exclude("com.google.code.gson", "gson")
|
||||||
|
}
|
||||||
api(libs.mcprotocollib) {
|
api(libs.mcprotocollib) {
|
||||||
exclude("io.netty", "netty-all")
|
exclude("io.netty", "netty-all")
|
||||||
exclude("net.raphimc", "MinecraftAuth")
|
exclude("net.raphimc", "MinecraftAuth")
|
||||||
@@ -60,6 +70,7 @@ dependencies {
|
|||||||
|
|
||||||
// Test
|
// Test
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
testImplementation(libs.gson.runtime) // Record support
|
||||||
testImplementation(libs.mockito)
|
testImplementation(libs.mockito)
|
||||||
|
|
||||||
// Annotation Processors
|
// Annotation Processors
|
||||||
@@ -68,6 +79,8 @@ dependencies {
|
|||||||
annotationProcessor(projects.ap)
|
annotationProcessor(projects.ap)
|
||||||
|
|
||||||
api(libs.events)
|
api(libs.events)
|
||||||
|
|
||||||
|
api(libs.bstats)
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.processResources {
|
tasks.processResources {
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ public final class Constants {
|
|||||||
|
|
||||||
public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/";
|
public static final String MINECRAFT_SKIN_SERVER_URL = "https://textures.minecraft.net/texture/";
|
||||||
|
|
||||||
|
public static final int CONFIG_VERSION = 5;
|
||||||
|
|
||||||
|
public static final int BSTATS_ID = 5273;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
URI wsUri = null;
|
URI wsUri = null;
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -25,14 +25,14 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser;
|
package org.geysermc.geyser;
|
||||||
|
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
public class FloodgateKeyLoader {
|
public class FloodgateKeyLoader {
|
||||||
public static Path getKeyPath(GeyserJacksonConfiguration config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
public static Path getKeyPath(GeyserConfig config, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
||||||
// Always prioritize Floodgate's key, if it is installed.
|
// Always prioritize Floodgate's key, if it is installed.
|
||||||
// This mostly prevents people from trying to copy the key and corrupting it in the process
|
// This mostly prevents people from trying to copy the key and corrupting it in the process
|
||||||
if (floodgateDataFolder != null) {
|
if (floodgateDataFolder != null) {
|
||||||
@@ -45,13 +45,7 @@ public class FloodgateKeyLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Path floodgateKey;
|
Path floodgateKey = geyserDataFolder.resolve(config.advanced().floodgateKeyFile());
|
||||||
if (config.getFloodgateKeyFile().equals("public-key.pem")) {
|
|
||||||
logger.debug("Floodgate 2.0 doesn't use a public/private key system anymore. We'll search for key.pem instead");
|
|
||||||
floodgateKey = geyserDataFolder.resolve("key.pem");
|
|
||||||
} else {
|
|
||||||
floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Files.exists(floodgateKey)) {
|
if (!Files.exists(floodgateKey)) {
|
||||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed"));
|
logger.error(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed"));
|
||||||
|
|||||||
@@ -27,12 +27,16 @@ package org.geysermc.geyser;
|
|||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.ConfigLoader;
|
||||||
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
||||||
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
|
import org.geysermc.geyser.util.metrics.ProvidedMetricsPlatform;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@@ -68,11 +72,19 @@ public interface GeyserBootstrap {
|
|||||||
void onGeyserShutdown();
|
void onGeyserShutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current GeyserConfiguration
|
* Returns the platform type this Geyser instance is running on.
|
||||||
*
|
*
|
||||||
* @return The current GeyserConfiguration
|
* @return the PlatformType this Geyser instance is running on.
|
||||||
*/
|
*/
|
||||||
GeyserConfiguration getGeyserConfig();
|
@NonNull
|
||||||
|
PlatformType platformType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current GeyserConfig
|
||||||
|
*
|
||||||
|
* @return The current GeyserConfig
|
||||||
|
*/
|
||||||
|
GeyserConfig config();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current GeyserLogger
|
* Returns the current GeyserLogger
|
||||||
@@ -194,4 +206,18 @@ public interface GeyserBootstrap {
|
|||||||
* Tests if Floodgate is installed, loads the Floodgate key if so, and returns the result of Floodgate installed.
|
* Tests if Floodgate is installed, loads the Floodgate key if so, and returns the result of Floodgate installed.
|
||||||
*/
|
*/
|
||||||
boolean testFloodgatePluginPresent();
|
boolean testFloodgatePluginPresent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEMPORARY - will be removed after The Merge:tm:.
|
||||||
|
*/
|
||||||
|
Path getFloodgateKeyPath();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
default MetricsPlatform createMetricsPlatform() {
|
||||||
|
return new ProvidedMetricsPlatform();
|
||||||
|
}
|
||||||
|
|
||||||
|
default <T extends GeyserConfig> T loadConfig(Class<T> configClass) {
|
||||||
|
return new ConfigLoader(this).createFolder().load(configClass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,8 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser;
|
package org.geysermc.geyser;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.google.gson.Gson;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import io.netty.channel.epoll.Epoll;
|
import io.netty.channel.epoll.Epoll;
|
||||||
import io.netty.util.NettyRuntime;
|
import io.netty.util.NettyRuntime;
|
||||||
import io.netty.util.concurrent.DefaultThreadFactory;
|
import io.netty.util.concurrent.DefaultThreadFactory;
|
||||||
@@ -40,6 +38,11 @@ import net.kyori.adventure.text.Component;
|
|||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import net.raphimc.minecraftauth.msa.data.MsaConstants;
|
import net.raphimc.minecraftauth.msa.data.MsaConstants;
|
||||||
import net.raphimc.minecraftauth.msa.model.MsaApplicationConfig;
|
import net.raphimc.minecraftauth.msa.model.MsaApplicationConfig;
|
||||||
|
import org.bstats.MetricsBase;
|
||||||
|
import org.bstats.charts.AdvancedPie;
|
||||||
|
import org.bstats.charts.DrilldownPie;
|
||||||
|
import org.bstats.charts.SimplePie;
|
||||||
|
import org.bstats.charts.SingleLineChart;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@@ -68,7 +71,8 @@ import org.geysermc.geyser.api.network.RemoteServer;
|
|||||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
|
import org.geysermc.geyser.configuration.GeyserPluginConfig;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.erosion.UnixSocketClientListener;
|
import org.geysermc.geyser.erosion.UnixSocketClientListener;
|
||||||
import org.geysermc.geyser.event.GeyserEventBus;
|
import org.geysermc.geyser.event.GeyserEventBus;
|
||||||
@@ -79,6 +83,7 @@ import org.geysermc.geyser.level.BedrockDimension;
|
|||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
import org.geysermc.geyser.network.netty.GeyserServer;
|
import org.geysermc.geyser.network.netty.GeyserServer;
|
||||||
|
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
import org.geysermc.geyser.registry.loader.ResourcePackLoader;
|
||||||
@@ -97,15 +102,17 @@ import org.geysermc.geyser.text.MinecraftLocale;
|
|||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
import org.geysermc.geyser.util.AssetUtils;
|
import org.geysermc.geyser.util.AssetUtils;
|
||||||
import org.geysermc.geyser.util.CodeOfConductManager;
|
import org.geysermc.geyser.util.CodeOfConductManager;
|
||||||
import org.geysermc.geyser.util.CooldownUtils;
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
import org.geysermc.geyser.util.Metrics;
|
|
||||||
import org.geysermc.geyser.util.NewsHandler;
|
import org.geysermc.geyser.util.NewsHandler;
|
||||||
import org.geysermc.geyser.util.VersionCheckUtils;
|
import org.geysermc.geyser.util.VersionCheckUtils;
|
||||||
import org.geysermc.geyser.util.WebUtils;
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
|
import org.geysermc.geyser.util.metrics.MetricsPlatform;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
@@ -129,12 +136,7 @@ import java.util.regex.Pattern;
|
|||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserImpl implements GeyserApi, EventRegistrar {
|
public class GeyserImpl implements GeyserApi, EventRegistrar {
|
||||||
public static final ObjectMapper JSON_MAPPER = new ObjectMapper()
|
public static final Gson GSON = JsonUtils.createGson();
|
||||||
.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 String NAME = "Geyser";
|
public static final String NAME = "Geyser";
|
||||||
public static final String GIT_VERSION = BuildData.GIT_VERSION;
|
public static final String GIT_VERSION = BuildData.GIT_VERSION;
|
||||||
@@ -167,13 +169,12 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
private ScheduledExecutorService scheduledThread;
|
private ScheduledExecutorService scheduledThread;
|
||||||
|
|
||||||
private GeyserServer geyserServer;
|
private GeyserServer geyserServer;
|
||||||
private final PlatformType platformType;
|
|
||||||
private final GeyserBootstrap bootstrap;
|
private final GeyserBootstrap bootstrap;
|
||||||
|
|
||||||
private final GeyserEventBus eventBus;
|
private final GeyserEventBus eventBus;
|
||||||
private final GeyserExtensionManager extensionManager;
|
private final GeyserExtensionManager extensionManager;
|
||||||
|
|
||||||
private Metrics metrics;
|
private MetricsBase metrics;
|
||||||
|
|
||||||
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
|
private PendingMicrosoftAuthentication pendingMicrosoftAuthentication;
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
@@ -194,12 +195,11 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
@Setter
|
@Setter
|
||||||
private boolean isEnabled;
|
private boolean isEnabled;
|
||||||
|
|
||||||
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
|
private GeyserImpl(GeyserBootstrap bootstrap) {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
Geyser.set(this);
|
Geyser.set(this);
|
||||||
|
|
||||||
this.platformType = platformType;
|
|
||||||
this.bootstrap = bootstrap;
|
this.bootstrap = bootstrap;
|
||||||
|
|
||||||
/* Initialize event bus */
|
/* Initialize event bus */
|
||||||
@@ -218,10 +218,12 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
// Setup encryption early so we don't start if we can't auth
|
// Setup encryption early so we don't start if we can't auth
|
||||||
|
if (config().advanced().bedrock().validateBedrockLogin()) {
|
||||||
try {
|
try {
|
||||||
EncryptionUtils.getMojangPublicKey();
|
EncryptionUtils.getMojangPublicKey();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable t) {
|
||||||
throw new RuntimeException("Cannot setup authentication! Are you offline? ", e);
|
GeyserImpl.getInstance().getLogger().error("Unable to set up encryption! This can be caused by your internet connection or the Minecraft api being unreachable. ", t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long startupTime = System.currentTimeMillis();
|
long startupTime = System.currentTimeMillis();
|
||||||
@@ -278,19 +280,19 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
|
|
||||||
startInstance();
|
startInstance();
|
||||||
|
|
||||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
GeyserConfig config = bootstrap.config();
|
||||||
|
|
||||||
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
double completeTime = (System.currentTimeMillis() - startupTime) / 1000D;
|
||||||
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
|
String message = GeyserLocale.getLocaleStringLog("geyser.core.finish.done", new DecimalFormat("#.###").format(completeTime));
|
||||||
message += " " + GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
message += " " + GeyserLocale.getLocaleStringLog("geyser.core.finish.console");
|
||||||
logger.info(message);
|
logger.info(message);
|
||||||
|
|
||||||
if (platformType == PlatformType.STANDALONE) {
|
if (platformType() == PlatformType.STANDALONE) {
|
||||||
if (config.getRemote().authType() != AuthType.FLOODGATE) {
|
if (config.java().authType() != AuthType.FLOODGATE) {
|
||||||
// If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server
|
// If the auth-type is Floodgate, then this Geyser instance is probably owned by the Java server
|
||||||
logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn"));
|
logger.warning(GeyserLocale.getLocaleStringLog("geyser.core.movement_warn"));
|
||||||
}
|
}
|
||||||
} else if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
} else if (config.java().authType() == AuthType.FLOODGATE) {
|
||||||
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,7 +310,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GeyserLogger logger = bootstrap.getGeyserLogger();
|
GeyserLogger logger = bootstrap.getGeyserLogger();
|
||||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
GeyserConfig config = bootstrap.config();
|
||||||
|
|
||||||
ScoreboardUpdater.init();
|
ScoreboardUpdater.init();
|
||||||
|
|
||||||
@@ -316,6 +318,23 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
|
|
||||||
Registries.RESOURCE_PACKS.load();
|
Registries.RESOURCE_PACKS.load();
|
||||||
|
|
||||||
|
// Warnings to users who enable options that they might not need.
|
||||||
|
if (config.advanced().bedrock().useHaproxyProtocol()) {
|
||||||
|
logger.warning("Geyser is configured to expect HAProxy protocol for incoming Bedrock connections.");
|
||||||
|
logger.warning("If you do not know what this is, open the Geyser config, and set \"use-haproxy-protocol\" under the \"advanced/bedrock\" section to \"false\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.advanced().java().useHaproxyProtocol()) {
|
||||||
|
logger.warning("Geyser is configured to use proxy protocol when connecting to the Java server.");
|
||||||
|
logger.warning("If you do not know what this is, open the Geyser config, and set \"use-haproxy-protocol\" under the \"advanced/java\" section to \"false\".");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.advanced().bedrock().validateBedrockLogin()) {
|
||||||
|
logger.error("XBOX AUTHENTICATION IS DISABLED ON THIS GEYSER INSTANCE!");
|
||||||
|
logger.error("While this allows using Bedrock edition proxies, it also opens up the ability for hackers to connect with any username they choose.");
|
||||||
|
logger.error("To change this, set \"disable-xbox-auth\" to \"false\" in Geyser's config file.");
|
||||||
|
}
|
||||||
|
|
||||||
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
String geyserUdpPort = System.getProperty("geyserUdpPort", "");
|
||||||
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
String pluginUdpPort = geyserUdpPort.isEmpty() ? System.getProperty("pluginUdpPort", "") : geyserUdpPort;
|
||||||
if ("-1".equals(pluginUdpPort)) {
|
if ("-1".equals(pluginUdpPort)) {
|
||||||
@@ -324,33 +343,30 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
boolean portPropertyApplied = false;
|
boolean portPropertyApplied = false;
|
||||||
String pluginUdpAddress = System.getProperty("geyserUdpAddress", System.getProperty("pluginUdpAddress", ""));
|
String pluginUdpAddress = System.getProperty("geyserUdpAddress", System.getProperty("pluginUdpAddress", ""));
|
||||||
|
|
||||||
if (platformType != PlatformType.STANDALONE) {
|
if (platformType() != PlatformType.STANDALONE) {
|
||||||
int javaPort = bootstrap.getServerPort();
|
int javaPort = bootstrap.getServerPort();
|
||||||
if (config.getRemote().address().equals("auto")) {
|
|
||||||
config.setAutoconfiguredRemote(true);
|
|
||||||
String serverAddress = bootstrap.getServerBindAddress();
|
String serverAddress = bootstrap.getServerBindAddress();
|
||||||
if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) {
|
if (!serverAddress.isEmpty() && !"0.0.0.0".equals(serverAddress)) {
|
||||||
config.getRemote().setAddress(serverAddress);
|
config.java().address(serverAddress);
|
||||||
} else {
|
} else {
|
||||||
// Set the remote address to localhost since that is where we are always connecting
|
// Set the remote address to localhost since that is where we are always connecting
|
||||||
try {
|
try {
|
||||||
config.getRemote().setAddress(InetAddress.getLocalHost().getHostAddress());
|
config.java().address(InetAddress.getLocalHost().getHostAddress());
|
||||||
} catch (UnknownHostException ex) {
|
} catch (UnknownHostException ex) {
|
||||||
logger.debug("Unknown host when trying to find localhost.");
|
logger.debug("Unknown host when trying to find localhost.");
|
||||||
if (config.isDebugMode()) {
|
if (config.debugMode()) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
config.getRemote().setAddress(InetAddress.getLoopbackAddress().getHostAddress());
|
config.java().address(InetAddress.getLoopbackAddress().getHostAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (javaPort != -1) {
|
if (javaPort != -1) {
|
||||||
config.getRemote().setPort(javaPort);
|
config.java().port(javaPort);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean forceMatchServerPort = "server".equals(pluginUdpPort);
|
boolean forceMatchServerPort = "server".equals(pluginUdpPort);
|
||||||
if ((config.getBedrock().isCloneRemotePort() || forceMatchServerPort) && javaPort != -1) {
|
if ((config.bedrock().cloneRemotePort() || forceMatchServerPort) && javaPort != -1) {
|
||||||
config.getBedrock().setPort(javaPort);
|
config.bedrock().port(javaPort);
|
||||||
if (forceMatchServerPort) {
|
if (forceMatchServerPort) {
|
||||||
if (geyserUdpPort.isEmpty()) {
|
if (geyserUdpPort.isEmpty()) {
|
||||||
logger.info("Port set from system generic property to match Java server.");
|
logger.info("Port set from system generic property to match Java server.");
|
||||||
@@ -364,15 +380,15 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
if ("server".equals(pluginUdpAddress)) {
|
if ("server".equals(pluginUdpAddress)) {
|
||||||
String address = bootstrap.getServerBindAddress();
|
String address = bootstrap.getServerBindAddress();
|
||||||
if (!address.isEmpty()) {
|
if (!address.isEmpty()) {
|
||||||
config.getBedrock().setAddress(address);
|
config.bedrock().address(address);
|
||||||
}
|
}
|
||||||
} else if (!pluginUdpAddress.isEmpty()) {
|
} else if (!pluginUdpAddress.isEmpty()) {
|
||||||
config.getBedrock().setAddress(pluginUdpAddress);
|
config.bedrock().address(pluginUdpAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!portPropertyApplied && !pluginUdpPort.isEmpty()) {
|
if (!portPropertyApplied && !pluginUdpPort.isEmpty()) {
|
||||||
int port = Integer.parseInt(pluginUdpPort);
|
int port = Integer.parseInt(pluginUdpPort);
|
||||||
config.getBedrock().setPort(port);
|
config.bedrock().port(port);
|
||||||
if (geyserUdpPort.isEmpty()) {
|
if (geyserUdpPort.isEmpty()) {
|
||||||
logger.info("Port set from generic system property: " + port);
|
logger.info("Port set from generic system property: " + port);
|
||||||
} else {
|
} else {
|
||||||
@@ -380,16 +396,17 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platformType != PlatformType.VIAPROXY) {
|
|
||||||
|
if (platformType() != PlatformType.VIAPROXY) {
|
||||||
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
boolean floodgatePresent = bootstrap.testFloodgatePluginPresent();
|
||||||
if (config.getRemote().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
if (config.java().authType() == AuthType.FLOODGATE && !floodgatePresent) {
|
||||||
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
logger.severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.not_installed") + " "
|
||||||
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
+ GeyserLocale.getLocaleStringLog("geyser.bootstrap.floodgate.disabling"));
|
||||||
return;
|
return;
|
||||||
} else if (config.isAutoconfiguredRemote() && floodgatePresent) {
|
} else if (floodgatePresent) {
|
||||||
// Floodgate installed means that the user wants Floodgate authentication
|
// Floodgate installed means that the user wants Floodgate authentication
|
||||||
logger.debug("Auto-setting to Floodgate authentication.");
|
logger.debug("Auto-setting to Floodgate authentication.");
|
||||||
config.getRemote().setAuthType(AuthType.FLOODGATE);
|
config.java().authType(AuthType.FLOODGATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,7 +419,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
if (parsedPort < 1 || parsedPort > 65535) {
|
if (parsedPort < 1 || parsedPort > 65535) {
|
||||||
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
|
throw new NumberFormatException("The broadcast port must be between 1 and 65535 inclusive!");
|
||||||
}
|
}
|
||||||
config.getBedrock().setBroadcastPort(parsedPort);
|
config.advanced().bedrock().broadcastPort(parsedPort);
|
||||||
logger.info("Broadcast port set from system property: " + parsedPort);
|
logger.info("Broadcast port set from system property: " + parsedPort);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
|
logger.error(String.format("Invalid broadcast port from system property: %s! Defaulting to configured port.", broadcastPort + " (" + e.getMessage() + ")"));
|
||||||
@@ -410,23 +427,27 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// It's set to 0 only if no system property or manual config value was set
|
// It's set to 0 only if no system property or manual config value was set
|
||||||
if (config.getBedrock().broadcastPort() == 0) {
|
if (config.advanced().bedrock().broadcastPort() == 0) {
|
||||||
config.getBedrock().setBroadcastPort(config.getBedrock().port());
|
config.advanced().bedrock().broadcastPort(config.bedrock().port());
|
||||||
}
|
}
|
||||||
|
|
||||||
String remoteAddress = config.getRemote().address();
|
if (!(config instanceof GeyserPluginConfig)) {
|
||||||
|
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.
|
// Filters whether it is not an IP address or localhost, because otherwise it is not possible to find out an SRV entry.
|
||||||
if (!IP_REGEX.matcher(remoteAddress).matches() && !remoteAddress.equalsIgnoreCase("localhost")) {
|
if (!IP_REGEX.matcher(remoteAddress).matches() && !remoteAddress.equalsIgnoreCase("localhost")) {
|
||||||
String[] record = WebUtils.findSrvRecord(this, remoteAddress);
|
String[] record = WebUtils.findSrvRecord(this, remoteAddress);
|
||||||
if (record != null) {
|
if (record != null) {
|
||||||
int remotePort = Integer.parseInt(record[2]);
|
int remotePort = Integer.parseInt(record[2]);
|
||||||
config.getRemote().setAddress(remoteAddress = record[3]);
|
config.java().address(remoteAddress = record[3]);
|
||||||
config.getRemote().setPort(remotePort);
|
config.java().port(remotePort);
|
||||||
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
|
logger.debug("Found SRV record \"" + remoteAddress + ":" + remotePort + "\"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (!config.advanced().java().useDirectConnection()) {
|
||||||
|
logger.warning("The use-direct-connection config option is deprecated. Please reach out to us on Discord if there's a reason it needs to be disabled.");
|
||||||
|
}
|
||||||
|
|
||||||
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.getPendingAuthenticationTimeout());
|
pendingMicrosoftAuthentication = new PendingMicrosoftAuthentication(config.pendingAuthenticationTimeout());
|
||||||
|
|
||||||
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
||||||
|
|
||||||
@@ -438,20 +459,19 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
logger.debug("Epoll is not available; Erosion's Unix socket handling will not work.");
|
logger.debug("Epoll is not available; Erosion's Unix socket handling will not work.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
|
BedrockDimension.changeBedrockNetherId(config.gameplay().netherRoofWorkaround()); // Apply End dimension ID workaround to Nether
|
||||||
BedrockDimension.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
|
||||||
|
|
||||||
Integer bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads");
|
int bedrockThreadCount = Integer.getInteger("Geyser.BedrockNetworkThreads", -1);
|
||||||
if (bedrockThreadCount == null) {
|
if (bedrockThreadCount == -1) {
|
||||||
// Copy the code from Netty's default thread count fallback
|
// Copy the code from Netty's default thread count fallback
|
||||||
bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
|
bedrockThreadCount = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserServer = new GeyserServer(this, bedrockThreadCount);
|
this.geyserServer = new GeyserServer(this, bedrockThreadCount);
|
||||||
this.geyserServer.bind(new InetSocketAddress(config.getBedrock().address(), config.getBedrock().port()))
|
this.geyserServer.bind(new InetSocketAddress(config.bedrock().address(), config.bedrock().port()))
|
||||||
.whenComplete((avoid, throwable) -> {
|
.whenComplete((avoid, throwable) -> {
|
||||||
String address = config.getBedrock().address();
|
String address = config.bedrock().address();
|
||||||
String port = String.valueOf(config.getBedrock().port()); // otherwise we get commas
|
String port = String.valueOf(config.bedrock().port()); // otherwise we get commas
|
||||||
|
|
||||||
if (throwable == null) {
|
if (throwable == null) {
|
||||||
if ("0.0.0.0".equals(address)) {
|
if ("0.0.0.0".equals(address)) {
|
||||||
@@ -469,9 +489,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
}).join();
|
}).join();
|
||||||
|
|
||||||
if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
if (config.java().authType() == AuthType.FLOODGATE) {
|
||||||
try {
|
try {
|
||||||
Key key = new AesKeyProducer().produceFrom(config.getFloodgateKeyPath());
|
Key key = new AesKeyProducer().produceFrom(bootstrap.getFloodgateKeyPath());
|
||||||
cipher = new AesCipher(new Base64Topping());
|
cipher = new AesCipher(new Base64Topping());
|
||||||
cipher.init(key);
|
cipher.init(key);
|
||||||
logger.debug("Loaded Floodgate key!");
|
logger.debug("Loaded Floodgate key!");
|
||||||
@@ -483,28 +503,55 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getMetrics().isEnabled()) {
|
MetricsPlatform metricsPlatform = bootstrap.createMetricsPlatform();
|
||||||
metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger(""));
|
if (metricsPlatform != null && metricsPlatform.enabled()) {
|
||||||
metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size));
|
metrics = new MetricsBase(
|
||||||
|
"server-implementation",
|
||||||
|
metricsPlatform.serverUuid(),
|
||||||
|
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,
|
||||||
|
metricsPlatform.logFailedRequests(),
|
||||||
|
metricsPlatform.logSentData(),
|
||||||
|
metricsPlatform.logResponseStatusText(),
|
||||||
|
metricsPlatform.disableRelocateCheck()
|
||||||
|
);
|
||||||
|
metrics.addCustomChart(new SingleLineChart("players", sessionManager::size));
|
||||||
// Prevent unwanted words best we can
|
// Prevent unwanted words best we can
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().authType().toString().toLowerCase(Locale.ROOT)));
|
metrics.addCustomChart(new SimplePie("authMode", () -> config.java().authType().toString().toLowerCase(Locale.ROOT)));
|
||||||
|
|
||||||
Map<String, Map<String, Integer>> platformTypeMap = new HashMap<>();
|
Map<String, Map<String, Integer>> platformTypeMap = new HashMap<>();
|
||||||
Map<String, Integer> serverPlatform = new HashMap<>();
|
Map<String, Integer> serverPlatform = new HashMap<>();
|
||||||
serverPlatform.put(bootstrap.getServerPlatform(), 1);
|
serverPlatform.put(bootstrap.getServerPlatform(), 1);
|
||||||
platformTypeMap.put(platformType().platformName(), serverPlatform);
|
platformTypeMap.put(platformType().platformName(), serverPlatform);
|
||||||
|
|
||||||
metrics.addCustomChart(new Metrics.DrilldownPie("platform", () -> {
|
metrics.addCustomChart(new DrilldownPie("platform", () -> {
|
||||||
// By the end, we should return, for example:
|
// By the end, we should return, for example:
|
||||||
// Geyser-Spigot => (Paper, 1)
|
// Geyser-Spigot => (Paper, 1)
|
||||||
return platformTypeMap;
|
return platformTypeMap;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
|
metrics.addCustomChart(new SimplePie("defaultLocale", GeyserLocale::getDefaultLocale));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserImpl.VERSION));
|
metrics.addCustomChart(new SimplePie("version", () -> GeyserImpl.VERSION));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("javaHaProxyProtocol", () -> String.valueOf(config.getRemote().isUseProxyProtocol())));
|
metrics.addCustomChart(new SimplePie("javaHaProxyProtocol", () -> String.valueOf(config.advanced().java().useHaproxyProtocol())));
|
||||||
metrics.addCustomChart(new Metrics.SimplePie("bedrockHaProxyProtocol", () -> String.valueOf(config.getBedrock().isEnableProxyProtocol())));
|
metrics.addCustomChart(new SimplePie("bedrockHaProxyProtocol", () -> String.valueOf(config.advanced().bedrock().useHaproxyProtocol())));
|
||||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> {
|
metrics.addCustomChart(new AdvancedPie("playerPlatform", () -> {
|
||||||
Map<String, Integer> valueMap = new HashMap<>();
|
Map<String, Integer> valueMap = new HashMap<>();
|
||||||
for (GeyserSession session : sessionManager.getAllSessions()) {
|
for (GeyserSession session : sessionManager.getAllSessions()) {
|
||||||
if (session == null) continue;
|
if (session == null) continue;
|
||||||
@@ -518,7 +565,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
return valueMap;
|
return valueMap;
|
||||||
}));
|
}));
|
||||||
metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> {
|
metrics.addCustomChart(new AdvancedPie("playerVersion", () -> {
|
||||||
Map<String, Integer> valueMap = new HashMap<>();
|
Map<String, Integer> valueMap = new HashMap<>();
|
||||||
for (GeyserSession session : sessionManager.getAllSessions()) {
|
for (GeyserSession session : sessionManager.getAllSessions()) {
|
||||||
if (session == null) continue;
|
if (session == null) continue;
|
||||||
@@ -540,7 +587,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
platformMap.put(bootstrap.getServerPlatform(), 1);
|
platformMap.put(bootstrap.getServerPlatform(), 1);
|
||||||
versionMap.put(minecraftVersion, platformMap);
|
versionMap.put(minecraftVersion, platformMap);
|
||||||
|
|
||||||
metrics.addCustomChart(new Metrics.DrilldownPie("minecraftServerVersion", () -> {
|
metrics.addCustomChart(new DrilldownPie("minecraftServerVersion", () -> {
|
||||||
// By the end, we should return, for example:
|
// By the end, we should return, for example:
|
||||||
// 1.16.5 => (Spigot, 1)
|
// 1.16.5 => (Spigot, 1)
|
||||||
return versionMap;
|
return versionMap;
|
||||||
@@ -549,7 +596,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
|
|
||||||
// The following code can be attributed to the PaperMC project
|
// The following code can be attributed to the PaperMC project
|
||||||
// https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614
|
// https://github.com/PaperMC/Paper/blob/master/Spigot-Server-Patches/0005-Paper-Metrics.patch#L614
|
||||||
metrics.addCustomChart(new Metrics.DrilldownPie("javaVersion", () -> {
|
metrics.addCustomChart(new DrilldownPie("javaVersion", () -> {
|
||||||
Map<String, Map<String, Integer>> map = new HashMap<>();
|
Map<String, Map<String, Integer>> map = new HashMap<>();
|
||||||
String javaVersion = System.getProperty("java.version");
|
String javaVersion = System.getProperty("java.version");
|
||||||
Map<String, Integer> entry = new HashMap<>();
|
Map<String, Integer> entry = new HashMap<>();
|
||||||
@@ -584,22 +631,21 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
metrics = null;
|
metrics = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getRemote().authType() == AuthType.ONLINE) {
|
if (config.java().authType() == AuthType.ONLINE) {
|
||||||
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
// May be written/read to on multiple threads from each GeyserSession as well as writing the config
|
||||||
savedAuthChains = new ConcurrentHashMap<>();
|
savedAuthChains = new ConcurrentHashMap<>();
|
||||||
|
Type type = new TypeToken<Map<String, String>>() { }.getType();
|
||||||
|
|
||||||
File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
File authChainsFile = bootstrap.getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
||||||
if (authChainsFile.exists()) {
|
if (authChainsFile.exists()) {
|
||||||
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
|
||||||
|
|
||||||
Map<String, String> authChainFile = null;
|
Map<String, String> authChainFile = null;
|
||||||
try {
|
try (FileReader reader = new FileReader(authChainsFile)) {
|
||||||
authChainFile = JSON_MAPPER.readValue(authChainsFile, type);
|
authChainFile = GSON.fromJson(reader, type);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.error("Cannot load saved user tokens!", e);
|
logger.error("Cannot load saved user tokens!", e);
|
||||||
}
|
}
|
||||||
if (authChainFile != null) {
|
if (authChainFile != null) {
|
||||||
List<String> validUsers = config.getSavedUserLogins();
|
List<String> validUsers = config.savedUserLogins();
|
||||||
boolean doWrite = false;
|
boolean doWrite = false;
|
||||||
for (Map.Entry<String, String> entry : authChainFile.entrySet()) {
|
for (Map.Entry<String, String> entry : authChainFile.entrySet()) {
|
||||||
String user = entry.getKey();
|
String user = entry.getKey();
|
||||||
@@ -627,7 +673,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.isNotifyOnNewBedrockUpdate()) {
|
if (config.notifyOnNewBedrockUpdate()) {
|
||||||
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -703,6 +749,10 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
runIfNonNull(newsHandler, NewsHandler::shutdown);
|
runIfNonNull(newsHandler, NewsHandler::shutdown);
|
||||||
runIfNonNull(erosionUnixListener, UnixSocketClientListener::close);
|
runIfNonNull(erosionUnixListener, UnixSocketClientListener::close);
|
||||||
|
|
||||||
|
if (bootstrap.getGeyserPingPassthrough() instanceof GeyserLegacyPingPassthrough legacyPingPassthrough) {
|
||||||
|
legacyPingPassthrough.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
ResourcePackLoader.clear();
|
ResourcePackLoader.clear();
|
||||||
CodeOfConductManager.getInstance().save();
|
CodeOfConductManager.getInstance().save();
|
||||||
|
|
||||||
@@ -777,13 +827,13 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public RemoteServer defaultRemoteServer() {
|
public RemoteServer defaultRemoteServer() {
|
||||||
return getConfig().getRemote();
|
return config().java();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public BedrockListener bedrockListener() {
|
public BedrockListener bedrockListener() {
|
||||||
return getConfig().getBedrock();
|
return config().bedrock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -801,7 +851,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
@Override
|
@Override
|
||||||
@NonNull
|
@NonNull
|
||||||
public PlatformType platformType() {
|
public PlatformType platformType() {
|
||||||
return platformType;
|
return bootstrap.platformType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -828,9 +878,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
return Integer.parseInt(BUILD_NUMBER);
|
return Integer.parseInt(BUILD_NUMBER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GeyserImpl load(PlatformType platformType, GeyserBootstrap bootstrap) {
|
public static GeyserImpl load(GeyserBootstrap bootstrap) {
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
return new GeyserImpl(platformType, bootstrap);
|
return new GeyserImpl(bootstrap);
|
||||||
}
|
}
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
@@ -853,8 +903,8 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
return bootstrap.getGeyserLogger();
|
return bootstrap.getGeyserLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GeyserConfiguration getConfig() {
|
public GeyserConfig config() {
|
||||||
return bootstrap.getGeyserConfig();
|
return bootstrap.config();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldManager getWorldManager() {
|
public WorldManager getWorldManager() {
|
||||||
@@ -867,7 +917,7 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void saveAuthChain(@NonNull String bedrockName, @NonNull String authChain) {
|
public void saveAuthChain(@NonNull String bedrockName, @NonNull String authChain) {
|
||||||
if (!getConfig().getSavedUserLogins().contains(bedrockName)) {
|
if (!config().savedUserLogins().contains(bedrockName)) {
|
||||||
// Do not save this login
|
// Do not save this login
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -889,11 +939,9 @@ public class GeyserImpl implements GeyserApi, EventRegistrar {
|
|||||||
scheduledThread.execute(() -> {
|
scheduledThread.execute(() -> {
|
||||||
// Ensure all writes are handled on the same thread
|
// Ensure all writes are handled on the same thread
|
||||||
File savedAuthChains = getBootstrap().getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
File savedAuthChains = getBootstrap().getSavedUserLoginsFolder().resolve(Constants.SAVED_AUTH_CHAINS_FILE).toFile();
|
||||||
TypeReference<Map<String, String>> type = new TypeReference<>() { };
|
Type type = new TypeToken<Map<String, String>>() { }.getType();
|
||||||
try (FileWriter writer = new FileWriter(savedAuthChains)) {
|
try (FileWriter writer = new FileWriter(savedAuthChains)) {
|
||||||
JSON_MAPPER.writerFor(type)
|
GSON.toJson(this.savedAuthChains, type, writer);
|
||||||
.withDefaultPrettyPrinter()
|
|
||||||
.writeValue(writer, this.savedAuthChains);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
getLogger().error("Unable to write saved refresh tokens!", e);
|
getLogger().error("Unable to write saved refresh tokens!", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,10 @@ public interface GeyserLogger extends GeyserCommandSource {
|
|||||||
* @param object the object to log
|
* @param object the object to log
|
||||||
*/
|
*/
|
||||||
default void debug(@Nullable Object object) {
|
default void debug(@Nullable Object object) {
|
||||||
debug(String.valueOf(object));
|
if (isDebug()) {
|
||||||
|
// Don't create String object by default
|
||||||
|
info(String.valueOf(object));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ public class CommandRegistry implements EventRegistrar {
|
|||||||
|
|
||||||
private static final String GEYSER_ROOT_PERMISSION = "geyser.command";
|
private static final String GEYSER_ROOT_PERMISSION = "geyser.command";
|
||||||
|
|
||||||
public final static boolean STANDALONE_COMMAND_MANAGER = GeyserImpl.getInstance().getPlatformType() == PlatformType.STANDALONE ||
|
public final static boolean STANDALONE_COMMAND_MANAGER = GeyserImpl.getInstance().platformType() == PlatformType.STANDALONE ||
|
||||||
GeyserImpl.getInstance().getPlatformType() == PlatformType.VIAPROXY;
|
GeyserImpl.getInstance().platformType() == PlatformType.VIAPROXY;
|
||||||
|
|
||||||
protected final GeyserImpl geyser;
|
protected final GeyserImpl geyser;
|
||||||
private final CommandManager<GeyserCommandSource> cloud;
|
private final CommandManager<GeyserCommandSource> cloud;
|
||||||
@@ -171,7 +171,7 @@ public class CommandRegistry implements EventRegistrar {
|
|||||||
registerBuiltInCommand(new CustomOptionsCommand("options", "geyser.commands.options.desc", "geyser.command.options"));
|
registerBuiltInCommand(new CustomOptionsCommand("options", "geyser.commands.options.desc", "geyser.command.options"));
|
||||||
registerBuiltInCommand(new QuickActionsCommand("quickactions", "geyser.commands.quickactions.desc", "geyser.command.quickactions"));
|
registerBuiltInCommand(new QuickActionsCommand("quickactions", "geyser.commands.quickactions.desc", "geyser.command.quickactions"));
|
||||||
|
|
||||||
if (this.geyser.getPlatformType() == PlatformType.STANDALONE) {
|
if (this.geyser.platformType() == PlatformType.STANDALONE) {
|
||||||
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,12 +25,13 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.util.TriState;
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.util.LoopbackUtil;
|
import org.geysermc.geyser.util.LoopbackUtil;
|
||||||
import org.geysermc.geyser.util.WebUtils;
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
import org.incendo.cloud.CommandManager;
|
import org.incendo.cloud.CommandManager;
|
||||||
@@ -79,7 +80,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
|
|
||||||
// Replace "<" and ">" symbols if they are present to avoid the common issue of people including them
|
// Replace "<" and ">" symbols if they are present to avoid the common issue of people including them
|
||||||
final String ip = ipArgument.replace("<", "").replace(">", "");
|
final String ip = ipArgument.replace("<", "").replace(">", "");
|
||||||
final int port = portArgument != null ? portArgument : geyser.getConfig().getBedrock().broadcastPort(); // default bedrock port
|
final int port = portArgument != null ? portArgument : geyser.config().advanced().bedrock().broadcastPort(); // default bedrock port
|
||||||
|
|
||||||
// Issue: people commonly checking placeholders
|
// Issue: people commonly checking placeholders
|
||||||
if (ip.equals("ip")) {
|
if (ip.equals("ip")) {
|
||||||
@@ -105,42 +106,42 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserConfiguration config = geyser.getConfig();
|
GeyserConfig config = geyser.config();
|
||||||
|
|
||||||
// Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing
|
// Issue: do the ports not line up? We only check this if players don't override the broadcast port - if they do, they (hopefully) know what they're doing
|
||||||
if (config.getBedrock().broadcastPort() == config.getBedrock().port()) {
|
if (config.advanced().bedrock().broadcastPort() == config.bedrock().port()) {
|
||||||
if (port != config.getBedrock().port()) {
|
if (port != config.bedrock().port()) {
|
||||||
if (portArgument != null) {
|
if (portArgument != null) {
|
||||||
source.sendMessage("The port you are testing with (" + port + ") is not the same as you set in your Geyser configuration ("
|
source.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() + ")");
|
||||||
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `port` in the config.");
|
source.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.bedrock().cloneRemotePort()) {
|
||||||
source.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
source.sendMessage("You have `clone-remote-port` enabled. This option ignores the `bedrock` `port` in the config, and uses the Java server port instead.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
source.sendMessage("You did not specify the port to check (add it with \":<port>\"), " +
|
source.sendMessage("You did not specify the port to check (add it with \":<port>\"), " +
|
||||||
"and the default port 19132 does not match the port in your Geyser configuration ("
|
"and the default port 19132 does not match the port in your Geyser configuration ("
|
||||||
+ config.getBedrock().port() + ")!");
|
+ config.bedrock().port() + ")!");
|
||||||
source.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
source.sendMessage("Re-run the command with that port, or change the port in the config under `bedrock` `port`.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (config.getBedrock().broadcastPort() != port) {
|
if (config.advanced().bedrock().broadcastPort() != port) {
|
||||||
source.sendMessage("The port you are testing with (" + port + ") is not the same as the broadcast port set in your Geyser configuration ("
|
source.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.advanced().bedrock().broadcastPort() + "). ");
|
||||||
source.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
|
source.sendMessage("You ONLY need to change the broadcast port if clients connects with a port different from the port Geyser is running on.");
|
||||||
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config.");
|
source.sendMessage("Re-run the command with the port in the config, or change the `bedrock` `broadcast-port` in the config.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue: is the `bedrock` `address` in the config different?
|
// Issue: is the `bedrock` `address` in the config different?
|
||||||
if (!config.getBedrock().address().equals("0.0.0.0")) {
|
if (!config.bedrock().address().equals("0.0.0.0")) {
|
||||||
source.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
|
source.sendMessage("The address specified in `bedrock` `address` is not \"0.0.0.0\" - this may cause issues unless this is deliberate and intentional.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue: did someone turn on enable-proxy-protocol, and they didn't mean it?
|
// Issue: did someone turn on enable-proxy-protocol, and they didn't mean it?
|
||||||
if (config.getBedrock().isEnableProxyProtocol()) {
|
if (config.advanced().bedrock().useHaproxyProtocol()) {
|
||||||
source.sendMessage("You have the `enable-proxy-protocol` setting enabled. " +
|
source.sendMessage("You have the `use-haproxy-protocol` setting enabled. " +
|
||||||
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
|
"Unless you're deliberately using additional software that REQUIRES this setting, you may not need it enabled.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +172,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
CONNECTION_TEST_MOTD = connectionTestMotd;
|
CONNECTION_TEST_MOTD = connectionTestMotd;
|
||||||
|
|
||||||
source.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait...");
|
source.sendMessage("Testing server connection to " + ip + " with port: " + port + " now. Please wait...");
|
||||||
JsonNode output;
|
JsonObject output;
|
||||||
try {
|
try {
|
||||||
String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8);
|
String hostname = URLEncoder.encode(ip, StandardCharsets.UTF_8);
|
||||||
output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + hostname + "&port=" + port);
|
output = WebUtils.getJson("https://checker.geysermc.org/ping?hostname=" + hostname + "&port=" + port);
|
||||||
@@ -179,18 +180,18 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
CONNECTION_TEST_MOTD = null;
|
CONNECTION_TEST_MOTD = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (output.get("success").asBoolean()) {
|
if (output.get("success").getAsBoolean()) {
|
||||||
JsonNode cache = output.get("cache");
|
JsonObject cache = output.getAsJsonObject("cache");
|
||||||
String when;
|
String when;
|
||||||
if (cache.get("fromCache").asBoolean()) {
|
if (cache.get("fromCache").isJsonPrimitive()) {
|
||||||
when = cache.get("secondsSince").asInt() + " seconds ago";
|
when = cache.get("secondsSince").getAsBoolean() + " seconds ago";
|
||||||
} else {
|
} else {
|
||||||
when = "now";
|
when = "now";
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode ping = output.get("ping");
|
JsonObject ping = output.getAsJsonObject("ping");
|
||||||
JsonNode pong = ping.get("pong");
|
JsonObject pong = ping.getAsJsonObject("pong");
|
||||||
String remoteMotd = pong.get("motd").asText();
|
String remoteMotd = pong.get("motd").getAsString();
|
||||||
if (!connectionTestMotd.equals(remoteMotd)) {
|
if (!connectionTestMotd.equals(remoteMotd)) {
|
||||||
source.sendMessage("The MOTD did not match when we pinged the server (we got '" + remoteMotd + "'). " +
|
source.sendMessage("The MOTD did not match when we pinged the server (we got '" + remoteMotd + "'). " +
|
||||||
"Did you supply the correct IP and port of your server?");
|
"Did you supply the correct IP and port of your server?");
|
||||||
@@ -198,7 +199,7 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ping.get("tcpFirst").asBoolean()) {
|
if (ping.get("tcpFirst").getAsBoolean()) {
|
||||||
source.sendMessage("Your server hardware likely has some sort of firewall preventing people from joining easily. See https://geysermc.link/ovh-firewall for more information.");
|
source.sendMessage("Your server hardware likely has some sort of firewall preventing people from joining easily. See https://geysermc.link/ovh-firewall for more information.");
|
||||||
sendLinks(source);
|
sendLinks(source);
|
||||||
return;
|
return;
|
||||||
@@ -210,9 +211,9 @@ public class ConnectionTestCommand extends GeyserCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
source.sendMessage("Your server is likely unreachable from outside the network!");
|
source.sendMessage("Your server is likely unreachable from outside the network!");
|
||||||
JsonNode message = output.get("message");
|
JsonElement message = output.get("message");
|
||||||
if (message != null && !message.asText().isEmpty()) {
|
if (message != null && !message.getAsString().isEmpty()) {
|
||||||
source.sendMessage("Got the error message: " + message.asText());
|
source.sendMessage("Got the error message: " + message.getAsString());
|
||||||
}
|
}
|
||||||
sendLinks(source);
|
sendLinks(source);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -25,11 +25,14 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import com.google.gson.Gson;
|
||||||
import com.fasterxml.jackson.core.util.DefaultIndenter;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
|
import com.google.gson.JsonObject;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonParseException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.util.TriState;
|
import org.geysermc.geyser.api.util.TriState;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
@@ -38,16 +41,12 @@ import org.geysermc.geyser.dump.DumpInfo;
|
|||||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
import org.geysermc.geyser.util.WebUtils;
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
import org.incendo.cloud.CommandManager;
|
import org.incendo.cloud.CommandManager;
|
||||||
import org.incendo.cloud.context.CommandContext;
|
import org.incendo.cloud.context.CommandContext;
|
||||||
import org.incendo.cloud.suggestion.SuggestionProvider;
|
import org.incendo.cloud.suggestion.SuggestionProvider;
|
||||||
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static org.incendo.cloud.parser.standard.StringArrayParser.stringArrayParser;
|
import static org.incendo.cloud.parser.standard.StringArrayParser.stringArrayParser;
|
||||||
|
|
||||||
public class DumpCommand extends GeyserCommand {
|
public class DumpCommand extends GeyserCommand {
|
||||||
@@ -56,7 +55,6 @@ public class DumpCommand extends GeyserCommand {
|
|||||||
private static final Iterable<String> SUGGESTIONS = List.of("full", "offline", "logs");
|
private static final Iterable<String> SUGGESTIONS = List.of("full", "offline", "logs");
|
||||||
|
|
||||||
private final GeyserImpl geyser;
|
private final GeyserImpl geyser;
|
||||||
private static final ObjectMapper MAPPER = new ObjectMapper();
|
|
||||||
private static final String DUMP_URL = "https://dump.geysermc.org/";
|
private static final String DUMP_URL = "https://dump.geysermc.org/";
|
||||||
|
|
||||||
public DumpCommand(GeyserImpl geyser, String name, String description, String permission) {
|
public DumpCommand(GeyserImpl geyser, String name, String description, String permission) {
|
||||||
@@ -111,20 +109,14 @@ public class DumpCommand extends GeyserCommand {
|
|||||||
|
|
||||||
AsteriskSerializer.showSensitive = showSensitive;
|
AsteriskSerializer.showSensitive = showSensitive;
|
||||||
|
|
||||||
|
Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
|
||||||
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", source.locale()));
|
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collecting", source.locale()));
|
||||||
String dumpData;
|
String dumpData;
|
||||||
try {
|
try {
|
||||||
DumpInfo dump = new DumpInfo(geyser, addLog);
|
DumpInfo dump = new DumpInfo(geyser, addLog);
|
||||||
|
dumpData = gson.toJson(dump);
|
||||||
if (offlineDump) {
|
} catch (Exception e) {
|
||||||
DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
|
|
||||||
// Make arrays easier to read
|
|
||||||
prettyPrinter.indentArraysWith(new DefaultIndenter(" ", "\n"));
|
|
||||||
dumpData = MAPPER.writer(prettyPrinter).writeValueAsString(dump);
|
|
||||||
} else {
|
|
||||||
dumpData = MAPPER.writeValueAsString(dump);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", source.locale()));
|
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.collect_error", source.locale()));
|
||||||
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
|
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.collect_error_short"), e);
|
||||||
return;
|
return;
|
||||||
@@ -150,11 +142,11 @@ public class DumpCommand extends GeyserCommand {
|
|||||||
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", source.locale()));
|
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.uploading", source.locale()));
|
||||||
|
|
||||||
String response = null;
|
String response = null;
|
||||||
JsonNode responseNode;
|
JsonObject responseNode;
|
||||||
try {
|
try {
|
||||||
response = WebUtils.post(DUMP_URL + "documents", dumpData);
|
response = WebUtils.post(DUMP_URL + "documents", dumpData);
|
||||||
responseNode = MAPPER.readTree(response);
|
responseNode = JsonUtils.parseJson(response);
|
||||||
} catch (IOException e) {
|
} catch (Throwable e) {
|
||||||
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", source.locale()));
|
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error", source.locale()));
|
||||||
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
|
geyser.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.commands.dump.upload_error_short"), e);
|
||||||
if (e instanceof JsonParseException && response != null) {
|
if (e instanceof JsonParseException && response != null) {
|
||||||
@@ -164,11 +156,11 @@ public class DumpCommand extends GeyserCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!responseNode.has("key")) {
|
if (!responseNode.has("key")) {
|
||||||
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", source.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").asText() : response));
|
source.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.commands.dump.upload_error_short", source.locale()) + ": " + (responseNode.has("message") ? responseNode.get("message").getAsString() : response));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadedDumpUrl = DUMP_URL + responseNode.get("key").asText();
|
uploadedDumpUrl = DUMP_URL + responseNode.get("key").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", source.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
|
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.dump.message", source.locale()) + " " + ChatColor.DARK_AQUA + uploadedDumpUrl);
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.command.defaults;
|
package org.geysermc.geyser.command.defaults;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonObject;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
@@ -77,7 +77,7 @@ public class VersionCommand extends GeyserCommand {
|
|||||||
GeyserImpl.NAME, GeyserImpl.VERSION, SUPPORTED_JAVA_RANGE, SUPPORTED_BEDROCK_RANGE));
|
GeyserImpl.NAME, GeyserImpl.VERSION, SUPPORTED_JAVA_RANGE, SUPPORTED_BEDROCK_RANGE));
|
||||||
|
|
||||||
// Disable update checking in dev mode and for players in Geyser Standalone
|
// Disable update checking in dev mode and for players in Geyser Standalone
|
||||||
if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.getPlatformType() == PlatformType.STANDALONE)) {
|
if (!GeyserImpl.getInstance().isProductionEnvironment() || (!source.isConsole() && geyser.platformType() == PlatformType.STANDALONE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +89,8 @@ public class VersionCommand extends GeyserCommand {
|
|||||||
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", source.locale()));
|
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.checking", source.locale()));
|
||||||
try {
|
try {
|
||||||
int buildNumber = this.geyser.buildNumber();
|
int buildNumber = this.geyser.buildNumber();
|
||||||
JsonNode response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest");
|
JsonObject response = WebUtils.getJson("https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest");
|
||||||
int latestBuildNumber = response.get("build").asInt();
|
int latestBuildNumber = response.get("build").getAsInt();
|
||||||
|
|
||||||
if (latestBuildNumber == buildNumber) {
|
if (latestBuildNumber == buildNumber) {
|
||||||
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", source.locale()));
|
source.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.commands.version.no_updates", source.locale()));
|
||||||
|
|||||||
@@ -25,21 +25,18 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.command.standalone;
|
package org.geysermc.geyser.command.standalone;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@ConfigSerializable
|
||||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
public class PermissionConfiguration {
|
public class PermissionConfiguration {
|
||||||
|
|
||||||
@JsonProperty("default-permissions")
|
|
||||||
private Set<String> defaultPermissions = Collections.emptySet();
|
private Set<String> defaultPermissions = Collections.emptySet();
|
||||||
|
|
||||||
@JsonProperty("default-denied-permissions")
|
|
||||||
private Set<String> defaultDeniedPermissions = Collections.emptySet();
|
private Set<String> defaultDeniedPermissions = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,217 @@
|
|||||||
|
/*
|
||||||
|
* 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 com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.checkerframework.common.returnsreceiver.qual.This;
|
||||||
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurateException;
|
||||||
|
import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions;
|
||||||
|
import org.spongepowered.configurate.objectmapping.meta.Processor;
|
||||||
|
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||||
|
import org.spongepowered.configurate.yaml.NodeStyle;
|
||||||
|
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public final class ConfigLoader {
|
||||||
|
private static final String HEADER = """
|
||||||
|
--------------------------------
|
||||||
|
Geyser Configuration File
|
||||||
|
|
||||||
|
A bridge between Minecraft: Bedrock Edition and Minecraft: Java Edition.
|
||||||
|
|
||||||
|
GitHub: https://github.com/GeyserMC/Geyser
|
||||||
|
Discord: https://discord.gg/geysermc
|
||||||
|
Wiki: https://geysermc.org/wiki
|
||||||
|
|
||||||
|
NOTICE: See https://geysermc.org/wiki/geyser/setup/ for the setup guide. Many video tutorials are outdated.
|
||||||
|
In most cases, especially with server hosting providers, further hosting-specific configuration is required.
|
||||||
|
--------------------------------""";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only nullable for testing.
|
||||||
|
*/
|
||||||
|
private final @Nullable GeyserBootstrap bootstrap;
|
||||||
|
private PlatformType platformType;
|
||||||
|
private @Nullable Consumer<CommentedConfigurationNode> transformer;
|
||||||
|
private File configFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only set during testing.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
CommentedConfigurationNode configurationNode;
|
||||||
|
|
||||||
|
public ConfigLoader(GeyserBootstrap bootstrap) {
|
||||||
|
this.bootstrap = bootstrap;
|
||||||
|
this.platformType = bootstrap.platformType();
|
||||||
|
configFile = new File(bootstrap.getConfigFolder().toFile(), "config.yml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
ConfigLoader(File file) {
|
||||||
|
this.bootstrap = null;
|
||||||
|
configFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the directory as indicated by {@link GeyserBootstrap#getConfigFolder()}
|
||||||
|
*/
|
||||||
|
@This
|
||||||
|
public ConfigLoader createFolder() {
|
||||||
|
Path dataFolder = this.bootstrap.getConfigFolder();
|
||||||
|
if (!dataFolder.toFile().exists()) {
|
||||||
|
if (!dataFolder.toFile().mkdir()) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("Failed to create config folder: " + dataFolder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@This
|
||||||
|
public ConfigLoader transformer(Consumer<CommentedConfigurationNode> transformer) {
|
||||||
|
this.transformer = transformer;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@This
|
||||||
|
public ConfigLoader configFile(File configFile) {
|
||||||
|
this.configFile = configFile;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null if the config failed to load.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public <T extends GeyserConfig> T load(Class<T> configClass) {
|
||||||
|
try {
|
||||||
|
return load0(configClass);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
bootstrap.getGeyserLogger().error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends GeyserConfig> T load0(Class<T> configClass) throws ConfigurateException {
|
||||||
|
var loader = createLoader(configFile);
|
||||||
|
|
||||||
|
CommentedConfigurationNode node = loader.load();
|
||||||
|
boolean originallyEmpty = !configFile.exists() || node.isNull();
|
||||||
|
|
||||||
|
ConfigurationTransformation.Versioned migrations = ConfigMigrations.TRANSFORMER.apply(configClass, bootstrap);
|
||||||
|
int currentVersion = migrations.version(node);
|
||||||
|
migrations.apply(node);
|
||||||
|
int newVersion = migrations.version(node);
|
||||||
|
|
||||||
|
T config = node.get(configClass);
|
||||||
|
|
||||||
|
// Serialize the instance to ensure strict field ordering. Additionally, if we serialized back
|
||||||
|
// to the old node, existing nodes would only have their value changed, keeping their position
|
||||||
|
// at the top of the ordered map, forcing all new nodes to the bottom (regardless of field order).
|
||||||
|
// For that reason, we must also create a new node.
|
||||||
|
CommentedConfigurationNode newRoot = CommentedConfigurationNode.root(loader.defaultOptions());
|
||||||
|
newRoot.set(config);
|
||||||
|
|
||||||
|
if (originallyEmpty || currentVersion != newVersion) {
|
||||||
|
if (!originallyEmpty && currentVersion > 4) {
|
||||||
|
// Only copy comments over if the file already existed, and we are going to replace it
|
||||||
|
|
||||||
|
// Second case: Version 4 is pre-configurate where there were commented out nodes.
|
||||||
|
// These get treated as comments on lower nodes, which produces very undesirable results.
|
||||||
|
ConfigurationCommentMover.moveComments(node, newRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
loader.save(newRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transformer != null) {
|
||||||
|
// We transform AFTER saving so that these specific transformations aren't applied to file.
|
||||||
|
transformer.accept(newRoot);
|
||||||
|
config = newRoot.get(configClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.bootstrap != null) { // Null for testing only.
|
||||||
|
this.bootstrap.getGeyserLogger().setDebug(config.debugMode());
|
||||||
|
} else {
|
||||||
|
this.configurationNode = newRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
CommentedConfigurationNode loadConfigurationNode(Class<? extends GeyserConfig> configClass, PlatformType platformType) throws ConfigurateException {
|
||||||
|
this.platformType = platformType;
|
||||||
|
load0(configClass);
|
||||||
|
return configurationNode.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private YamlConfigurationLoader createLoader(File file) {
|
||||||
|
return YamlConfigurationLoader.builder()
|
||||||
|
.file(file)
|
||||||
|
.indent(2)
|
||||||
|
.nodeStyle(NodeStyle.BLOCK)
|
||||||
|
.defaultOptions(options -> InterfaceDefaultOptions.addTo(options, builder -> {
|
||||||
|
builder.addProcessor(ExcludePlatform.class, excludePlatform(platformType.platformName()))
|
||||||
|
.addProcessor(PluginSpecific.class, integrationSpecific(platformType != PlatformType.STANDALONE));
|
||||||
|
})
|
||||||
|
.shouldCopyDefaults(false) // If we use ConfigurationNode#get(type, default), do not write the default back to the node.
|
||||||
|
.header(ConfigLoader.HEADER)
|
||||||
|
.serializers(builder -> builder.register(new LowercaseEnumSerializer())))
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Processor.Factory<ExcludePlatform, Object> excludePlatform(String thisPlatform) {
|
||||||
|
return (data, fieldType) -> (value, destination) -> {
|
||||||
|
for (String platform : data.platforms()) {
|
||||||
|
if (thisPlatform.equals(platform)) {
|
||||||
|
//noinspection DataFlowIssue
|
||||||
|
destination.parent().removeChild(destination.key());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Processor.Factory<PluginSpecific, Object> integrationSpecific(boolean thisConfigPlugin) {
|
||||||
|
return (data, fieldType) -> (value, destination) -> {
|
||||||
|
if (data.forPlugin() != thisConfigPlugin) {
|
||||||
|
//noinspection DataFlowIssue
|
||||||
|
destination.parent().removeChild(destination.key());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,198 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 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 org.geysermc.geyser.GeyserBootstrap;
|
||||||
|
import org.spongepowered.configurate.ConfigurateException;
|
||||||
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.transformation.ConfigurationTransformation;
|
||||||
|
import org.spongepowered.configurate.transformation.TransformAction;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import static org.spongepowered.configurate.NodePath.path;
|
||||||
|
import static org.spongepowered.configurate.transformation.TransformAction.remove;
|
||||||
|
import static org.spongepowered.configurate.transformation.TransformAction.rename;
|
||||||
|
|
||||||
|
public class ConfigMigrations {
|
||||||
|
|
||||||
|
public static final BiFunction<Class<? extends GeyserConfig>, GeyserBootstrap, ConfigurationTransformation.Versioned> TRANSFORMER = (configClass, bootstrap) ->
|
||||||
|
ConfigurationTransformation.versionedBuilder()
|
||||||
|
.versionKey("config-version")
|
||||||
|
.addVersion(5, ConfigurationTransformation.builder()
|
||||||
|
// Java section
|
||||||
|
.addAction(path("remote"), rename("java"))
|
||||||
|
.addAction(path("remote", "address"), (path, value) -> {
|
||||||
|
if ("auto".equals(value.getString())) {
|
||||||
|
// Auto-convert back to localhost
|
||||||
|
value.set("127.0.0.1");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Motd section
|
||||||
|
.addAction(path("bedrock", "motd1"), renameAndMove("motd", "primary-motd"))
|
||||||
|
.addAction(path("bedrock", "motd2"), renameAndMove("motd", "secondary-motd"))
|
||||||
|
.addAction(path("passthrough-motd"), moveTo("motd"))
|
||||||
|
.addAction(path("passthrough-player-counts"), moveTo("motd"))
|
||||||
|
.addAction(path("ping-passthrough-interval"), moveTo("motd"))
|
||||||
|
.addAction(path("max-players"), moveTo("motd"))
|
||||||
|
.addAction(path("legacy-ping-passthrough"), configClass == GeyserRemoteConfig.class ? remove() : (path, value) -> {
|
||||||
|
// Invert value
|
||||||
|
value.set(!value.getBoolean());
|
||||||
|
return new Object[]{ "motd", "integrated-ping-passthrough" };
|
||||||
|
})
|
||||||
|
|
||||||
|
// gameplay
|
||||||
|
.addAction(path("command-suggestions"), moveTo("gameplay"))
|
||||||
|
.addAction(path("forward-player-ping"), moveTo("gameplay"))
|
||||||
|
.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 new Object[]{ "gameplay", "show-cooldown" };
|
||||||
|
})
|
||||||
|
.addAction(path("bedrock", "server-name"), moveTo("gameplay"))
|
||||||
|
.addAction(path("show-coordinates"), moveTo("gameplay"))
|
||||||
|
.addAction(path("disable-bedrock-scaffolding"), moveTo("gameplay"))
|
||||||
|
.addAction(path("custom-skull-render-distance"), moveTo("gameplay"))
|
||||||
|
.addAction(path("force-resource-packs"), moveTo("gameplay"))
|
||||||
|
.addAction(path("xbox-achievements-enabled"), moveTo("gameplay"))
|
||||||
|
.addAction(path("unusable-space-block"), moveTo("gameplay"))
|
||||||
|
.addAction(path("unusable-space-block"), moveTo("gameplay"))
|
||||||
|
.addAction(path("add-non-bedrock-items"), renameAndMove("gameplay", "enable-custom-content"))
|
||||||
|
.addAction(path("above-bedrock-nether-building"), renameAndMove("gameplay", "nether-roof-workaround"))
|
||||||
|
.addAction(path("xbox-achievements-enabled"), moveTo("gameplay"))
|
||||||
|
// NOTE: We're not explicitly removing the allow-custom-skulls option, it will already be removed since it
|
||||||
|
// won't be written back. If we remove it, we can't query the value of it!
|
||||||
|
.addAction(path("max-visible-custom-skulls"), (path, value) -> {
|
||||||
|
ConfigurationNode parent = value.parent();
|
||||||
|
if (parent != null && parent.isMap()) {
|
||||||
|
ConfigurationNode allowCustomSkulls = parent.childrenMap().get("allow-custom-skulls");
|
||||||
|
if (allowCustomSkulls != null && !allowCustomSkulls.getBoolean()) {
|
||||||
|
value.set(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Object[]{ "gameplay", "max-visible-custom-skulls" };
|
||||||
|
})
|
||||||
|
.addAction(path("emote-offhand-workaround"), (path, value) -> {
|
||||||
|
String previous = value.getString();
|
||||||
|
if (!Objects.equals(previous, "disabled") && bootstrap != null) {
|
||||||
|
bootstrap.getGeyserLogger().warning("The emote-offhand-workaround option has been removed from Geyser. If you still wish to have this functionality, use this Geyser extension: https://github.com/GeyserMC/EmoteOffhandExtension/");
|
||||||
|
}
|
||||||
|
if (Objects.equals(previous, "no-emotes")) {
|
||||||
|
value.set(false);
|
||||||
|
return new Object[]{ "gameplay", "show-emotes" };
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
|
||||||
|
// For the warning!
|
||||||
|
.addAction(path("allow-third-party-capes"), (node, value) -> {
|
||||||
|
if (bootstrap != null) {
|
||||||
|
bootstrap.getGeyserLogger().warning("Third-party ears/capes have been removed from Geyser. If you still wish to have this functionality, use this Geyser extension: https://github.com/GeyserMC/ThirdPartyCosmetics");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.addAction(path("allow-third-party-ears"), (node, value) -> {
|
||||||
|
if (bootstrap != null) {
|
||||||
|
bootstrap.getGeyserLogger().warning("Third-party ears/capes have been removed from Geyser. If you still wish to have this functionality, use this Geyser extension: https://github.com/GeyserMC/ThirdPartyCosmetics");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
|
||||||
|
// Advanced section
|
||||||
|
.addAction(path("cache-images"), moveTo("advanced"))
|
||||||
|
.addAction(path("scoreboard-packet-threshold"), moveTo("advanced"))
|
||||||
|
.addAction(path("add-team-suggestions"), moveTo("advanced"))
|
||||||
|
.addAction(path("floodgate-key-file"), (path, value) -> {
|
||||||
|
// Elimate any legacy config values
|
||||||
|
if ("public-key.pem".equals(value.getString())) {
|
||||||
|
value.set("key.pem");
|
||||||
|
}
|
||||||
|
return new Object[]{ "advanced", "floodgate-key-file" };
|
||||||
|
})
|
||||||
|
|
||||||
|
// Bedrock
|
||||||
|
.addAction(path("bedrock", "broadcast-port"), moveTo("advanced", "bedrock"))
|
||||||
|
.addAction(path("bedrock", "compression-level"), moveTo("advanced", "bedrock"))
|
||||||
|
.addAction(path("bedrock", "enable-proxy-protocol"), renameAndMove("advanced", "bedrock", "use-haproxy-protocol"))
|
||||||
|
.addAction(path("bedrock", "proxy-protocol-whitelisted-ips"), renameAndMove("advanced", "bedrock", "haproxy-protocol-whitelisted-ips"))
|
||||||
|
.addAction(path("mtu"), moveTo("advanced", "bedrock"))
|
||||||
|
|
||||||
|
// Java
|
||||||
|
.addAction(path("remote", "use-proxy-protocol"), renameAndMove("advanced", "java", "use-haproxy-protocol"))
|
||||||
|
.addAction(path("disable-compression"), moveTo("advanced", "java"))
|
||||||
|
.addAction(path("use-direct-connection"), moveTo("advanced", "java"))
|
||||||
|
|
||||||
|
// Other
|
||||||
|
.addAction(path("default-locale"), (path, value) -> {
|
||||||
|
if (value.getString() == null) {
|
||||||
|
value.set("system");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})
|
||||||
|
.addAction(path("metrics", "uuid"), (path, value) -> {
|
||||||
|
if ("generateduuid".equals(value.getString())) {
|
||||||
|
// Manually copied config without Metrics UUID creation?
|
||||||
|
value.set(UUID.randomUUID());
|
||||||
|
}
|
||||||
|
return new Object[]{ "metrics-uuid" };
|
||||||
|
})
|
||||||
|
.addAction(path("metrics", "enabled"), (path, value) -> {
|
||||||
|
// Move to the root, not in the Metrics class.
|
||||||
|
return new Object[]{ "enable-metrics" };
|
||||||
|
})
|
||||||
|
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
static TransformAction renameAndMove(String... newPath) {
|
||||||
|
return ((path, value) -> Arrays.stream(newPath).toArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
static TransformAction moveTo(String... newPath) {
|
||||||
|
return (path, value) -> {
|
||||||
|
Object[] arr = path.array();
|
||||||
|
if (arr.length == 0) {
|
||||||
|
throw new ConfigurateException(value, "The root node cannot be renamed!");
|
||||||
|
} else {
|
||||||
|
// create a new array with space for newPath segments + the original last segment
|
||||||
|
Object[] result = new Object[newPath.length + 1];
|
||||||
|
System.arraycopy(newPath, 0, result, 0, newPath.length);
|
||||||
|
result[newPath.length] = arr[arr.length - 1];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurationVisitor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves comments from a different node and puts them on this node
|
||||||
|
*/
|
||||||
|
public final class ConfigurationCommentMover implements ConfigurationVisitor.Stateless<RuntimeException> {
|
||||||
|
|
||||||
|
private final CommentedConfigurationNode otherRoot;
|
||||||
|
|
||||||
|
private ConfigurationCommentMover(@NonNull CommentedConfigurationNode otherNode) {
|
||||||
|
this.otherRoot = otherNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enterNode(final ConfigurationNode node) {
|
||||||
|
if (!(node instanceof CommentedConfigurationNode destination)) {
|
||||||
|
// Should not occur because all nodes in a tree are the same type,
|
||||||
|
// and our static method below ensures this visitor is only used on CommentedConfigurationNodes
|
||||||
|
throw new IllegalStateException(node.path() + " is not a CommentedConfigurationNode");
|
||||||
|
}
|
||||||
|
// Node with the same path
|
||||||
|
CommentedConfigurationNode source = otherRoot.node(node.path());
|
||||||
|
|
||||||
|
moveSingle(source, destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void moveSingle(@NonNull CommentedConfigurationNode source, @NonNull CommentedConfigurationNode destination) {
|
||||||
|
// Only transfer the comment, overriding if necessary
|
||||||
|
String comment = source.comment();
|
||||||
|
if (comment != null) {
|
||||||
|
destination.comment(comment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves comments from a source node and its children to a destination node and its children (of a different tree), overriding if necessary.
|
||||||
|
* Comments are only moved to the destination node and its children which exist.
|
||||||
|
* Comments are only moved to and from nodes with the exact same path.
|
||||||
|
*
|
||||||
|
* @param source the source of the comments, which must be the topmost parent of a tree
|
||||||
|
* @param destination the destination of the comments, any node in a different tree
|
||||||
|
*/
|
||||||
|
public static void moveComments(@NonNull CommentedConfigurationNode source, @NonNull CommentedConfigurationNode destination) {
|
||||||
|
if (source.parent() != null) {
|
||||||
|
throw new IllegalArgumentException("source is not the base of the tree it is within: " + source.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.isNull()) {
|
||||||
|
// It has no value(s), but may still have a comment on it. Don't both traversing the whole destination tree.
|
||||||
|
moveSingle(source, destination);
|
||||||
|
} else {
|
||||||
|
destination.visit(new ConfigurationCommentMover(source));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -23,20 +23,15 @@
|
|||||||
* @link https://github.com/GeyserMC/Geyser
|
* @link https://github.com/GeyserMC/Geyser
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.geysermc.geyser.platform.standalone;
|
package org.geysermc.geyser.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import java.lang.annotation.ElementType;
|
||||||
import lombok.Getter;
|
import java.lang.annotation.Retention;
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
|
||||||
import java.nio.file.Paths;
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface ExcludePlatform {
|
||||||
@Getter
|
String[] platforms();
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public final class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration {
|
|
||||||
@Override
|
|
||||||
public Path getFloodgateKeyPath() {
|
|
||||||
return Paths.get(getFloodgateKeyFile());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,475 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.geyser.Constants;
|
||||||
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
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.text.AsteriskSerializer;
|
||||||
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
public interface GeyserConfig {
|
||||||
|
@Comment("Network settings for the Bedrock listener")
|
||||||
|
BedrockConfig bedrock();
|
||||||
|
|
||||||
|
@Comment("Network settings for the Java server connection")
|
||||||
|
JavaConfig java();
|
||||||
|
|
||||||
|
@Comment("MOTD settings")
|
||||||
|
MotdConfig motd();
|
||||||
|
|
||||||
|
@Comment("Gameplay options that affect Bedrock players")
|
||||||
|
GameplayConfig gameplay();
|
||||||
|
|
||||||
|
@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.")
|
||||||
|
@DefaultString(GeyserLocale.SYSTEM_LOCALE)
|
||||||
|
@NonNull
|
||||||
|
String defaultLocale();
|
||||||
|
|
||||||
|
@Comment("Whether player IP addresses will be logged by the server.")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean logPlayerIpAddresses();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
For online mode authentication type only.
|
||||||
|
Stores a list of Bedrock player usernames that should have their Java Edition account saved after login.
|
||||||
|
This saves a token that can be reused to authenticate the player later. This does not save emails or passwords,
|
||||||
|
but you should still be cautious when adding to this list and giving others access to this Geyser instance's files.
|
||||||
|
Removing a name from this list will delete its cached login information on the next Geyser startup.
|
||||||
|
The file that tokens will be saved in is in the same folder as this config, named "saved-refresh-tokens.json".""")
|
||||||
|
default List<String> savedUserLogins() {
|
||||||
|
return List.of("ThisExampleUsernameShouldBeLongEnoughToNeverBeAnXboxUsername",
|
||||||
|
"ThisOtherExampleUsernameShouldAlsoBeLongEnough");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
For online mode authentication type only.
|
||||||
|
Specify how many seconds to wait while user authorizes Geyser to access their Microsoft account.
|
||||||
|
User is allowed to disconnect from the server during this period.""")
|
||||||
|
@DefaultNumeric(120)
|
||||||
|
int pendingAuthenticationTimeout();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to alert the console and operators that a new Geyser version is available that supports a Bedrock version
|
||||||
|
that this Geyser version does not support. It's recommended to keep this option enabled, as many Bedrock platforms
|
||||||
|
auto-update.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean notifyOnNewBedrockUpdate();
|
||||||
|
|
||||||
|
@Comment("Advanced configuration options. These usually do not need modifications.")
|
||||||
|
AdvancedConfig advanced();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
bStats is a stat tracker that is entirely anonymous and tracks only basic information
|
||||||
|
about Geyser, such as how many people are online, how many servers are using Geyser,
|
||||||
|
what OS is being used, etc. You can learn more about bStats here: https://bstats.org/.
|
||||||
|
https://bstats.org/plugin/server-implementation/GeyserMC""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
@ExcludePlatform(platforms = {"BungeeCord", "Spigot", "Velocity"}) // bStats platform versions used
|
||||||
|
boolean enableMetrics();
|
||||||
|
|
||||||
|
@Comment("The bstats metrics uuid. Do not touch!")
|
||||||
|
@ExcludePlatform(platforms = {"BungeeCord", "Spigot", "Velocity"}) // bStats platform versions used
|
||||||
|
default UUID metricsUuid() {
|
||||||
|
return UUID.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Comment("If debug messages should be sent through console")
|
||||||
|
boolean debugMode();
|
||||||
|
|
||||||
|
@Comment("Do not change!")
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
default int configVersion() {
|
||||||
|
return Constants.CONFIG_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface BedrockConfig extends BedrockListener {
|
||||||
|
@Comment("""
|
||||||
|
The IP address that Geyser will bind on to listen for incoming Bedrock connections.
|
||||||
|
Generally, you should only change this if you want to limit what IPs can connect to your server.""")
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
@DefaultString("0.0.0.0")
|
||||||
|
@AsteriskSerializer.Asterisk
|
||||||
|
String address();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
The port that will Geyser will listen on for incoming Bedrock connections.
|
||||||
|
Since Minecraft: Bedrock Edition uses UDP, this port must allow UDP traffic.""")
|
||||||
|
@Override
|
||||||
|
@DefaultNumeric(19132)
|
||||||
|
@NumericRange(from = 0, to = 65535)
|
||||||
|
int port();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Some hosting services change your Java port everytime you start the server and require the same port to be used for Bedrock.
|
||||||
|
This option makes the Bedrock port the same as the Java port every time you start the server.""")
|
||||||
|
@DefaultBoolean
|
||||||
|
@PluginSpecific
|
||||||
|
boolean cloneRemotePort();
|
||||||
|
|
||||||
|
void address(String address);
|
||||||
|
void port(int port);
|
||||||
|
|
||||||
|
@Exclude
|
||||||
|
@Override
|
||||||
|
default int broadcastPort() {
|
||||||
|
return GeyserImpl.getInstance().config().advanced().bedrock().broadcastPort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Exclude
|
||||||
|
@Override
|
||||||
|
default String primaryMotd() {
|
||||||
|
return GeyserImpl.getInstance().config().motd().primaryMotd();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Exclude
|
||||||
|
@Override
|
||||||
|
default String secondaryMotd() {
|
||||||
|
return GeyserImpl.getInstance().config().motd().secondaryMotd();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Exclude
|
||||||
|
@Override
|
||||||
|
default String serverName() {
|
||||||
|
return GeyserImpl.getInstance().config().gameplay().serverName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface JavaConfig extends RemoteServer {
|
||||||
|
void address(String address);
|
||||||
|
void port(int port);
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
What type of authentication Bedrock players will be checked against when logging into the Java server.
|
||||||
|
Can be "floodgate" (see https://wiki.geysermc.org/floodgate/), "online", or "offline".""")
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
default AuthType authType() {
|
||||||
|
return AuthType.ONLINE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void authType(AuthType authType);
|
||||||
|
boolean forwardHostname();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Exclude
|
||||||
|
default String minecraftVersion() {
|
||||||
|
return GameProtocol.getJavaMinecraftVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Exclude
|
||||||
|
default int protocolVersion() {
|
||||||
|
return GameProtocol.getJavaProtocolVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Exclude
|
||||||
|
default boolean resolveSrv() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface MotdConfig {
|
||||||
|
@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();
|
||||||
|
@DefaultString("Another Geyser server.")
|
||||||
|
String secondaryMotd();
|
||||||
|
|
||||||
|
@Comment("Whether Geyser should relay the MOTD from the Java server to Bedrock players.")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean passthroughMotd();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Maximum amount of players that can connect.
|
||||||
|
This is only visual, and is only applied if passthrough-motd is disabled.""")
|
||||||
|
@DefaultNumeric(100)
|
||||||
|
int maxPlayers();
|
||||||
|
|
||||||
|
@Comment("Whether to relay the player count and max players from the Java server to Bedrock players.")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean passthroughPlayerCounts();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to 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.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
@PluginSpecific
|
||||||
|
boolean integratedPingPassthrough();
|
||||||
|
|
||||||
|
@Comment("How often to ping the Java server to refresh MOTD and player count, in seconds.")
|
||||||
|
@DefaultNumeric(3)
|
||||||
|
int pingPassthroughInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface GameplayConfig {
|
||||||
|
|
||||||
|
@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("""
|
||||||
|
Allow a fake cooldown indicator to be sent. Bedrock players otherwise do not see a cooldown as they still use 1.8 combat.
|
||||||
|
Please note: if the cooldown is enabled, some users may see a black box during the cooldown sequence, like below:
|
||||||
|
https://geysermc.org/img/external/cooldown_indicator.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\"""")
|
||||||
|
default CooldownUtils.CooldownType showCooldown() {
|
||||||
|
return CooldownUtils.CooldownType.TITLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
@Comment("""
|
||||||
|
Bedrock clients can freeze when opening up the command prompt for the first time if given a lot of commands.
|
||||||
|
Disabling this will prevent command suggestions from being sent and solve freezing for Bedrock clients.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean commandSuggestions();
|
||||||
|
|
||||||
|
@Comment("Controls if coordinates are shown to players.")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean showCoordinates();
|
||||||
|
|
||||||
|
@Comment("Whether Bedrock players are blocked from performing their scaffolding-style bridging.")
|
||||||
|
boolean disableBedrockScaffolding();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Bedrock prevents building and displaying blocks above Y127 in the Nether.
|
||||||
|
This config option works around that by changing the Nether dimension ID to the End ID.
|
||||||
|
The main downside to this is that the entire Nether will have the same red fog rather than having different fog for each biome.""")
|
||||||
|
boolean netherRoofWorkaround();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to show Bedrock Edition emotes to other Bedrock Edition players.
|
||||||
|
""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean emotesEnabled();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Which item to use to mark unavailable slots in a Bedrock player inventory. Examples of this are the 2x2 crafting grid while in creative,
|
||||||
|
or custom inventory menus with sizes different from the usual 3x9. A barrier block is the default item.
|
||||||
|
This config option can be set to any Bedrock item identifier. If you want to set this to a custom item, make sure that you specify the item in the following format: "geyser_custom:<mapping-name>"
|
||||||
|
""")
|
||||||
|
@DefaultString("minecraft:barrier")
|
||||||
|
String unusableSpaceBlock();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to add any items and blocks which normally does not exist in Bedrock Edition.
|
||||||
|
This should only need to be disabled if using a proxy that does not use the "transfer packet" style of server switching.
|
||||||
|
If this is disabled, furnace minecart items will be mapped to hopper minecart items.
|
||||||
|
Geyser's block, item, and skull mappings systems will also be disabled.
|
||||||
|
This option requires a restart of Geyser in order to change its setting.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean enableCustomContent();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Force clients to load all resource packs if there are any.
|
||||||
|
If set to false, it allows the user to connect to the server even if they don't
|
||||||
|
want to download the resource packs.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean forceResourcePacks();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to automatically serve a resource pack that is required for some Geyser features to all connecting Bedrock players.
|
||||||
|
If enabled, force-resource-packs will be enabled.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean enableIntegratedPack();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to forward player ping to the server. While enabling this will allow Bedrock players to have more accurate
|
||||||
|
ping, it may also cause players to time out more easily.""")
|
||||||
|
boolean forwardPlayerPing();
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
@Comment("""
|
||||||
|
Allows Xbox achievements to be unlocked.
|
||||||
|
If a player types in an unknown command, they will receive a message that states cheats are disabled.
|
||||||
|
Otherwise, commands work as expected.""")
|
||||||
|
boolean xboxAchievementsEnabled();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
The maximum number of custom skulls to be displayed per player. Increasing this may decrease performance on weaker devices.
|
||||||
|
A value of 0 will disable all custom skulls.
|
||||||
|
Setting this to -1 will cause all custom skulls to be displayed regardless of distance or number.""")
|
||||||
|
@DefaultNumeric(128)
|
||||||
|
int maxVisibleCustomSkulls();
|
||||||
|
|
||||||
|
@Comment("The radius in blocks around the player in which custom skulls are displayed.")
|
||||||
|
@DefaultNumeric(32)
|
||||||
|
int customSkullRenderDistance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface AdvancedBedrockConfig {
|
||||||
|
@Comment("""
|
||||||
|
The port to broadcast to Bedrock clients with the MOTD that they should use to connect to the server.
|
||||||
|
A value of 0 will broadcast the port specified above.
|
||||||
|
DO NOT change this unless Geyser runs on a different port than the one that is used to connect.""")
|
||||||
|
@DefaultNumeric(0)
|
||||||
|
@NumericRange(from = 0, to = 65535)
|
||||||
|
int broadcastPort();
|
||||||
|
|
||||||
|
void broadcastPort(int port);
|
||||||
|
|
||||||
|
@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.""")
|
||||||
|
@DefaultNumeric(6)
|
||||||
|
@NumericRange(from = -1, to = 9)
|
||||||
|
int compressionLevel();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to expect HAPROXY protocol for connecting Bedrock clients.
|
||||||
|
This is useful only when you are running a UDP reverse proxy in front of your Geyser instance.
|
||||||
|
IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT!""")
|
||||||
|
@DefaultBoolean
|
||||||
|
boolean useHaproxyProtocol();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
A list of allowed HAPROXY protocol speaking proxy IP addresses/subnets. Only effective when "use-proxy-protocol" is enabled, and
|
||||||
|
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.""")
|
||||||
|
default List<String> haproxyProtocolWhitelistedIps() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
The internet supports a maximum MTU of 1492 but could cause issues with packet fragmentation.
|
||||||
|
1400 is the default.""")
|
||||||
|
@DefaultNumeric(1400)
|
||||||
|
int mtu();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
This option disables the auth step Geyser performs for connecting Bedrock players.
|
||||||
|
It can be used to allow connections from ProxyPass and WaterdogPE. In these cases, make sure that users
|
||||||
|
cannot directly connect to this Geyser instance. See https://www.spigotmc.org/wiki/firewall-guide/ for
|
||||||
|
assistance - and use UDP instead of TCP.
|
||||||
|
Disabling Bedrock authentication for other use-cases is NOT SUPPORTED, as it allows anyone to spoof usernames, and is therefore a security risk.
|
||||||
|
All Floodgate functionality (including skin uploading and account linking) will also not work when this option is disabled.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean validateBedrockLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface AdvancedJavaConfig {
|
||||||
|
@Comment("""
|
||||||
|
Whether to enable HAPROXY protocol when connecting to the Java server.
|
||||||
|
This is useful only when:
|
||||||
|
1) Your Java server supports HAPROXY protocol (it probably doesn't)
|
||||||
|
2) You run Velocity or BungeeCord with the option enabled in the proxy's main config.
|
||||||
|
IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT!""")
|
||||||
|
boolean useHaproxyProtocol();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether to connect directly into the Java server without creating a TCP connection.
|
||||||
|
This should only be disabled if a plugin that interfaces with packets or the network does not work correctly with Geyser.
|
||||||
|
If enabled, the remote address and port sections are ignored.
|
||||||
|
If disabled, expect performance decrease and latency increase.
|
||||||
|
""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
@PluginSpecific
|
||||||
|
boolean useDirectConnection();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether Geyser should attempt to disable packet compression (from the Java Server to Geyser) for Bedrock players.
|
||||||
|
This should be a benefit as there is no need to compress data when Java packets aren't being handled over the network.
|
||||||
|
This requires use-direct-connection to be true.
|
||||||
|
""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
@PluginSpecific
|
||||||
|
boolean disableCompression();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface AdvancedConfig {
|
||||||
|
@Comment("""
|
||||||
|
Specify how many days player skin images will be cached to disk to save downloading them from the internet.
|
||||||
|
A value of 0 is disabled. (Default: 0)""")
|
||||||
|
int cacheImages();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle
|
||||||
|
a lot of scoreboard packets per second, this can cause serious lag.
|
||||||
|
This option allows you to specify after how many Scoreboard packets per seconds
|
||||||
|
the Scoreboard updates will be limited to four updates per second.""")
|
||||||
|
@DefaultNumeric(20)
|
||||||
|
int scoreboardPacketThreshold();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
Whether Geyser should send team names in command suggestions.
|
||||||
|
Disable this if you have a lot of teams used that you don't need as suggestions.""")
|
||||||
|
@DefaultBoolean(true)
|
||||||
|
boolean addTeamSuggestions();
|
||||||
|
|
||||||
|
@Comment("""
|
||||||
|
A list of remote resource pack urls to send to the Bedrock client for downloading.
|
||||||
|
The Bedrock client is very picky about how these are delivered - please see our wiki page for further info: https://geysermc.org/wiki/geyser/packs/
|
||||||
|
""")
|
||||||
|
default List<String> resourcePackUrls() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cannot be type File yet because we may want to hide it in plugin instances.
|
||||||
|
@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.""")
|
||||||
|
@DefaultString("key.pem")
|
||||||
|
String floodgateKeyFile();
|
||||||
|
|
||||||
|
@Comment("Advanced networking options for the Geyser to Java server connection")
|
||||||
|
AdvancedJavaConfig java();
|
||||||
|
|
||||||
|
@Comment("Advanced networking options for Geyser's Bedrock listener")
|
||||||
|
AdvancedBedrockConfig bedrock();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-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 com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
|
||||||
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.CIDRMatcher;
|
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface GeyserConfiguration {
|
|
||||||
/**
|
|
||||||
* If the config was originally 'auto' before the values changed
|
|
||||||
*/
|
|
||||||
void setAutoconfiguredRemote(boolean autoconfiguredRemote);
|
|
||||||
|
|
||||||
// Modify this when you introduce breaking changes into the config
|
|
||||||
int CURRENT_CONFIG_VERSION = 4;
|
|
||||||
|
|
||||||
IBedrockConfiguration getBedrock();
|
|
||||||
|
|
||||||
IRemoteConfiguration getRemote();
|
|
||||||
|
|
||||||
List<String> getSavedUserLogins();
|
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
boolean isCommandSuggestions();
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
boolean isPassthroughMotd();
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
boolean isPassthroughPlayerCounts();
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
boolean isLegacyPingPassthrough();
|
|
||||||
|
|
||||||
int getPingPassthroughInterval();
|
|
||||||
|
|
||||||
boolean isForwardPlayerPing();
|
|
||||||
|
|
||||||
int getMaxPlayers();
|
|
||||||
|
|
||||||
boolean isDebugMode();
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
boolean isAllowThirdPartyCapes();
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
boolean isAllowThirdPartyEars();
|
|
||||||
|
|
||||||
String getShowCooldown();
|
|
||||||
|
|
||||||
boolean isShowCoordinates();
|
|
||||||
|
|
||||||
boolean isDisableBedrockScaffolding();
|
|
||||||
|
|
||||||
EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround();
|
|
||||||
|
|
||||||
String getDefaultLocale();
|
|
||||||
|
|
||||||
Path getFloodgateKeyPath();
|
|
||||||
|
|
||||||
boolean isAddNonBedrockItems();
|
|
||||||
|
|
||||||
boolean isAboveBedrockNetherBuilding();
|
|
||||||
|
|
||||||
boolean isForceResourcePacks();
|
|
||||||
|
|
||||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
|
||||||
boolean isXboxAchievementsEnabled();
|
|
||||||
|
|
||||||
int getCacheImages();
|
|
||||||
|
|
||||||
boolean isAllowCustomSkulls();
|
|
||||||
|
|
||||||
int getMaxVisibleCustomSkulls();
|
|
||||||
|
|
||||||
int getCustomSkullRenderDistance();
|
|
||||||
|
|
||||||
boolean isLogPlayerIpAddresses();
|
|
||||||
|
|
||||||
boolean isNotifyOnNewBedrockUpdate();
|
|
||||||
|
|
||||||
String getUnusableSpaceBlock();
|
|
||||||
|
|
||||||
IMetricsInfo getMetrics();
|
|
||||||
|
|
||||||
int getPendingAuthenticationTimeout();
|
|
||||||
|
|
||||||
boolean isAutoconfiguredRemote();
|
|
||||||
|
|
||||||
interface IBedrockConfiguration extends BedrockListener {
|
|
||||||
void setAddress(String address);
|
|
||||||
|
|
||||||
void setPort(int port);
|
|
||||||
|
|
||||||
void setBroadcastPort(int broadcastPort);
|
|
||||||
|
|
||||||
boolean isCloneRemotePort();
|
|
||||||
|
|
||||||
int getCompressionLevel();
|
|
||||||
|
|
||||||
boolean isEnableProxyProtocol();
|
|
||||||
|
|
||||||
List<String> getProxyProtocolWhitelistedIPs();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Unmodifiable list of {@link CIDRMatcher}s from {@link #getProxyProtocolWhitelistedIPs()}
|
|
||||||
*/
|
|
||||||
List<CIDRMatcher> getWhitelistedIPsMatchers();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRemoteConfiguration extends RemoteServer {
|
|
||||||
|
|
||||||
void setAddress(String address);
|
|
||||||
|
|
||||||
void setPort(int port);
|
|
||||||
|
|
||||||
boolean isUseProxyProtocol();
|
|
||||||
|
|
||||||
boolean isForwardHost();
|
|
||||||
|
|
||||||
default String minecraftVersion() {
|
|
||||||
return GameProtocol.getJavaMinecraftVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
default int protocolVersion() {
|
|
||||||
return GameProtocol.getJavaProtocolVersion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAuthType(AuthType authType);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IMetricsInfo {
|
|
||||||
|
|
||||||
boolean isEnabled();
|
|
||||||
|
|
||||||
String getUniqueId();
|
|
||||||
}
|
|
||||||
|
|
||||||
int getScoreboardPacketThreshold();
|
|
||||||
|
|
||||||
// if u have offline mode enabled pls be safe
|
|
||||||
boolean isEnableProxyConnections();
|
|
||||||
|
|
||||||
int getMtu();
|
|
||||||
|
|
||||||
boolean isUseDirectConnection();
|
|
||||||
|
|
||||||
boolean isDisableCompression();
|
|
||||||
|
|
||||||
int getConfigVersion();
|
|
||||||
|
|
||||||
static void checkGeyserConfiguration(GeyserConfiguration geyserConfig, GeyserLogger geyserLogger) {
|
|
||||||
if (geyserConfig.getConfigVersion() < CURRENT_CONFIG_VERSION) {
|
|
||||||
geyserLogger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.outdated"));
|
|
||||||
} else if (geyserConfig.getConfigVersion() > CURRENT_CONFIG_VERSION) {
|
|
||||||
geyserLogger.warning(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.too_new"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,26 +25,20 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.configuration;
|
package org.geysermc.geyser.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
@ConfigSerializable
|
||||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
|
||||||
public class GeyserCustomSkullConfiguration {
|
public class GeyserCustomSkullConfiguration {
|
||||||
@JsonProperty("player-usernames")
|
|
||||||
private List<String> playerUsernames;
|
private List<String> playerUsernames;
|
||||||
|
|
||||||
@JsonProperty("player-uuids")
|
|
||||||
private List<String> playerUUIDs;
|
private List<String> playerUUIDs;
|
||||||
|
|
||||||
@JsonProperty("player-profiles")
|
|
||||||
private List<String> playerProfiles;
|
private List<String> playerProfiles;
|
||||||
|
|
||||||
@JsonProperty("skin-hashes")
|
|
||||||
private List<String> skinHashes;
|
private List<String> skinHashes;
|
||||||
|
|
||||||
public List<String> getPlayerUsernames() {
|
public List<String> getPlayerUsernames() {
|
||||||
@@ -62,4 +56,14 @@ public class GeyserCustomSkullConfiguration {
|
|||||||
public List<String> getPlayerSkinHashes() {
|
public List<String> getPlayerSkinHashes() {
|
||||||
return Objects.requireNonNullElse(skinHashes, Collections.emptyList());
|
return Objects.requireNonNullElse(skinHashes, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "GeyserCustomSkullConfiguration{" +
|
||||||
|
"playerUsernames=" + playerUsernames +
|
||||||
|
", playerUUIDs=" + playerUUIDs +
|
||||||
|
", playerProfiles=" + playerProfiles +
|
||||||
|
", skinHashes=" + skinHashes +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,366 +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.configuration;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
|
||||||
import org.geysermc.geyser.network.CIDRMatcher;
|
|
||||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
|
||||||
import org.geysermc.geyser.util.WebUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
@SuppressWarnings("FieldMayBeFinal") // Jackson requires that the fields are not final
|
|
||||||
public abstract class GeyserJacksonConfiguration implements GeyserConfiguration {
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
private boolean autoconfiguredRemote = false;
|
|
||||||
|
|
||||||
private BedrockConfiguration bedrock = new BedrockConfiguration();
|
|
||||||
private RemoteConfiguration remote = new RemoteConfiguration();
|
|
||||||
|
|
||||||
@JsonProperty("saved-user-logins")
|
|
||||||
private List<String> savedUserLogins = Collections.emptyList();
|
|
||||||
|
|
||||||
@JsonProperty("floodgate-key-file")
|
|
||||||
private String floodgateKeyFile = "key.pem";
|
|
||||||
|
|
||||||
public abstract Path getFloodgateKeyPath();
|
|
||||||
|
|
||||||
@JsonProperty("command-suggestions")
|
|
||||||
private boolean commandSuggestions = true;
|
|
||||||
|
|
||||||
@JsonProperty("passthrough-motd")
|
|
||||||
private boolean isPassthroughMotd = false;
|
|
||||||
|
|
||||||
@JsonProperty("passthrough-player-counts")
|
|
||||||
private boolean isPassthroughPlayerCounts = false;
|
|
||||||
|
|
||||||
@JsonProperty("legacy-ping-passthrough")
|
|
||||||
private boolean isLegacyPingPassthrough = false;
|
|
||||||
|
|
||||||
@JsonProperty("ping-passthrough-interval")
|
|
||||||
private int pingPassthroughInterval = 3;
|
|
||||||
|
|
||||||
@JsonProperty("forward-player-ping")
|
|
||||||
private boolean forwardPlayerPing = false;
|
|
||||||
|
|
||||||
@JsonProperty("max-players")
|
|
||||||
private int maxPlayers = 100;
|
|
||||||
|
|
||||||
@JsonProperty("debug-mode")
|
|
||||||
private boolean debugMode = false;
|
|
||||||
|
|
||||||
@JsonProperty("allow-third-party-capes")
|
|
||||||
private boolean allowThirdPartyCapes = false;
|
|
||||||
|
|
||||||
@JsonProperty("show-cooldown")
|
|
||||||
private String showCooldown = "title";
|
|
||||||
|
|
||||||
@JsonProperty("show-coordinates")
|
|
||||||
private boolean showCoordinates = true;
|
|
||||||
|
|
||||||
@JsonProperty("disable-bedrock-scaffolding")
|
|
||||||
private boolean disableBedrockScaffolding = false;
|
|
||||||
|
|
||||||
@JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class)
|
|
||||||
@JsonProperty("emote-offhand-workaround")
|
|
||||||
private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED;
|
|
||||||
|
|
||||||
@JsonProperty("allow-third-party-ears")
|
|
||||||
private boolean allowThirdPartyEars = false;
|
|
||||||
|
|
||||||
@JsonProperty("default-locale")
|
|
||||||
private String defaultLocale = null; // is null by default so system language takes priority
|
|
||||||
|
|
||||||
@JsonProperty("cache-images")
|
|
||||||
private int cacheImages = 0;
|
|
||||||
|
|
||||||
@JsonProperty("allow-custom-skulls")
|
|
||||||
private boolean allowCustomSkulls = true;
|
|
||||||
|
|
||||||
@JsonProperty("max-visible-custom-skulls")
|
|
||||||
private int maxVisibleCustomSkulls = 128;
|
|
||||||
|
|
||||||
@JsonProperty("custom-skull-render-distance")
|
|
||||||
private int customSkullRenderDistance = 32;
|
|
||||||
|
|
||||||
@JsonProperty("add-non-bedrock-items")
|
|
||||||
private boolean addNonBedrockItems = true;
|
|
||||||
|
|
||||||
@JsonProperty("above-bedrock-nether-building")
|
|
||||||
private boolean aboveBedrockNetherBuilding = false;
|
|
||||||
|
|
||||||
@JsonProperty("force-resource-packs")
|
|
||||||
private boolean forceResourcePacks = true;
|
|
||||||
|
|
||||||
@JsonProperty("xbox-achievements-enabled")
|
|
||||||
private boolean xboxAchievementsEnabled = false;
|
|
||||||
|
|
||||||
@JsonProperty("log-player-ip-addresses")
|
|
||||||
private boolean logPlayerIpAddresses = true;
|
|
||||||
|
|
||||||
@JsonProperty("notify-on-new-bedrock-update")
|
|
||||||
private boolean notifyOnNewBedrockUpdate = true;
|
|
||||||
|
|
||||||
@JsonProperty("unusable-space-block")
|
|
||||||
private String unusableSpaceBlock = "minecraft:barrier";
|
|
||||||
|
|
||||||
private MetricsInfo metrics = new MetricsInfo();
|
|
||||||
|
|
||||||
@JsonProperty("pending-authentication-timeout")
|
|
||||||
private int pendingAuthenticationTimeout = 120;
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public static class BedrockConfiguration implements IBedrockConfiguration {
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
|
||||||
@JsonProperty("address")
|
|
||||||
@Setter
|
|
||||||
private String address = "0.0.0.0";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull String address() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@JsonProperty("port")
|
|
||||||
private int port = 19132;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int port() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@JsonProperty("broadcast-port")
|
|
||||||
private int broadcastPort = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int broadcastPort() {
|
|
||||||
return broadcastPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonProperty("clone-remote-port")
|
|
||||||
private boolean cloneRemotePort = false;
|
|
||||||
|
|
||||||
@JsonProperty("motd1")
|
|
||||||
private String motd1 = "GeyserMC";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String primaryMotd() {
|
|
||||||
return motd1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("motd2")
|
|
||||||
private String motd2 = "Geyser";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String secondaryMotd() {
|
|
||||||
return motd2;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("server-name")
|
|
||||||
private String serverName = GeyserImpl.NAME;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String serverName() {
|
|
||||||
return serverName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("compression-level")
|
|
||||||
private int compressionLevel = 6;
|
|
||||||
|
|
||||||
public int getCompressionLevel() {
|
|
||||||
return Math.max(-1, Math.min(compressionLevel, 9));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonProperty("enable-proxy-protocol")
|
|
||||||
private boolean enableProxyProtocol = false;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonProperty("proxy-protocol-whitelisted-ips")
|
|
||||||
private List<String> proxyProtocolWhitelistedIPs = Collections.emptyList();
|
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
private List<CIDRMatcher> whitelistedIPsMatchers = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
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: 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public static class RemoteConfiguration implements IRemoteConfiguration {
|
|
||||||
@Setter
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
|
||||||
@JsonProperty("address")
|
|
||||||
private String address = "auto";
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String address() {
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonDeserialize(using = PortDeserializer.class)
|
|
||||||
@Setter
|
|
||||||
@JsonProperty("port")
|
|
||||||
private int port = 25565;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int port() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Setter
|
|
||||||
@JsonDeserialize(using = AuthTypeDeserializer.class)
|
|
||||||
@JsonProperty("auth-type")
|
|
||||||
private AuthType authType = AuthType.ONLINE;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public @NonNull AuthType authType() {
|
|
||||||
return authType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean resolveSrv() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonProperty("use-proxy-protocol")
|
|
||||||
private boolean useProxyProtocol = false;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonProperty("forward-hostname")
|
|
||||||
private boolean forwardHost = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public static class MetricsInfo implements IMetricsInfo {
|
|
||||||
private boolean enabled = true;
|
|
||||||
|
|
||||||
@JsonDeserialize(using = MetricsIdDeserializer.class)
|
|
||||||
@JsonProperty("uuid")
|
|
||||||
private String uniqueId = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
private static class MetricsIdDeserializer extends JsonDeserializer<String> {
|
|
||||||
@Override
|
|
||||||
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
|
||||||
String uuid = p.getValueAsString();
|
|
||||||
if ("generateduuid".equals(uuid)) {
|
|
||||||
// Compensate for configs not copied from the jar
|
|
||||||
return UUID.randomUUID().toString();
|
|
||||||
}
|
|
||||||
return uuid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@JsonProperty("scoreboard-packet-threshold")
|
|
||||||
private int scoreboardPacketThreshold = 10;
|
|
||||||
|
|
||||||
@JsonProperty("enable-proxy-connections")
|
|
||||||
private boolean enableProxyConnections = false;
|
|
||||||
|
|
||||||
@JsonProperty("mtu")
|
|
||||||
private int mtu = 1400;
|
|
||||||
|
|
||||||
@JsonProperty("use-direct-connection")
|
|
||||||
private boolean useDirectConnection = true;
|
|
||||||
|
|
||||||
@JsonProperty("disable-compression")
|
|
||||||
private boolean isDisableCompression = true;
|
|
||||||
|
|
||||||
@JsonProperty("config-version")
|
|
||||||
private int configVersion = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure that the port deserializes in the config as a number no matter what.
|
|
||||||
*/
|
|
||||||
protected static class PortDeserializer extends JsonDeserializer<Integer> {
|
|
||||||
@Override
|
|
||||||
public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
|
||||||
String value = p.getValueAsString();
|
|
||||||
try {
|
|
||||||
return Integer.parseInt(value);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
System.err.println(GeyserLocale.getLocaleStringLog("geyser.bootstrap.config.invalid_port"));
|
|
||||||
return 25565;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class AuthTypeDeserializer extends JsonDeserializer<AuthType> {
|
|
||||||
@Override
|
|
||||||
public AuthType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
|
||||||
return AuthType.getByName(p.getValueAsString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.spongepowered.configurate.interfaces.meta.Exclude;
|
||||||
|
import org.spongepowered.configurate.interfaces.meta.Field;
|
||||||
|
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||||
|
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
public interface GeyserPluginConfig extends GeyserConfig {
|
||||||
|
@Override
|
||||||
|
IntegratedJavaConfig java();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
PluginMotdConfig motd();
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface PluginMotdConfig extends MotdConfig {
|
||||||
|
@Comment("""
|
||||||
|
How often to ping the Java server to refresh MOTD and player count, in seconds.
|
||||||
|
Only relevant if integrated-ping-passthrough is disabled.""")
|
||||||
|
@Override
|
||||||
|
int pingPassthroughInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface IntegratedJavaConfig extends JavaConfig {
|
||||||
|
@Override
|
||||||
|
@Field
|
||||||
|
String address();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void address(String address);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Field
|
||||||
|
int port();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void port(int port);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Exclude
|
||||||
|
default boolean forwardHostname() {
|
||||||
|
return true; // No need to worry about suspicious behavior flagging the server.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* 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 org.geysermc.geyser.text.AsteriskSerializer;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for any instance where the Java server is detached from Geyser.
|
||||||
|
*/
|
||||||
|
@ConfigSerializable
|
||||||
|
public interface GeyserRemoteConfig extends GeyserConfig {
|
||||||
|
@Override
|
||||||
|
RemoteConfig java();
|
||||||
|
|
||||||
|
@ConfigSerializable
|
||||||
|
interface RemoteConfig extends JavaConfig {
|
||||||
|
@Override
|
||||||
|
@Comment("The IP address of the Java Edition server.")
|
||||||
|
@DefaultString("127.0.0.1")
|
||||||
|
@AsteriskSerializer.Asterisk
|
||||||
|
String address();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Comment("The port of the Java Edition server.")
|
||||||
|
@DefaultNumeric(25565)
|
||||||
|
@NumericRange(from = 0, to = 65535)
|
||||||
|
int port();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Comment("""
|
||||||
|
Whether to forward the hostname that the Bedrock client used to connect over to the Java server.
|
||||||
|
This is designed to be used for forced hosts on proxies.""")
|
||||||
|
boolean forwardHostname();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,26 +25,17 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.configuration;
|
package org.geysermc.geyser.configuration;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import java.lang.annotation.ElementType;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import java.lang.annotation.Retention;
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
import java.io.IOException;
|
/**
|
||||||
|
* Add to a config value to indicate this field is only for plugin versions of Geyser,
|
||||||
public enum EmoteOffhandWorkaroundOption {
|
* or vice-versa.
|
||||||
NO_EMOTES,
|
*/
|
||||||
EMOTES_AND_OFFHAND,
|
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
|
||||||
DISABLED;
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface PluginSpecific {
|
||||||
public static class Deserializer extends JsonDeserializer<EmoteOffhandWorkaroundOption> {
|
boolean forPlugin() default true;
|
||||||
@Override
|
|
||||||
public EmoteOffhandWorkaroundOption deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
|
||||||
String value = p.getValueAsString();
|
|
||||||
return switch (value) {
|
|
||||||
case "no-emotes" -> NO_EMOTES;
|
|
||||||
case "emotes-and-offhand" -> EMOTES_AND_OFFHAND;
|
|
||||||
default -> DISABLED;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.dump;
|
package org.geysermc.geyser.dump;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
@@ -38,7 +39,7 @@ public class BootstrapDumpInfo {
|
|||||||
private final PlatformType platform;
|
private final PlatformType platform;
|
||||||
|
|
||||||
public BootstrapDumpInfo() {
|
public BootstrapDumpInfo() {
|
||||||
this.platform = GeyserImpl.getInstance().getPlatformType();
|
this.platform = GeyserImpl.getInstance().platformType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -55,7 +56,7 @@ public class BootstrapDumpInfo {
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public static class ListenerInfo {
|
public static class ListenerInfo {
|
||||||
|
|
||||||
@AsteriskSerializer.Asterisk(isIp = true)
|
@JsonAdapter(value = AsteriskSerializer.class)
|
||||||
public String ip;
|
public String ip;
|
||||||
public int port;
|
public int port;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,28 +25,39 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.dump;
|
package org.geysermc.geyser.dump;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.floodgate.util.DeviceOs;
|
import org.geysermc.floodgate.util.DeviceOs;
|
||||||
import org.geysermc.floodgate.util.FloodgateInfoHolder;
|
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
import org.geysermc.geyser.api.extension.Extension;
|
||||||
import org.geysermc.geyser.api.util.MinecraftVersion;
|
import org.geysermc.geyser.api.util.MinecraftVersion;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.pack.ResourcePackHolder;
|
||||||
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.AsteriskSerializer;
|
import org.geysermc.geyser.text.AsteriskSerializer;
|
||||||
import org.geysermc.geyser.util.CpuUtils;
|
import org.geysermc.geyser.util.CpuUtils;
|
||||||
import org.geysermc.geyser.util.FileUtils;
|
import org.geysermc.geyser.util.FileUtils;
|
||||||
import org.geysermc.geyser.util.WebUtils;
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurationNode;
|
||||||
|
import org.spongepowered.configurate.ConfigurationOptions;
|
||||||
|
import org.spongepowered.configurate.interfaces.InterfaceDefaultOptions;
|
||||||
|
import org.spongepowered.configurate.serialize.SerializationException;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -57,12 +68,15 @@ import java.net.Socket;
|
|||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class DumpInfo {
|
public class DumpInfo {
|
||||||
@JsonIgnore
|
|
||||||
private static final long MEGABYTE = 1024L * 1024L;
|
private static final long MEGABYTE = 1024L * 1024L;
|
||||||
|
|
||||||
private final DumpInfo.VersionInfo versionInfo;
|
private final DumpInfo.VersionInfo versionInfo;
|
||||||
@@ -71,16 +85,17 @@ public class DumpInfo {
|
|||||||
private final Locale systemLocale;
|
private final Locale systemLocale;
|
||||||
private final String systemEncoding;
|
private final String systemEncoding;
|
||||||
private final GitInfo gitInfo;
|
private final GitInfo gitInfo;
|
||||||
private final GeyserConfiguration config;
|
private Object config;
|
||||||
private final Floodgate floodgate;
|
|
||||||
private final Object2IntMap<DeviceOs> userPlatforms;
|
private final Object2IntMap<DeviceOs> userPlatforms;
|
||||||
private final int connectionAttempts;
|
private final int connectionAttempts;
|
||||||
private final HashInfo hashInfo;
|
private final String hash;
|
||||||
private final RamInfo ramInfo;
|
private final RamInfo ramInfo;
|
||||||
private LogsInfo logsInfo;
|
private LogsInfo logsInfo;
|
||||||
private final BootstrapDumpInfo bootstrapInfo;
|
private final BootstrapDumpInfo bootstrapInfo;
|
||||||
private final FlagsInfo flagsInfo;
|
private final FlagsInfo flagsInfo;
|
||||||
private final List<ExtensionInfo> extensionInfo;
|
private final List<ExtensionInfo> extensionInfo;
|
||||||
|
private final List<PackInfo> packInfo;
|
||||||
|
private final MappingInfo mappingInfo;
|
||||||
|
|
||||||
public DumpInfo(GeyserImpl geyser, boolean addLog) {
|
public DumpInfo(GeyserImpl geyser, boolean addLog) {
|
||||||
this.versionInfo = new VersionInfo();
|
this.versionInfo = new VersionInfo();
|
||||||
@@ -92,27 +107,35 @@ public class DumpInfo {
|
|||||||
|
|
||||||
this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY);
|
this.gitInfo = new GitInfo(GeyserImpl.BUILD_NUMBER, GeyserImpl.COMMIT.substring(0, 7), GeyserImpl.COMMIT, GeyserImpl.BRANCH, GeyserImpl.REPOSITORY);
|
||||||
|
|
||||||
this.config = geyser.getConfig();
|
|
||||||
this.floodgate = new Floodgate();
|
|
||||||
|
|
||||||
String md5Hash = "unknown";
|
|
||||||
String sha256Hash = "unknown";
|
|
||||||
try {
|
try {
|
||||||
// https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
|
// Workaround for JsonAdapter not being allowed on methods
|
||||||
// https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java
|
ConfigurationOptions options = InterfaceDefaultOptions.addTo(ConfigurationOptions.defaults(), builder ->
|
||||||
File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
builder.addProcessor(AsteriskSerializer.Asterisk.class, String.class, AsteriskSerializer.CONFIGURATE_SERIALIZER))
|
||||||
ByteSource byteSource = Files.asByteSource(file);
|
.shouldCopyDefaults(false);
|
||||||
// Jenkins uses MD5 for its hash - TODO remove
|
|
||||||
//noinspection UnstableApiUsage,deprecation
|
ConfigurationNode configNode = CommentedConfigurationNode.root(options);
|
||||||
md5Hash = byteSource.hash(Hashing.md5()).toString();
|
configNode.set(geyser.config());
|
||||||
//noinspection UnstableApiUsage
|
this.config = toGson(configNode);
|
||||||
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
|
} catch (SerializationException e) {
|
||||||
} catch (Exception e) {
|
e.printStackTrace();
|
||||||
if (this.config.isDebugMode()) {
|
if (geyser.config().debugMode()) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.hashInfo = new HashInfo(md5Hash, sha256Hash);
|
|
||||||
|
String sha256Hash = "unknown";
|
||||||
|
try {
|
||||||
|
// https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
|
||||||
|
File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||||
|
ByteSource byteSource = Files.asByteSource(file);
|
||||||
|
//noinspection UnstableApiUsage
|
||||||
|
sha256Hash = byteSource.hash(Hashing.sha256()).toString();
|
||||||
|
} catch (Exception e) {
|
||||||
|
if (geyser.config().debugMode()) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.hash = sha256Hash;
|
||||||
|
|
||||||
this.ramInfo = new RamInfo();
|
this.ramInfo = new RamInfo();
|
||||||
|
|
||||||
@@ -140,6 +163,44 @@ public class DumpInfo {
|
|||||||
for (Extension extension : GeyserApi.api().extensionManager().extensions()) {
|
for (Extension extension : GeyserApi.api().extensionManager().extensions()) {
|
||||||
this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().apiVersion(), extension.description().main(), extension.description().authors()));
|
this.extensionInfo.add(new ExtensionInfo(extension.isEnabled(), extension.name(), extension.description().version(), extension.description().apiVersion(), extension.description().main(), extension.description().authors()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.packInfo = Registries.RESOURCE_PACKS.get().values().stream()
|
||||||
|
.map(PackInfo::new)
|
||||||
|
.toList();
|
||||||
|
this.mappingInfo = new MappingInfo(BlockRegistries.CUSTOM_BLOCKS.get().length,
|
||||||
|
BlockRegistries.CUSTOM_SKULLS.get().size(),
|
||||||
|
Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_PROTOCOL).getCustomIdMappings().size()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonElement toGson(ConfigurationNode node) {
|
||||||
|
if (node.isMap()) {
|
||||||
|
JsonObject object = new JsonObject();
|
||||||
|
node.childrenMap().forEach((key, value) -> {
|
||||||
|
JsonElement json = toGson(value);
|
||||||
|
object.add(key.toString(), json);
|
||||||
|
});
|
||||||
|
return object;
|
||||||
|
} else if (node.isList()) {
|
||||||
|
JsonArray array = new JsonArray();
|
||||||
|
node.childrenList().forEach(childNode -> array.add(toGson(childNode)));
|
||||||
|
return array;
|
||||||
|
} else {
|
||||||
|
return convertRawScalar(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonElement convertRawScalar(ConfigurationNode node) {
|
||||||
|
final @Nullable Object value = node.rawScalar();
|
||||||
|
if (value == null) {
|
||||||
|
return JsonNull.INSTANCE;
|
||||||
|
} else if (value instanceof Number n) {
|
||||||
|
return new JsonPrimitive(n);
|
||||||
|
} else if (value instanceof Boolean b) {
|
||||||
|
return new JsonPrimitive(b);
|
||||||
|
} else {
|
||||||
|
return new JsonPrimitive(value.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
@@ -233,17 +294,6 @@ public class DumpInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Getter
|
|
||||||
public static class Floodgate {
|
|
||||||
private final Properties gitInfo;
|
|
||||||
private final Object config;
|
|
||||||
|
|
||||||
Floodgate() {
|
|
||||||
this.gitInfo = FloodgateInfoHolder.getGitProperties();
|
|
||||||
this.config = FloodgateInfoHolder.getConfig();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public static class LogsInfo {
|
public static class LogsInfo {
|
||||||
private String link;
|
private String link;
|
||||||
@@ -253,16 +303,13 @@ public class DumpInfo {
|
|||||||
Map<String, String> fields = new HashMap<>();
|
Map<String, String> fields = new HashMap<>();
|
||||||
fields.put("content", FileUtils.readAllLines(geyser.getBootstrap().getLogsPath()).collect(Collectors.joining("\n")));
|
fields.put("content", FileUtils.readAllLines(geyser.getBootstrap().getLogsPath()).collect(Collectors.joining("\n")));
|
||||||
|
|
||||||
JsonNode logData = GeyserImpl.JSON_MAPPER.readTree(WebUtils.postForm("https://api.mclo.gs/1/log", fields));
|
JsonObject logData = new JsonParser().parse(WebUtils.postForm("https://api.mclo.gs/1/log", fields)).getAsJsonObject();
|
||||||
|
|
||||||
this.link = logData.get("url").textValue();
|
this.link = logData.get("url").getAsString();
|
||||||
} catch (IOException ignored) { }
|
} catch (IOException ignored) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record HashInfo(String md5Hash, String sha256Hash) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public record RamInfo(long free, long total, long max) {
|
public record RamInfo(long free, long total, long max) {
|
||||||
public RamInfo() {
|
public RamInfo() {
|
||||||
this(Runtime.getRuntime().freeMemory() / MEGABYTE,
|
this(Runtime.getRuntime().freeMemory() / MEGABYTE,
|
||||||
@@ -283,7 +330,17 @@ public class DumpInfo {
|
|||||||
public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) {
|
public record ExtensionInfo(boolean enabled, String name, String version, String apiVersion, String main, List<String> authors) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GitInfo(String buildNumber, @JsonProperty("git.commit.id.abbrev") String commitHashAbbrev, @JsonProperty("git.commit.id") String commitHash,
|
public record GitInfo(String buildNumber, @SerializedName("git.commit.id.abbrev") String commitHashAbbrev, @SerializedName("git.commit.id") String commitHash,
|
||||||
@JsonProperty("git.branch") String branchName, @JsonProperty("git.remote.origin.url") String originUrl) {
|
@SerializedName("git.branch") String branchName, @SerializedName("git.remote.origin.url") String originUrl) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public record PackInfo(String name, String type) {
|
||||||
|
|
||||||
|
public PackInfo(ResourcePackHolder holder) {
|
||||||
|
this(holder.pack().manifest().header().name(), holder.codec().getClass().getSimpleName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record MappingInfo(int customBlocks, int customSkulls, int customItems) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public record EntityDefinition<T extends Entity>(EntityFactory<T> factory, Entit
|
|||||||
|
|
||||||
if (translator.acceptedType() != metadata.getType()) {
|
if (translator.acceptedType() != metadata.getType()) {
|
||||||
GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
|
GeyserImpl.getInstance().getLogger().warning("Metadata ID " + metadata.getId() + " was received with type " + metadata.getType() + " but we expected " + translator.acceptedType() + " for " + entity.getDefinition().entityType());
|
||||||
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
if (GeyserImpl.getInstance().config().debugMode()) {
|
||||||
GeyserImpl.getInstance().getLogger().debug(metadata.toString());
|
GeyserImpl.getInstance().getLogger().debug(metadata.toString());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -220,7 +220,7 @@ public class Entity implements GeyserEntity {
|
|||||||
|
|
||||||
flagsDirty = false;
|
flagsDirty = false;
|
||||||
|
|
||||||
if (session.getGeyser().getConfig().isDebugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
|
if (session.getGeyser().config().debugMode() && PRINT_ENTITY_SPAWN_DEBUG) {
|
||||||
EntityType type = definition.entityType();
|
EntityType type = definition.entityType();
|
||||||
String name = type != null ? type.name() : getClass().getSimpleName();
|
String name = type != null ? type.name() : getClass().getSimpleName();
|
||||||
session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
session.getGeyser().getLogger().debug("Spawned entity " + name + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.geysermc.geyser.api.pack.exception.ResourcePackException;
|
|||||||
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
import org.geysermc.geyser.api.pack.option.ResourcePackOption;
|
||||||
import org.geysermc.geyser.pack.GeyserResourcePack;
|
import org.geysermc.geyser.pack.GeyserResourcePack;
|
||||||
import org.geysermc.geyser.pack.ResourcePackHolder;
|
import org.geysermc.geyser.pack.ResourcePackHolder;
|
||||||
|
import org.geysermc.geyser.util.GeyserIntegratedPackUtil;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -42,11 +43,12 @@ import java.util.Objects;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class GeyserDefineResourcePacksEventImpl extends GeyserDefineResourcePacksEvent {
|
public class GeyserDefineResourcePacksEventImpl extends GeyserDefineResourcePacksEvent implements GeyserIntegratedPackUtil {
|
||||||
private final Map<UUID, ResourcePackHolder> packs;
|
private final Map<UUID, ResourcePackHolder> packs;
|
||||||
|
|
||||||
public GeyserDefineResourcePacksEventImpl(Map<UUID, ResourcePackHolder> packMap) {
|
public GeyserDefineResourcePacksEventImpl(Map<UUID, ResourcePackHolder> packMap) {
|
||||||
this.packs = packMap;
|
this.packs = packMap;
|
||||||
|
registerGeyserPack(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,6 +63,8 @@ public class GeyserDefineResourcePacksEventImpl extends GeyserDefineResourcePack
|
|||||||
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preProcessPack(pack);
|
||||||
|
|
||||||
UUID uuid = resourcePack.uuid();
|
UUID uuid = resourcePack.uuid();
|
||||||
if (packs.containsKey(uuid)) {
|
if (packs.containsKey(uuid)) {
|
||||||
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
||||||
@@ -120,4 +124,14 @@ public class GeyserDefineResourcePacksEventImpl extends GeyserDefineResourcePack
|
|||||||
|
|
||||||
holder.optionHolder().validateAndAdd(holder.pack(), options);
|
holder.optionHolder().validateAndAdd(holder.pack(), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterIntegratedPack() {
|
||||||
|
unregister(INTEGRATED_PACK_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean integratedPackRegistered() {
|
||||||
|
return packs.containsKey(INTEGRATED_PACK_UUID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import org.geysermc.geyser.pack.ResourcePackHolder;
|
|||||||
import org.geysermc.geyser.pack.option.OptionHolder;
|
import org.geysermc.geyser.pack.option.OptionHolder;
|
||||||
import org.geysermc.geyser.registry.Registries;
|
import org.geysermc.geyser.registry.Registries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.GeyserIntegratedPackUtil;
|
||||||
|
|
||||||
import java.util.AbstractMap;
|
import java.util.AbstractMap;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -55,7 +56,7 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent {
|
public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksEvent implements GeyserIntegratedPackUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The packs for this Session. A {@link ResourcePackHolder} may contain resource pack options registered
|
* The packs for this Session. A {@link ResourcePackHolder} may contain resource pack options registered
|
||||||
@@ -103,6 +104,8 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
|
|||||||
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
throw new ResourcePackException(ResourcePackException.Cause.UNKNOWN_IMPLEMENTATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
preProcessPack(pack);
|
||||||
|
|
||||||
UUID uuid = resourcePack.uuid();
|
UUID uuid = resourcePack.uuid();
|
||||||
if (packs.containsKey(uuid)) {
|
if (packs.containsKey(uuid)) {
|
||||||
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
throw new ResourcePackException(ResourcePackException.Cause.DUPLICATE);
|
||||||
@@ -178,6 +181,16 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
|
|||||||
holder.validateAndAdd(pack, options);
|
holder.validateAndAdd(pack, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unregisterIntegratedPack() {
|
||||||
|
unregister(INTEGRATED_PACK_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean integratedPackRegistered() {
|
||||||
|
return packs.containsKey(INTEGRATED_PACK_UUID);
|
||||||
|
}
|
||||||
|
|
||||||
// Methods used internally for e.g. ordered packs, or resource pack entries
|
// Methods used internally for e.g. ordered packs, or resource pack entries
|
||||||
|
|
||||||
public List<ResourcePackStackPacket.Entry> orderedPacks() {
|
public List<ResourcePackStackPacket.Entry> orderedPacks() {
|
||||||
@@ -201,7 +214,14 @@ public class SessionLoadResourcePacksEventImpl extends SessionLoadResourcePacksE
|
|||||||
public List<ResourcePacksInfoPacket.Entry> infoPacketEntries() {
|
public List<ResourcePacksInfoPacket.Entry> infoPacketEntries() {
|
||||||
List<ResourcePacksInfoPacket.Entry> entries = new ArrayList<>();
|
List<ResourcePacksInfoPacket.Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
boolean anyCdn = packs.values().stream().anyMatch(holder -> holder.codec() instanceof UrlPackCodec);
|
||||||
|
boolean warned = false;
|
||||||
|
|
||||||
for (ResourcePackHolder holder : packs.values()) {
|
for (ResourcePackHolder holder : packs.values()) {
|
||||||
|
if (!warned && anyCdn && !(holder.codec() instanceof UrlPackCodec)) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("Mixing pack codecs will result in all UrlPackCodec delivered packs to fall back to non-cdn delivery!");
|
||||||
|
warned = true;
|
||||||
|
}
|
||||||
GeyserResourcePack pack = holder.pack();
|
GeyserResourcePack pack = holder.pack();
|
||||||
ResourcePackManifest.Header header = pack.manifest().header();
|
ResourcePackManifest.Header header = pack.manifest().header();
|
||||||
entries.add(new ResourcePacksInfoPacket.Entry(
|
entries.add(new ResourcePacksInfoPacket.Entry(
|
||||||
|
|||||||
@@ -460,7 +460,7 @@ public final class ClickPlan {
|
|||||||
} else if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) {
|
} else if (action.click.actionType == ContainerActionType.MOVE_TO_HOTBAR_SLOT) {
|
||||||
stateIdIncrements = 1;
|
stateIdIncrements = 1;
|
||||||
} else {
|
} else {
|
||||||
if (session.getGeyser().getConfig().isDebugMode()) {
|
if (session.getGeyser().config().debugMode()) {
|
||||||
session.getGeyser().getLogger().debug("Not sure how to handle state ID hack in crafting table: " + plan);
|
session.getGeyser().getLogger().debug("Not sure how to handle state ID hack in crafting table: " + plan);
|
||||||
}
|
}
|
||||||
stateIdIncrements = 1;
|
stateIdIncrements = 1;
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
super(geyser, session);
|
super(geyser, session);
|
||||||
|
|
||||||
ZlibCompression compression = new ZlibCompression(Zlib.RAW);
|
ZlibCompression compression = new ZlibCompression(Zlib.RAW);
|
||||||
compression.setLevel(this.geyser.getConfig().getBedrock().getCompressionLevel());
|
compression.setLevel(this.geyser.config().advanced().bedrock().compressionLevel());
|
||||||
this.compressionStrategy = new SimpleCompressionStrategy(compression);
|
this.compressionStrategy = new SimpleCompressionStrategy(compression);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,14 +236,17 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
// Can happen if an error occurs in the resource pack event; that'll disconnect the player
|
// Can happen if an error occurs in the resource pack event; that'll disconnect the player
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
}
|
}
|
||||||
|
session.integratedPackActive(resourcePackLoadEvent.isIntegratedPackActive());
|
||||||
|
|
||||||
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
|
||||||
resourcePacksInfo.getResourcePackInfos().addAll(this.resourcePackLoadEvent.infoPacketEntries());
|
resourcePacksInfo.getResourcePackInfos().addAll(this.resourcePackLoadEvent.infoPacketEntries());
|
||||||
resourcePacksInfo.setVibrantVisualsForceDisabled(!session.isAllowVibrantVisuals());
|
resourcePacksInfo.setVibrantVisualsForceDisabled(!session.isAllowVibrantVisuals());
|
||||||
|
|
||||||
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().getConfig().isForceResourcePacks());
|
resourcePacksInfo.setForcedToAccept(GeyserImpl.getInstance().config().gameplay().forceResourcePacks() ||
|
||||||
|
resourcePackLoadEvent.isIntegratedPackActive());
|
||||||
resourcePacksInfo.setWorldTemplateId(UUID.randomUUID());
|
resourcePacksInfo.setWorldTemplateId(UUID.randomUUID());
|
||||||
resourcePacksInfo.setWorldTemplateVersion("*");
|
resourcePacksInfo.setWorldTemplateVersion("*");
|
||||||
|
|
||||||
session.sendUpstreamPacket(resourcePacksInfo);
|
session.sendUpstreamPacket(resourcePacksInfo);
|
||||||
|
|
||||||
GeyserLocale.loadGeyserLocale(session.locale());
|
GeyserLocale.loadGeyserLocale(session.locale());
|
||||||
@@ -264,7 +267,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
switch (packet.getStatus()) {
|
switch (packet.getStatus()) {
|
||||||
case COMPLETED -> {
|
case COMPLETED -> {
|
||||||
finishedResourcePackSending = true;
|
finishedResourcePackSending = true;
|
||||||
if (geyser.getConfig().getRemote().authType() != AuthType.ONLINE) {
|
if (geyser.config().java().authType() != AuthType.ONLINE) {
|
||||||
session.authenticate(session.getAuthData().name());
|
session.authenticate(session.getAuthData().name());
|
||||||
} else if (!couldLoginUserByName(session.getAuthData().name())) {
|
} else if (!couldLoginUserByName(session.getAuthData().name())) {
|
||||||
// We must spawn the white world
|
// We must spawn the white world
|
||||||
@@ -296,6 +299,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
|
|
||||||
session.sendUpstreamPacket(stackPacket);
|
session.sendUpstreamPacket(stackPacket);
|
||||||
}
|
}
|
||||||
|
case REFUSED -> session.disconnect("disconnectionScreen.resourcePack");
|
||||||
default -> {
|
default -> {
|
||||||
GeyserImpl.getInstance().getLogger().debug("received unknown status packet: " + packet);
|
GeyserImpl.getInstance().getLogger().debug("received unknown status packet: " + packet);
|
||||||
session.disconnect("disconnectionScreen.resourcePack");
|
session.disconnect("disconnectionScreen.resourcePack");
|
||||||
@@ -315,7 +319,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean couldLoginUserByName(String bedrockUsername) {
|
private boolean couldLoginUserByName(String bedrockUsername) {
|
||||||
if (geyser.getConfig().getSavedUserLogins().contains(bedrockUsername)) {
|
if (geyser.config().savedUserLogins().contains(bedrockUsername)) {
|
||||||
String authChain = geyser.authChainFor(bedrockUsername);
|
String authChain = geyser.authChainFor(bedrockUsername);
|
||||||
if (authChain != null) {
|
if (authChain != null) {
|
||||||
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name()));
|
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.auth.stored_credentials", session.getAuthData().name()));
|
||||||
@@ -425,7 +429,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||||||
// Also flushes packets
|
// Also flushes packets
|
||||||
// Avoids bursting slower / delayed clients
|
// Avoids bursting slower / delayed clients
|
||||||
session.sendUpstreamPacketImmediately(data);
|
session.sendUpstreamPacketImmediately(data);
|
||||||
GeyserImpl.getInstance().getScheduledThread().schedule(this::processNextChunk, PACKET_SEND_DELAY, TimeUnit.MILLISECONDS);
|
session.scheduleInEventLoop(this::processNextChunk, PACKET_SEND_DELAY, TimeUnit.MILLISECONDS);
|
||||||
} else {
|
} else {
|
||||||
session.sendUpstreamPacket(data);
|
session.sendUpstreamPacket(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public abstract class GeyserInjector {
|
|||||||
* @param bootstrap the bootstrap of the Geyser instance.
|
* @param bootstrap the bootstrap of the Geyser instance.
|
||||||
*/
|
*/
|
||||||
public void initializeLocalChannel(GeyserBootstrap bootstrap) {
|
public void initializeLocalChannel(GeyserBootstrap bootstrap) {
|
||||||
if (!bootstrap.getGeyserConfig().isUseDirectConnection()) {
|
if (!bootstrap.config().advanced().java().useDirectConnection()) {
|
||||||
bootstrap.getGeyserLogger().debug("Disabling direct injection!");
|
bootstrap.getGeyserLogger().debug("Disabling direct injection!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ import org.cloudburstmc.protocol.bedrock.BedrockPong;
|
|||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.api.event.connection.ConnectionRequestEvent;
|
import org.geysermc.geyser.api.event.connection.ConnectionRequestEvent;
|
||||||
import org.geysermc.geyser.command.defaults.ConnectionTestCommand;
|
import org.geysermc.geyser.command.defaults.ConnectionTestCommand;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.event.type.GeyserBedrockPingEventImpl;
|
import org.geysermc.geyser.event.type.GeyserBedrockPingEventImpl;
|
||||||
import org.geysermc.geyser.network.CIDRMatcher;
|
import org.geysermc.geyser.network.CIDRMatcher;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
@@ -60,10 +60,13 @@ import org.geysermc.geyser.ping.IGeyserPingPassthrough;
|
|||||||
import org.geysermc.geyser.skin.SkinProvider;
|
import org.geysermc.geyser.skin.SkinProvider;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
import org.geysermc.geyser.translator.text.MessageTranslator;
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
import org.geysermc.geyser.util.WebUtils;
|
||||||
import org.geysermc.mcprotocollib.network.helper.TransportHelper;
|
import org.geysermc.mcprotocollib.network.helper.TransportHelper;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -131,7 +134,7 @@ public final class GeyserServer {
|
|||||||
this.listenCount = 1;
|
this.listenCount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
if (this.geyser.config().advanced().bedrock().useHaproxyProtocol()) {
|
||||||
this.proxiedAddresses = ExpiringMap.builder()
|
this.proxiedAddresses = ExpiringMap.builder()
|
||||||
.expiration(30 + 1, TimeUnit.MINUTES)
|
.expiration(30 + 1, TimeUnit.MINUTES)
|
||||||
.expirationPolicy(ExpirationPolicy.ACCESSED).build();
|
.expirationPolicy(ExpirationPolicy.ACCESSED).build();
|
||||||
@@ -139,7 +142,7 @@ public final class GeyserServer {
|
|||||||
this.proxiedAddresses = null;
|
this.proxiedAddresses = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.broadcastPort = geyser.getConfig().getBedrock().broadcastPort();
|
this.broadcastPort = geyser.config().advanced().bedrock().broadcastPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> bind(InetSocketAddress address) {
|
public CompletableFuture<Void> bind(InetSocketAddress address) {
|
||||||
@@ -161,12 +164,12 @@ public final class GeyserServer {
|
|||||||
.addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this));
|
.addAfter(RakServerOfflineHandler.NAME, RakPingHandler.NAME, new RakPingHandler(this));
|
||||||
|
|
||||||
// Add proxy handler
|
// Add proxy handler
|
||||||
boolean isProxyProtocol = this.geyser.getConfig().getBedrock().isEnableProxyProtocol();
|
boolean isProxyProtocol = this.geyser.config().advanced().bedrock().useHaproxyProtocol();
|
||||||
if (isProxyProtocol) {
|
if (isProxyProtocol) {
|
||||||
channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler());
|
channel.pipeline().addFirst("proxy-protocol-decoder", new ProxyServerHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs().isEmpty();
|
boolean isWhitelistedProxyProtocol = isProxyProtocol && !this.geyser.config().advanced().bedrock().haproxyProtocolWhitelistedIps().isEmpty();
|
||||||
if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) {
|
if (Boolean.parseBoolean(System.getProperty("Geyser.RakRateLimitingDisabled", "false")) || isWhitelistedProxyProtocol) {
|
||||||
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
|
// We would already block any non-whitelisted IP addresses in onConnectionRequest so we can remove the rate limiter
|
||||||
channel.pipeline().remove(RakServerRateLimiter.NAME);
|
channel.pipeline().remove(RakServerRateLimiter.NAME);
|
||||||
@@ -200,7 +203,7 @@ public final class GeyserServer {
|
|||||||
|
|
||||||
@SuppressWarnings("Convert2MethodRef")
|
@SuppressWarnings("Convert2MethodRef")
|
||||||
private ServerBootstrap createBootstrap() {
|
private ServerBootstrap createBootstrap() {
|
||||||
if (this.geyser.getConfig().isDebugMode()) {
|
if (this.geyser.config().debugMode()) {
|
||||||
this.geyser.getLogger().debug("Transport type: " + TRANSPORT.method().name());
|
this.geyser.getLogger().debug("Transport type: " + TRANSPORT.method().name());
|
||||||
if (TRANSPORT.datagramChannelClass() == NioDatagramChannel.class) {
|
if (TRANSPORT.datagramChannelClass() == NioDatagramChannel.class) {
|
||||||
if (System.getProperties().contains("disableNativeEventLoop")) {
|
if (System.getProperties().contains("disableNativeEventLoop")) {
|
||||||
@@ -216,7 +219,7 @@ public final class GeyserServer {
|
|||||||
|
|
||||||
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
|
GeyserServerInitializer serverInitializer = new GeyserServerInitializer(this.geyser);
|
||||||
playerGroup = serverInitializer.getEventLoopGroup();
|
playerGroup = serverInitializer.getEventLoopGroup();
|
||||||
this.geyser.getLogger().debug("Setting MTU to " + this.geyser.getConfig().getMtu());
|
this.geyser.getLogger().debug("Setting MTU to " + this.geyser.config().advanced().bedrock().mtu());
|
||||||
|
|
||||||
int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT);
|
int rakPacketLimit = positivePropOrDefault("Geyser.RakPacketLimit", DEFAULT_PACKET_LIMIT);
|
||||||
this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit);
|
this.geyser.getLogger().debug("Setting RakNet packet limit to " + rakPacketLimit);
|
||||||
@@ -231,7 +234,7 @@ public final class GeyserServer {
|
|||||||
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannelClass()))
|
.channelFactory(RakChannelFactory.server(TRANSPORT.datagramChannelClass()))
|
||||||
.group(group, childGroup)
|
.group(group, childGroup)
|
||||||
.option(RakChannelOption.RAK_HANDLE_PING, true)
|
.option(RakChannelOption.RAK_HANDLE_PING, true)
|
||||||
.option(RakChannelOption.RAK_MAX_MTU, this.geyser.getConfig().getMtu())
|
.option(RakChannelOption.RAK_MAX_MTU, this.geyser.config().advanced().bedrock().mtu())
|
||||||
.option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit)
|
.option(RakChannelOption.RAK_PACKET_LIMIT, rakPacketLimit)
|
||||||
.option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
|
.option(RakChannelOption.RAK_GLOBAL_PACKET_LIMIT, rakGlobalPacketLimit)
|
||||||
.option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie)
|
.option(RakChannelOption.RAK_SEND_COOKIE, rakSendCookie)
|
||||||
@@ -239,10 +242,10 @@ public final class GeyserServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
|
public boolean onConnectionRequest(InetSocketAddress inetSocketAddress) {
|
||||||
List<String> allowedProxyIPs = geyser.getConfig().getBedrock().getProxyProtocolWhitelistedIPs();
|
List<String> allowedProxyIPs = geyser.config().advanced().bedrock().haproxyProtocolWhitelistedIps();
|
||||||
if (geyser.getConfig().getBedrock().isEnableProxyProtocol() && !allowedProxyIPs.isEmpty()) {
|
if (geyser.config().advanced().bedrock().useHaproxyProtocol() && !allowedProxyIPs.isEmpty()) {
|
||||||
boolean isWhitelistedIP = false;
|
boolean isWhitelistedIP = false;
|
||||||
for (CIDRMatcher matcher : geyser.getConfig().getBedrock().getWhitelistedIPsMatchers()) {
|
for (CIDRMatcher matcher : getWhitelistedIPsMatchers()) {
|
||||||
if (matcher.matches(inetSocketAddress.getAddress())) {
|
if (matcher.matches(inetSocketAddress.getAddress())) {
|
||||||
isWhitelistedIP = true;
|
isWhitelistedIP = true;
|
||||||
break;
|
break;
|
||||||
@@ -256,8 +259,8 @@ public final class GeyserServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String ip;
|
String ip;
|
||||||
if (geyser.getConfig().isLogPlayerIpAddresses()) {
|
if (geyser.config().logPlayerIpAddresses()) {
|
||||||
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
if (geyser.config().advanced().bedrock().useHaproxyProtocol()) {
|
||||||
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
|
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
|
||||||
} else {
|
} else {
|
||||||
ip = inetSocketAddress.toString();
|
ip = inetSocketAddress.toString();
|
||||||
@@ -283,10 +286,10 @@ public final class GeyserServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) {
|
public BedrockPong onQuery(Channel channel, InetSocketAddress inetSocketAddress) {
|
||||||
if (geyser.getConfig().isDebugMode() && PRINT_DEBUG_PINGS) {
|
if (geyser.config().debugMode() && PRINT_DEBUG_PINGS) {
|
||||||
String ip;
|
String ip;
|
||||||
if (geyser.getConfig().isLogPlayerIpAddresses()) {
|
if (geyser.config().logPlayerIpAddresses()) {
|
||||||
if (geyser.getConfig().getBedrock().isEnableProxyProtocol()) {
|
if (geyser.config().advanced().bedrock().useHaproxyProtocol()) {
|
||||||
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
|
ip = this.proxiedAddresses.getOrDefault(inetSocketAddress, inetSocketAddress).toString();
|
||||||
} else {
|
} else {
|
||||||
ip = inetSocketAddress.toString();
|
ip = inetSocketAddress.toString();
|
||||||
@@ -297,10 +300,10 @@ public final class GeyserServer {
|
|||||||
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
|
geyser.getLogger().debug(GeyserLocale.getLocaleStringLog("geyser.network.pinged", ip));
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserConfiguration config = geyser.getConfig();
|
GeyserConfig config = geyser.config();
|
||||||
|
|
||||||
GeyserPingInfo pingInfo = null;
|
GeyserPingInfo pingInfo = null;
|
||||||
if (config.isPassthroughMotd() || config.isPassthroughPlayerCounts()) {
|
if (config.motd().passthroughMotd() || config.motd().passthroughPlayerCounts()) {
|
||||||
IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough();
|
IGeyserPingPassthrough pingPassthrough = geyser.getBootstrap().getGeyserPingPassthrough();
|
||||||
if (pingPassthrough != null) {
|
if (pingPassthrough != null) {
|
||||||
pingInfo = pingPassthrough.getPingInformation(inetSocketAddress);
|
pingInfo = pingPassthrough.getPingInformation(inetSocketAddress);
|
||||||
@@ -317,25 +320,25 @@ public final class GeyserServer {
|
|||||||
.ipv6Port(this.broadcastPort)
|
.ipv6Port(this.broadcastPort)
|
||||||
.serverId(channel.config().getOption(RakChannelOption.RAK_GUID));
|
.serverId(channel.config().getOption(RakChannelOption.RAK_GUID));
|
||||||
|
|
||||||
if (config.isPassthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
|
if (config.motd().passthroughMotd() && pingInfo != null && pingInfo.getDescription() != null) {
|
||||||
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
String[] motd = MessageTranslator.convertMessageLenient(pingInfo.getDescription()).split("\n");
|
||||||
String mainMotd = (motd.length > 0) ? motd[0] : config.getBedrock().primaryMotd(); // First line of the motd.
|
String mainMotd = (motd.length > 0) ? motd[0] : config.motd().primaryMotd(); // First line of the motd.
|
||||||
String subMotd = (motd.length > 1) ? motd[1] : config.getBedrock().secondaryMotd(); // Second line of the motd if present, otherwise default.
|
String subMotd = (motd.length > 1) ? motd[1] : config.motd().secondaryMotd(); // Second line of the motd if present, otherwise default.
|
||||||
|
|
||||||
pong.motd(mainMotd.trim());
|
pong.motd(mainMotd.trim());
|
||||||
pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
|
pong.subMotd(subMotd.trim()); // Trimmed to shift it to the left, prevents the universe from collapsing on us just because we went 2 characters over the text box's limit.
|
||||||
} else {
|
} else {
|
||||||
pong.motd(config.getBedrock().primaryMotd());
|
pong.motd(config.motd().primaryMotd());
|
||||||
pong.subMotd(config.getBedrock().secondaryMotd());
|
pong.subMotd(config.motd().secondaryMotd());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placed here to prevent overriding values set in the ping event.
|
// Placed here to prevent overriding values set in the ping event.
|
||||||
if (config.isPassthroughPlayerCounts() && pingInfo != null) {
|
if (config.motd().passthroughPlayerCounts() && pingInfo != null) {
|
||||||
pong.playerCount(pingInfo.getPlayers().getOnline());
|
pong.playerCount(pingInfo.getPlayers().getOnline());
|
||||||
pong.maximumPlayerCount(pingInfo.getPlayers().getMax());
|
pong.maximumPlayerCount(pingInfo.getPlayers().getMax());
|
||||||
} else {
|
} else {
|
||||||
pong.playerCount(geyser.getSessionManager().getSessions().size());
|
pong.playerCount(geyser.getSessionManager().getSessions().size());
|
||||||
pong.maximumPlayerCount(config.getMaxPlayers());
|
pong.maximumPlayerCount(config.motd().maxPlayers());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress));
|
this.geyser.eventBus().fire(new GeyserBedrockPingEventImpl(pong, inetSocketAddress));
|
||||||
@@ -386,6 +389,35 @@ public final class GeyserServer {
|
|||||||
return pong;
|
return pong;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<CIDRMatcher> whitelistedIPsMatchers = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Unmodifiable list of {@link CIDRMatcher}s from {@link GeyserConfig.AdvancedBedrockConfig#haproxyProtocolWhitelistedIps()}
|
||||||
|
*/
|
||||||
|
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().advanced().bedrock().haproxyProtocolWhitelistedIps()) {
|
||||||
|
if (!ip.startsWith("http")) {
|
||||||
|
whitelistedCIDRs.add(ip);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebUtils.getLineStream(ip).forEach(whitelistedCIDRs::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.whitelistedIPsMatchers = matchers = whitelistedCIDRs.stream()
|
||||||
|
.map(CIDRMatcher::new)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Collections.unmodifiableList(matchers);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the throwable from the given supplier, or the throwable caught while calling the supplier.
|
* @return the throwable from the given supplier, or the throwable caught while calling the supplier.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -25,21 +25,23 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.pack;
|
package org.geysermc.geyser.pack;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.google.gson.JsonArray;
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
import com.google.gson.JsonElement;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
import org.geysermc.geyser.api.pack.ResourcePackManifest;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.lang.reflect.Type;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public record GeyserResourcePackManifest(
|
public record GeyserResourcePackManifest(
|
||||||
@JsonProperty("format_version") int formatVersion,
|
@SerializedName("format_version") int formatVersion,
|
||||||
Header header,
|
Header header,
|
||||||
Collection<Module> modules,
|
Collection<Module> modules,
|
||||||
Collection<Dependency> dependencies,
|
Collection<Dependency> dependencies,
|
||||||
@@ -60,13 +62,13 @@ public record GeyserResourcePackManifest(
|
|||||||
this.settings = ensureNonNull(settings);
|
this.settings = ensureNonNull(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record Header(UUID uuid, Version version, String name, String description, @JsonProperty("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { }
|
public record Header(UUID uuid, Version version, String name, String description, @SerializedName("min_engine_version") Version minimumSupportedMinecraftVersion) implements ResourcePackManifest.Header { }
|
||||||
|
|
||||||
public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { }
|
public record Module(UUID uuid, Version version, String type, String description) implements ResourcePackManifest.Module { }
|
||||||
|
|
||||||
public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { }
|
public record Dependency(UUID uuid, Version version) implements ResourcePackManifest.Dependency { }
|
||||||
|
|
||||||
public record Subpack(@JsonProperty("folder_name") String folderName, String name, @JsonProperty("memory_tier") Float memoryTier) implements ResourcePackManifest.Subpack { }
|
public record Subpack(@SerializedName("folder_name") String folderName, String name, @SerializedName("memory_tier") Float memoryTier) implements ResourcePackManifest.Subpack { }
|
||||||
|
|
||||||
public record Setting(String type, String text) implements ResourcePackManifest.Setting { }
|
public record Setting(String type, String text) implements ResourcePackManifest.Setting { }
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@ public record GeyserResourcePackManifest(
|
|||||||
return Collections.unmodifiableCollection(collection);
|
return Collections.unmodifiableCollection(collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonDeserialize(using = Version.VersionDeserializer.class)
|
@JsonAdapter(value = Version.VersionDeserializer.class)
|
||||||
public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version {
|
public record Version(int major, int minor, int patch) implements ResourcePackManifest.Version {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -83,11 +85,11 @@ public record GeyserResourcePackManifest(
|
|||||||
return major + "." + minor + "." + patch;
|
return major + "." + minor + "." + patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class VersionDeserializer extends JsonDeserializer<Version> {
|
public static class VersionDeserializer implements JsonDeserializer<Version> {
|
||||||
@Override
|
@Override
|
||||||
public Version deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
|
public Version deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
int[] version = ctxt.readValue(p, int[].class);
|
JsonArray array = json.getAsJsonArray();
|
||||||
return new Version(version[0], version[1], version[2]);
|
return new Version(array.get(0).getAsInt(), array.get(1).getAsInt(), array.get(2).getAsInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class SkullResourcePackManager {
|
|||||||
|
|
||||||
Path packPath = cachePath.resolve("player_skulls.mcpack");
|
Path packPath = cachePath.resolve("player_skulls.mcpack");
|
||||||
File packFile = packPath.toFile();
|
File packFile = packPath.toFile();
|
||||||
if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
if (BlockRegistries.CUSTOM_SKULLS.get().isEmpty() || !GeyserImpl.getInstance().config().gameplay().enableCustomContent()) {
|
||||||
packFile.delete(); // No need to keep resource pack
|
packFile.delete(); // No need to keep resource pack
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ public class SkullResourcePackManager {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache.");
|
GeyserImpl.getInstance().getLogger().debug("Unable to clean up skull skin cache.");
|
||||||
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
if (GeyserImpl.getInstance().config().debugMode()) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
|
* Copyright (c) 2019-2025 GeyserMC. http://geysermc.org
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -25,8 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.ping;
|
package org.geysermc.geyser.ping;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonParseException;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
|
||||||
import io.netty.handler.codec.haproxy.HAProxyCommand;
|
import io.netty.handler.codec.haproxy.HAProxyCommand;
|
||||||
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
|
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
|
||||||
import io.netty.util.NetUtil;
|
import io.netty.util.NetUtil;
|
||||||
@@ -34,18 +33,29 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||||||
import org.cloudburstmc.nbt.util.VarInts;
|
import org.cloudburstmc.nbt.util.VarInts;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.network.GameProtocol;
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.net.*;
|
import java.io.DataInputStream;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.ConnectException;
|
||||||
|
import java.net.Inet4Address;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runnable {
|
public class GeyserLegacyPingPassthrough extends Thread implements IGeyserPingPassthrough, Runnable {
|
||||||
private static final byte[] HAPROXY_BINARY_PREFIX = new byte[]{13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10};
|
private static final byte[] HAPROXY_BINARY_PREFIX = new byte[]{13, 10, 13, 10, 0, 13, 10, 81, 85, 73, 84, 10};
|
||||||
|
|
||||||
private final GeyserImpl geyser;
|
private final GeyserImpl geyser;
|
||||||
|
private final long interval;
|
||||||
|
|
||||||
public GeyserLegacyPingPassthrough(GeyserImpl geyser) {
|
public GeyserLegacyPingPassthrough(GeyserImpl geyser, int interval) {
|
||||||
this.geyser = geyser;
|
this.geyser = geyser;
|
||||||
|
this.interval = interval * 1000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
private GeyserPingInfo pingInfo;
|
private GeyserPingInfo pingInfo;
|
||||||
@@ -56,12 +66,14 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||||||
* @return GeyserPingPassthrough, or null if not initialized
|
* @return GeyserPingPassthrough, or null if not initialized
|
||||||
*/
|
*/
|
||||||
public static @Nullable IGeyserPingPassthrough init(GeyserImpl geyser) {
|
public static @Nullable IGeyserPingPassthrough init(GeyserImpl geyser) {
|
||||||
if (geyser.getConfig().isPassthroughMotd() || geyser.getConfig().isPassthroughPlayerCounts()) {
|
if (geyser.config().motd().passthroughMotd() || geyser.config().motd().passthroughPlayerCounts()) {
|
||||||
GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(geyser);
|
|
||||||
// Ensure delay is not zero
|
// Ensure delay is not zero
|
||||||
int interval = (geyser.getConfig().getPingPassthroughInterval() == 0) ? 1 : geyser.getConfig().getPingPassthroughInterval();
|
int interval = (geyser.config().motd().pingPassthroughInterval() == 0) ? 1 : geyser.config().motd().pingPassthroughInterval();
|
||||||
geyser.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s).");
|
geyser.getLogger().debug("Scheduling ping passthrough at an interval of " + interval + " second(s).");
|
||||||
geyser.getScheduledThread().scheduleAtFixedRate(pingPassthrough, 1, interval, TimeUnit.SECONDS);
|
GeyserLegacyPingPassthrough pingPassthrough = new GeyserLegacyPingPassthrough(geyser, interval);
|
||||||
|
pingPassthrough.setName("Geyser LegacyPingPassthrough Thread");
|
||||||
|
pingPassthrough.setDaemon(true);
|
||||||
|
pingPassthrough.start();
|
||||||
return pingPassthrough;
|
return pingPassthrough;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -74,9 +86,10 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
while (!geyser.isShuttingDown() && !geyser.isReloading()) {
|
||||||
try (Socket socket = new Socket()) {
|
try (Socket socket = new Socket()) {
|
||||||
String address = geyser.getConfig().getRemote().address();
|
String address = geyser.config().java().address();
|
||||||
int port = geyser.getConfig().getRemote().port();
|
int port = geyser.config().java().port();
|
||||||
InetSocketAddress endpoint = new InetSocketAddress(address, port);
|
InetSocketAddress endpoint = new InetSocketAddress(address, port);
|
||||||
socket.connect(endpoint, 5000);
|
socket.connect(endpoint, 5000);
|
||||||
|
|
||||||
@@ -93,7 +106,7 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||||||
byte[] buffer;
|
byte[] buffer;
|
||||||
|
|
||||||
try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) {
|
try (DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream())) {
|
||||||
if (geyser.getConfig().getRemote().isUseProxyProtocol()) {
|
if (geyser.config().advanced().java().useHaproxyProtocol()) {
|
||||||
// HAProxy support
|
// HAProxy support
|
||||||
// Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78
|
// Based on https://github.com/netty/netty/blob/d8ad931488f6b942dabe28ecd6c399b4438da0a8/codec-haproxy/src/main/java/io/netty/handler/codec/haproxy/HAProxyMessageEncoder.java#L78
|
||||||
dataOutputStream.write(HAPROXY_BINARY_PREFIX);
|
dataOutputStream.write(HAPROXY_BINARY_PREFIX);
|
||||||
@@ -130,11 +143,11 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pingInfo = GeyserImpl.JSON_MAPPER.readValue(buffer, GeyserPingInfo.class);
|
this.pingInfo = JsonUtils.fromJson(buffer, GeyserPingInfo.class);
|
||||||
} catch (SocketTimeoutException | ConnectException ex) {
|
} catch (SocketTimeoutException | ConnectException ex) {
|
||||||
this.pingInfo = null;
|
this.pingInfo = null;
|
||||||
this.geyser.getLogger().debug("Connection timeout for ping passthrough.");
|
this.geyser.getLogger().debug("Connection timeout for ping passthrough.");
|
||||||
} catch (JsonParseException | JsonMappingException ex) {
|
} catch (JsonSyntaxException ex) {
|
||||||
this.geyser.getLogger().error("Failed to parse json when pinging server!", ex);
|
this.geyser.getLogger().error("Failed to parse json when pinging server!", ex);
|
||||||
} catch (EOFException e) {
|
} catch (EOFException e) {
|
||||||
this.pingInfo = null;
|
this.pingInfo = null;
|
||||||
@@ -145,5 +158,12 @@ public class GeyserLegacyPingPassthrough implements IGeyserPingPassthrough, Runn
|
|||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
this.geyser.getLogger().error("IO error while trying to use legacy ping passthrough", e);
|
this.geyser.getLogger().error("IO error while trying to use legacy ping passthrough", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Thread.sleep(interval);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,21 +25,25 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.ping;
|
package org.geysermc.geyser.ping;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.fasterxml.jackson.annotation.JsonSetter;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The structure of this class and its nested classes are specifically
|
* The structure of this class and its nested classes are specifically
|
||||||
* designed for the format received by {@link GeyserLegacyPingPassthrough}.
|
* designed for the format received by {@link GeyserLegacyPingPassthrough}.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public class GeyserPingInfo {
|
public class GeyserPingInfo {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@JsonAdapter(DescriptionDeserializer.class)
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
private Players players;
|
private Players players;
|
||||||
@@ -58,13 +62,7 @@ public class GeyserPingInfo {
|
|||||||
this.players = new Players(maxPlayers, onlinePlayers);
|
this.players = new Players(maxPlayers, onlinePlayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonSetter("description")
|
|
||||||
void setDescription(JsonNode description) {
|
|
||||||
this.description = description.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
public static class Players {
|
public static class Players {
|
||||||
|
|
||||||
private int max;
|
private int max;
|
||||||
@@ -79,4 +77,14 @@ public class GeyserPingInfo {
|
|||||||
this.online = online;
|
this.online = online;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* So GSON does not complain how we are treating Description - it will be converted to a proper Component later.
|
||||||
|
*/
|
||||||
|
private static final class DescriptionDeserializer implements JsonDeserializer<String> {
|
||||||
|
@Override
|
||||||
|
public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
return json.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ public class PacketTranslatorRegistry<T> extends AbstractMappedRegistry<Class<?
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
if (GeyserImpl.getInstance().config().debugMode()) {
|
||||||
if (!IGNORED_PACKETS.contains(clazz)) {
|
if (!IGNORED_PACKETS.contains(clazz)) {
|
||||||
GeyserImpl.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
GeyserImpl.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,16 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.loader;
|
package org.geysermc.geyser.registry.loader;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class BiomeIdentifierRegistryLoader implements RegistryLoader<String, Object2IntMap<String>> {
|
public class BiomeIdentifierRegistryLoader implements RegistryLoader<String, Object2IntMap<String>> {
|
||||||
@@ -43,12 +45,12 @@ public class BiomeIdentifierRegistryLoader implements RegistryLoader<String, Obj
|
|||||||
// crashes the client. Therefore, we need to have a list of all valid Bedrock biome IDs with which we can use from.
|
// crashes the client. Therefore, we need to have a list of all valid Bedrock biome IDs with which we can use from.
|
||||||
// The server sends the corresponding Java network IDs, so we don't need to worry about that now.
|
// The server sends the corresponding Java network IDs, so we don't need to worry about that now.
|
||||||
|
|
||||||
// Reference variable for Jackson to read off of
|
// Reference variable for Gson to read off of
|
||||||
TypeReference<Map<String, BiomeEntry>> biomeEntriesType = new TypeReference<>() { };
|
Type biomeEntriesType = new TypeToken<Map<String, BiomeEntry>>() { }.getType();
|
||||||
Map<String, BiomeEntry> biomeEntries;
|
Map<String, BiomeEntry> biomeEntries;
|
||||||
|
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/biomes.json")) {
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/biomes.json")) {
|
||||||
biomeEntries = GeyserImpl.JSON_MAPPER.readValue(stream, biomeEntriesType);
|
biomeEntries = JsonUtils.fromJson(stream, biomeEntriesType);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError("Unable to load Bedrock runtime biomes", e);
|
throw new AssertionError("Unable to load Bedrock runtime biomes", e);
|
||||||
}
|
}
|
||||||
@@ -66,7 +68,7 @@ public class BiomeIdentifierRegistryLoader implements RegistryLoader<String, Obj
|
|||||||
/**
|
/**
|
||||||
* The Bedrock network ID for this biome.
|
* The Bedrock network ID for this biome.
|
||||||
*/
|
*/
|
||||||
@JsonProperty("bedrock_id")
|
@SerializedName("bedrock_id")
|
||||||
private int bedrockId;
|
private int bedrockId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,12 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.loader;
|
package org.geysermc.geyser.registry.loader;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Map;
|
import java.io.InputStreamReader;
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An abstract registry loader for loading effects from a resource path.
|
* An abstract registry loader for loading effects from a resource path.
|
||||||
@@ -38,21 +38,14 @@ import java.util.WeakHashMap;
|
|||||||
* @param <T> the value
|
* @param <T> the value
|
||||||
*/
|
*/
|
||||||
public abstract class EffectRegistryLoader<T> implements RegistryLoader<String, T> {
|
public abstract class EffectRegistryLoader<T> implements RegistryLoader<String, T> {
|
||||||
private static final Map<String, JsonNode> loadedFiles = new WeakHashMap<>();
|
|
||||||
|
|
||||||
public void loadFile(String input) {
|
public JsonObject loadFile(String input) {
|
||||||
if (!loadedFiles.containsKey(input)) {
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input);
|
||||||
JsonNode effects;
|
InputStreamReader reader = new InputStreamReader(stream)) {
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) {
|
//noinspection deprecation
|
||||||
effects = GeyserImpl.JSON_MAPPER.readTree(stream);
|
return new JsonParser().parse(reader).getAsJsonObject();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load registrations for " + input, e);
|
throw new AssertionError("Unable to load registrations for " + input, e);
|
||||||
}
|
}
|
||||||
loadedFiles.put(input, effects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public JsonNode get(String input) {
|
|
||||||
return loadedFiles.get(input);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,15 +25,15 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.loader;
|
package org.geysermc.geyser.registry.loader;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonElement;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
|
import com.google.gson.JsonObject;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
import org.cloudburstmc.protocol.bedrock.data.LevelEvent;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.LevelEventType;
|
import org.cloudburstmc.protocol.bedrock.data.LevelEventType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.registry.type.ParticleMapping;
|
import org.geysermc.geyser.registry.type.ParticleMapping;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -44,16 +44,13 @@ public class ParticleTypesRegistryLoader extends EffectRegistryLoader<Map<Partic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<ParticleType, ParticleMapping> load(String input) {
|
public Map<ParticleType, ParticleMapping> load(String input) {
|
||||||
this.loadFile(input);
|
JsonObject particlesJson = this.loadFile(input);
|
||||||
|
|
||||||
Iterator<Map.Entry<String, JsonNode>> particlesIterator = this.get(input).fields();
|
|
||||||
Map<ParticleType, ParticleMapping> particles = new Object2ObjectOpenHashMap<>();
|
Map<ParticleType, ParticleMapping> particles = new Object2ObjectOpenHashMap<>();
|
||||||
try {
|
try {
|
||||||
while (particlesIterator.hasNext()) {
|
for (Map.Entry<String, JsonElement> entry : particlesJson.entrySet()) {
|
||||||
Map.Entry<String, JsonNode> entry = particlesIterator.next();
|
|
||||||
String key = entry.getKey().toUpperCase(Locale.ROOT);
|
String key = entry.getKey().toUpperCase(Locale.ROOT);
|
||||||
JsonNode bedrockId = entry.getValue().get("bedrockId");
|
JsonElement bedrockId = entry.getValue().getAsJsonObject().get("bedrockId");
|
||||||
JsonNode eventType = entry.getValue().get("eventType");
|
JsonElement eventType = entry.getValue().getAsJsonObject().get("eventType");
|
||||||
if (eventType == null && bedrockId == null) {
|
if (eventType == null && bedrockId == null) {
|
||||||
GeyserImpl.getInstance().getLogger().debug("Skipping particle mapping " + key + " because no Bedrock equivalent exists.");
|
GeyserImpl.getInstance().getLogger().debug("Skipping particle mapping " + key + " because no Bedrock equivalent exists.");
|
||||||
continue;
|
continue;
|
||||||
@@ -63,16 +60,16 @@ public class ParticleTypesRegistryLoader extends EffectRegistryLoader<Map<Partic
|
|||||||
if (eventType != null) {
|
if (eventType != null) {
|
||||||
try {
|
try {
|
||||||
// Check if we have a particle type mapping
|
// Check if we have a particle type mapping
|
||||||
type = org.cloudburstmc.protocol.bedrock.data.ParticleType.valueOf(eventType.asText().toUpperCase(Locale.ROOT));
|
type = org.cloudburstmc.protocol.bedrock.data.ParticleType.valueOf(eventType.getAsString().toUpperCase(Locale.ROOT));
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
// No particle type; try level event
|
// No particle type; try level event
|
||||||
type = LevelEvent.valueOf(eventType.asText().toUpperCase(Locale.ROOT));
|
type = LevelEvent.valueOf(eventType.getAsString().toUpperCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
particles.put(ParticleType.valueOf(key), new ParticleMapping(
|
particles.put(ParticleType.valueOf(key), new ParticleMapping(
|
||||||
type,
|
type,
|
||||||
bedrockId == null ? null : bedrockId.asText())
|
bedrockId == null ? null : bedrockId.getAsString())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|||||||
@@ -124,20 +124,19 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||||||
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
GeyserLoadResourcePacksEvent event = new GeyserLoadResourcePacksEvent(resourcePacks);
|
||||||
GeyserImpl.getInstance().eventBus().fire(event);
|
GeyserImpl.getInstance().eventBus().fire(event);
|
||||||
|
|
||||||
|
GeyserDefineResourcePacksEventImpl defineEvent = new GeyserDefineResourcePacksEventImpl(packMap);
|
||||||
|
|
||||||
for (Path path : event.resourcePacks()) {
|
for (Path path : event.resourcePacks()) {
|
||||||
try {
|
try {
|
||||||
GeyserResourcePack pack = readPack(path).build();
|
defineEvent.register(readPack(path).build());
|
||||||
packMap.put(pack.uuid(), ResourcePackHolder.of(pack));
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
GeyserImpl.getInstance().getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", path));
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all remote resource packs from the config before firing the new event
|
// Load all remote resource packs from the config before firing the new event
|
||||||
// TODO configurate
|
loadRemotePacks(defineEvent);
|
||||||
//packMap.putAll(loadRemotePacks());
|
|
||||||
|
|
||||||
GeyserDefineResourcePacksEventImpl defineEvent = new GeyserDefineResourcePacksEventImpl(packMap);
|
|
||||||
GeyserImpl.getInstance().eventBus().fire(defineEvent);
|
GeyserImpl.getInstance().eventBus().fire(defineEvent);
|
||||||
|
|
||||||
// After loading the new resource packs: let's clean up the old url packs
|
// After loading the new resource packs: let's clean up the old url packs
|
||||||
@@ -225,7 +224,7 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<UUID, ResourcePackHolder> loadRemotePacks() {
|
private void loadRemotePacks(GeyserDefineResourcePacksEventImpl event) {
|
||||||
GeyserImpl instance = GeyserImpl.getInstance();
|
GeyserImpl instance = GeyserImpl.getInstance();
|
||||||
// Unable to make this a static variable, as the test would fail
|
// Unable to make this a static variable, as the test would fail
|
||||||
final Path cachedDirectory = instance.getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs");
|
final Path cachedDirectory = instance.getBootstrap().getConfigFolder().resolve("cache").resolve("remote_packs");
|
||||||
@@ -235,19 +234,14 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||||||
Files.createDirectories(cachedDirectory);
|
Files.createDirectories(cachedDirectory);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
instance.getLogger().error("Could not create remote pack cache directory", e);
|
instance.getLogger().error("Could not create remote pack cache directory", e);
|
||||||
return new Object2ObjectOpenHashMap<>();
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//List<String> remotePackUrls = instance.getConfig().getResourcePackUrls();
|
List<String> remotePackUrls = instance.config().advanced().resourcePackUrls();
|
||||||
List<String> remotePackUrls = List.of();
|
|
||||||
Map<UUID, ResourcePackHolder> packMap = new Object2ObjectOpenHashMap<>();
|
|
||||||
|
|
||||||
for (String url : remotePackUrls) {
|
for (String url : remotePackUrls) {
|
||||||
try {
|
try {
|
||||||
GeyserUrlPackCodec codec = new GeyserUrlPackCodec(url);
|
event.register(new GeyserUrlPackCodec(url).create());
|
||||||
GeyserResourcePack pack = codec.create();
|
|
||||||
packMap.put(pack.uuid(), ResourcePackHolder.of(pack));
|
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
instance.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", url));
|
instance.getLogger().error(GeyserLocale.getLocaleStringLog("geyser.resource_pack.broken", url));
|
||||||
instance.getLogger().error(e.getMessage());
|
instance.getLogger().error(e.getMessage());
|
||||||
@@ -256,8 +250,6 @@ public class ResourcePackLoader implements RegistryLoader<Path, Map<UUID, Resour
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return packMap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -25,8 +25,8 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.loader;
|
package org.geysermc.geyser.registry.loader;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonElement;
|
||||||
import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent;
|
import com.google.gson.JsonObject;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import org.cloudburstmc.protocol.bedrock.data.LevelEventType;
|
import org.cloudburstmc.protocol.bedrock.data.LevelEventType;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
@@ -34,8 +34,8 @@ import org.geysermc.geyser.translator.level.event.LevelEventTranslator;
|
|||||||
import org.geysermc.geyser.translator.level.event.PlaySoundEventTranslator;
|
import org.geysermc.geyser.translator.level.event.PlaySoundEventTranslator;
|
||||||
import org.geysermc.geyser.translator.level.event.SoundEventEventTranslator;
|
import org.geysermc.geyser.translator.level.event.SoundEventEventTranslator;
|
||||||
import org.geysermc.geyser.translator.level.event.SoundLevelEventTranslator;
|
import org.geysermc.geyser.translator.level.event.SoundLevelEventTranslator;
|
||||||
|
import org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEvent;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,37 +47,36 @@ public class SoundEventsRegistryLoader extends EffectRegistryLoader<Map<LevelEve
|
|||||||
public Map<LevelEvent, LevelEventTranslator> load(String input) {
|
public Map<LevelEvent, LevelEventTranslator> load(String input) {
|
||||||
this.loadFile(input);
|
this.loadFile(input);
|
||||||
|
|
||||||
Iterator<Map.Entry<String, JsonNode>> effectsIterator = this.get(input).fields();
|
JsonObject effectsJson = this.loadFile(input);
|
||||||
Map<LevelEvent, LevelEventTranslator> soundEffects = new Object2ObjectOpenHashMap<>();
|
Map<LevelEvent, LevelEventTranslator> soundEffects = new Object2ObjectOpenHashMap<>();
|
||||||
while (effectsIterator.hasNext()) {
|
for (Map.Entry<String, JsonElement> entry : effectsJson.entrySet()) {
|
||||||
Map.Entry<String, JsonNode> entry = effectsIterator.next();
|
JsonObject node = entry.getValue().getAsJsonObject();
|
||||||
JsonNode node = entry.getValue();
|
|
||||||
try {
|
try {
|
||||||
String type = node.get("type").asText();
|
String type = node.get("type").getAsString();
|
||||||
LevelEvent javaEffect = null;
|
LevelEvent javaEffect = null;
|
||||||
LevelEventTranslator transformer = null;
|
LevelEventTranslator transformer = null;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "soundLevel" -> {
|
case "soundLevel" -> {
|
||||||
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
||||||
LevelEventType levelEventType = org.cloudburstmc.protocol.bedrock.data.LevelEvent.valueOf(node.get("name").asText());
|
LevelEventType levelEventType = org.cloudburstmc.protocol.bedrock.data.LevelEvent.valueOf(node.get("name").getAsString());
|
||||||
int data = node.has("data") ? node.get("data").intValue() : 0;
|
int data = node.has("data") ? node.get("data").getAsInt() : 0;
|
||||||
transformer = new SoundLevelEventTranslator(levelEventType, data);
|
transformer = new SoundLevelEventTranslator(levelEventType, data);
|
||||||
}
|
}
|
||||||
case "soundEvent" -> {
|
case "soundEvent" -> {
|
||||||
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
||||||
org.cloudburstmc.protocol.bedrock.data.SoundEvent soundEvent = org.cloudburstmc.protocol.bedrock.data.SoundEvent.valueOf(node.get("name").asText());
|
org.cloudburstmc.protocol.bedrock.data.SoundEvent soundEvent = org.cloudburstmc.protocol.bedrock.data.SoundEvent.valueOf(node.get("name").getAsString());
|
||||||
String identifier = node.has("identifier") ? node.get("identifier").asText() : "";
|
String identifier = node.has("identifier") ? node.get("identifier").getAsString() : "";
|
||||||
int extraData = node.has("extraData") ? node.get("extraData").intValue() : -1;
|
int extraData = node.has("extraData") ? node.get("extraData").getAsInt() : -1;
|
||||||
transformer = new SoundEventEventTranslator(soundEvent, identifier, extraData);
|
transformer = new SoundEventEventTranslator(soundEvent, identifier, extraData);
|
||||||
}
|
}
|
||||||
case "playSound" -> {
|
case "playSound" -> {
|
||||||
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
javaEffect = org.geysermc.mcprotocollib.protocol.data.game.level.event.LevelEventType.valueOf(entry.getKey());
|
||||||
String name = node.get("name").asText();
|
String name = node.get("name").getAsString();
|
||||||
float volume = node.has("volume") ? node.get("volume").floatValue() : 1.0f;
|
float volume = node.has("volume") ? node.get("volume").getAsFloat() : 1.0f;
|
||||||
boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").booleanValue();
|
boolean pitchSub = node.has("pitch_sub") && node.get("pitch_sub").getAsBoolean();
|
||||||
float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").floatValue() : 1.0f;
|
float pitchMul = node.has("pitch_mul") ? node.get("pitch_mul").getAsFloat() : 1.0f;
|
||||||
float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").floatValue() : 0.0f;
|
float pitchAdd = node.has("pitch_add") ? node.get("pitch_add").getAsFloat() : 0.0f;
|
||||||
boolean relative = !node.has("relative") || node.get("relative").booleanValue();
|
boolean relative = !node.has("relative") || node.get("relative").getAsBoolean();
|
||||||
transformer = new PlaySoundEventTranslator(name, volume, pitchSub, pitchMul, pitchAdd, relative);
|
transformer = new PlaySoundEventTranslator(name, volume, pitchSub, pitchMul, pitchAdd, relative);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,16 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.loader;
|
package org.geysermc.geyser.registry.loader;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonElement;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.registry.type.SoundMapping;
|
import org.geysermc.geyser.registry.type.SoundMapping;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,27 +43,27 @@ import java.util.Map;
|
|||||||
public class SoundRegistryLoader implements RegistryLoader<String, Map<String, SoundMapping>> {
|
public class SoundRegistryLoader implements RegistryLoader<String, Map<String, SoundMapping>> {
|
||||||
@Override
|
@Override
|
||||||
public Map<String, SoundMapping> load(String input) {
|
public Map<String, SoundMapping> load(String input) {
|
||||||
JsonNode soundsTree;
|
JsonObject soundsJson;
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input)) {
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow(input);
|
||||||
soundsTree = GeyserImpl.JSON_MAPPER.readTree(stream);
|
InputStreamReader isr = new InputStreamReader(stream)) {
|
||||||
|
//noinspection deprecation
|
||||||
|
soundsJson = new JsonParser().parse(isr).getAsJsonObject();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError("Unable to load sound mappings", e);
|
throw new AssertionError("Unable to load sound mappings", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, SoundMapping> soundMappings = new HashMap<>();
|
Map<String, SoundMapping> soundMappings = new HashMap<>();
|
||||||
Iterator<Map.Entry<String, JsonNode>> soundsIterator = soundsTree.fields();
|
for (Map.Entry<String, JsonElement> entry : soundsJson.entrySet()) {
|
||||||
while (soundsIterator.hasNext()) {
|
JsonObject brMap = entry.getValue().getAsJsonObject();
|
||||||
Map.Entry<String, JsonNode> next = soundsIterator.next();
|
String javaSound = entry.getKey();
|
||||||
JsonNode brMap = next.getValue();
|
|
||||||
String javaSound = next.getKey();
|
|
||||||
soundMappings.put(javaSound, new SoundMapping(
|
soundMappings.put(javaSound, new SoundMapping(
|
||||||
javaSound,
|
javaSound,
|
||||||
brMap.has("bedrock_mapping") && brMap.get("bedrock_mapping").isTextual() ? brMap.get("bedrock_mapping").asText() : null,
|
brMap.has("bedrock_mapping") ? brMap.get("bedrock_mapping").getAsString() : null,
|
||||||
brMap.has("playsound_mapping") && brMap.get("playsound_mapping").isTextual() ? brMap.get("playsound_mapping").asText() : null,
|
brMap.has("playsound_mapping") ? brMap.get("playsound_mapping").getAsString() : null,
|
||||||
brMap.has("extra_data") && brMap.get("extra_data").isInt() ? brMap.get("extra_data").asInt() : -1,
|
brMap.has("extra_data") ? brMap.get("extra_data").getAsInt() : -1,
|
||||||
brMap.has("identifier") && brMap.get("identifier").isTextual() ? brMap.get("identifier").asText() : null,
|
brMap.has("identifier") ? brMap.get("identifier").getAsString() : null,
|
||||||
brMap.has("level_event") && brMap.get("level_event").isBoolean() && brMap.get("level_event").asBoolean(),
|
brMap.has("level_event") && brMap.get("level_event").getAsBoolean(),
|
||||||
brMap.has("pitch_adjust") && brMap.get("pitch_adjust").isNumber() ? brMap.get("pitch_adjust").floatValue() : 1.0f
|
brMap.has("pitch_adjust") ? brMap.get("pitch_adjust").getAsFloat() : 1.0f
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.mappings;
|
package org.geysermc.geyser.registry.mappings;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@@ -35,6 +36,7 @@ import org.geysermc.geyser.registry.mappings.util.CustomBlockMapping;
|
|||||||
import org.geysermc.geyser.registry.mappings.versions.MappingsReader;
|
import org.geysermc.geyser.registry.mappings.versions.MappingsReader;
|
||||||
import org.geysermc.geyser.registry.mappings.versions.MappingsReader_v1;
|
import org.geysermc.geyser.registry.mappings.versions.MappingsReader_v1;
|
||||||
|
|
||||||
|
import java.io.FileReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@@ -95,10 +97,10 @@ public class MappingsConfigReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable JsonNode getMappingsRoot(Path file) {
|
public @Nullable JsonObject getMappingsRoot(Path file) {
|
||||||
JsonNode mappingsRoot;
|
JsonObject mappingsRoot;
|
||||||
try {
|
try (FileReader reader = new FileReader(file.toFile())) {
|
||||||
mappingsRoot = GeyserImpl.JSON_MAPPER.readTree(file.toFile());
|
mappingsRoot = (JsonObject) new JsonParser().parse(reader);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e);
|
GeyserImpl.getInstance().getLogger().error("Failed to read custom mapping file: " + file, e);
|
||||||
return null;
|
return null;
|
||||||
@@ -112,8 +114,8 @@ public class MappingsConfigReader {
|
|||||||
return mappingsRoot;
|
return mappingsRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFormatVersion(JsonNode mappingsRoot, Path file) {
|
public int getFormatVersion(JsonObject mappingsRoot, Path file) {
|
||||||
int formatVersion = mappingsRoot.get("format_version").asInt();
|
int formatVersion = mappingsRoot.get("format_version").getAsInt();
|
||||||
if (!this.mappingReaders.containsKey(formatVersion)) {
|
if (!this.mappingReaders.containsKey(formatVersion)) {
|
||||||
GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion);
|
GeyserImpl.getInstance().getLogger().error("Mappings file " + file + " has an unknown format version: " + formatVersion);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -122,7 +124,7 @@ public class MappingsConfigReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void readItemMappingsFromJson(Path file, BiConsumer<String, CustomItemData> consumer) {
|
public void readItemMappingsFromJson(Path file, BiConsumer<String, CustomItemData> consumer) {
|
||||||
JsonNode mappingsRoot = getMappingsRoot(file);
|
JsonObject mappingsRoot = getMappingsRoot(file);
|
||||||
|
|
||||||
if (mappingsRoot == null) {
|
if (mappingsRoot == null) {
|
||||||
return;
|
return;
|
||||||
@@ -138,7 +140,7 @@ public class MappingsConfigReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void readBlockMappingsFromJson(Path file, BiConsumer<String, CustomBlockMapping> consumer) {
|
public void readBlockMappingsFromJson(Path file, BiConsumer<String, CustomBlockMapping> consumer) {
|
||||||
JsonNode mappingsRoot = getMappingsRoot(file);
|
JsonObject mappingsRoot = getMappingsRoot(file);
|
||||||
|
|
||||||
if (mappingsRoot == null) {
|
if (mappingsRoot == null) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.mappings.versions;
|
package org.geysermc.geyser.registry.mappings.versions;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonObject;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
import org.geysermc.geyser.api.item.custom.CustomRenderOffsets;
|
||||||
@@ -36,14 +36,14 @@ import java.nio.file.Path;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
public abstract class MappingsReader {
|
public abstract class MappingsReader {
|
||||||
public abstract void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer);
|
public abstract void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> consumer);
|
||||||
public abstract void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer);
|
public abstract void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer);
|
||||||
|
|
||||||
public abstract CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException;
|
public abstract CustomItemData readItemMappingEntry(JsonObject node) throws InvalidCustomMappingsFileException;
|
||||||
public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException;
|
public abstract CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException;
|
||||||
|
|
||||||
protected @Nullable CustomRenderOffsets fromJsonNode(JsonNode node) {
|
protected @Nullable CustomRenderOffsets fromJsonObject(JsonObject node) {
|
||||||
if (node == null || !node.isObject()) {
|
if (node == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,9 +53,8 @@ public abstract class MappingsReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonNode node, String hand) {
|
protected CustomRenderOffsets.@Nullable Hand getHandOffsets(JsonObject node, String hand) {
|
||||||
JsonNode tmpNode = node.get(hand);
|
if (!(node.get(hand) instanceof JsonObject tmpNode)) {
|
||||||
if (tmpNode == null || !tmpNode.isObject()) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,9 +64,8 @@ public abstract class MappingsReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonNode node, String perspective) {
|
protected CustomRenderOffsets.@Nullable Offset getPerspectiveOffsets(JsonObject node, String perspective) {
|
||||||
JsonNode tmpNode = node.get(perspective);
|
if (!(node.get(perspective) instanceof JsonObject tmpNode)) {
|
||||||
if (tmpNode == null || !tmpNode.isObject()) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,9 +76,8 @@ public abstract class MappingsReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonNode node, String offsetType) {
|
protected CustomRenderOffsets.@Nullable OffsetXYZ getOffsetXYZ(JsonObject node, String offsetType) {
|
||||||
JsonNode tmpNode = node.get(offsetType);
|
if (!(node.get(offsetType) instanceof JsonObject tmpNode)) {
|
||||||
if (tmpNode == null || !tmpNode.isObject()) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +86,9 @@ public abstract class MappingsReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new CustomRenderOffsets.OffsetXYZ(
|
return new CustomRenderOffsets.OffsetXYZ(
|
||||||
tmpNode.get("x").floatValue(),
|
tmpNode.get("x").getAsFloat(),
|
||||||
tmpNode.get("y").floatValue(),
|
tmpNode.get("y").getAsFloat(),
|
||||||
tmpNode.get("z").floatValue()
|
tmpNode.get("z").getAsFloat()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,10 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.mappings.versions;
|
package org.geysermc.geyser.registry.mappings.versions;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonArray;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
@@ -34,9 +36,14 @@ import org.geysermc.geyser.GeyserImpl;
|
|||||||
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
import org.geysermc.geyser.api.block.custom.CustomBlockData;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
import org.geysermc.geyser.api.block.custom.CustomBlockPermutation;
|
||||||
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
import org.geysermc.geyser.api.block.custom.CustomBlockState;
|
||||||
import org.geysermc.geyser.api.block.custom.component.*;
|
import org.geysermc.geyser.api.block.custom.component.BoxComponent;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.CustomBlockComponents;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.GeometryComponent;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.MaterialInstance;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.PlacementConditions;
|
||||||
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType;
|
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.BlockFilterType;
|
||||||
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face;
|
import org.geysermc.geyser.api.block.custom.component.PlacementConditions.Face;
|
||||||
|
import org.geysermc.geyser.api.block.custom.component.TransformationComponent;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||||
import org.geysermc.geyser.api.util.CreativeCategory;
|
import org.geysermc.geyser.api.util.CreativeCategory;
|
||||||
@@ -57,7 +64,13 @@ import org.geysermc.geyser.util.MathUtils;
|
|||||||
import org.geysermc.geyser.util.MinecraftKey;
|
import org.geysermc.geyser.util.MinecraftKey;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
@@ -68,7 +81,7 @@ import java.util.stream.Collectors;
|
|||||||
*/
|
*/
|
||||||
public class MappingsReader_v1 extends MappingsReader {
|
public class MappingsReader_v1 extends MappingsReader {
|
||||||
@Override
|
@Override
|
||||||
public void readItemMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
public void readItemMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
||||||
this.readItemMappingsV1(file, mappingsRoot, consumer);
|
this.readItemMappingsV1(file, mappingsRoot, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,24 +89,24 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
* Read item block from a JSON node
|
* Read item block from a JSON node
|
||||||
*
|
*
|
||||||
* @param file The path to the file
|
* @param file The path to the file
|
||||||
* @param mappingsRoot The {@link JsonNode} containing the mappings
|
* @param mappingsRoot The {@link JsonObject} containing the mappings
|
||||||
* @param consumer The consumer to accept the mappings
|
* @param consumer The consumer to accept the mappings
|
||||||
* @see #readBlockMappingsV1(Path, JsonNode, BiConsumer)
|
* @see #readBlockMappingsV1(Path, JsonObject, BiConsumer)
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void readBlockMappings(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer) {
|
public void readBlockMappings(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer) {
|
||||||
this.readBlockMappingsV1(file, mappingsRoot, consumer);
|
this.readBlockMappingsV1(file, mappingsRoot, consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readItemMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
public void readItemMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomItemData> consumer) {
|
||||||
JsonNode itemsNode = mappingsRoot.get("items");
|
JsonObject itemsNode = mappingsRoot.getAsJsonObject("items");
|
||||||
|
|
||||||
if (itemsNode != null && itemsNode.isObject()) {
|
if (itemsNode != null) {
|
||||||
itemsNode.fields().forEachRemaining(entry -> {
|
itemsNode.entrySet().forEach(entry -> {
|
||||||
if (entry.getValue().isArray()) {
|
if (entry.getValue() instanceof JsonArray array) {
|
||||||
entry.getValue().forEach(data -> {
|
array.forEach(data -> {
|
||||||
try {
|
try {
|
||||||
CustomItemData customItemData = this.readItemMappingEntry(data);
|
CustomItemData customItemData = this.readItemMappingEntry((JsonObject) data);
|
||||||
consumer.accept(entry.getKey(), customItemData);
|
consumer.accept(entry.getKey(), customItemData);
|
||||||
} catch (InvalidCustomMappingsFileException e) {
|
} catch (InvalidCustomMappingsFileException e) {
|
||||||
GeyserImpl.getInstance().getLogger().error("Error in registering items for custom mapping file: " + file.toString(), e);
|
GeyserImpl.getInstance().getLogger().error("Error in registering items for custom mapping file: " + file.toString(), e);
|
||||||
@@ -108,19 +121,17 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
* Read block mappings from a JSON node
|
* Read block mappings from a JSON node
|
||||||
*
|
*
|
||||||
* @param file The path to the file
|
* @param file The path to the file
|
||||||
* @param mappingsRoot The {@link JsonNode} containing the mappings
|
* @param mappingsRoot The {@link JsonObject} containing the mappings
|
||||||
* @param consumer The consumer to accept the mappings
|
* @param consumer The consumer to accept the mappings
|
||||||
* @see #readBlockMappings(Path, JsonNode, BiConsumer)
|
* @see #readBlockMappings(Path, JsonObject, BiConsumer)
|
||||||
*/
|
*/
|
||||||
public void readBlockMappingsV1(Path file, JsonNode mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer) {
|
public void readBlockMappingsV1(Path file, JsonObject mappingsRoot, BiConsumer<String, CustomBlockMapping> consumer) {
|
||||||
JsonNode blocksNode = mappingsRoot.get("blocks");
|
if (mappingsRoot.get("blocks") instanceof JsonObject blocksNode) {
|
||||||
|
blocksNode.entrySet().forEach(entry -> {
|
||||||
if (blocksNode != null && blocksNode.isObject()) {
|
if (entry.getValue() instanceof JsonObject jsonObject) {
|
||||||
blocksNode.fields().forEachRemaining(entry -> {
|
|
||||||
if (entry.getValue().isObject()) {
|
|
||||||
try {
|
try {
|
||||||
String identifier = MinecraftKey.key(entry.getKey()).asString();
|
String identifier = MinecraftKey.key(entry.getKey()).asString();
|
||||||
CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, entry.getValue());
|
CustomBlockMapping customBlockMapping = this.readBlockMappingEntry(identifier, jsonObject);
|
||||||
consumer.accept(identifier, customBlockMapping);
|
consumer.accept(identifier, customBlockMapping);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
GeyserImpl.getInstance().getLogger().error("Error in registering blocks for custom mapping file: " + file.toString());
|
GeyserImpl.getInstance().getLogger().error("Error in registering blocks for custom mapping file: " + file.toString());
|
||||||
@@ -131,85 +142,85 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private CustomItemOptions readItemCustomItemOptions(JsonNode node) {
|
private CustomItemOptions readItemCustomItemOptions(JsonObject node) {
|
||||||
CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder();
|
CustomItemOptions.Builder customItemOptions = CustomItemOptions.builder();
|
||||||
|
|
||||||
JsonNode customModelData = node.get("custom_model_data");
|
JsonElement customModelData = node.get("custom_model_data");
|
||||||
if (customModelData != null && customModelData.isInt()) {
|
if (customModelData != null && customModelData.isJsonPrimitive()) {
|
||||||
customItemOptions.customModelData(customModelData.asInt());
|
customItemOptions.customModelData(customModelData.getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode damagePredicate = node.get("damage_predicate");
|
JsonElement damagePredicate = node.get("damage_predicate");
|
||||||
if (damagePredicate != null && damagePredicate.isInt()) {
|
if (damagePredicate != null && damagePredicate.isJsonPrimitive()) {
|
||||||
customItemOptions.damagePredicate(damagePredicate.asInt());
|
customItemOptions.damagePredicate(damagePredicate.getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode unbreakable = node.get("unbreakable");
|
JsonElement unbreakable = node.get("unbreakable");
|
||||||
if (unbreakable != null && unbreakable.isBoolean()) {
|
if (unbreakable != null && unbreakable.isJsonPrimitive()) {
|
||||||
customItemOptions.unbreakable(unbreakable.asBoolean());
|
customItemOptions.unbreakable(unbreakable.getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode defaultItem = node.get("default");
|
JsonElement defaultItem = node.get("default");
|
||||||
if (defaultItem != null && defaultItem.isBoolean()) {
|
if (defaultItem != null && defaultItem.isJsonPrimitive()) {
|
||||||
customItemOptions.defaultItem(defaultItem.asBoolean());
|
customItemOptions.defaultItem(defaultItem.getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
return customItemOptions.build();
|
return customItemOptions.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CustomItemData readItemMappingEntry(JsonNode node) throws InvalidCustomMappingsFileException {
|
public CustomItemData readItemMappingEntry(JsonObject node) throws InvalidCustomMappingsFileException {
|
||||||
if (node == null || !node.isObject()) {
|
if (node == null) {
|
||||||
throw new InvalidCustomMappingsFileException("Invalid item mappings entry");
|
throw new InvalidCustomMappingsFileException("Invalid item mappings entry");
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode name = node.get("name");
|
JsonElement name = node.get("name");
|
||||||
if (name == null || !name.isTextual() || name.asText().isEmpty()) {
|
if (name == null || !name.isJsonPrimitive() || name.getAsString().isEmpty()) {
|
||||||
throw new InvalidCustomMappingsFileException("An item entry has no name");
|
throw new InvalidCustomMappingsFileException("An item entry has no name");
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomItemData.Builder customItemData = CustomItemData.builder()
|
CustomItemData.Builder customItemData = CustomItemData.builder()
|
||||||
.name(name.asText())
|
.name(name.getAsString())
|
||||||
.customItemOptions(this.readItemCustomItemOptions(node));
|
.customItemOptions(this.readItemCustomItemOptions(node));
|
||||||
|
|
||||||
//The next entries are optional
|
//The next entries are optional
|
||||||
if (node.has("display_name")) {
|
if (node.has("display_name")) {
|
||||||
customItemData.displayName(node.get("display_name").asText());
|
customItemData.displayName(node.get("display_name").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("icon")) {
|
if (node.has("icon")) {
|
||||||
customItemData.icon(node.get("icon").asText());
|
customItemData.icon(node.get("icon").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("creative_category")) {
|
if (node.has("creative_category")) {
|
||||||
customItemData.creativeCategory(node.get("creative_category").asInt());
|
customItemData.creativeCategory(node.get("creative_category").getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("creative_group")) {
|
if (node.has("creative_group")) {
|
||||||
customItemData.creativeGroup(node.get("creative_group").asText());
|
customItemData.creativeGroup(node.get("creative_group").getAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("allow_offhand")) {
|
if (node.has("allow_offhand")) {
|
||||||
customItemData.allowOffhand(node.get("allow_offhand").asBoolean());
|
customItemData.allowOffhand(node.get("allow_offhand").getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("display_handheld")) {
|
if (node.has("display_handheld")) {
|
||||||
customItemData.displayHandheld(node.get("display_handheld").asBoolean());
|
customItemData.displayHandheld(node.get("display_handheld").getAsBoolean());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("texture_size")) {
|
if (node.has("texture_size")) {
|
||||||
customItemData.textureSize(node.get("texture_size").asInt());
|
customItemData.textureSize(node.get("texture_size").getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("render_offsets")) {
|
if (node.has("render_offsets")) {
|
||||||
JsonNode tmpNode = node.get("render_offsets");
|
JsonObject tmpNode = node.getAsJsonObject("render_offsets");
|
||||||
|
|
||||||
customItemData.renderOffsets(fromJsonNode(tmpNode));
|
customItemData.renderOffsets(fromJsonObject(tmpNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.get("tags") instanceof ArrayNode tags) {
|
if (node.get("tags") instanceof JsonArray tags) {
|
||||||
Set<String> tagsSet = new ObjectOpenHashSet<>();
|
Set<String> tagsSet = new ObjectOpenHashSet<>();
|
||||||
tags.forEach(tag -> tagsSet.add(tag.asText()));
|
tags.forEach(tag -> tagsSet.add(tag.getAsString()));
|
||||||
customItemData.tags(tagsSet);
|
customItemData.tags(tagsSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,26 +231,26 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
* Read a block mapping entry from a JSON node and Java identifier
|
* Read a block mapping entry from a JSON node and Java identifier
|
||||||
*
|
*
|
||||||
* @param identifier The Java identifier of the block
|
* @param identifier The Java identifier of the block
|
||||||
* @param node The {@link JsonNode} containing the block mapping entry
|
* @param node The {@link JsonObject} containing the block mapping entry
|
||||||
* @return The {@link CustomBlockMapping} record to be read by {@link org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator}
|
* @return The {@link CustomBlockMapping} record to be read by {@link org.geysermc.geyser.registry.populator.CustomBlockRegistryPopulator}
|
||||||
* @throws InvalidCustomMappingsFileException If the JSON node is invalid
|
* @throws InvalidCustomMappingsFileException If the JSON node is invalid
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public CustomBlockMapping readBlockMappingEntry(String identifier, JsonNode node) throws InvalidCustomMappingsFileException {
|
public CustomBlockMapping readBlockMappingEntry(String identifier, JsonObject node) throws InvalidCustomMappingsFileException {
|
||||||
if (node == null || !node.isObject()) {
|
if (node == null) {
|
||||||
throw new InvalidCustomMappingsFileException("Invalid block mappings entry:" + node);
|
throw new InvalidCustomMappingsFileException("Invalid block mappings entry:" + node);
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = node.get("name").asText();
|
String name = node.get("name").getAsString();
|
||||||
if (name == null || name.isEmpty()) {
|
if (name == null || name.isEmpty()) {
|
||||||
throw new InvalidCustomMappingsFileException("A block entry has no name");
|
throw new InvalidCustomMappingsFileException("A block entry has no name");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").asBoolean();
|
boolean includedInCreativeInventory = node.has("included_in_creative_inventory") && node.get("included_in_creative_inventory").getAsBoolean();
|
||||||
|
|
||||||
CreativeCategory creativeCategory = CreativeCategory.NONE;
|
CreativeCategory creativeCategory = CreativeCategory.NONE;
|
||||||
if (node.has("creative_category")) {
|
if (node.has("creative_category")) {
|
||||||
String categoryName = node.get("creative_category").asText();
|
String categoryName = node.get("creative_category").getAsString();
|
||||||
try {
|
try {
|
||||||
creativeCategory = CreativeCategory.valueOf(categoryName.toUpperCase());
|
creativeCategory = CreativeCategory.valueOf(categoryName.toUpperCase());
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
@@ -249,11 +260,11 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
String creativeGroup = "";
|
String creativeGroup = "";
|
||||||
if (node.has("creative_group")) {
|
if (node.has("creative_group")) {
|
||||||
creativeGroup = node.get("creative_group").asText();
|
creativeGroup = node.get("creative_group").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is true, we will only register the states the user has specified rather than all the possible block states
|
// If this is true, we will only register the states the user has specified rather than all the possible block states
|
||||||
boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").asBoolean();
|
boolean onlyOverrideStates = node.has("only_override_states") && node.get("only_override_states").getAsBoolean();
|
||||||
|
|
||||||
// Create the data for the overall block
|
// Create the data for the overall block
|
||||||
CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder()
|
CustomBlockData.Builder customBlockDataBuilder = new GeyserCustomBlockData.Builder()
|
||||||
@@ -273,12 +284,9 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
Map<String, CustomBlockComponentsMapping> componentsMap = new LinkedHashMap<>();
|
Map<String, CustomBlockComponentsMapping> componentsMap = new LinkedHashMap<>();
|
||||||
|
|
||||||
JsonNode stateOverrides = node.get("state_overrides");
|
if (node.get("state_overrides") instanceof JsonObject stateOverrides) {
|
||||||
if (stateOverrides != null && stateOverrides.isObject()) {
|
|
||||||
// Load components for specific Java block states
|
// Load components for specific Java block states
|
||||||
Iterator<Map.Entry<String, JsonNode>> fields = stateOverrides.fields();
|
for (Map.Entry<String, JsonElement> overrideEntry : stateOverrides.entrySet()) {
|
||||||
while (fields.hasNext()) {
|
|
||||||
Map.Entry<String, JsonNode> overrideEntry = fields.next();
|
|
||||||
String state = identifier + "[" + overrideEntry.getKey() + "]";
|
String state = identifier + "[" + overrideEntry.getKey() + "]";
|
||||||
if (!BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.get().containsKey(state)) {
|
if (!BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.get().containsKey(state)) {
|
||||||
throw new InvalidCustomMappingsFileException("Unknown Java block state: " + state + " for state_overrides.");
|
throw new InvalidCustomMappingsFileException("Unknown Java block state: " + state + " for state_overrides.");
|
||||||
@@ -358,12 +366,12 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
/**
|
/**
|
||||||
* Creates a {@link CustomBlockComponents} object for the passed state override or base block node, Java block state identifier, and custom block name
|
* Creates a {@link CustomBlockComponents} object for the passed state override or base block node, Java block state identifier, and custom block name
|
||||||
*
|
*
|
||||||
* @param node the state override or base block {@link JsonNode}
|
* @param element the state override or base block {@link JsonObject}
|
||||||
* @param stateKey the Java block state identifier
|
* @param stateKey the Java block state identifier
|
||||||
* @param name the name of the custom block
|
* @param name the name of the custom block
|
||||||
* @return the {@link CustomBlockComponents} object
|
* @return the {@link CustomBlockComponents} object
|
||||||
*/
|
*/
|
||||||
private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonNode node, String stateKey, String name) {
|
private CustomBlockComponentsMapping createCustomBlockComponentsMapping(JsonElement element, String stateKey, String name) {
|
||||||
// This is needed to find the correct selection box for the given block
|
// This is needed to find the correct selection box for the given block
|
||||||
int id = BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1);
|
int id = BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.getOrDefault(stateKey, -1);
|
||||||
BoxComponent boxComponent = createBoxComponent(id);
|
BoxComponent boxComponent = createBoxComponent(id);
|
||||||
@@ -372,7 +380,7 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
.collisionBox(boxComponent)
|
.collisionBox(boxComponent)
|
||||||
.selectionBox(boxComponent);
|
.selectionBox(boxComponent);
|
||||||
|
|
||||||
if (node == null) {
|
if (!(element instanceof JsonObject node)) {
|
||||||
// No other components were defined
|
// No other components were defined
|
||||||
return new CustomBlockComponentsMapping(builder.build(), extendedBoxComponent);
|
return new CustomBlockComponentsMapping(builder.build(), extendedBoxComponent);
|
||||||
}
|
}
|
||||||
@@ -394,28 +402,28 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
// We set this to max value by default so that we may dictate the correct destroy time ourselves
|
// We set this to max value by default so that we may dictate the correct destroy time ourselves
|
||||||
float destructibleByMining = Float.MAX_VALUE;
|
float destructibleByMining = Float.MAX_VALUE;
|
||||||
if (node.has("destructible_by_mining")) {
|
if (node.has("destructible_by_mining")) {
|
||||||
destructibleByMining = node.get("destructible_by_mining").floatValue();
|
destructibleByMining = node.get("destructible_by_mining").getAsFloat();
|
||||||
}
|
}
|
||||||
builder.destructibleByMining(destructibleByMining);
|
builder.destructibleByMining(destructibleByMining);
|
||||||
|
|
||||||
if (node.has("geometry")) {
|
if (node.has("geometry")) {
|
||||||
if (node.get("geometry").isTextual()) {
|
if (node.get("geometry").isJsonPrimitive()) {
|
||||||
builder.geometry(new GeyserGeometryComponent.Builder()
|
builder.geometry(new GeyserGeometryComponent.Builder()
|
||||||
.identifier(node.get("geometry").asText())
|
.identifier(node.get("geometry").getAsString())
|
||||||
.build());
|
.build());
|
||||||
} else {
|
} else {
|
||||||
JsonNode geometry = node.get("geometry");
|
JsonObject geometry = node.getAsJsonObject("geometry");
|
||||||
GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder();
|
GeometryComponent.Builder geometryBuilder = new GeyserGeometryComponent.Builder();
|
||||||
if (geometry.has("identifier")) {
|
if (geometry.has("identifier")) {
|
||||||
geometryBuilder.identifier(geometry.get("identifier").asText());
|
geometryBuilder.identifier(geometry.get("identifier").getAsString());
|
||||||
}
|
}
|
||||||
if (geometry.has("bone_visibility")) {
|
if (geometry.has("bone_visibility")) {
|
||||||
JsonNode boneVisibility = geometry.get("bone_visibility");
|
if (geometry.get("bone_visibility") instanceof JsonObject boneVisibility) {
|
||||||
if (boneVisibility.isObject()) {
|
|
||||||
Map<String, String> boneVisibilityMap = new Object2ObjectOpenHashMap<>();
|
Map<String, String> boneVisibilityMap = new Object2ObjectOpenHashMap<>();
|
||||||
boneVisibility.fields().forEachRemaining(entry -> {
|
boneVisibility.entrySet().forEach(entry -> {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
String value = entry.getValue().isBoolean() ? (entry.getValue().asBoolean() ? "1" : "0") : entry.getValue().asText();
|
String value = entry.getValue() instanceof JsonPrimitive primitive && primitive.isBoolean()
|
||||||
|
? (entry.getValue().getAsBoolean() ? "1" : "0") : entry.getValue().getAsString();
|
||||||
boneVisibilityMap.put(key, value);
|
boneVisibilityMap.put(key, value);
|
||||||
});
|
});
|
||||||
geometryBuilder.boneVisibility(boneVisibilityMap);
|
geometryBuilder.boneVisibility(boneVisibilityMap);
|
||||||
@@ -427,30 +435,30 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
|
|
||||||
String displayName = name;
|
String displayName = name;
|
||||||
if (node.has("display_name")) {
|
if (node.has("display_name")) {
|
||||||
displayName = node.get("display_name").asText();
|
displayName = node.get("display_name").getAsString();
|
||||||
}
|
}
|
||||||
builder.displayName(displayName);
|
builder.displayName(displayName);
|
||||||
|
|
||||||
if (node.has("friction")) {
|
if (node.has("friction")) {
|
||||||
builder.friction(node.get("friction").floatValue());
|
builder.friction(node.get("friction").getAsFloat());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("light_emission")) {
|
if (node.has("light_emission")) {
|
||||||
builder.lightEmission(node.get("light_emission").asInt());
|
builder.lightEmission(node.get("light_emission").getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("light_dampening")) {
|
if (node.has("light_dampening")) {
|
||||||
builder.lightDampening(node.get("light_dampening").asInt());
|
builder.lightDampening(node.get("light_dampening").getAsInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean placeAir = true;
|
boolean placeAir = true;
|
||||||
if (node.has("place_air")) {
|
if (node.has("place_air")) {
|
||||||
placeAir = node.get("place_air").asBoolean();
|
placeAir = node.get("place_air").getAsBoolean();
|
||||||
}
|
}
|
||||||
builder.placeAir(placeAir);
|
builder.placeAir(placeAir);
|
||||||
|
|
||||||
if (node.has("transformation")) {
|
if (node.has("transformation")) {
|
||||||
JsonNode transformation = node.get("transformation");
|
JsonObject transformation = node.getAsJsonObject("transformation");
|
||||||
|
|
||||||
int rotationX = 0;
|
int rotationX = 0;
|
||||||
int rotationY = 0;
|
int rotationY = 0;
|
||||||
@@ -463,22 +471,22 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
float transformZ = 0;
|
float transformZ = 0;
|
||||||
|
|
||||||
if (transformation.has("rotation")) {
|
if (transformation.has("rotation")) {
|
||||||
JsonNode rotation = transformation.get("rotation");
|
JsonArray rotation = transformation.getAsJsonArray("rotation");
|
||||||
rotationX = rotation.get(0).asInt();
|
rotationX = rotation.get(0).getAsInt();
|
||||||
rotationY = rotation.get(1).asInt();
|
rotationY = rotation.get(1).getAsInt();
|
||||||
rotationZ = rotation.get(2).asInt();
|
rotationZ = rotation.get(2).getAsInt();
|
||||||
}
|
}
|
||||||
if (transformation.has("scale")) {
|
if (transformation.has("scale")) {
|
||||||
JsonNode scale = transformation.get("scale");
|
JsonArray scale = transformation.getAsJsonArray("scale");
|
||||||
scaleX = scale.get(0).floatValue();
|
scaleX = scale.get(0).getAsFloat();
|
||||||
scaleY = scale.get(1).floatValue();
|
scaleY = scale.get(1).getAsFloat();
|
||||||
scaleZ = scale.get(2).floatValue();
|
scaleZ = scale.get(2).getAsFloat();
|
||||||
}
|
}
|
||||||
if (transformation.has("translation")) {
|
if (transformation.has("translation")) {
|
||||||
JsonNode translation = transformation.get("translation");
|
JsonArray translation = transformation.getAsJsonArray("translation");
|
||||||
transformX = translation.get(0).floatValue();
|
transformX = translation.get(0).getAsFloat();
|
||||||
transformY = translation.get(1).floatValue();
|
transformY = translation.get(1).getAsFloat();
|
||||||
transformZ = translation.get(2).floatValue();
|
transformZ = translation.get(2).getAsFloat();
|
||||||
}
|
}
|
||||||
builder.transformation(new TransformationComponent(rotationX, rotationY, rotationZ, scaleX, scaleY, scaleZ, transformX, transformY, transformZ));
|
builder.transformation(new TransformationComponent(rotationX, rotationY, rotationZ, scaleX, scaleY, scaleZ, transformX, transformY, transformZ));
|
||||||
}
|
}
|
||||||
@@ -490,12 +498,10 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("material_instances")) {
|
if (node.has("material_instances")) {
|
||||||
JsonNode materialInstances = node.get("material_instances");
|
if (node.get("material_instances") instanceof JsonObject materialInstances) {
|
||||||
if (materialInstances.isObject()) {
|
materialInstances.entrySet().forEach(entry -> {
|
||||||
materialInstances.fields().forEachRemaining(entry -> {
|
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
JsonNode value = entry.getValue();
|
if (entry.getValue() instanceof JsonObject value) {
|
||||||
if (value.isObject()) {
|
|
||||||
MaterialInstance materialInstance = createMaterialInstanceComponent(value);
|
MaterialInstance materialInstance = createMaterialInstanceComponent(value);
|
||||||
builder.materialInstance(key, materialInstance);
|
builder.materialInstance(key, materialInstance);
|
||||||
}
|
}
|
||||||
@@ -503,27 +509,21 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.has("placement_filter")) {
|
if (node.get("placement_filter") instanceof JsonObject placementFilter) {
|
||||||
JsonNode placementFilter = node.get("placement_filter");
|
if (placementFilter.get("conditions") instanceof JsonArray conditions) {
|
||||||
if (placementFilter.isObject()) {
|
|
||||||
if (placementFilter.has("conditions")) {
|
|
||||||
JsonNode conditions = placementFilter.get("conditions");
|
|
||||||
if (conditions.isArray()) {
|
|
||||||
List<PlacementConditions> filter = createPlacementFilterComponent(conditions);
|
List<PlacementConditions> filter = createPlacementFilterComponent(conditions);
|
||||||
builder.placementFilter(filter);
|
builder.placementFilter(filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tags can be applied so that blocks will match return true when queried for the tag
|
// Tags can be applied so that blocks will match return true when queried for the tag
|
||||||
// Potentially useful for resource pack creators
|
// Potentially useful for resource pack creators
|
||||||
// Ideally we could programmatically extract the tags here https://wiki.bedrock.dev/blocks/block-tags.html
|
// Ideally we could programmatically extract the tags here https://wiki.bedrock.dev/blocks/block-tags.html
|
||||||
// This would let us automatically apply the correct vanilla tags to blocks
|
// This would let us automatically apply the correct vanilla tags to blocks
|
||||||
// However, its worth noting that vanilla tools do not currently honor these tags anyway
|
// However, its worth noting that vanilla tools do not currently honor these tags anyway
|
||||||
if (node.get("tags") instanceof ArrayNode tags) {
|
if (node.get("tags") instanceof JsonArray tags) {
|
||||||
Set<String> tagsSet = new ObjectOpenHashSet<>();
|
Set<String> tagsSet = new ObjectOpenHashSet<>();
|
||||||
tags.forEach(tag -> tagsSet.add(tag.asText()));
|
tags.forEach(tag -> tagsSet.add(tag.getAsString()));
|
||||||
builder.tags(tagsSet);
|
builder.tags(tagsSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -613,21 +613,21 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
/**
|
/**
|
||||||
* Creates a {@link BoxComponent} from a JSON Node
|
* Creates a {@link BoxComponent} from a JSON Node
|
||||||
*
|
*
|
||||||
* @param node the JSON node
|
* @param element the JSON node
|
||||||
* @return the {@link BoxComponent}
|
* @return the {@link BoxComponent}
|
||||||
*/
|
*/
|
||||||
private @Nullable BoxComponent createBoxComponent(JsonNode node) {
|
private @Nullable BoxComponent createBoxComponent(JsonElement element) {
|
||||||
if (node != null && node.isObject()) {
|
if (element instanceof JsonObject node) {
|
||||||
if (node.has("origin") && node.has("size")) {
|
if (node.has("origin") && node.has("size")) {
|
||||||
JsonNode origin = node.get("origin");
|
JsonArray origin = node.getAsJsonArray("origin");
|
||||||
float originX = origin.get(0).floatValue();
|
float originX = origin.get(0).getAsFloat();
|
||||||
float originY = origin.get(1).floatValue();
|
float originY = origin.get(1).getAsFloat();
|
||||||
float originZ = origin.get(2).floatValue();
|
float originZ = origin.get(2).getAsFloat();
|
||||||
|
|
||||||
JsonNode size = node.get("size");
|
JsonArray size = node.getAsJsonArray("size");
|
||||||
float sizeX = size.get(0).floatValue();
|
float sizeX = size.get(0).getAsFloat();
|
||||||
float sizeY = size.get(1).floatValue();
|
float sizeY = size.get(1).getAsFloat();
|
||||||
float sizeZ = size.get(2).floatValue();
|
float sizeZ = size.get(2).getAsFloat();
|
||||||
|
|
||||||
return new BoxComponent(originX, originY, originZ, sizeX, sizeY, sizeZ);
|
return new BoxComponent(originX, originY, originZ, sizeX, sizeY, sizeZ);
|
||||||
}
|
}
|
||||||
@@ -642,26 +642,26 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
* @param node the material instance node
|
* @param node the material instance node
|
||||||
* @return the {@link MaterialInstance}
|
* @return the {@link MaterialInstance}
|
||||||
*/
|
*/
|
||||||
private MaterialInstance createMaterialInstanceComponent(JsonNode node) {
|
private MaterialInstance createMaterialInstanceComponent(JsonObject node) {
|
||||||
// Set default values, and use what the user provides if they have provided something
|
// Set default values, and use what the user provides if they have provided something
|
||||||
String texture = null;
|
String texture = null;
|
||||||
if (node.has("texture")) {
|
if (node.has("texture")) {
|
||||||
texture = node.get("texture").asText();
|
texture = node.get("texture").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
String renderMethod = "opaque";
|
String renderMethod = "opaque";
|
||||||
if (node.has("render_method")) {
|
if (node.has("render_method")) {
|
||||||
renderMethod = node.get("render_method").asText();
|
renderMethod = node.get("render_method").getAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean faceDimming = true;
|
boolean faceDimming = true;
|
||||||
if (node.has("face_dimming")) {
|
if (node.has("face_dimming")) {
|
||||||
faceDimming = node.get("face_dimming").asBoolean();
|
faceDimming = node.get("face_dimming").getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean ambientOcclusion = true;
|
boolean ambientOcclusion = true;
|
||||||
if (node.has("ambient_occlusion")) {
|
if (node.has("ambient_occlusion")) {
|
||||||
ambientOcclusion = node.get("ambient_occlusion").asBoolean();
|
ambientOcclusion = node.get("ambient_occlusion").getAsBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new GeyserMaterialInstance.Builder()
|
return new GeyserMaterialInstance.Builder()
|
||||||
@@ -678,32 +678,33 @@ public class MappingsReader_v1 extends MappingsReader {
|
|||||||
* @param node the conditions node
|
* @param node the conditions node
|
||||||
* @return the list of {@link PlacementConditions}
|
* @return the list of {@link PlacementConditions}
|
||||||
*/
|
*/
|
||||||
private List<PlacementConditions> createPlacementFilterComponent(JsonNode node) {
|
private List<PlacementConditions> createPlacementFilterComponent(JsonArray node) {
|
||||||
List<PlacementConditions> conditions = new ArrayList<>();
|
List<PlacementConditions> conditions = new ArrayList<>();
|
||||||
|
|
||||||
// The structure of the placement filter component is the most complex of the current components
|
// The structure of the placement filter component is the most complex of the current components
|
||||||
// Each condition effectively separated into two arrays: one of allowed faces, and one of blocks/block Molang queries
|
// Each condition effectively separated into two arrays: one of allowed faces, and one of blocks/block Molang queries
|
||||||
node.forEach(condition -> {
|
node.forEach(json -> {
|
||||||
|
if (!(json instanceof JsonObject condition)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Set<Face> faces = EnumSet.noneOf(Face.class);
|
Set<Face> faces = EnumSet.noneOf(Face.class);
|
||||||
if (condition.has("allowed_faces")) {
|
if (condition.has("allowed_faces")) {
|
||||||
JsonNode allowedFaces = condition.get("allowed_faces");
|
if (condition.get("allowed_faces") instanceof JsonArray allowedFaces) {
|
||||||
if (allowedFaces.isArray()) {
|
allowedFaces.forEach(face -> faces.add(Face.valueOf(face.getAsString().toUpperCase())));
|
||||||
allowedFaces.forEach(face -> faces.add(Face.valueOf(face.asText().toUpperCase())));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LinkedHashMap<String, BlockFilterType> blockFilters = new LinkedHashMap<>();
|
LinkedHashMap<String, BlockFilterType> blockFilters = new LinkedHashMap<>();
|
||||||
if (condition.has("block_filter")) {
|
if (condition.has("block_filter")) {
|
||||||
JsonNode blockFilter = condition.get("block_filter");
|
if (condition.get("block_filter") instanceof JsonArray blockFilter) {
|
||||||
if (blockFilter.isArray()) {
|
|
||||||
blockFilter.forEach(filter -> {
|
blockFilter.forEach(filter -> {
|
||||||
if (filter.isObject()) {
|
if (filter instanceof JsonObject jsonObject) {
|
||||||
if (filter.has("tags")) {
|
if (jsonObject.has("tags")) {
|
||||||
JsonNode tags = filter.get("tags");
|
JsonElement tags = jsonObject.get("tags");
|
||||||
blockFilters.put(tags.asText(), BlockFilterType.TAG);
|
blockFilters.put(tags.getAsString(), BlockFilterType.TAG);
|
||||||
}
|
}
|
||||||
} else if (filter.isTextual()) {
|
} else if (filter instanceof JsonPrimitive primitive && primitive.isString()) {
|
||||||
blockFilters.put(filter.asText(), BlockFilterType.BLOCK);
|
blockFilters.put(filter.getAsString(), BlockFilterType.BLOCK);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,12 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.populator;
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Interner;
|
import com.google.common.collect.Interner;
|
||||||
import com.google.common.collect.Interners;
|
import com.google.common.collect.Interners;
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
@@ -65,6 +66,7 @@ import org.geysermc.geyser.registry.populator.conversion.Conversion827_819;
|
|||||||
import org.geysermc.geyser.registry.populator.conversion.Conversion844_827;
|
import org.geysermc.geyser.registry.populator.conversion.Conversion844_827;
|
||||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||||
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -441,21 +443,21 @@ public final class BlockRegistryPopulator {
|
|||||||
BLOCKS_NBT = blocksNbt;
|
BLOCKS_NBT = blocksNbt;
|
||||||
JAVA_BLOCKS_SIZE = blocksNbt.size();
|
JAVA_BLOCKS_SIZE = blocksNbt.size();
|
||||||
|
|
||||||
JsonNode blockInteractionsJson;
|
JsonObject blockInteractionsJson;
|
||||||
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
|
try (InputStream stream = GeyserImpl.getInstance().getBootstrap().getResourceOrThrow("mappings/interactions.json")) {
|
||||||
blockInteractionsJson = GeyserImpl.JSON_MAPPER.readTree(stream);
|
blockInteractionsJson = JsonUtils.fromJson(stream);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load Java block interaction mappings", e);
|
throw new AssertionError("Unable to load Java block interaction mappings", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockRegistries.INTERACTIVE.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("always_consumes")));
|
BlockRegistries.INTERACTIVE.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("always_consumes")));
|
||||||
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet((ArrayNode) blockInteractionsJson.get("requires_may_build")));
|
BlockRegistries.INTERACTIVE_MAY_BUILD.set(toBlockStateSet(blockInteractionsJson.getAsJsonArray("requires_may_build")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BitSet toBlockStateSet(ArrayNode node) {
|
private static BitSet toBlockStateSet(JsonArray node) {
|
||||||
BitSet blockStateSet = new BitSet(node.size());
|
BitSet blockStateSet = new BitSet(node.size());
|
||||||
for (JsonNode javaIdentifier : node) {
|
for (JsonElement javaIdentifier : node) {
|
||||||
blockStateSet.set(BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.textValue()));
|
blockStateSet.set(BlockRegistries.JAVA_BLOCK_STATE_IDENTIFIER_TO_ID.get().getInt(javaIdentifier.getAsString()));
|
||||||
}
|
}
|
||||||
return blockStateSet;
|
return blockStateSet;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,9 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.populator;
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import org.cloudburstmc.nbt.NbtMap;
|
import org.cloudburstmc.nbt.NbtMap;
|
||||||
import org.cloudburstmc.nbt.NbtMapBuilder;
|
import org.cloudburstmc.nbt.NbtMapBuilder;
|
||||||
@@ -40,6 +42,7 @@ import org.geysermc.geyser.GeyserImpl;
|
|||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.registry.type.BlockMappings;
|
import org.geysermc.geyser.registry.type.BlockMappings;
|
||||||
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
import org.geysermc.geyser.registry.type.GeyserBedrockBlock;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
import org.geysermc.geyser.registry.type.GeyserMappingItem;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -62,20 +65,21 @@ public class CreativeItemRegistryPopulator {
|
|||||||
static List<CreativeItemGroup> readCreativeItemGroups(ItemRegistryPopulator.PaletteVersion palette, List<CreativeItemData> creativeItemData) {
|
static List<CreativeItemGroup> readCreativeItemGroups(ItemRegistryPopulator.PaletteVersion palette, List<CreativeItemData> creativeItemData) {
|
||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
JsonNode creativeItemEntries;
|
JsonArray creativeItemEntries;
|
||||||
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/creative_items.%s.json", palette.version()))) {
|
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/creative_items.%s.json", palette.version()))) {
|
||||||
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("groups");
|
creativeItemEntries = JsonUtils.fromJson(stream).getAsJsonArray("groups");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load creative item groups", e);
|
throw new AssertionError("Unable to load creative item groups", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<CreativeItemGroup> creativeItemGroups = new ArrayList<>();
|
List<CreativeItemGroup> creativeItemGroups = new ArrayList<>();
|
||||||
for (JsonNode creativeItemEntry : creativeItemEntries) {
|
for (JsonElement creativeItemEntry : creativeItemEntries) {
|
||||||
CreativeItemCategory category = CreativeItemCategory.valueOf(creativeItemEntry.get("category").asText().toUpperCase(Locale.ROOT));
|
JsonObject creativeItemEntryObject = creativeItemEntry.getAsJsonObject();
|
||||||
String name = creativeItemEntry.get("name").asText();
|
CreativeItemCategory category = CreativeItemCategory.valueOf(creativeItemEntryObject.get("category").getAsString().toUpperCase(Locale.ROOT));
|
||||||
|
String name = creativeItemEntryObject.get("name").getAsString();
|
||||||
|
|
||||||
JsonNode icon = creativeItemEntry.get("icon");
|
JsonElement icon = creativeItemEntryObject.get("icon");
|
||||||
String identifier = icon.get("id").asText();
|
String identifier = icon.getAsJsonObject().get("id").getAsString();
|
||||||
|
|
||||||
ItemData itemData;
|
ItemData itemData;
|
||||||
if (identifier.equals("minecraft:air")) {
|
if (identifier.equals("minecraft:air")) {
|
||||||
@@ -98,32 +102,33 @@ public class CreativeItemRegistryPopulator {
|
|||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
// Load creative items
|
// Load creative items
|
||||||
JsonNode creativeItemEntries;
|
JsonArray creativeItemEntries;
|
||||||
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/creative_items.%s.json", palette.version()))) {
|
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/creative_items.%s.json", palette.version()))) {
|
||||||
creativeItemEntries = GeyserImpl.JSON_MAPPER.readTree(stream).get("items");
|
creativeItemEntries = JsonUtils.fromJson(stream).getAsJsonArray("items");
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load creative items", e);
|
throw new AssertionError("Unable to load creative items", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion());
|
BlockMappings blockMappings = BlockRegistries.BLOCKS.forVersion(palette.protocolVersion());
|
||||||
for (JsonNode itemNode : creativeItemEntries) {
|
for (JsonElement itemNode : creativeItemEntries) {
|
||||||
ItemData.Builder itemBuilder = createItemData(itemNode, items, blockMappings, definitions);
|
ItemData.Builder itemBuilder = createItemData((JsonObject) itemNode, items, blockMappings, definitions);
|
||||||
if (itemBuilder == null) {
|
if (itemBuilder == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupId = itemNode.get("groupId") != null ? itemNode.get("groupId").asInt() : 0;
|
var groupIdElement = itemNode.getAsJsonObject().get("groupId");
|
||||||
|
int groupId = groupIdElement != null ? groupIdElement.getAsInt() : 0;
|
||||||
|
|
||||||
itemConsumer.accept(itemBuilder, groupId);
|
itemConsumer.accept(itemBuilder, groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ItemData.@Nullable Builder createItemData(JsonNode itemNode, Map<String, GeyserMappingItem> items, BlockMappings blockMappings, Map<String, ItemDefinition> definitions) {
|
private static ItemData.@Nullable Builder createItemData(JsonObject itemNode, Map<String, GeyserMappingItem> items, BlockMappings blockMappings, Map<String, ItemDefinition> definitions) {
|
||||||
int count = 1;
|
int count = 1;
|
||||||
int damage = 0;
|
int damage = 0;
|
||||||
NbtMap tag = null;
|
NbtMap tag = null;
|
||||||
|
|
||||||
String identifier = itemNode.get("id").textValue();
|
String identifier = itemNode.get("id").getAsString();
|
||||||
for (BiPredicate<String, Integer> predicate : JAVA_ONLY_ITEM_FILTER) {
|
for (BiPredicate<String, Integer> predicate : JAVA_ONLY_ITEM_FILTER) {
|
||||||
if (predicate.test(identifier, damage)) {
|
if (predicate.test(identifier, damage)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -150,20 +155,20 @@ public class CreativeItemRegistryPopulator {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode damageNode = itemNode.get("damage");
|
JsonElement damageNode = itemNode.get("damage");
|
||||||
if (damageNode != null) {
|
if (damageNode != null) {
|
||||||
damage = damageNode.asInt();
|
damage = damageNode.getAsInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode countNode = itemNode.get("count");
|
JsonElement countNode = itemNode.get("count");
|
||||||
if (countNode != null) {
|
if (countNode != null) {
|
||||||
count = countNode.asInt();
|
count = countNode.getAsInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
GeyserBedrockBlock blockDefinition = null;
|
GeyserBedrockBlock blockDefinition = null;
|
||||||
JsonNode blockStateNode;
|
JsonElement blockStateNode;
|
||||||
if ((blockStateNode = itemNode.get("block_state_b64")) != null) {
|
if ((blockStateNode = itemNode.get("block_state_b64")) != null) {
|
||||||
byte[] bytes = Base64.getDecoder().decode(blockStateNode.asText());
|
byte[] bytes = Base64.getDecoder().decode(blockStateNode.getAsString());
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||||
try {
|
try {
|
||||||
NbtMap stateTag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
NbtMap stateTag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
||||||
@@ -182,9 +187,9 @@ public class CreativeItemRegistryPopulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode nbtNode = itemNode.get("nbt_b64");
|
JsonElement nbtNode = itemNode.get("nbt_b64");
|
||||||
if (nbtNode != null) {
|
if (nbtNode != null) {
|
||||||
byte[] bytes = Base64.getDecoder().decode(nbtNode.asText());
|
byte[] bytes = Base64.getDecoder().decode(nbtNode.getAsString());
|
||||||
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
|
||||||
try {
|
try {
|
||||||
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
tag = (NbtMap) NbtUtils.createReaderLE(bais).readTag();
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ public class CustomBlockRegistryPopulator {
|
|||||||
* @param stage the stage to populate
|
* @param stage the stage to populate
|
||||||
*/
|
*/
|
||||||
public static void populate(Stage stage) {
|
public static void populate(Stage stage) {
|
||||||
if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
if (!GeyserImpl.getInstance().config().gameplay().enableCustomContent()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ public class CustomSkullRegistryPopulator {
|
|||||||
SkullResourcePackManager.SKULL_SKINS.clear(); // Remove skins after reloading
|
SkullResourcePackManager.SKULL_SKINS.clear(); // Remove skins after reloading
|
||||||
BlockRegistries.CUSTOM_SKULLS.set(Object2ObjectMaps.emptyMap());
|
BlockRegistries.CUSTOM_SKULLS.set(Object2ObjectMaps.emptyMap());
|
||||||
|
|
||||||
if (!GeyserImpl.getInstance().getConfig().isAddNonBedrockItems()) {
|
if (!GeyserImpl.getInstance().config().gameplay().enableCustomContent()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,9 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.populator;
|
package org.geysermc.geyser.registry.populator;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.MultimapBuilder;
|
import com.google.common.collect.MultimapBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
import it.unimi.dsi.fastutil.Pair;
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
@@ -82,8 +82,10 @@ import org.geysermc.geyser.registry.type.ItemMapping;
|
|||||||
import org.geysermc.geyser.registry.type.ItemMappings;
|
import org.geysermc.geyser.registry.type.ItemMappings;
|
||||||
import org.geysermc.geyser.registry.type.NonVanillaItemRegistration;
|
import org.geysermc.geyser.registry.type.NonVanillaItemRegistration;
|
||||||
import org.geysermc.geyser.registry.type.PaletteItem;
|
import org.geysermc.geyser.registry.type.PaletteItem;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -201,17 +203,17 @@ public class ItemRegistryPopulator {
|
|||||||
|
|
||||||
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
GeyserBootstrap bootstrap = GeyserImpl.getInstance().getBootstrap();
|
||||||
|
|
||||||
TypeReference<Map<String, GeyserMappingItem>> mappingItemsType = new TypeReference<>() { };
|
Type mappingItemsType = new TypeToken<Map<String, GeyserMappingItem>>() { }.getType();
|
||||||
|
|
||||||
Map<String, GeyserMappingItem> items;
|
Map<String, GeyserMappingItem> items;
|
||||||
try (InputStream stream = bootstrap.getResourceOrThrow("mappings/items.json")) {
|
try (InputStream stream = bootstrap.getResourceOrThrow("mappings/items.json")) {
|
||||||
// Load item mappings from Java Edition to Bedrock Edition
|
// Load item mappings from Java Edition to Bedrock Edition
|
||||||
items = GeyserImpl.JSON_MAPPER.readValue(stream, mappingItemsType);
|
items = JsonUtils.fromJson(stream, mappingItemsType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
throw new AssertionError("Unable to load Java runtime item IDs", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean customItemsAllowed = GeyserImpl.getInstance().getConfig().isAddNonBedrockItems();
|
boolean customItemsAllowed = GeyserImpl.getInstance().config().gameplay().enableCustomContent();
|
||||||
|
|
||||||
// List values here is important compared to HashSet - we need to preserve the order of what's given to us
|
// List values here is important compared to HashSet - we need to preserve the order of what's given to us
|
||||||
// (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom
|
// (as of 1.19.2 Java) to replicate some edge cases in Java predicate behavior where it checks from the bottom
|
||||||
@@ -228,11 +230,11 @@ public class ItemRegistryPopulator {
|
|||||||
|
|
||||||
/* Load item palette */
|
/* Load item palette */
|
||||||
for (PaletteVersion palette : paletteVersions) {
|
for (PaletteVersion palette : paletteVersions) {
|
||||||
TypeReference<List<PaletteItem>> paletteEntriesType = new TypeReference<>() {};
|
Type paletteEntriesType = new TypeToken<List<PaletteItem>>() { }.getType();
|
||||||
|
|
||||||
List<PaletteItem> itemEntries;
|
List<PaletteItem> itemEntries;
|
||||||
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/runtime_item_states.%s.json", palette.version()))) {
|
try (InputStream stream = bootstrap.getResourceOrThrow(String.format("bedrock/runtime_item_states.%s.json", palette.version()))) {
|
||||||
itemEntries = GeyserImpl.JSON_MAPPER.readValue(stream, paletteEntriesType);
|
itemEntries = JsonUtils.fromJson(stream, paletteEntriesType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
throw new AssertionError("Unable to load Bedrock runtime item IDs", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.registry.type;
|
package org.geysermc.geyser.registry.type;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@@ -43,13 +43,13 @@ import lombok.With;
|
|||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
public class GeyserMappingItem {
|
public class GeyserMappingItem {
|
||||||
@JsonProperty("bedrock_identifier") String bedrockIdentifier;
|
@SerializedName("bedrock_identifier") String bedrockIdentifier;
|
||||||
@JsonProperty("bedrock_data") int bedrockData;
|
@SerializedName("bedrock_data") int bedrockData;
|
||||||
Integer firstBlockRuntimeId;
|
Integer firstBlockRuntimeId;
|
||||||
Integer lastBlockRuntimeId;
|
Integer lastBlockRuntimeId;
|
||||||
@JsonProperty("tool_type") String toolType;
|
@SerializedName("tool_type") String toolType;
|
||||||
@JsonProperty("armor_type") String armorType;
|
@SerializedName("armor_type") String armorType;
|
||||||
@JsonProperty("protection_value") int protectionValue;
|
@SerializedName("protection_value") int protectionValue;
|
||||||
@JsonProperty("is_edible") boolean edible = false;
|
@SerializedName("is_edible") boolean edible = false;
|
||||||
@JsonProperty("is_entity_placer") boolean entityPlacer = false;
|
@SerializedName("is_entity_placer") boolean entityPlacer = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,9 @@ import static org.geysermc.geyser.scoreboard.UpdateType.REMOVE;
|
|||||||
*/
|
*/
|
||||||
public final class Scoreboard {
|
public final class Scoreboard {
|
||||||
private static final boolean SHOW_SCOREBOARD_LOGS = Boolean.parseBoolean(System.getProperty("Geyser.ShowScoreboardLogs", "true"));
|
private static final boolean SHOW_SCOREBOARD_LOGS = Boolean.parseBoolean(System.getProperty("Geyser.ShowScoreboardLogs", "true"));
|
||||||
private static final boolean ADD_TEAM_SUGGESTIONS = Boolean.parseBoolean(System.getProperty("Geyser.AddTeamSuggestions", "true"));
|
private static final boolean ADD_TEAM_SUGGESTIONS = Boolean.parseBoolean(
|
||||||
|
System.getProperty("Geyser.AddTeamSuggestions", String.valueOf(GeyserImpl.getInstance().config().advanced().addTeamSuggestions()))
|
||||||
|
);
|
||||||
|
|
||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
private final GeyserLogger logger;
|
private final GeyserLogger logger;
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ package org.geysermc.geyser.scoreboard;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.WorldCache;
|
import org.geysermc.geyser.session.cache.WorldCache;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
@@ -46,9 +46,9 @@ public final class ScoreboardUpdater extends Thread {
|
|||||||
private static final boolean DEBUG_ENABLED;
|
private static final boolean DEBUG_ENABLED;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
GeyserConfiguration config = GeyserImpl.getInstance().getConfig();
|
GeyserConfig config = GeyserImpl.getInstance().config();
|
||||||
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
|
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.advanced().scoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
|
||||||
DEBUG_ENABLED = config.isDebugMode();
|
DEBUG_ENABLED = config.debugMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final GeyserImpl geyser = GeyserImpl.getInstance();
|
private final GeyserImpl geyser = GeyserImpl.getInstance();
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.session;
|
package org.geysermc.geyser.session;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
@@ -125,8 +124,7 @@ import org.geysermc.geyser.api.skin.SkinData;
|
|||||||
import org.geysermc.geyser.api.util.PlatformType;
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.CommandRegistry;
|
import org.geysermc.geyser.command.CommandRegistry;
|
||||||
import org.geysermc.geyser.command.GeyserCommandSource;
|
import org.geysermc.geyser.command.GeyserCommandSource;
|
||||||
import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.GeyserEntityData;
|
import org.geysermc.geyser.entity.GeyserEntityData;
|
||||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||||
@@ -256,8 +254,6 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
@Getter
|
@Getter
|
||||||
public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
||||||
|
|
||||||
private static final Gson GSON = new Gson();
|
|
||||||
|
|
||||||
private final GeyserImpl geyser;
|
private final GeyserImpl geyser;
|
||||||
private final UpstreamSession upstream;
|
private final UpstreamSession upstream;
|
||||||
private DownstreamSession downstream;
|
private DownstreamSession downstream;
|
||||||
@@ -715,7 +711,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server.
|
* A cache of IDs from ClientboundKeepAlivePackets that have been sent to the Bedrock client, but haven't been returned to the server.
|
||||||
* Only used if {@link GeyserConfiguration#isForwardPlayerPing()} is enabled.
|
* Only used if {@link GeyserConfig.GameplayConfig#forwardPlayerPing()} is enabled.
|
||||||
*/
|
*/
|
||||||
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
private final Queue<Long> keepAliveCache = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
@@ -753,6 +749,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
@Accessors(fluent = true)
|
@Accessors(fluent = true)
|
||||||
private boolean hasAcceptedCodeOfConduct = false;
|
private boolean hasAcceptedCodeOfConduct = false;
|
||||||
|
|
||||||
|
@Accessors(fluent = true)
|
||||||
|
@Setter
|
||||||
|
private boolean integratedPackActive = false;
|
||||||
|
|
||||||
private final Set<InputLocksFlag> inputLocksSet = EnumSet.noneOf(InputLocksFlag.class);
|
private final Set<InputLocksFlag> inputLocksSet = EnumSet.noneOf(InputLocksFlag.class);
|
||||||
private boolean inputLockDirty;
|
private boolean inputLockDirty;
|
||||||
|
|
||||||
@@ -799,12 +799,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
this.spawned = false;
|
this.spawned = false;
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
|
|
||||||
if (geyser.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) {
|
|
||||||
this.emotes = new HashSet<>();
|
this.emotes = new HashSet<>();
|
||||||
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
geyser.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes()));
|
||||||
} else {
|
|
||||||
this.emotes = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.remoteServer = geyser.defaultRemoteServer();
|
this.remoteServer = geyser.defaultRemoteServer();
|
||||||
}
|
}
|
||||||
@@ -870,7 +866,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
upstream.sendPacket(playStatusPacket);
|
upstream.sendPacket(playStatusPacket);
|
||||||
|
|
||||||
SetCommandsEnabledPacket setCommandsEnabledPacket = new SetCommandsEnabledPacket();
|
SetCommandsEnabledPacket setCommandsEnabledPacket = new SetCommandsEnabledPacket();
|
||||||
setCommandsEnabledPacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled());
|
setCommandsEnabledPacket.setCommandsEnabled(!geyser.config().gameplay().xboxAchievementsEnabled());
|
||||||
upstream.sendPacket(setCommandsEnabledPacket);
|
upstream.sendPacket(setCommandsEnabledPacket);
|
||||||
|
|
||||||
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
UpdateAttributesPacket attributesPacket = new UpdateAttributesPacket();
|
||||||
@@ -929,7 +925,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
MinecraftProfile mcProfile;
|
MinecraftProfile mcProfile;
|
||||||
MinecraftToken mcToken;
|
MinecraftToken mcToken;
|
||||||
try {
|
try {
|
||||||
JsonObject parsedAuthChain = GSON.fromJson(authChain, JsonObject.class);
|
JsonObject parsedAuthChain = GeyserImpl.GSON.fromJson(authChain, JsonObject.class);
|
||||||
if (parsedAuthChain.has("mcProfile")) { // Old Minecraft v4 auth chain
|
if (parsedAuthChain.has("mcProfile")) { // Old Minecraft v4 auth chain
|
||||||
parsedAuthChain = MinecraftAuth4To5Migrator.migrateJavaSave(parsedAuthChain, GeyserImpl.OAUTH_CONFIG);
|
parsedAuthChain = MinecraftAuth4To5Migrator.migrateJavaSave(parsedAuthChain, GeyserImpl.OAUTH_CONFIG);
|
||||||
}
|
}
|
||||||
@@ -946,7 +942,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
new GameProfile(mcProfile.getId(), mcProfile.getName()),
|
new GameProfile(mcProfile.getId(), mcProfile.getName()),
|
||||||
mcToken.getToken()
|
mcToken.getToken()
|
||||||
);
|
);
|
||||||
geyser.saveAuthChain(bedrockUsername(), GSON.toJson(JavaAuthManager.toJson(authManager)));
|
geyser.saveAuthChain(bedrockUsername(), GeyserImpl.GSON.toJson(JavaAuthManager.toJson(authManager)));
|
||||||
return Boolean.TRUE;
|
return Boolean.TRUE;
|
||||||
}).whenComplete((successful, ex) -> {
|
}).whenComplete((successful, ex) -> {
|
||||||
if (this.closed) {
|
if (this.closed) {
|
||||||
@@ -1039,7 +1035,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Save our auth chain for later use
|
// Save our auth chain for later use
|
||||||
geyser.saveAuthChain(bedrockUsername(), GSON.toJson(JavaAuthManager.toJson(result)));
|
geyser.saveAuthChain(bedrockUsername(), GeyserImpl.GSON.toJson(JavaAuthManager.toJson(result)));
|
||||||
return true;
|
return true;
|
||||||
}).getNow(false);
|
}).getNow(false);
|
||||||
}
|
}
|
||||||
@@ -1091,10 +1087,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
// Disable automatic creation of a new TcpClientSession when transferring - we don't use that functionality.
|
// Disable automatic creation of a new TcpClientSession when transferring - we don't use that functionality.
|
||||||
this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false);
|
this.downstream.getSession().setFlag(MinecraftConstants.FOLLOW_TRANSFERS, false);
|
||||||
|
|
||||||
if (geyser.getConfig().getRemote().isUseProxyProtocol()) {
|
if (geyser.config().advanced().java().useHaproxyProtocol()) {
|
||||||
downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
|
downstream.setFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS, upstream.getAddress());
|
||||||
}
|
}
|
||||||
if (geyser.getConfig().isForwardPlayerPing()) {
|
if (geyser.config().gameplay().forwardPlayerPing()) {
|
||||||
// Let Geyser handle sending the keep alive
|
// Let Geyser handle sending the keep alive
|
||||||
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
downstream.setFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, false);
|
||||||
}
|
}
|
||||||
@@ -1140,7 +1136,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
} else {
|
} else {
|
||||||
// Downstream's disconnect will fire an event that prints a log message
|
// Downstream's disconnect will fire an event that prints a log message
|
||||||
// Otherwise, we print a message here
|
// Otherwise, we print a message here
|
||||||
String address = geyser.getConfig().isLogPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : "<IP address withheld>";
|
String address = geyser.config().logPlayerIpAddresses() ? upstream.getAddress().getAddress().toString() : "<IP address withheld>";
|
||||||
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, MessageTranslator.convertMessage(reason)));
|
geyser.getLogger().info(GeyserLocale.getLocaleStringLog("geyser.network.disconnect", address, MessageTranslator.convertMessage(reason)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1740,7 +1736,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
startGamePacket.setLevelGameType(GameType.SURVIVAL);
|
startGamePacket.setLevelGameType(GameType.SURVIVAL);
|
||||||
startGamePacket.setDifficulty(1);
|
startGamePacket.setDifficulty(1);
|
||||||
startGamePacket.setDefaultSpawn(Vector3i.ZERO);
|
startGamePacket.setDefaultSpawn(Vector3i.ZERO);
|
||||||
startGamePacket.setAchievementsDisabled(!geyser.getConfig().isXboxAchievementsEnabled());
|
startGamePacket.setAchievementsDisabled(!geyser.config().gameplay().xboxAchievementsEnabled());
|
||||||
startGamePacket.setCurrentTick(-1);
|
startGamePacket.setCurrentTick(-1);
|
||||||
startGamePacket.setEduEditionOffers(0);
|
startGamePacket.setEduEditionOffers(0);
|
||||||
startGamePacket.setEduFeaturesEnabled(false);
|
startGamePacket.setEduFeaturesEnabled(false);
|
||||||
@@ -1750,7 +1746,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
startGamePacket.setBroadcastingToLan(true);
|
startGamePacket.setBroadcastingToLan(true);
|
||||||
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
|
startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
|
||||||
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
|
startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
|
||||||
startGamePacket.setCommandsEnabled(!geyser.getConfig().isXboxAchievementsEnabled());
|
startGamePacket.setCommandsEnabled(!geyser.config().gameplay().xboxAchievementsEnabled());
|
||||||
startGamePacket.setTexturePacksRequired(false);
|
startGamePacket.setTexturePacksRequired(false);
|
||||||
startGamePacket.setBonusChestEnabled(false);
|
startGamePacket.setBonusChestEnabled(false);
|
||||||
startGamePacket.setStartingWithMap(false);
|
startGamePacket.setStartingWithMap(false);
|
||||||
@@ -1768,7 +1764,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
startGamePacket.setEducationProductionId("");
|
startGamePacket.setEducationProductionId("");
|
||||||
startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty());
|
startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty());
|
||||||
|
|
||||||
String serverName = geyser.getConfig().getBedrock().serverName();
|
String serverName = geyser.config().gameplay().serverName();
|
||||||
startGamePacket.setLevelId(serverName);
|
startGamePacket.setLevelId(serverName);
|
||||||
startGamePacket.setLevelName(serverName);
|
startGamePacket.setLevelName(serverName);
|
||||||
|
|
||||||
@@ -1914,7 +1910,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) {
|
public void sendDownstreamPacket(Packet packet, ProtocolState intendedState) {
|
||||||
// protocol can be null when we're not yet logged in (online auth)
|
// protocol can be null when we're not yet logged in (online auth)
|
||||||
if (protocol == null) {
|
if (protocol == null) {
|
||||||
if (geyser.getConfig().isDebugMode()) {
|
if (geyser.config().debugMode()) {
|
||||||
geyser.getLogger().debug("Tried to send downstream packet with no downstream session!");
|
geyser.getLogger().debug("Tried to send downstream packet with no downstream session!");
|
||||||
Thread.dumpStack();
|
Thread.dumpStack();
|
||||||
}
|
}
|
||||||
@@ -1940,7 +1936,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
// Channel is only null before the connection has initialized
|
// Channel is only null before the connection has initialized
|
||||||
geyser.getLogger().warning("Tried to send a packet to the Java server too early!");
|
geyser.getLogger().warning("Tried to send a packet to the Java server too early!");
|
||||||
if (geyser.getConfig().isDebugMode()) {
|
if (geyser.config().debugMode()) {
|
||||||
Thread.dumpStack();
|
Thread.dumpStack();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -2431,7 +2427,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
|
|
||||||
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
|
private void softEnumPacket(String name, SoftEnumUpdateType type, String enums) {
|
||||||
// There is no need to send command enums if command suggestions are disabled
|
// There is no need to send command enums if command suggestions are disabled
|
||||||
if (!this.geyser.getConfig().isCommandSuggestions()) {
|
if (!this.geyser.config().gameplay().commandSuggestions()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();
|
UpdateSoftEnumPacket packet = new UpdateSoftEnumPacket();
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String address;
|
String address;
|
||||||
if (geyser.getConfig().getRemote().isForwardHost()) {
|
if (geyser.config().java().forwardHostname()) {
|
||||||
address = session.joinAddress();
|
address = session.joinAddress();
|
||||||
} else {
|
} else {
|
||||||
address = intentionPacket.getHostname();
|
address = intentionPacket.getHostname();
|
||||||
@@ -172,7 +172,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||||||
customDisconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale);
|
customDisconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.authentication_type_mismatch", locale);
|
||||||
// Explain that they may be looking for Floodgate.
|
// Explain that they may be looking for Floodgate.
|
||||||
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog(
|
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog(
|
||||||
geyser.getPlatformType() == PlatformType.STANDALONE ?
|
geyser.platformType() == PlatformType.STANDALONE ?
|
||||||
"geyser.network.remote.floodgate_explanation_standalone"
|
"geyser.network.remote.floodgate_explanation_standalone"
|
||||||
: "geyser.network.remote.floodgate_explanation_plugin",
|
: "geyser.network.remote.floodgate_explanation_plugin",
|
||||||
Constants.FLOODGATE_DOWNLOAD_LOCATION
|
Constants.FLOODGATE_DOWNLOAD_LOCATION
|
||||||
@@ -180,7 +180,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||||||
} else {
|
} else {
|
||||||
// Likely that Floodgate is not configured correctly.
|
// Likely that Floodgate is not configured correctly.
|
||||||
customDisconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale);
|
customDisconnectMessage = GeyserLocale.getPlayerLocaleString("geyser.network.remote.floodgate_login_error", locale);
|
||||||
if (geyser.getPlatformType() == PlatformType.STANDALONE) {
|
if (geyser.platformType() == PlatformType.STANDALONE) {
|
||||||
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone"));
|
geyser.getLogger().warning(GeyserLocale.getLocaleStringLog("geyser.network.remote.floodgate_login_error_standalone"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||||||
} else {
|
} else {
|
||||||
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
|
GeyserImpl.getInstance().getLogger().error("An exception occurred: ", cause);
|
||||||
}
|
}
|
||||||
if (geyser.getConfig().isDebugMode()) {
|
if (geyser.config().debugMode()) {
|
||||||
cause.printStackTrace();
|
cause.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +233,7 @@ public class GeyserSessionAdapter extends SessionAdapter {
|
|||||||
(event.getPacketClass() != null ? "(" + event.getPacketClass().getSimpleName() + ") " : "") +
|
(event.getPacketClass() != null ? "(" + event.getPacketClass().getSimpleName() + ") " : "") +
|
||||||
event.getCause().getMessage())
|
event.getCause().getMessage())
|
||||||
);
|
);
|
||||||
if (geyser.getConfig().isDebugMode())
|
if (geyser.config().debugMode())
|
||||||
event.getCause().printStackTrace();
|
event.getCause().printStackTrace();
|
||||||
event.setSuppress(true);
|
event.setSuppress(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public final class SessionDisconnectListener {
|
|||||||
String serverDisconnectMessage = MessageTranslator.convertMessage(disconnectReason, session.locale());
|
String serverDisconnectMessage = MessageTranslator.convertMessage(disconnectReason, session.locale());
|
||||||
if (testForOutdatedServer(disconnectReason)) {
|
if (testForOutdatedServer(disconnectReason)) {
|
||||||
String locale = session.locale();
|
String locale = session.locale();
|
||||||
PlatformType platform = session.getGeyser().getPlatformType();
|
PlatformType platform = session.getGeyser().platformType();
|
||||||
String outdatedType = (platform == PlatformType.BUNGEECORD || platform == PlatformType.VELOCITY || platform == PlatformType.VIAPROXY) ?
|
String outdatedType = (platform == PlatformType.BUNGEECORD || platform == PlatformType.VELOCITY || platform == PlatformType.VIAPROXY) ?
|
||||||
"geyser.network.remote.outdated.proxy" : "geyser.network.remote.outdated.server";
|
"geyser.network.remote.outdated.proxy" : "geyser.network.remote.outdated.server";
|
||||||
event.disconnectReason(GeyserLocale.getPlayerLocaleString(outdatedType, locale, GameProtocol.getJavaVersions().get(0)) + '\n'
|
event.disconnectReason(GeyserLocale.getPlayerLocaleString(outdatedType, locale, GameProtocol.getJavaVersions().get(0)) + '\n'
|
||||||
|
|||||||
@@ -25,93 +25,97 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.session.auth;
|
package org.geysermc.geyser.session.auth;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.google.gson.JsonDeserializationContext;
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
import com.google.gson.JsonDeserializer;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
import com.google.gson.annotations.JsonAdapter;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.floodgate.util.DeviceOs;
|
import org.geysermc.floodgate.util.DeviceOs;
|
||||||
import org.geysermc.floodgate.util.InputMode;
|
import org.geysermc.floodgate.util.InputMode;
|
||||||
import org.geysermc.floodgate.util.UiProfile;
|
import org.geysermc.floodgate.util.UiProfile;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
|
||||||
@Getter
|
@Getter
|
||||||
public final class BedrockClientData {
|
public final class BedrockClientData {
|
||||||
@JsonProperty(value = "GameVersion")
|
@SerializedName(value = "GameVersion")
|
||||||
private String gameVersion;
|
private String gameVersion;
|
||||||
@JsonProperty(value = "ServerAddress")
|
@SerializedName(value = "ServerAddress")
|
||||||
private String serverAddress;
|
private String serverAddress;
|
||||||
@JsonProperty(value = "ThirdPartyName")
|
@SerializedName(value = "ThirdPartyName")
|
||||||
private String username;
|
private String username;
|
||||||
@JsonProperty(value = "LanguageCode")
|
@SerializedName(value = "LanguageCode")
|
||||||
private String languageCode;
|
private String languageCode;
|
||||||
|
|
||||||
@JsonProperty(value = "SkinId")
|
@SerializedName(value = "SkinId")
|
||||||
private String skinId;
|
private String skinId;
|
||||||
@JsonProperty(value = "SkinData")
|
@SerializedName(value = "SkinData")
|
||||||
private String skinData;
|
private String skinData;
|
||||||
@JsonProperty(value = "SkinImageHeight")
|
@SerializedName(value = "SkinImageHeight")
|
||||||
private int skinImageHeight;
|
private int skinImageHeight;
|
||||||
@JsonProperty(value = "SkinImageWidth")
|
@SerializedName(value = "SkinImageWidth")
|
||||||
private int skinImageWidth;
|
private int skinImageWidth;
|
||||||
@JsonProperty(value = "CapeId")
|
@SerializedName(value = "CapeId")
|
||||||
private String capeId;
|
private String capeId;
|
||||||
@JsonProperty(value = "CapeData")
|
@SerializedName(value = "CapeData")
|
||||||
|
@JsonAdapter(value = StringToByteDeserializer.class)
|
||||||
private byte[] capeData;
|
private byte[] capeData;
|
||||||
@JsonProperty(value = "CapeImageHeight")
|
@SerializedName(value = "CapeImageHeight")
|
||||||
private int capeImageHeight;
|
private int capeImageHeight;
|
||||||
@JsonProperty(value = "CapeImageWidth")
|
@SerializedName(value = "CapeImageWidth")
|
||||||
private int capeImageWidth;
|
private int capeImageWidth;
|
||||||
@JsonProperty(value = "CapeOnClassicSkin")
|
@SerializedName(value = "CapeOnClassicSkin")
|
||||||
private boolean capeOnClassicSkin;
|
private boolean capeOnClassicSkin;
|
||||||
@JsonProperty(value = "SkinResourcePatch")
|
@SerializedName(value = "SkinResourcePatch")
|
||||||
private String geometryName;
|
private String geometryName;
|
||||||
@JsonProperty(value = "SkinGeometryData")
|
@SerializedName(value = "SkinGeometryData")
|
||||||
private String geometryData;
|
private String geometryData;
|
||||||
@JsonProperty(value = "PersonaSkin")
|
@SerializedName(value = "PersonaSkin")
|
||||||
private boolean personaSkin;
|
private boolean personaSkin;
|
||||||
@JsonProperty(value = "PremiumSkin")
|
@SerializedName(value = "PremiumSkin")
|
||||||
private boolean premiumSkin;
|
private boolean premiumSkin;
|
||||||
|
|
||||||
@JsonProperty(value = "DeviceId")
|
@SerializedName(value = "DeviceId")
|
||||||
private String deviceId;
|
private String deviceId;
|
||||||
@JsonProperty(value = "DeviceModel")
|
@SerializedName(value = "DeviceModel")
|
||||||
private String deviceModel;
|
private String deviceModel;
|
||||||
@JsonProperty(value = "DeviceOS")
|
@SerializedName(value = "DeviceOS")
|
||||||
private DeviceOs deviceOs;
|
private DeviceOs deviceOs;
|
||||||
@JsonProperty(value = "UIProfile")
|
@SerializedName(value = "UIProfile")
|
||||||
private UiProfile uiProfile;
|
private UiProfile uiProfile;
|
||||||
@JsonProperty(value = "GuiScale")
|
@SerializedName(value = "GuiScale")
|
||||||
private int guiScale;
|
private int guiScale;
|
||||||
@JsonProperty(value = "CurrentInputMode")
|
@SerializedName(value = "CurrentInputMode")
|
||||||
private InputMode currentInputMode;
|
private InputMode currentInputMode;
|
||||||
@JsonProperty(value = "DefaultInputMode")
|
@SerializedName(value = "DefaultInputMode")
|
||||||
private InputMode defaultInputMode;
|
private InputMode defaultInputMode;
|
||||||
@JsonProperty("PlatformOnlineId")
|
@SerializedName("PlatformOnlineId")
|
||||||
private String platformOnlineId;
|
private String platformOnlineId;
|
||||||
@JsonProperty(value = "PlatformOfflineId")
|
@SerializedName(value = "PlatformOfflineId")
|
||||||
private String platformOfflineId;
|
private String platformOfflineId;
|
||||||
@JsonProperty(value = "SelfSignedId")
|
@SerializedName(value = "SelfSignedId")
|
||||||
private UUID selfSignedId;
|
private UUID selfSignedId;
|
||||||
@JsonProperty(value = "ClientRandomId")
|
@SerializedName(value = "ClientRandomId")
|
||||||
private long clientRandomId;
|
private long clientRandomId;
|
||||||
|
|
||||||
@JsonProperty(value = "ArmSize")
|
@SerializedName(value = "ArmSize")
|
||||||
private String armSize;
|
private String armSize;
|
||||||
@JsonProperty(value = "SkinAnimationData")
|
@SerializedName(value = "SkinAnimationData")
|
||||||
private String skinAnimationData;
|
private String skinAnimationData;
|
||||||
@JsonProperty(value = "SkinColor")
|
@SerializedName(value = "SkinColor")
|
||||||
private String skinColor;
|
private String skinColor;
|
||||||
@JsonProperty(value = "ThirdPartyNameOnly")
|
@SerializedName(value = "ThirdPartyNameOnly")
|
||||||
private boolean thirdPartyNameOnly;
|
private boolean thirdPartyNameOnly;
|
||||||
@JsonProperty(value = "PlayFabId")
|
@SerializedName(value = "PlayFabId")
|
||||||
private String playFabId;
|
private String playFabId;
|
||||||
|
|
||||||
@JsonIgnore
|
|
||||||
@Setter
|
@Setter
|
||||||
private String originalString = null;
|
private transient String originalString = null;
|
||||||
|
|
||||||
public DeviceOs getDeviceOs() {
|
public DeviceOs getDeviceOs() {
|
||||||
return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN;
|
return deviceOs != null ? deviceOs : DeviceOs.UNKNOWN;
|
||||||
@@ -128,4 +132,11 @@ public final class BedrockClientData {
|
|||||||
public UiProfile getUiProfile() {
|
public UiProfile getUiProfile() {
|
||||||
return uiProfile != null ? uiProfile : UiProfile.CLASSIC;
|
return uiProfile != null ? uiProfile : UiProfile.CLASSIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class StringToByteDeserializer implements JsonDeserializer<byte[]> {
|
||||||
|
@Override
|
||||||
|
public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
return json.getAsString().getBytes(StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public final class BundleCache {
|
|||||||
if (itemStack.is(session, ItemTag.BUNDLES)) {
|
if (itemStack.is(session, ItemTag.BUNDLES)) {
|
||||||
if (itemStack.getBundleData() != null) {
|
if (itemStack.getBundleData() != null) {
|
||||||
session.getGeyser().getLogger().warning("Stack has bundle data already! It should not!");
|
session.getGeyser().getLogger().warning("Stack has bundle data already! It should not!");
|
||||||
if (session.getGeyser().getConfig().isDebugMode()) {
|
if (session.getGeyser().getLogger().isDebug()) {
|
||||||
session.getGeyser().getLogger().debug("Player: " + session.javaUsername());
|
session.getGeyser().getLogger().debug("Player: " + session.javaUsername());
|
||||||
session.getGeyser().getLogger().debug("Stack: " + itemStack);
|
session.getGeyser().getLogger().debug("Stack: " + itemStack);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ package org.geysermc.geyser.session.cache;
|
|||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfig;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.util.CooldownUtils;
|
import org.geysermc.geyser.util.CooldownUtils;
|
||||||
|
|
||||||
@@ -54,15 +54,16 @@ public class PreferencesCache {
|
|||||||
private boolean prefersCustomSkulls;
|
private boolean prefersCustomSkulls;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}.
|
* Which CooldownType the client prefers. Initially set to the config default.
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Setter
|
||||||
private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown();
|
private CooldownUtils.CooldownType cooldownPreference;
|
||||||
|
|
||||||
public PreferencesCache(GeyserSession session) {
|
public PreferencesCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
|
||||||
prefersCustomSkulls = session.getGeyser().getConfig().isAllowCustomSkulls();
|
prefersCustomSkulls = session.getGeyser().config().gameplay().maxVisibleCustomSkulls() != 0;
|
||||||
|
cooldownPreference = session.getGeyser().config().gameplay().showCooldown();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +72,10 @@ public class PreferencesCache {
|
|||||||
* If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply: <br>
|
* If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply: <br>
|
||||||
* <br>
|
* <br>
|
||||||
* {@link GeyserSession#isReducedDebugInfo()} is enabled
|
* {@link GeyserSession#isReducedDebugInfo()} is enabled
|
||||||
* {@link GeyserConfiguration#isShowCoordinates()} is disabled
|
* {@link GeyserConfig.GameplayConfig#showCoordinates()} is disabled
|
||||||
*/
|
*/
|
||||||
public void updateShowCoordinates() {
|
public void updateShowCoordinates() {
|
||||||
allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().getConfig().isShowCoordinates();
|
allowShowCoordinates = !session.isReducedDebugInfo() && session.getGeyser().config().gameplay().showCoordinates();
|
||||||
session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates);
|
session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +83,6 @@ public class PreferencesCache {
|
|||||||
* @return true if the session prefers custom skulls, and the config allows them.
|
* @return true if the session prefers custom skulls, and the config allows them.
|
||||||
*/
|
*/
|
||||||
public boolean showCustomSkulls() {
|
public boolean showCustomSkulls() {
|
||||||
return prefersCustomSkulls && session.getGeyser().getConfig().isAllowCustomSkulls();
|
return prefersCustomSkulls && session.getGeyser().config().gameplay().maxVisibleCustomSkulls() != 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,11 +67,11 @@ public class SkullCache {
|
|||||||
|
|
||||||
public SkullCache(GeyserSession session) {
|
public SkullCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.maxVisibleSkulls = session.getGeyser().getConfig().getMaxVisibleCustomSkulls();
|
this.maxVisibleSkulls = session.getGeyser().config().gameplay().maxVisibleCustomSkulls();
|
||||||
this.cullingEnabled = this.maxVisibleSkulls != -1;
|
this.cullingEnabled = this.maxVisibleSkulls != -1;
|
||||||
|
|
||||||
// Normal skulls are not rendered beyond 64 blocks
|
// Normal skulls are not rendered beyond 64 blocks
|
||||||
int distance = Math.min(session.getGeyser().getConfig().getCustomSkullRenderDistance(), 64);
|
int distance = Math.min(session.getGeyser().config().gameplay().customSkullRenderDistance(), 64);
|
||||||
this.skullRenderDistanceSquared = distance * distance;
|
this.skullRenderDistanceSquared = distance * distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ public class SkullCache {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty);
|
session.getGeyser().getLogger().debug("Player skull with invalid Skin tag: " + position + " Textures: " + texturesProperty);
|
||||||
if (GeyserImpl.getInstance().getConfig().isDebugMode()) {
|
if (GeyserImpl.getInstance().config().debugMode()) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,9 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.skin;
|
package org.geysermc.geyser.skin;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.google.gson.JsonArray;
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.google.gson.JsonObject;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.google.gson.JsonSyntaxException;
|
||||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
||||||
import org.geysermc.floodgate.util.WebsocketEventType;
|
import org.geysermc.floodgate.util.WebsocketEventType;
|
||||||
@@ -37,6 +35,7 @@ import org.geysermc.geyser.Constants;
|
|||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.JsonUtils;
|
||||||
import org.geysermc.geyser.util.PluginMessageUtils;
|
import org.geysermc.geyser.util.PluginMessageUtils;
|
||||||
import org.java_websocket.client.WebSocketClient;
|
import org.java_websocket.client.WebSocketClient;
|
||||||
import org.java_websocket.handshake.ServerHandshake;
|
import org.java_websocket.handshake.ServerHandshake;
|
||||||
@@ -52,7 +51,6 @@ import java.util.concurrent.ThreadLocalRandom;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public final class FloodgateSkinUploader {
|
public final class FloodgateSkinUploader {
|
||||||
private final ObjectMapper JACKSON = new ObjectMapper();
|
|
||||||
private final List<String> skinQueue = new ArrayList<>();
|
private final List<String> skinQueue = new ArrayList<>();
|
||||||
|
|
||||||
private final GeyserLogger logger;
|
private final GeyserLogger logger;
|
||||||
@@ -79,15 +77,14 @@ public final class FloodgateSkinUploader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMessage(String message) {
|
public void onMessage(String message) {
|
||||||
// The reason why I don't like Jackson
|
|
||||||
try {
|
try {
|
||||||
JsonNode node = JACKSON.readTree(message);
|
JsonObject node = JsonUtils.parseJson(message);
|
||||||
if (node.has("error")) {
|
if (node.has("error")) {
|
||||||
logger.error("Got an error: " + node.get("error").asText());
|
logger.error("Got an error: " + node.get("error").getAsString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int typeId = node.get("event_id").asInt();
|
int typeId = node.get("event_id").getAsInt();
|
||||||
WebsocketEventType type = WebsocketEventType.fromId(typeId);
|
WebsocketEventType type = WebsocketEventType.fromId(typeId);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
logger.warning(String.format(
|
logger.warning(String.format(
|
||||||
@@ -98,11 +95,11 @@ public final class FloodgateSkinUploader {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SUBSCRIBER_CREATED:
|
case SUBSCRIBER_CREATED:
|
||||||
id = node.get("id").asInt();
|
id = node.get("id").getAsInt();
|
||||||
verifyCode = node.get("verify_code").asText();
|
verifyCode = node.get("verify_code").getAsString();
|
||||||
break;
|
break;
|
||||||
case SUBSCRIBER_COUNT:
|
case SUBSCRIBER_COUNT:
|
||||||
subscribersCount = node.get("subscribers_count").asInt();
|
subscribersCount = node.get("subscribers_count").getAsInt();
|
||||||
break;
|
break;
|
||||||
case SKIN_UPLOADED:
|
case SKIN_UPLOADED:
|
||||||
// if Geyser is the only subscriber we have send it to the server manually
|
// if Geyser is the only subscriber we have send it to the server manually
|
||||||
@@ -111,19 +108,19 @@ public final class FloodgateSkinUploader {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
String xuid = node.get("xuid").asText();
|
String xuid = node.get("xuid").getAsString();
|
||||||
GeyserSession session = geyser.connectionByXuid(xuid);
|
GeyserSession session = geyser.connectionByXuid(xuid);
|
||||||
|
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
if (!node.get("success").asBoolean()) {
|
if (!node.get("success").getAsBoolean()) {
|
||||||
logger.info("Failed to upload skin for " + session.bedrockUsername());
|
logger.info("Failed to upload skin for " + session.bedrockUsername());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonNode data = node.get("data");
|
JsonObject data = node.getAsJsonObject("data");
|
||||||
|
|
||||||
String value = data.get("value").asText();
|
String value = data.get("value").getAsString();
|
||||||
String signature = data.get("signature").asText();
|
String signature = data.get("signature").getAsString();
|
||||||
|
|
||||||
byte[] bytes = (value + '\0' + signature)
|
byte[] bytes = (value + '\0' + signature)
|
||||||
.getBytes(StandardCharsets.UTF_8);
|
.getBytes(StandardCharsets.UTF_8);
|
||||||
@@ -131,8 +128,8 @@ public final class FloodgateSkinUploader {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LOG_MESSAGE:
|
case LOG_MESSAGE:
|
||||||
String logMessage = node.get("message").asText();
|
String logMessage = node.get("message").getAsString();
|
||||||
switch (node.get("priority").asInt()) {
|
switch (node.get("priority").getAsInt()) {
|
||||||
case -1 -> logger.debug("Got a message from skin uploader: " + logMessage);
|
case -1 -> logger.debug("Got a message from skin uploader: " + logMessage);
|
||||||
case 0 -> logger.info("Got a message from skin uploader: " + logMessage);
|
case 0 -> logger.info("Got a message from skin uploader: " + logMessage);
|
||||||
case 1 -> logger.error("Got a message from skin uploader: " + logMessage);
|
case 1 -> logger.error("Got a message from skin uploader: " + logMessage);
|
||||||
@@ -150,20 +147,19 @@ public final class FloodgateSkinUploader {
|
|||||||
@Override
|
@Override
|
||||||
public void onClose(int code, String reason, boolean remote) {
|
public void onClose(int code, String reason, boolean remote) {
|
||||||
if (reason != null && !reason.isEmpty()) {
|
if (reason != null && !reason.isEmpty()) {
|
||||||
// The reason why I don't like Jackson
|
|
||||||
try {
|
try {
|
||||||
JsonNode node = JACKSON.readTree(reason);
|
JsonObject node = JsonUtils.parseJson(reason);
|
||||||
// info means that the uploader itself did nothing wrong
|
// info means that the uploader itself did nothing wrong
|
||||||
if (node.has("info")) {
|
if (node.has("info")) {
|
||||||
String info = node.get("info").asText();
|
String info = node.get("info").getAsString();
|
||||||
logger.debug("Got disconnected from the skin uploader: " + info);
|
logger.debug("Got disconnected from the skin uploader: " + info);
|
||||||
}
|
}
|
||||||
// error means that the uploader did something wrong
|
// error means that the uploader did something wrong
|
||||||
if (node.has("error")) {
|
if (node.has("error")) {
|
||||||
String error = node.get("error").asText();
|
String error = node.get("error").getAsString();
|
||||||
logger.info("Got disconnected from the skin uploader: " + error);
|
logger.info("Got disconnected from the skin uploader: " + error);
|
||||||
}
|
}
|
||||||
} catch (JsonProcessingException ignored) {
|
} catch (JsonSyntaxException ignored) {
|
||||||
// ignore invalid json
|
// ignore invalid json
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error while handling onClose", e);
|
logger.error("Error while handling onClose", e);
|
||||||
@@ -198,24 +194,17 @@ public final class FloodgateSkinUploader {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectNode node = JACKSON.createObjectNode();
|
JsonObject node = new JsonObject();
|
||||||
if (chainData != null) {
|
if (chainData != null) {
|
||||||
ArrayNode chainDataNode = JACKSON.createArrayNode();
|
JsonArray chainDataNode = new JsonArray();
|
||||||
chainData.forEach(chainDataNode::add);
|
chainData.forEach(chainDataNode::add);
|
||||||
node.set("chain_data", chainDataNode);
|
node.add("chain_data", chainDataNode);
|
||||||
} else {
|
} else {
|
||||||
node.put("token", token);
|
node.addProperty("token", token);
|
||||||
}
|
}
|
||||||
node.put("client_data", clientData);
|
node.addProperty("client_data", clientData);
|
||||||
|
|
||||||
// The reason why I don't like Jackson
|
String jsonString = node.toString();
|
||||||
String jsonString;
|
|
||||||
try {
|
|
||||||
jsonString = JACKSON.writeValueAsString(node);
|
|
||||||
} catch (Exception e) {
|
|
||||||
logger.error("Failed to upload skin", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client.isOpen()) {
|
if (client.isOpen()) {
|
||||||
client.send(jsonString);
|
client.send(jsonString);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user