Skip to content

Commit 6a51d82

Browse files
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.
1 parent 19c6648 commit 6a51d82

File tree

18 files changed

+520
-339
lines changed

18 files changed

+520
-339
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* @author GeyserMC
23+
* @link https://github.com/GeyserMC/Geyser
24+
*/
25+
26+
package org.geysermc.geyser.api.event.lifecycle;
27+
28+
import org.checkerframework.checker.nullness.qual.NonNull;
29+
import org.geysermc.event.Event;
30+
import org.geysermc.geyser.api.event.EventBus;
31+
import org.geysermc.geyser.api.event.EventRegistrar;
32+
import org.geysermc.geyser.api.extension.ExtensionManager;
33+
34+
/**
35+
* Called when Geyser finished reloading and is accepting Bedrock connections again.
36+
* Equivalent to the {@link GeyserPostInitializeEvent}
37+
*
38+
* @param extensionManager the extension manager
39+
* @param eventBus the event bus
40+
*/
41+
public record GeyserPostReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright (c) 2019-2024 GeyserMC. http://geysermc.org
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to deal
6+
* in the Software without restriction, including without limitation the rights
7+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+
* copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+
* THE SOFTWARE.
21+
*
22+
* @author GeyserMC
23+
* @link https://github.com/GeyserMC/Geyser
24+
*/
25+
26+
package org.geysermc.geyser.api.event.lifecycle;
27+
28+
import org.checkerframework.checker.nullness.qual.NonNull;
29+
import org.geysermc.event.Event;
30+
import org.geysermc.geyser.api.event.EventBus;
31+
import org.geysermc.geyser.api.event.EventRegistrar;
32+
import org.geysermc.geyser.api.extension.ExtensionManager;
33+
34+
/**
35+
* Called when Geyser is about to reload. Primarily aimed at extensions, so they can decide on their own what to reload.
36+
* After this event is fired, some lifecycle events can be fired again - such as the {@link GeyserLoadResourcePacksEvent}.
37+
*
38+
* @param extensionManager the extension manager
39+
* @param eventBus the event bus
40+
*/
41+
public record GeyserPreReloadEvent(@NonNull ExtensionManager extensionManager, @NonNull EventBus<EventRegistrar> eventBus) implements Event {
42+
}

bootstrap/bungeecord/src/main/java/org/geysermc/geyser/platform/bungeecord/GeyserBungeePlugin.java

+81-60
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,11 @@
3232
import net.md_5.bungee.protocol.ProtocolConstants;
3333
import org.checkerframework.checker.nullness.qual.NonNull;
3434
import org.checkerframework.checker.nullness.qual.Nullable;
35-
import org.geysermc.geyser.api.util.PlatformType;
3635
import org.geysermc.geyser.GeyserBootstrap;
3736
import org.geysermc.geyser.GeyserImpl;
3837
import org.geysermc.geyser.api.command.Command;
3938
import org.geysermc.geyser.api.extension.Extension;
39+
import org.geysermc.geyser.api.util.PlatformType;
4040
import org.geysermc.geyser.command.GeyserCommandManager;
4141
import org.geysermc.geyser.configuration.GeyserConfiguration;
4242
import org.geysermc.geyser.dump.BootstrapDumpInfo;
@@ -70,11 +70,13 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
7070

7171
private GeyserImpl geyser;
7272

73-
private static boolean INITIALIZED = false;
74-
75-
@SuppressWarnings({"JavaReflectionMemberAccess", "ResultOfMethodCallIgnored"})
7673
@Override
7774
public void onLoad() {
75+
onGeyserInitialize();
76+
}
77+
78+
@Override
79+
public void onGeyserInitialize() {
7880
GeyserLocale.init(this);
7981

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

94-
if (!getDataFolder().exists())
95-
getDataFolder().mkdir();
96-
97-
try {
98-
if (!getDataFolder().exists())
99-
getDataFolder().mkdir();
100-
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"),
101-
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
102-
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
103-
} catch (IOException ex) {
104-
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
105-
ex.printStackTrace();
96+
if (!this.loadConfig()) {
10697
return;
10798
}
108-
10999
this.geyserLogger = new GeyserBungeeLogger(getLogger(), geyserConfig.isDebugMode());
110100
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
111-
112101
this.geyser = GeyserImpl.load(PlatformType.BUNGEECORD, this);
102+
this.geyserInjector = new GeyserBungeeInjector(this);
113103
}
114104

115105
@Override
116106
public void onEnable() {
117-
118-
// Force-disable query if enabled, or else Geyser won't enable
119-
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
120-
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
121-
try {
122-
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
123-
queryField.setAccessible(true);
124-
queryField.setBoolean(info, false);
125-
geyserLogger.warning("We force-disabled query on port " + info.getQueryPort() + " in order for Geyser to boot up successfully. " +
126-
"To remove this message, disable query in your proxy's config.");
127-
} catch (NoSuchFieldException | IllegalAccessException e) {
128-
geyserLogger.warning("Could not force-disable query. Geyser may not start correctly!");
129-
if (geyserLogger.isDebug()) {
130-
e.printStackTrace();
131-
}
132-
}
133-
}
134-
}
135-
136107
// Big hack - Bungee does not provide us an event to listen to, so schedule a repeating
137108
// task that waits for a field to be filled which is set after the plugin enable
138109
// process is complete
139-
if (!INITIALIZED) {
140-
this.awaitStartupCompletion(0);
141-
} else {
142-
// No need to "wait" for startup completion, just start Geyser - we're reloading.
143-
this.postStartup();
144-
}
110+
this.awaitStartupCompletion(0);
145111
}
146112

