mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Feature: Rework startup logic (#4377)
* This makes `geyser reload` work the same across all platforms. For example, it ensures that we reload the config to the greatest extent possible (with the exception of compression/injection settings). Additionally, this clears up issues where Extensions were previously disabled during reloading - instead, the new Pre and Post reload events allow extensions to reload whatever necessary on their own.
This commit is contained in:
parent
19c6648bc2
commit
6a51d8298f
18 changed files with 539 additions and 358 deletions
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser finished reloading and is accepting Bedrock connections again.
|
||||||
|
* Equivalent to the {@link GeyserPostInitializeEvent}
|
||||||
|
*
|
||||||
|
* @param extensionManager the extension manager
|
||||||
|
* @param eventBus the event bus
|
||||||
|
*/
|
||||||
|
public record GeyserPostReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* 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.api.event.lifecycle;
|
||||||
|
|
||||||
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
import org.geysermc.event.Event;
|
||||||
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
|
import org.geysermc.geyser.api.extension.ExtensionManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when Geyser is about to reload. Primarily aimed at extensions, so they can decide on their own what to reload.
|
||||||
|
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserLoadResourcePacksEvent}.
|
||||||
|
*
|
||||||
|
* @param extensionManager the extension manager
|
||||||
|
* @param eventBus the event bus
|
||||||
|
*/
|
||||||
|
public record GeyserPreReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
|
||||||
|
}
|
|
@ -32,11 +32,11 @@ 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.api.util.PlatformType;
|
|
||||||
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.command.Command;
|
import org.geysermc.geyser.api.command.Command;
|
||||||
import org.geysermc.geyser.api.extension.Extension;
|
import org.geysermc.geyser.api.extension.Extension;
|
||||||
|
import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
@ -70,11 +70,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
@SuppressWarnings({"JavaReflectionMemberAccess", "ResultOfMethodCallIgnored"})
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserInitialize() {
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
|
|
||||||
// Copied from ViaVersion.
|
// Copied from ViaVersion.
|
||||||
|
@ -91,29 +93,62 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
getLogger().warning("/_____________\\");
|
getLogger().warning("/_____________\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getDataFolder().exists())
|
if (!this.loadConfig()) {
|
||||||
getDataFolder().mkdir();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!getDataFolder().exists())
|
|
||||||
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) {
|
|
||||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
|
||||||
|
this.geyserInjector = new GeyserBungeeInjector(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
||||||
|
// task that waits for a field to be filled which is set after the plugin enable
|
||||||
|
// process is complete
|
||||||
|
this.awaitStartupCompletion(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void awaitStartupCompletion(int tries) {
|
||||||
|
// After 20 tries give up waiting. This will happen just after 3 minutes approximately
|
||||||
|
if (tries >= 20) {
|
||||||
|
this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " +
|
||||||
|
"If all your plugins are loaded properly, this is a bug! " +
|
||||||
|
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
|
||||||
|
this.onGeyserEnable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
||||||
|
listenersField.setAccessible(true);
|
||||||
|
|
||||||
|
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
this.getProxy().getScheduler().schedule(this, this::onGeyserEnable, tries, TimeUnit.SECONDS);
|
||||||
|
} else {
|
||||||
|
this.awaitStartupCompletion(++tries);
|
||||||
|
}
|
||||||
|
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
||||||
|
ex.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onGeyserEnable() {
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
} else {
|
||||||
|
// For consistency with other platforms - create command manager before GeyserImpl#start()
|
||||||
|
// This ensures the command events are called before the item/block ones are
|
||||||
|
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
|
this.geyserCommandManager.init();
|
||||||
|
}
|
||||||
|
|
||||||
// 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()) {
|
||||||
|
@ -133,54 +168,20 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
|
GeyserImpl.start();
|
||||||
// task that waits for a field to be filled which is set after the plugin enable
|
|
||||||
// process is complete
|
|
||||||
if (!INITIALIZED) {
|
|
||||||
this.awaitStartupCompletion(0);
|
|
||||||
} else {
|
|
||||||
// No need to "wait" for startup completion, just start Geyser - we're reloading.
|
|
||||||
this.postStartup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
private void awaitStartupCompletion(int tries) {
|
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
// After 20 tries give up waiting. This will happen
|
} else {
|
||||||
// just after 3 minutes approximately
|
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
||||||
if (tries >= 20) {
|
}
|
||||||
this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " +
|
|
||||||
"If all your plugins are loaded properly, this is a bug! " +
|
// No need to re-register commands or re-init injector when reloading
|
||||||
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
this.postStartup();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
this.geyserInjector.initializeLocalChannel(this);
|
||||||
Field listenersField = BungeeCord.getInstance().getClass().getDeclaredField("listeners");
|
|
||||||
listenersField.setAccessible(true);
|
|
||||||
|
|
||||||
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
|
|
||||||
if (listeners.isEmpty()) {
|
|
||||||
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
|
|
||||||
} else {
|
|
||||||
this.awaitStartupCompletion(++tries);
|
|
||||||
}
|
|
||||||
} catch (NoSuchFieldException | IllegalAccessException ex) {
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postStartup() {
|
|
||||||
GeyserImpl.start();
|
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
|
||||||
this.geyserInjector = new GeyserBungeeInjector(this);
|
|
||||||
this.geyserInjector.initializeLocalChannel(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
|
||||||
this.geyserCommandManager.init();
|
|
||||||
|
|
||||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
|
||||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||||
|
@ -191,18 +192,17 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
|
|
||||||
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
|
||||||
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
|
||||||
} else {
|
|
||||||
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
|
|
||||||
}
|
|
||||||
|
|
||||||
INITIALIZED = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserBungeeConfiguration getGeyserConfig() {
|
public GeyserBungeeConfiguration getGeyserConfig() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
|
@ -278,4 +283,20 @@ 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) {
|
||||||
|
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ package org.geysermc.geyser.platform.fabric;
|
||||||
|
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
import net.fabricmc.api.EnvType;
|
import net.fabricmc.api.EnvType;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||||
|
@ -66,13 +68,14 @@ import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
|
|
||||||
|
@Getter
|
||||||
private static GeyserFabricMod instance;
|
private static GeyserFabricMod instance;
|
||||||
|
|
||||||
private boolean reloading;
|
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
private ModContainer mod;
|
private ModContainer mod;
|
||||||
private Path dataFolder;
|
private Path dataFolder;
|
||||||
|
|
||||||
|
@Setter
|
||||||
private MinecraftServer server;
|
private MinecraftServer server;
|
||||||
|
|
||||||
private GeyserCommandManager geyserCommandManager;
|
private GeyserCommandManager geyserCommandManager;
|
||||||
|
@ -85,64 +88,45 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
public void onInitialize() {
|
public void onInitialize() {
|
||||||
instance = this;
|
instance = this;
|
||||||
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
|
||||||
|
onGeyserInitialize();
|
||||||
this.onEnable();
|
|
||||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
|
||||||
// Set as an event so we can get the proper IP and port if needed
|
|
||||||
ServerLifecycleEvents.SERVER_STARTED.register(this::startGeyser);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onGeyserInitialize() {
|
||||||
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
|
||||||
if (!dataFolder.toFile().exists()) {
|
// Set as an event, so we can get the proper IP and port if needed
|
||||||
//noinspection ResultOfMethodCallIgnored
|
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
|
||||||
dataFolder.toFile().mkdir();
|
this.server = server;
|
||||||
|
onGeyserEnable();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init dataFolder first as local language overrides call getConfigFolder()
|
// These are only registered once
|
||||||
GeyserLocale.init(this);
|
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
|
||||||
|
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
||||||
|
|
||||||
try {
|
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
GeyserLocale.init(this);
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
if (!loadConfig()) {
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
|
||||||
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
|
||||||
|
|
||||||
if (server == null) {
|
|
||||||
// Server has yet to start
|
|
||||||
// Register onDisable so players are properly kicked
|
|
||||||
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onDisable());
|
|
||||||
|
|
||||||
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
|
|
||||||
} else {
|
|
||||||
// Server has started and this is a reload
|
|
||||||
startGeyser(this.server);
|
|
||||||
reloading = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Initialize core Geyser.
|
public void onGeyserEnable() {
|
||||||
* A function, as it needs to be called in different places depending on if Geyser is being reloaded or not.
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
*
|
if (!loadConfig()) {
|
||||||
* @param server The minecraft server.
|
return;
|
||||||
*/
|
}
|
||||||
public void startGeyser(MinecraftServer server) {
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
this.server = server;
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
} else {
|
||||||
GeyserImpl.start();
|
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
|
this.geyserCommandManager.init();
|
||||||
|
}
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
@ -150,8 +134,12 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
GeyserImpl.start();
|
||||||
this.geyserCommandManager.init();
|
|
||||||
|
// No need to re-register commands, or re-recreate the world manager when reloading
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
this.geyserWorldManager = new GeyserFabricWorldManager(server);
|
||||||
|
|
||||||
|
@ -201,14 +189,19 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
geyser = null;
|
geyser = null;
|
||||||
}
|
}
|
||||||
if (!reloading) {
|
this.server = null;
|
||||||
this.server = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -291,11 +284,22 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReloading(boolean reloading) {
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
this.reloading = reloading;
|
private boolean loadConfig() {
|
||||||
}
|
try {
|
||||||
|
if (!dataFolder.toFile().exists()) {
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
dataFolder.toFile().mkdir();
|
||||||
|
}
|
||||||
|
|
||||||
public static GeyserFabricMod getInstance() {
|
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
|
||||||
return instance;
|
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
|
||||||
|
return true;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ import net.minecraft.commands.CommandSourceStack;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.command.GeyserCommand;
|
import org.geysermc.geyser.command.GeyserCommand;
|
||||||
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
import org.geysermc.geyser.command.GeyserCommandExecutor;
|
||||||
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.ChatColor;
|
import org.geysermc.geyser.text.ChatColor;
|
||||||
import org.geysermc.geyser.text.GeyserLocale;
|
import org.geysermc.geyser.text.GeyserLocale;
|
||||||
|
@ -64,9 +63,6 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
|
||||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (this.command.name().equals("reload")) {
|
|
||||||
GeyserFabricMod.getInstance().setReloading(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.isBedrockOnly() && session == null) {
|
if (command.isBedrockOnly() && session == null) {
|
||||||
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.bedrock_only", sender.locale()));
|
||||||
|
|
|
@ -57,7 +57,8 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
|
||||||
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
|
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
|
||||||
if (cir.getReturnValueZ()) {
|
if (cir.getReturnValueZ()) {
|
||||||
// If the LAN is opened, starts Geyser.
|
// If the LAN is opened, starts Geyser.
|
||||||
GeyserFabricMod.getInstance().startGeyser((MinecraftServer) (Object) this);
|
GeyserFabricMod.getInstance().setServer((MinecraftServer) (Object) this);
|
||||||
|
GeyserFabricMod.getInstance().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
|
||||||
|
|
|
@ -81,10 +81,6 @@ import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
/**
|
|
||||||
* Determines if the plugin has been ran once before, including before /geyser reload.
|
|
||||||
*/
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
private GeyserSpigotCommandManager geyserCommandManager;
|
private GeyserSpigotCommandManager geyserCommandManager;
|
||||||
private GeyserSpigotConfiguration geyserConfig;
|
private GeyserSpigotConfiguration geyserConfig;
|
||||||
|
@ -102,6 +98,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
onGeyserInitialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserInitialize() {
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -118,6 +119,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server.message", "1.13.2"));
|
||||||
getLogger().severe("");
|
getLogger().severe("");
|
||||||
getLogger().severe("*********************************************");
|
getLogger().severe("*********************************************");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,6 +133,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
getLogger().severe(GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_server_type.message", "Paper"));
|
||||||
getLogger().severe("");
|
getLogger().severe("");
|
||||||
getLogger().severe("*********************************************");
|
getLogger().severe("*********************************************");
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -143,86 +146,72 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
getLogger().severe("This version of Spigot is using an outdated version of netty. Please use Paper instead!");
|
||||||
getLogger().severe("");
|
getLogger().severe("");
|
||||||
getLogger().severe("*********************************************");
|
getLogger().severe("*********************************************");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
|
||||||
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, GeyserSpigotConfiguration.class);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
this.geyserLogger = GeyserPaperLogger.supported() ? new GeyserPaperLogger(this, getLogger(), geyserConfig.isDebugMode())
|
||||||
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
: new GeyserSpigotLogger(getLogger(), geyserConfig.isDebugMode());
|
||||||
|
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
|
// Turn "(MC: 1.16.4)" into 1.16.4.
|
||||||
|
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
|
this.geyser = GeyserImpl.load(PlatformType.SPIGOT, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
if (this.geyserConfig == null) {
|
|
||||||
// We failed to initialize correctly
|
|
||||||
Bukkit.getPluginManager().disablePlugin(this);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
|
||||||
this.geyserCommandManager.init();
|
this.geyserCommandManager.init();
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
// Because Bukkit locks its command map upon startup, we need to
|
||||||
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
// add our plugin commands in onEnable, but populating the executor
|
||||||
Bukkit.getPluginManager().registerEvents(new Listener() {
|
// can happen at any time (later in #onGeyserEnable())
|
||||||
|
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
||||||
|
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
||||||
|
// Thanks again, Bukkit
|
||||||
|
try {
|
||||||
|
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
@EventHandler
|
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
||||||
public void onServerLoaded(ServerLoadEvent event) {
|
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
||||||
// Wait until all plugins have loaded so Geyser can start
|
|
||||||
postStartup();
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
|
|
||||||
// Because Bukkit locks its command map upon startup, we need to
|
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
||||||
// add our plugin commands in onEnable, but populating the executor
|
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
||||||
// can happen at any time
|
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
||||||
CommandMap commandMap = GeyserSpigotCommandManager.getCommandMap();
|
|
||||||
for (Extension extension : this.geyserCommandManager.extensionCommands().keySet()) {
|
|
||||||
// Thanks again, Bukkit
|
|
||||||
try {
|
|
||||||
Constructor<PluginCommand> constructor = PluginCommand.class.getDeclaredConstructor(String.class, Plugin.class);
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
|
|
||||||
PluginCommand pluginCommand = constructor.newInstance(extension.description().id(), this);
|
|
||||||
pluginCommand.setDescription("The main command for the " + extension.name() + " Geyser extension!");
|
|
||||||
|
|
||||||
commandMap.register(extension.description().id(), "geyserext", pluginCommand);
|
|
||||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException ex) {
|
|
||||||
this.geyserLogger.error("Failed to construct PluginCommand for extension " + extension.name(), ex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (INITIALIZED) {
|
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
|
||||||
// Reload; continue with post startup
|
Bukkit.getPluginManager().registerEvents(new Listener() {
|
||||||
postStartup();
|
|
||||||
}
|
@EventHandler
|
||||||
|
public void onServerLoaded(ServerLoadEvent event) {
|
||||||
|
if (event.getType() == ServerLoadEvent.LoadType.RELOAD) {
|
||||||
|
geyser.setShuttingDown(false);
|
||||||
|
}
|
||||||
|
onGeyserEnable();
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postStartup() {
|
public void onGeyserEnable() {
|
||||||
GeyserImpl.start();
|
// 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
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.geyserLogger.setDebug(this.geyserConfig.isDebugMode());
|
||||||
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
}
|
||||||
|
|
||||||
// Turn "(MC: 1.16.4)" into 1.16.4.
|
GeyserImpl.start();
|
||||||
this.minecraftVersion = Bukkit.getServer().getVersion().split("\\(MC: ")[1].split("\\)")[0];
|
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
this.geyserSpigotPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
@ -238,20 +227,16 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
geyserLogger.debug("Spigot ping passthrough type: " + (this.geyserSpigotPingPassthrough == null ? null : this.geyserSpigotPingPassthrough.getClass()));
|
||||||
|
|
||||||
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
|
// Don't need to re-create the world manager/re-register commands/reinject when reloading
|
||||||
if (isViaVersion) {
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
try {
|
return;
|
||||||
// Ensure that we have the latest 4.0.0 changes and not an older ViaVersion version
|
|
||||||
Class.forName("com.viaversion.viaversion.api.ViaManager");
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
GeyserSpigotVersionChecker.sendOutdatedViaVersionMessage(geyserLogger);
|
|
||||||
isViaVersion = false;
|
|
||||||
if (this.geyserConfig.isDebugMode()) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isViaVersion = Bukkit.getPluginManager().getPlugin("ViaVersion") != null;
|
||||||
|
|
||||||
|
// Check to ensure the current setup can support the protocol version Geyser uses
|
||||||
|
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
||||||
|
|
||||||
// We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib
|
// We want to do this late in the server startup process to allow plugins such as ViaVersion and ProtocolLib
|
||||||
// To do their job injecting, then connect into *that*
|
// To do their job injecting, then connect into *that*
|
||||||
this.geyserInjector = new GeyserSpigotInjector(isViaVersion);
|
this.geyserInjector = new GeyserSpigotInjector(isViaVersion);
|
||||||
|
@ -278,6 +263,7 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
} else {
|
} else {
|
||||||
geyserLogger.debug("Not using NMS adapter as it is disabled via system property.");
|
geyserLogger.debug("Not using NMS adapter as it is disabled via system property.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.geyserWorldManager == null) {
|
if (this.geyserWorldManager == null) {
|
||||||
// No NMS adapter
|
// No NMS adapter
|
||||||
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
|
this.geyserWorldManager = new GeyserSpigotWorldManager(this);
|
||||||
|
@ -302,72 +288,72 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
command.setExecutor(new GeyserSpigotCommandExecutor(this.geyser, commands));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!INITIALIZED) {
|
// Register permissions so they appear in, for example, LuckPerms' UI
|
||||||
// Register permissions so they appear in, for example, LuckPerms' UI
|
// Re-registering permissions throws an error
|
||||||
// Re-registering permissions throws an error
|
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
||||||
for (Map.Entry<String, Command> entry : geyserCommandManager.commands().entrySet()) {
|
Command command = entry.getValue();
|
||||||
|
if (command.aliases().contains(entry.getKey())) {
|
||||||
|
// Don't register aliases
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||||
|
GeyserLocale.getLocaleStringLog(command.description()),
|
||||||
|
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register permissions for extension commands
|
||||||
|
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||||
|
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
||||||
Command command = entry.getValue();
|
Command command = entry.getValue();
|
||||||
if (command.aliases().contains(entry.getKey())) {
|
if (command.aliases().contains(entry.getKey())) {
|
||||||
// Don't register aliases
|
// Don't register aliases
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (command.permission().isBlank()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid registering the same permission twice, e.g. for the extension help commands
|
||||||
|
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
||||||
|
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
||||||
GeyserLocale.getLocaleStringLog(command.description()),
|
GeyserLocale.getLocaleStringLog(command.description()),
|
||||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register permissions for extension commands
|
|
||||||
for (Map.Entry<Extension, Map<String, Command>> commandEntry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
|
||||||
for (Map.Entry<String, Command> entry : commandEntry.getValue().entrySet()) {
|
|
||||||
Command command = entry.getValue();
|
|
||||||
if (command.aliases().contains(entry.getKey())) {
|
|
||||||
// Don't register aliases
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command.permission().isBlank()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid registering the same permission twice, e.g. for the extension help commands
|
|
||||||
if (Bukkit.getPluginManager().getPermission(command.permission()) != null) {
|
|
||||||
GeyserImpl.getInstance().getLogger().debug("Skipping permission " + command.permission() + " as it is already registered");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(command.permission(),
|
|
||||||
GeyserLocale.getLocaleStringLog(command.description()),
|
|
||||||
command.isSuggestedOpOnly() ? PermissionDefault.OP : PermissionDefault.TRUE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
|
||||||
"Whether update notifications can be seen", PermissionDefault.OP));
|
|
||||||
|
|
||||||
// Events cannot be unregistered - re-registering results in duplicate firings
|
|
||||||
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
|
||||||
|
|
||||||
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Bukkit.getPluginManager().addPermission(new Permission(Constants.UPDATE_PERMISSION,
|
||||||
|
"Whether update notifications can be seen", PermissionDefault.OP));
|
||||||
|
|
||||||
|
// Events cannot be unregistered - re-registering results in duplicate firings
|
||||||
|
GeyserSpigotBlockPlaceListener blockPlaceListener = new GeyserSpigotBlockPlaceListener(geyser, this.geyserWorldManager);
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(blockPlaceListener, this);
|
||||||
|
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserPistonListener(geyser, this.geyserWorldManager), this);
|
||||||
|
|
||||||
|
Bukkit.getServer().getPluginManager().registerEvents(new GeyserSpigotUpdateListener(), this);
|
||||||
|
|
||||||
boolean brigadierSupported = CommodoreProvider.isSupported();
|
boolean brigadierSupported = CommodoreProvider.isSupported();
|
||||||
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
geyserLogger.debug("Brigadier supported? " + brigadierSupported);
|
||||||
if (brigadierSupported) {
|
if (brigadierSupported) {
|
||||||
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
GeyserBrigadierSupport.loadBrigadier(this, geyserCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to ensure the current setup can support the protocol version Geyser uses
|
|
||||||
GeyserSpigotVersionChecker.checkForSupportedProtocol(geyserLogger, isViaVersion);
|
|
||||||
|
|
||||||
INITIALIZED = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
@ -376,6 +362,11 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisable() {
|
||||||
|
this.onGeyserShutdown();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeyserSpigotConfiguration getGeyserConfig() {
|
public GeyserSpigotConfiguration getGeyserConfig() {
|
||||||
return geyserConfig;
|
return geyserConfig;
|
||||||
|
@ -470,4 +461,25 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
private boolean loadConfig() {
|
||||||
|
// This is manually done instead of using Bukkit methods to save the config because otherwise comments get removed
|
||||||
|
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, GeyserSpigotConfiguration.class);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
Bukkit.getPluginManager().disablePlugin(this);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,9 @@ import org.apache.logging.log4j.core.Appender;
|
||||||
import org.apache.logging.log4j.core.Logger;
|
import org.apache.logging.log4j.core.Logger;
|
||||||
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
import org.apache.logging.log4j.core.appender.ConsoleAppender;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.geysermc.geyser.api.util.PlatformType;
|
|
||||||
import org.geysermc.geyser.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.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
|
||||||
|
@ -59,7 +59,12 @@ import java.lang.reflect.Method;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.*;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
|
@ -68,11 +73,10 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
private GeyserStandaloneConfiguration geyserConfig;
|
private GeyserStandaloneConfiguration geyserConfig;
|
||||||
private GeyserStandaloneLogger geyserLogger;
|
private GeyserStandaloneLogger geyserLogger;
|
||||||
private IGeyserPingPassthrough geyserPingPassthrough;
|
private IGeyserPingPassthrough geyserPingPassthrough;
|
||||||
|
|
||||||
private GeyserStandaloneGUI gui;
|
private GeyserStandaloneGUI gui;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private boolean useGui = System.console() == null && !isHeadless();
|
private boolean useGui = System.console() == null && !isHeadless();
|
||||||
|
private Logger log4jLogger;
|
||||||
private String configFilename = "config.yml";
|
private String configFilename = "config.yml";
|
||||||
|
|
||||||
private GeyserImpl geyser;
|
private GeyserImpl geyser;
|
||||||
|
@ -161,23 +165,19 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bootstrap.onEnable(useGuiOpts, configFilenameOpt);
|
bootstrap.useGui = useGuiOpts;
|
||||||
}
|
bootstrap.configFilename = configFilenameOpt;
|
||||||
|
bootstrap.onGeyserInitialize();
|
||||||
public void onEnable(boolean useGui, String configFilename) {
|
|
||||||
this.configFilename = configFilename;
|
|
||||||
this.useGui = useGui;
|
|
||||||
this.onEnable();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onGeyserInitialize() {
|
||||||
Logger logger = (Logger) LogManager.getRootLogger();
|
log4jLogger = (Logger) LogManager.getRootLogger();
|
||||||
for (Appender appender : logger.getAppenders().values()) {
|
for (Appender appender : log4jLogger.getAppenders().values()) {
|
||||||
// Remove the appender that is not in use
|
// Remove the appender that is not in use
|
||||||
// Prevents multiple appenders/double logging and removes harmless errors
|
// Prevents multiple appenders/double logging and removes harmless errors
|
||||||
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
if ((useGui && appender instanceof TerminalConsoleAppender) || (!useGui && appender instanceof ConsoleAppender)) {
|
||||||
logger.removeAppender(appender);
|
log4jLogger.removeAppender(appender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +190,12 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
LoopbackUtil.checkAndApplyLoopback(geyserLogger);
|
||||||
|
|
||||||
|
this.onGeyserEnable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserEnable() {
|
||||||
try {
|
try {
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
File configFile = FileUtils.fileOrCopiedFromResource(new File(configFilename), "config.yml",
|
||||||
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
@ -215,14 +220,15 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
// Allow libraries like Protocol to have their debug information passthrough
|
// Allow libraries like Protocol to have their debug information passthrough
|
||||||
logger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
log4jLogger.get().setLevel(geyserConfig.isDebugMode() ? Level.DEBUG : Level.INFO);
|
||||||
|
|
||||||
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
geyser = GeyserImpl.load(PlatformType.STANDALONE, this);
|
||||||
GeyserImpl.start();
|
|
||||||
|
|
||||||
geyserCommandManager = new GeyserCommandManager(geyser);
|
geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
geyserCommandManager.init();
|
geyserCommandManager.init();
|
||||||
|
|
||||||
|
GeyserImpl.start();
|
||||||
|
|
||||||
if (gui != null) {
|
if (gui != null) {
|
||||||
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
gui.enableCommands(geyser.getScheduledThread(), geyserCommandManager);
|
||||||
}
|
}
|
||||||
|
@ -250,7 +256,14 @@ public class GeyserStandaloneBootstrap implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
// We can re-register commands on standalone, so why not
|
||||||
|
GeyserImpl.getInstance().commandManager().getCommands().clear();
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
System.exit(0);
|
System.exit(0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements Gey
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void shutdown() {
|
protected void shutdown() {
|
||||||
GeyserImpl.getInstance().getBootstrap().onDisable();
|
GeyserImpl.getInstance().getBootstrap().onGeyserShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -32,10 +32,10 @@ import com.velocitypowered.api.event.proxy.ListenerBoundEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
import com.velocitypowered.api.event.proxy.ProxyInitializeEvent;
|
||||||
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
import com.velocitypowered.api.event.proxy.ProxyShutdownEvent;
|
||||||
import com.velocitypowered.api.network.ListenerType;
|
import com.velocitypowered.api.network.ListenerType;
|
||||||
|
import com.velocitypowered.api.network.ProtocolVersion;
|
||||||
import com.velocitypowered.api.plugin.Plugin;
|
import com.velocitypowered.api.plugin.Plugin;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.kyori.adventure.util.Codec;
|
|
||||||
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.GeyserBootstrap;
|
import org.geysermc.geyser.GeyserBootstrap;
|
||||||
|
@ -46,6 +46,7 @@ import org.geysermc.geyser.api.util.PlatformType;
|
||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
import org.geysermc.geyser.dump.BootstrapDumpInfo;
|
||||||
|
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.GeyserVelocityCommandExecutor;
|
import org.geysermc.geyser.platform.velocity.command.GeyserVelocityCommandExecutor;
|
||||||
|
@ -63,12 +64,6 @@ import java.util.UUID;
|
||||||
|
|
||||||
@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 {
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines if the plugin has been ran once before, including before /geyser reload.
|
|
||||||
*/
|
|
||||||
private static boolean INITIALIZED = false;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
|
@ -90,52 +85,54 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||||
private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/");
|
private final Path configFolder = Paths.get("plugins/" + GeyserImpl.NAME + "-Velocity/");
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onGeyserInitialize() {
|
||||||
try {
|
|
||||||
Codec.class.getMethod("codec", Codec.Decoder.class, Codec.Encoder.class);
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
// velocitypowered.com has a build that is very outdated
|
|
||||||
logger.error("Please download Velocity from https://papermc.io/downloads#Velocity - the 'stable' Velocity version " +
|
|
||||||
"that has likely been downloaded is very outdated and does not support 1.19.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GeyserLocale.init(this);
|
GeyserLocale.init(this);
|
||||||
|
|
||||||
try {
|
if (!ProtocolVersion.isSupported(GameProtocol.getJavaProtocolVersion())) {
|
||||||
if (!configFolder.toFile().exists())
|
logger.error(" / \\");
|
||||||
//noinspection ResultOfMethodCallIgnored
|
logger.error(" / \\");
|
||||||
configFolder.toFile().mkdirs();
|
logger.error(" / | \\");
|
||||||
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(),
|
logger.error(" / | \\ " + GeyserLocale.getLocaleStringLog("geyser.bootstrap.unsupported_proxy", proxyServer.getVersion().getName()));
|
||||||
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
logger.error(" / \\ " + GeyserLocale.getLocaleStringLog("geyser.may_not_work_as_intended_all_caps"));
|
||||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
logger.error(" / o \\");
|
||||||
} catch (IOException ex) {
|
logger.error("/_____________\\");
|
||||||
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
|
||||||
ex.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!loadConfig()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
|
this.geyserLogger = new GeyserVelocityLogger(logger, geyserConfig.isDebugMode());
|
||||||
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
|
||||||
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
|
this.geyser = GeyserImpl.load(PlatformType.VELOCITY, this);
|
||||||
|
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
||||||
// Hack: Normally triggered by ListenerBoundEvent, but that doesn't fire on /geyser reload
|
|
||||||
if (INITIALIZED) {
|
|
||||||
this.postStartup();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void postStartup() {
|
@Override
|
||||||
GeyserImpl.start();
|
public void onGeyserEnable() {
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
if (!INITIALIZED) {
|
if (!loadConfig()) {
|
||||||
this.geyserInjector = new GeyserVelocityInjector(proxyServer);
|
return;
|
||||||
// Will be initialized after the proxy has been bound
|
}
|
||||||
|
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
|
||||||
|
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
|
||||||
|
} else {
|
||||||
|
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
||||||
|
this.geyserCommandManager.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.geyserCommandManager = new GeyserCommandManager(geyser);
|
GeyserImpl.start();
|
||||||
this.geyserCommandManager.init();
|
|
||||||
|
if (geyserConfig.isLegacyPingPassthrough()) {
|
||||||
|
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
||||||
|
} else {
|
||||||
|
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to re-register commands when reloading
|
||||||
|
if (GeyserImpl.getInstance().isReloading()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
this.commandManager.register("geyser", new GeyserVelocityCommandExecutor(geyser, geyserCommandManager.getCommands()));
|
||||||
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
|
||||||
|
@ -147,17 +144,18 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||||
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
|
this.commandManager.register(entry.getKey().description().id(), new GeyserVelocityCommandExecutor(this.geyser, commands));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (geyserConfig.isLegacyPingPassthrough()) {
|
|
||||||
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
|
|
||||||
} else {
|
|
||||||
this.geyserPingPassthrough = new GeyserVelocityPingPassthrough(proxyServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
proxyServer.getEventManager().register(this, new GeyserVelocityUpdateListener());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisable() {
|
public void onGeyserDisable() {
|
||||||
|
if (geyser != null) {
|
||||||
|
geyser.disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGeyserShutdown() {
|
||||||
if (geyser != null) {
|
if (geyser != null) {
|
||||||
geyser.shutdown();
|
geyser.shutdown();
|
||||||
}
|
}
|
||||||
|
@ -188,26 +186,24 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onInit(ProxyInitializeEvent event) {
|
public void onInit(ProxyInitializeEvent event) {
|
||||||
onEnable();
|
this.onGeyserInitialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onShutdown(ProxyShutdownEvent event) {
|
public void onShutdown(ProxyShutdownEvent event) {
|
||||||
onDisable();
|
this.onGeyserShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Subscribe
|
@Subscribe
|
||||||
public void onProxyBound(ListenerBoundEvent event) {
|
public void onProxyBound(ListenerBoundEvent event) {
|
||||||
if (event.getListenerType() == ListenerType.MINECRAFT) {
|
if (event.getListenerType() == ListenerType.MINECRAFT) {
|
||||||
// Once listener is bound, do our startup process
|
// Once listener is bound, do our startup process
|
||||||
this.postStartup();
|
this.onGeyserEnable();
|
||||||
|
|
||||||
if (geyserInjector != null) {
|
if (geyserInjector != null) {
|
||||||
// After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too
|
// After this bound, we know that the channel initializer cannot change without it being ineffective for Velocity, too
|
||||||
geyserInjector.initializeLocalChannel(this);
|
geyserInjector.initializeLocalChannel(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
INITIALIZED = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,4 +238,21 @@ public class GeyserVelocityPlugin implements GeyserBootstrap {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
|
private boolean loadConfig() {
|
||||||
|
try {
|
||||||
|
if (!configFolder.toFile().exists())
|
||||||
|
//noinspection ResultOfMethodCallIgnored
|
||||||
|
configFolder.toFile().mkdirs();
|
||||||
|
File configFile = FileUtils.fileOrCopiedFromResource(configFolder.resolve("config.yml").toFile(),
|
||||||
|
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
|
||||||
|
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserVelocityConfiguration.class);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
|
||||||
|
ex.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,14 +44,28 @@ public interface GeyserBootstrap {
|
||||||
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
GeyserWorldManager DEFAULT_CHUNK_MANAGER = new GeyserWorldManager();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the GeyserBootstrap is enabled
|
* Called when the GeyserBootstrap is initialized.
|
||||||
|
* This will only be called once, when Geyser is loading. Calling this must
|
||||||
|
* happen before {@link #onGeyserEnable()}, since this "sets up" Geyser.
|
||||||
*/
|
*/
|
||||||
void onEnable();
|
void onGeyserInitialize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the GeyserBootstrap is disabled
|
* Called when the GeyserBootstrap is enabled/reloaded.
|
||||||
|
* This starts Geyser, after which, Geyser is in a player-accepting state.
|
||||||
*/
|
*/
|
||||||
void onDisable();
|
void onGeyserEnable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the GeyserBootstrap is disabled - either before a reload,
|
||||||
|
* of before fully shutting down.
|
||||||
|
*/
|
||||||
|
void onGeyserDisable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the GeyserBootstrap is shutting down.
|
||||||
|
*/
|
||||||
|
void onGeyserShutdown();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current GeyserConfiguration
|
* Returns the current GeyserConfiguration
|
||||||
|
|
|
@ -58,9 +58,7 @@ import org.geysermc.floodgate.news.NewsItemAction;
|
||||||
import org.geysermc.geyser.api.GeyserApi;
|
import org.geysermc.geyser.api.GeyserApi;
|
||||||
import org.geysermc.geyser.api.event.EventBus;
|
import org.geysermc.geyser.api.event.EventBus;
|
||||||
import org.geysermc.geyser.api.event.EventRegistrar;
|
import org.geysermc.geyser.api.event.EventRegistrar;
|
||||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPostInitializeEvent;
|
import org.geysermc.geyser.api.event.lifecycle.*;
|
||||||
import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent;
|
|
||||||
import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent;
|
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.api.network.BedrockListener;
|
import org.geysermc.geyser.api.network.BedrockListener;
|
||||||
import org.geysermc.geyser.api.network.RemoteServer;
|
import org.geysermc.geyser.api.network.RemoteServer;
|
||||||
|
@ -145,6 +143,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
private UnixSocketClientListener erosionUnixListener;
|
private UnixSocketClientListener erosionUnixListener;
|
||||||
|
|
||||||
|
@Setter
|
||||||
private volatile boolean shuttingDown = false;
|
private volatile boolean shuttingDown = false;
|
||||||
|
|
||||||
private ScheduledExecutorService scheduledThread;
|
private ScheduledExecutorService scheduledThread;
|
||||||
|
@ -162,8 +161,14 @@ public class GeyserImpl implements GeyserApi {
|
||||||
@Getter(AccessLevel.NONE)
|
@Getter(AccessLevel.NONE)
|
||||||
private Map<String, String> savedRefreshTokens;
|
private Map<String, String> savedRefreshTokens;
|
||||||
|
|
||||||
|
@Getter
|
||||||
private static GeyserImpl instance;
|
private static GeyserImpl instance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if we're currently reloading. Replaces per-bootstrap reload checks
|
||||||
|
*/
|
||||||
|
private volatile boolean isReloading;
|
||||||
|
|
||||||
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
|
private GeyserImpl(PlatformType platformType, GeyserBootstrap bootstrap) {
|
||||||
instance = this;
|
instance = this;
|
||||||
|
|
||||||
|
@ -172,13 +177,16 @@ public class GeyserImpl implements GeyserApi {
|
||||||
this.platformType = platformType;
|
this.platformType = platformType;
|
||||||
this.bootstrap = bootstrap;
|
this.bootstrap = bootstrap;
|
||||||
|
|
||||||
GeyserLocale.finalizeDefaultLocale(this);
|
|
||||||
|
|
||||||
/* Initialize event bus */
|
/* Initialize event bus */
|
||||||
this.eventBus = new GeyserEventBus();
|
this.eventBus = new GeyserEventBus();
|
||||||
|
|
||||||
/* Load Extensions */
|
/* Create Extension Manager */
|
||||||
this.extensionManager = new GeyserExtensionManager();
|
this.extensionManager = new GeyserExtensionManager();
|
||||||
|
|
||||||
|
/* Finalize locale loading now that we know the default locale from the config */
|
||||||
|
GeyserLocale.finalizeDefaultLocale(this);
|
||||||
|
|
||||||
|
/* Load Extensions */
|
||||||
this.extensionManager.init();
|
this.extensionManager.init();
|
||||||
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
this.eventBus.fire(new GeyserPreInitializeEvent(this.extensionManager, this.eventBus));
|
||||||
}
|
}
|
||||||
|
@ -236,11 +244,17 @@ public class GeyserImpl implements GeyserApi {
|
||||||
} else if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
} else if (config.getRemote().authType() == AuthType.FLOODGATE) {
|
||||||
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
VersionCheckUtils.checkForOutdatedFloodgate(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VersionCheckUtils.checkForOutdatedJava(logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startInstance() {
|
private void startInstance() {
|
||||||
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
|
this.scheduledThread = Executors.newSingleThreadScheduledExecutor(new DefaultThreadFactory("Geyser Scheduled Thread"));
|
||||||
|
|
||||||
|
if (isReloading) {
|
||||||
|
// If we're reloading, the default locale in the config might have changed.
|
||||||
|
GeyserLocale.finalizeDefaultLocale(this);
|
||||||
|
}
|
||||||
GeyserLogger logger = bootstrap.getGeyserLogger();
|
GeyserLogger logger = bootstrap.getGeyserLogger();
|
||||||
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
GeyserConfiguration config = bootstrap.getGeyserConfig();
|
||||||
|
|
||||||
|
@ -536,12 +550,15 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
|
newsHandler.handleNews(null, NewsItemAction.ON_SERVER_STARTED);
|
||||||
|
|
||||||
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
if (isReloading) {
|
||||||
|
this.eventBus.fire(new GeyserPostReloadEvent(this.extensionManager, this.eventBus));
|
||||||
|
} else {
|
||||||
|
this.eventBus.fire(new GeyserPostInitializeEvent(this.extensionManager, this.eventBus));
|
||||||
|
}
|
||||||
|
|
||||||
if (config.isNotifyOnNewBedrockUpdate()) {
|
if (config.isNotifyOnNewBedrockUpdate()) {
|
||||||
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
VersionCheckUtils.checkForGeyserUpdate(this::getLogger);
|
||||||
}
|
}
|
||||||
|
|
||||||
VersionCheckUtils.checkForOutdatedJava(logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -600,9 +617,8 @@ public class GeyserImpl implements GeyserApi {
|
||||||
return session.transfer(address, port);
|
return session.transfer(address, port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void disable() {
|
||||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown"));
|
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown"));
|
||||||
shuttingDown = true;
|
|
||||||
|
|
||||||
if (sessionManager.size() >= 1) {
|
if (sessionManager.size() >= 1) {
|
||||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size()));
|
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size()));
|
||||||
|
@ -616,7 +632,6 @@ public class GeyserImpl implements GeyserApi {
|
||||||
skinUploader.close();
|
skinUploader.close();
|
||||||
}
|
}
|
||||||
newsHandler.shutdown();
|
newsHandler.shutdown();
|
||||||
this.commandManager().getCommands().clear();
|
|
||||||
|
|
||||||
if (this.erosionUnixListener != null) {
|
if (this.erosionUnixListener != null) {
|
||||||
this.erosionUnixListener.close();
|
this.erosionUnixListener.close();
|
||||||
|
@ -624,16 +639,29 @@ public class GeyserImpl implements GeyserApi {
|
||||||
|
|
||||||
Registries.RESOURCE_PACKS.get().clear();
|
Registries.RESOURCE_PACKS.get().clear();
|
||||||
|
|
||||||
|
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void shutdown() {
|
||||||
|
shuttingDown = true;
|
||||||
|
this.disable();
|
||||||
|
this.commandManager().getCommands().clear();
|
||||||
|
|
||||||
|
// Disable extensions, fire the shutdown event
|
||||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||||
this.extensionManager.disableExtensions();
|
this.extensionManager.disableExtensions();
|
||||||
|
|
||||||
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
bootstrap.getGeyserLogger().info(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.done"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reload() {
|
public void reloadGeyser() {
|
||||||
shutdown();
|
isReloading = true;
|
||||||
this.extensionManager.enableExtensions();
|
this.eventBus.fire(new GeyserPreReloadEvent(this.extensionManager, this.eventBus));
|
||||||
bootstrap.onEnable();
|
|
||||||
|
bootstrap.onGeyserDisable();
|
||||||
|
bootstrap.onGeyserEnable();
|
||||||
|
|
||||||
|
isReloading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -744,9 +772,7 @@ public class GeyserImpl implements GeyserApi {
|
||||||
throw new RuntimeException("Geyser has not been loaded yet!");
|
throw new RuntimeException("Geyser has not been loaded yet!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// We've been reloaded
|
if (getInstance().isReloading()) {
|
||||||
if (instance.isShuttingDown()) {
|
|
||||||
instance.shuttingDown = false;
|
|
||||||
instance.startInstance();
|
instance.startInstance();
|
||||||
} else {
|
} else {
|
||||||
instance.initialize();
|
instance.initialize();
|
||||||
|
@ -797,8 +823,4 @@ public class GeyserImpl implements GeyserApi {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GeyserImpl getInstance() {
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class GeyserCommandManager {
|
||||||
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
registerBuiltInCommand(new StopCommand(geyser, "stop", "geyser.commands.stop.desc", "geyser.command.stop"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.geyser.extensionManager().extensions().size() > 0) {
|
if (!this.geyser.extensionManager().extensions().isEmpty()) {
|
||||||
registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions"));
|
registerBuiltInCommand(new ExtensionsCommand(this.geyser, "extensions", "geyser.commands.extensions.desc", "geyser.command.extensions"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ public class ReloadCommand extends GeyserCommand {
|
||||||
|
|
||||||
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
|
geyser.getSessionManager().disconnectAll("geyser.commands.reload.kick");
|
||||||
//FIXME Without the tiny wait, players do not get kicked - same happens when Geyser tries to disconnect all sessions on shutdown
|
//FIXME Without the tiny wait, players do not get kicked - same happens when Geyser tries to disconnect all sessions on shutdown
|
||||||
geyser.getScheduledThread().schedule(geyser::reload, 10, TimeUnit.MILLISECONDS);
|
geyser.getScheduledThread().schedule(geyser::reloadGeyser, 10, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class StopCommand extends GeyserCommand {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
geyser.getBootstrap().onDisable();
|
geyser.getBootstrap().onGeyserShutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -166,7 +166,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PacketSignal handle(LoginPacket loginPacket) {
|
public PacketSignal handle(LoginPacket loginPacket) {
|
||||||
if (geyser.isShuttingDown()) {
|
if (geyser.isShuttingDown() || geyser.isReloading()) {
|
||||||
// Don't allow new players in if we're no longer operating
|
// Don't allow new players in if we're no longer operating
|
||||||
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.message"));
|
session.disconnect(GeyserLocale.getLocaleStringLog("geyser.core.shutdown.kick.message"));
|
||||||
return PacketSignal.HANDLED;
|
return PacketSignal.HANDLED;
|
||||||
|
|
|
@ -62,7 +62,7 @@ public final class ScoreboardUpdater extends Thread {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (!geyser.isShuttingDown()) {
|
while (!geyser.isShuttingDown() && !geyser.isReloading()) {
|
||||||
try {
|
try {
|
||||||
long timeTillAction = getTimeTillNextAction();
|
long timeTillAction = getTimeTillNextAction();
|
||||||
if (timeTillAction > 0) {
|
if (timeTillAction > 0) {
|
||||||
|
|
|
@ -266,6 +266,7 @@ public final class EntityUtils {
|
||||||
* Convert Java GameMode to Bedrock GameType
|
* Convert Java GameMode to Bedrock GameType
|
||||||
* Needed to account for ordinal differences (spectator is 3 in Java, 6 in Bedrock)
|
* Needed to account for ordinal differences (spectator is 3 in Java, 6 in Bedrock)
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation") // Must use survival_viewer due to limitations on Bedrock's spectator gamemode
|
||||||
public static GameType toBedrockGamemode(GameMode gamemode) {
|
public static GameType toBedrockGamemode(GameMode gamemode) {
|
||||||
return switch (gamemode) {
|
return switch (gamemode) {
|
||||||
case CREATIVE -> GameType.CREATIVE;
|
case CREATIVE -> GameType.CREATIVE;
|
||||||
|
|
Loading…
Reference in a new issue