Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Rework startup logic #4377

Merged
merged 24 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
aa93980
init changes on startup logic rework
onebeastchris Nov 23, 2023
762ad1b
Init changes
onebeastchris Dec 12, 2023
e2b03b0
Bungee changes
onebeastchris Dec 12, 2023
f5eac04
Change spigot, standalone, velocity too
onebeastchris Dec 12, 2023
31dee25
Merge remote-tracking branch 'upstream/master' into rework-startup-logic
onebeastchris Dec 12, 2023
697269d
Undo DimensionUtils diff
onebeastchris Dec 12, 2023
2a1baab
- yeet reload hack in GeyserFabricCommandExecutor
onebeastchris Dec 12, 2023
10ca44a
oops
onebeastchris Dec 12, 2023
79ff36f
Merge remote-tracking branch 'upstream/master' into rework-startup-logic
onebeastchris Jan 3, 2024
a6f9cc2
finalize structure
onebeastchris Jan 3, 2024
9877ad2
- Geyser-Velocity works!
onebeastchris Jan 4, 2024
f5756db
Bungee query check should run again with reloading
onebeastchris Jan 4, 2024
61513c7
start before registering commands
onebeastchris Jan 4, 2024
80216fa
Split GeyserBootstrap disable and shutdown logic
onebeastchris Jan 17, 2024
1240c70
also commit the different changelist.. oops
onebeastchris Jan 17, 2024
8033925
yeet duplicate proxyshutdown event listener
onebeastchris Jan 17, 2024
ec3914e
Start Geyser directly on onEnable, since we cannot register commands …
onebeastchris Jan 17, 2024
d63d80e
some more consistency
onebeastchris Jan 29, 2024
4018da2
Merge remote-tracking branch 'upstream/master' into rework-startup-logic
onebeastchris Feb 12, 2024
ba14b70
Undo starting Geyser early on Spigot/Paper to not break compatability
onebeastchris Feb 12, 2024
2de7731
re-check for configured default locale during a reload
onebeastchris Feb 12, 2024
23607ca
cleaner checks, less needless diff
onebeastchris Feb 12, 2024
07c470c
just return early when we reload to avoid re-registering commands/re-…
onebeastchris Feb 12, 2024
6f8cb3e
suppress inverted method warning, re-add bungee hack comment
onebeastchris Feb 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@
import net.md_5.bungee.protocol.ProtocolConstants;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
Expand Down Expand Up @@ -70,11 +70,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {

private GeyserImpl geyser;

private static boolean INITIALIZED = false;

@SuppressWarnings({"JavaReflectionMemberAccess", "ResultOfMethodCallIgnored"})
@Override
public void onLoad() {
onGeyserInitialize();
}

@Override
public void onGeyserInitialize() {
GeyserLocale.init(this);

// Copied from ViaVersion.
Expand All @@ -91,68 +93,31 @@ public void onLoad() {
getLogger().warning("/_____________\\");
}

if (!getDataFolder().exists())
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();
if (!this.loadConfig()) {
return;
}

this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);

this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
this.geyserInjector = new GeyserBungeeInjector(this);
}

@Override
public void onEnable() {

// Force-disable query if enabled, or else Geyser won't enable
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
try {
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
queryField.setAccessible(true);
queryField.setBoolean(info, false);
geyserLogger.warning("We force-disabled query on port " + info.getQueryPort() + " in order for Geyser to boot up successfully. " +
"To remove this message, disable query in your proxy's config.");
} catch (NoSuchFieldException | IllegalAccessException e) {
geyserLogger.warning("Could not force-disable query. Geyser may not start correctly!");
if (geyserLogger.isDebug()) {
e.printStackTrace();
}
}
}
}

// 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
if (!INITIALIZED) {
this.awaitStartupCompletion(0);
} else {
// No need to "wait" for startup completion, just start Geyser - we're reloading.
this.postStartup();
}
this.awaitStartupCompletion(0);
}

@SuppressWarnings("unchecked")
private void awaitStartupCompletion(int tries) {
// After 20 tries give up waiting. This will happen
// just after 3 minutes approximately
// 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.postStartup();
this.onGeyserEnable();
return;
}

Expand All @@ -162,7 +127,7 @@ private void awaitStartupCompletion(int tries) {

Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
if (listeners.isEmpty()) {
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
this.getProxy().getScheduler().schedule(this, this::onGeyserEnable, tries, TimeUnit.SECONDS);
} else {
this.awaitStartupCompletion(++tries);
}
Expand All @@ -171,16 +136,52 @@ private void awaitStartupCompletion(int tries) {
}
}

private void postStartup() {
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
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
try {
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
queryField.setAccessible(true);
queryField.setBoolean(info, false);
geyserLogger.warning("We force-disabled query on port " + info.getQueryPort() + " in order for Geyser to boot up successfully. " +
"To remove this message, disable query in your proxy's config.");
} catch (NoSuchFieldException | IllegalAccessException e) {
geyserLogger.warning("Could not force-disable query. Geyser may not start correctly!");
if (geyserLogger.isDebug()) {
e.printStackTrace();
}
}
}
}

GeyserImpl.start();

if (!INITIALIZED) {
this.geyserInjector = new GeyserBungeeInjector(this);
this.geyserInjector.initializeLocalChannel(this);
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
}

this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
// No need to re-register commands or re-init injector when reloading
if (GeyserImpl.getInstance().isReloading()) {
return;
}

this.geyserInjector.initializeLocalChannel(this);

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()) {
Expand All @@ -191,18 +192,17 @@ private void postStartup() {

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());
@Override
public void onGeyserDisable() {
if (geyser != null) {
geyser.disable();
}

INITIALIZED = true;
}

@Override
public void onDisable() {
public void onGeyserShutdown() {
if (geyser != null) {
geyser.shutdown();
}
Expand All @@ -211,6 +211,11 @@ public void onDisable() {
}
}

@Override
public void onDisable() {
this.onGeyserShutdown();
}

@Override
public GeyserBungeeConfiguration getGeyserConfig() {
return geyserConfig;
Expand Down Expand Up @@ -278,4 +283,20 @@ private Optional<InetSocketAddress> findCompatibleListener() {
.map(info -> (InetSocketAddress) info.getSocketAddress())
.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;
}
}
Loading
Loading