147113
@SuppressWarnings("unchecked")
148114
private void awaitStartupCompletion(int tries) {
149-
// After 20 tries give up waiting. This will happen
150-
// just after 3 minutes approximately
115+
// After 20 tries give up waiting. This will happen just after 3 minutes approximately
151116
if (tries >= 20) {
152117
this.geyserLogger.warning("BungeeCord plugin startup is taking abnormally long, so Geyser is starting now. " +
153118
"If all your plugins are loaded properly, this is a bug! " +
154119
"If not, consider cutting down the amount of plugins on your proxy as it is causing abnormally slow starting times.");
155-
this.postStartup();
120+
this.onGeyserEnable();
156121
return;
157122
}
158123

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

163128
Collection<Channel> listeners = (Collection<Channel>) listenersField.get(BungeeCord.getInstance());
164129
if (listeners.isEmpty()) {
165-
this.getProxy().getScheduler().schedule(this, this::postStartup, tries, TimeUnit.SECONDS);
130+
this.getProxy().getScheduler().schedule(this, this::onGeyserEnable, tries, TimeUnit.SECONDS);
166131
} else {
167132
this.awaitStartupCompletion(++tries);
168133
}
@@ -171,16 +136,52 @@ private void awaitStartupCompletion(int tries) {
171136
}
172137
}
173138

174-
private void postStartup() {
139+
public void onGeyserEnable() {
140+
if (GeyserImpl.getInstance().isReloading()) {
141+
if (!loadConfig()) {
142+
return;
143+
}
144+
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
145+
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
146+
} else {
147+
// For consistency with other platforms - create command manager before GeyserImpl#start()
148+
// This ensures the command events are called before the item/block ones are
149+
this.geyserCommandManager = new GeyserCommandManager(geyser);
150+
this.geyserCommandManager.init();
151+
}
152+
153+
// Force-disable query if enabled, or else Geyser won't enable
154+
for (ListenerInfo info : getProxy().getConfig().getListeners()) {
155+
if (info.isQueryEnabled() && info.getQueryPort() == geyserConfig.getBedrock().port()) {
156+
try {
157+
Field queryField = ListenerInfo.class.getDeclaredField("queryEnabled");
158+
queryField.setAccessible(true);
159+
queryField.setBoolean(info, false);
160+
geyserLogger.warning("We force-disabled query on port " + info.getQueryPort() + " in order for Geyser to boot up successfully. " +
161+
"To remove this message, disable query in your proxy's config.");
162+
} catch (NoSuchFieldException | IllegalAccessException e) {
163+
geyserLogger.warning("Could not force-disable query. Geyser may not start correctly!");
164+
if (geyserLogger.isDebug()) {
165+
e.printStackTrace();
166+
}
167+
}
168+
}
169+
}
170+
175171
GeyserImpl.start();
176172

177-
if (!INITIALIZED) {
178-
this.geyserInjector = new GeyserBungeeInjector(this);
179-
this.geyserInjector.initializeLocalChannel(this);
173+
if (geyserConfig.isLegacyPingPassthrough()) {
174+
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
175+
} else {
176+
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
180177
}
181178

182-
this.geyserCommandManager = new GeyserCommandManager(geyser);
183-
this.geyserCommandManager.init();
179+
// No need to re-register commands or re-init injector when reloading
180+
if (GeyserImpl.getInstance().isReloading()) {
181+
return;
182+
}
183+
184+
this.geyserInjector.initializeLocalChannel(this);
184185

185186
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor("geyser", this.geyser, this.geyserCommandManager.getCommands()));
186187
for (Map.Entry<Extension, Map<String, Command>> entry : this.geyserCommandManager.extensionCommands().entrySet()) {
@@ -191,18 +192,17 @@ private void postStartup() {
191192

192193
this.getProxy().getPluginManager().registerCommand(this, new GeyserBungeeCommandExecutor(entry.getKey().description().id(), this.geyser, commands));
193194
}
195+
}
194196

195-
if (geyserConfig.isLegacyPingPassthrough()) {
196-
this.geyserBungeePingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
197-
} else {
198-
this.geyserBungeePingPassthrough = new GeyserBungeePingPassthrough(getProxy());
197+
@Override
198+
public void onGeyserDisable() {
199+
if (geyser != null) {
200+
geyser.disable();
199201
}
200-
201-
INITIALIZED = true;
202202
}
203203

204204
@Override
205-
public void onDisable() {
205+
public void onGeyserShutdown() {
206206
if (geyser != null) {
207207
geyser.shutdown();
208208
}
@@ -211,6 +211,11 @@ public void onDisable() {
211211
}
212212
}
213213

214+
@Override
215+
public void onDisable() {
216+
this.onGeyserShutdown();
217+
}
218+
214219
@Override
215220
public GeyserBungeeConfiguration getGeyserConfig() {
216221
return geyserConfig;
@@ -278,4 +283,20 @@ private Optional<InetSocketAddress> findCompatibleListener() {
278283
.map(info -> (InetSocketAddress) info.getSocketAddress())
279284
.findFirst();
280285
}
286+
287+
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
288+
private boolean loadConfig() {
289+
try {
290+
if (!getDataFolder().exists()) //noinspection ResultOfMethodCallIgnored
291+
getDataFolder().mkdir();
292+
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"),
293+
"config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
294+
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
295+
} catch (IOException ex) {
296+
getLogger().log(Level.SEVERE, GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
297+
ex.printStackTrace();
298+
return false;
299+
}
300+
return true;
301+
}
281302
}

0 commit comments

Comments
 (0)