Skip to content

Commit

Permalink
Lifecycle events (#27)
Browse files Browse the repository at this point in the history
* lifecycle events: implement server lifecycle events

* lifecycle events: implement client lifecycle events, separate world tick

* Fix issues noted by BasiqueEvangelist

* Appease the license checker

* Fill in some documentation gaps

* client gaps

* Address code reviews (#1)

* Remove misplaced client environment declaration

* Patch up some javadocs

Co-authored-by: ADudeCalledLeo <7997354+Leo40Git@users.noreply.github.com>
  • Loading branch information
i509VCB and Leo40Git committed Sep 23, 2021
1 parent 36f54a0 commit ea5692a
Show file tree
Hide file tree
Showing 16 changed files with 1,082 additions and 0 deletions.
12 changes: 12 additions & 0 deletions library/core/lifecycle-events/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
plugins {
id("qsl.module")
}

qslModule {
moduleName = "lifecycle-events"
version = "1.0.0"
library = "core"
coreDependencies([
"qsl-base"
])
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/*
* Copyright 2021 QuiltMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.lifecycle.api.client.event;

import org.quiltmc.qsl.base.api.event.ArrayEvent;

import net.minecraft.client.MinecraftClient;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

/**
* Events indicating the lifecycle of a Minecraft client.
*
* <p>The lifecycle of a Minecraft client, starts when the client is ready. The client will tick the client and then the
* integrated server if in single player.
*/
@Environment(EnvType.CLIENT)
public final class ClientLifecycleEvents {
// There is no STARTING event because there is no way to allow mods to register callbacks that early without possibly
// initializing the game's registries improperly in preLaunch.

/**
* An event indicating that a Minecraft client is ready to tick and render.
*
* <p>It should be noted this event is executed while the splash screen is visible, not the main menu.
*/
public static final ArrayEvent<Ready> READY = ArrayEvent.create(Ready.class, callbacks -> client -> {
for (var callback : callbacks) {
callback.readyClient(client);
}
});

/**
* An event indicating that a Minecraft client has finished its last tick and will shut down.
*
* <p>After this event is fired, the client will disconnect from the server if it is connected to one. Then, if the client
* was running an integrated server, the integrated server will be shut down. Finally, all client facilities are torn down.
*
* <h2>What should mods do when this event is executed?</h2>
*
* Mods which maintain session data when connected to a server should save that data here, as the client will still
* have access to the connected server.
*
* <p>If your mod has any data on the integrated server, avoid doing that here, use
* {@link org.quiltmc.qsl.lifecycle.api.event.ServerLifecycleEvents#STOPPING ServerLifecycleEvents.STOPPING}
* instead to clean up any data on the integrated server.
*/
public static final ArrayEvent<Stopping> STOPPING = ArrayEvent.create(Stopping.class, callbacks -> client -> {
for (var callback : callbacks) {
callback.stoppingClient(client);
}
});

/**
* An event indicating the client has finished shutdown and will exit.
*
* <p>The Java Virtual Machine will terminate after this event is executed.
*/
public static final ArrayEvent<Stopped> STOPPED = ArrayEvent.create(Stopped.class, callbacks -> client -> {
for (var callback : callbacks) {
callback.stoppedClient(client);
}
});

private ClientLifecycleEvents() {}

/**
* Functional interface to be implemented on callbacks for {@link #READY}.
* @see #READY
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Ready {
/**
* Called when a majority of client facilities have been initialized.
*
* <p>It should be noted this is executed while the splash screen is visible, not when the main menu is reached.
*
* @param client the client which is read.
*/
void readyClient(MinecraftClient client);
}

/**
* Functional interface to be implemented on callbacks for {@link #STOPPING}.
* @see #STOPPING
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Stopping {
/**
* Called when a Minecraft client has finished its last tick and is shutting down.
*
* @param client the client which is shutting down
*/
void stoppingClient(MinecraftClient client);
}

/**
* Functional interface to be implemented on callbacks for {@link #STOPPED}.
* @see #STOPPED
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Stopped {
/**
* Called when a Minecraft client has finished shutdown and the client will be exited.
*
* <p>This is typically executed just before the Java virtual machine is shut down.
*
* @param client the minecraft client which is exiting
*/
void stoppedClient(MinecraftClient client);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2021 QuiltMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.lifecycle.api.client.event;

import org.quiltmc.qsl.base.api.event.ArrayEvent;

import net.minecraft.client.MinecraftClient;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

/**
* Events indicating progress through the tick loop of a Minecraft client.
*
* <h2>A note of warning</h2>
*
* Callbacks registered to any of these events should ensure as little time as possible is spent executing, since the tick
* loop is a very hot code path.
*/
@Environment(EnvType.CLIENT)
public final class ClientTickEvents {
/**
* An event indicating an iteration of the client's tick loop will start.
*/
public static final ArrayEvent<Start> START = ArrayEvent.create(Start.class, callbacks -> client -> {
for (var callback : callbacks) {
callback.startClientTick(client);
}
});

/**
* An event indicating the client has finished an iteration of the tick loop.
*
* <p>Since there will be a time gap before the next tick, this is a great spot to run any asynchronous operations
* for the next tick.
*/
public static final ArrayEvent<End> END = ArrayEvent.create(End.class, callbacks -> client -> {
for (var callback : callbacks) {
callback.endClientTick(client);
}
});

private ClientTickEvents() {}

/**
* Functional interface to be implemented on callbacks for {@link #START}.
* @see #START
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Start {
/**
* Called before the client has started an iteration of the tick loop.
*
* @param client the client
*/
void startClientTick(MinecraftClient client);
}

/**
* Functional interface to be implemented on callbacks for {@link #END}.
* @see #END
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface End {
/**
* Called at the end of an iteration of the client's tick loop.
*
* @param client the client that finished ticking
*/
void endClientTick(MinecraftClient client);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2021 QuiltMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.quiltmc.qsl.lifecycle.api.client.event;

import org.quiltmc.qsl.base.api.event.ArrayEvent;

import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;

import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;

/**
* Events related to a ticking Minecraft client's world.
*
* <h2>A note of warning</h2>
*
* Callbacks registered to any of these events should ensure as little time as possible is spent executing, since the tick
* loop is a very hot code path.
*/
@Environment(EnvType.CLIENT)
public final class ClientWorldTickEvents {
/**
* An event indicating that a world will be ticked.
*/
public static final ArrayEvent<Start> START = ArrayEvent.create(Start.class, callbacks -> (client, world) -> {
for (var callback : callbacks) {
callback.startWorldTick(client, world);
}
});

/**
* An event indicating that a world has finished being ticked.
*/
public static final ArrayEvent<End> END = ArrayEvent.create(End.class, callbacks -> (client, world) -> {
for (var callback : callbacks) {
callback.endWorldTick(client, world);
}
});

private ClientWorldTickEvents() {}

/**
* Functional interface to be implemented on callbacks for {@link #START}.
* @see #START
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface Start {
/**
* Called before a world is ticked.
*
* @param client the client
* @param world the world being ticked
*/
void startWorldTick(MinecraftClient client, ClientWorld world);
}

/**
* Functional interface to be implemented on callbacks for {@link #END}.
* @see #END
*/
@FunctionalInterface
@Environment(EnvType.CLIENT)
public interface End {
/**
* Called after a world is ticked.
*
* @param client the client
* @param world the world being ticked
*/
void endWorldTick(MinecraftClient client, ClientWorld world);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Events to track the lifecycle of a Minecraft client.
*
* <p>The events in this package pertain to the logical Minecraft client, which handles input and output, rendering,
* connections to a server and starting/stopping the client's own integrated server. A Minecraft client operates using a
* tick loop and events are executed as the tick loop runs.
*
* <h2>The Minecraft client singleton and lifecycle events</h2>
*
* The {@link net.minecraft.client.MinecraftClient#getInstance() Minecraft client singleton} may be accessed at any time
* during mod initialization, however the singleton will be in an incomplete state during mod initialization.
* Many client facilities will not be completely setup at that point. The lifecycle events in this package are useful
* for be notified when most client facilities have been initialized and therefore can be safely interfaced with.
*
* <p>The events in {@link org.quiltmc.qsl.lifecycle.api.client.event.ClientLifecycleEvents} are executed during client
* initialization or shutdown.
*
* <p>The events in {@link org.quiltmc.qsl.lifecycle.api.client.event.ClientTickEvents} are executed as the tick loop is
* iterated.
*
* <p>To track initialization and shutdown of the client's integrated server, use
* {@link org.quiltmc.qsl.lifecycle.api.event.ServerLifecycleEvents}.
*
* @see org.quiltmc.qsl.lifecycle.api.client.event.ClientLifecycleEvents
* @see org.quiltmc.qsl.lifecycle.api.client.event.ClientTickEvents
* @see org.quiltmc.qsl.lifecycle.api.client.event.ClientWorldTickEvents
*/

package org.quiltmc.qsl.lifecycle.api.client.event;
Loading

0 comments on commit ea5692a

Please sign in to comment.