Lifecycle Events V1 [1.16] ()

* Lifecycle Events V1 (1.16)

Now includes Chunk and (Block)Entity (un)load events

(cherry picked from commit 82b372873fd930dd07768331f7b70f6f9d3d441d)

* Move around code to work on 1.16

* Checkstyle

* Remove primary server getters

(cherry picked from commit c84f36209d)

* IJ DO YOU SPEAK RESOLVING IMPORTS

* Prune the tests that shouldn't exist

(cherry picked from commit a5112223ca)

* Listen here checkstyle you bugger

* Split up events to individual interfaces. Make Chunk events use WorldChunk instead.

(cherry picked from commit 3431c0d894)
This commit is contained in:
i509VCB 2020-06-25 14:28:21 -07:00 committed by GitHub
parent 4d66bed54e
commit 16acbe5bfd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 2431 additions and 94 deletions
fabric-events-lifecycle-v0
fabric-item-api-v1
build.gradle
src
main
java/net/fabricmc/fabric
api/client/item/v1
mixin/item/client
resources
testmod
java/net/fabricmc/fabric/test/item/client
resources
fabric-lifecycle-events-v1
settings.gradle

View file

@ -1,6 +1,8 @@
archivesBaseName = "fabric-events-lifecycle-v0"
version = getSubprojectVersion(project, "0.1.3")
version = getSubprojectVersion(project, "0.2.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')
compile project(path: ':fabric-item-api-v1', configuration: 'dev')
compile project(path: ':fabric-lifecycle-events-v1', configuration: 'dev')
}

View file

@ -18,10 +18,16 @@ package net.fabricmc.fabric.api.event.client;
import net.minecraft.client.MinecraftClient;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Deprecated
public interface ClientTickCallback {
/**
* @deprecated Please use {@link ClientTickEvents#END_CLIENT_TICK}.
*/
@Deprecated
Event<ClientTickCallback> EVENT = EventFactory.createArrayBacked(ClientTickCallback.class,
(listeners) -> {
if (EventFactory.isProfilingEnabled()) {

View file

@ -25,10 +25,15 @@ import net.minecraft.text.Text;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
/**
* @deprecated Please use {@link net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback}
*/
@Deprecated
public interface ItemTooltipCallback {
/**
* Fired after the game has appended all base tooltip lines to the list.
*/
@Deprecated
Event<ItemTooltipCallback> EVENT = EventFactory.createArrayBacked(ItemTooltipCallback.class, (listeners) -> (stack, tooltipContext, lines) -> {
for (ItemTooltipCallback callback : listeners) {
callback.getTooltip(stack, tooltipContext, lines);

View file

@ -21,7 +21,12 @@ import net.minecraft.server.MinecraftServer;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Deprecated
public interface ServerStartCallback {
/**
* @deprecated Please use {@link net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents#SERVER_STARTED}
*/
@Deprecated
Event<ServerStartCallback> EVENT = EventFactory.createArrayBacked(ServerStartCallback.class,
(listeners) -> (server) -> {
for (ServerStartCallback event : listeners) {

View file

@ -21,7 +21,12 @@ import net.minecraft.server.MinecraftServer;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Deprecated
public interface ServerStopCallback {
/**
* @deprecated Please use {@link net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents#SERVER_STOPPING}
*/
@Deprecated
Event<ServerStopCallback> EVENT = EventFactory.createArrayBacked(ServerStopCallback.class,
(listeners) -> (server) -> {
for (ServerStopCallback event : listeners) {

View file

@ -20,8 +20,14 @@ import net.minecraft.server.MinecraftServer;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@Deprecated
public interface ServerTickCallback {
/**
* @deprecated Please use {@link ServerTickEvents#END_SERVER_TICK}
*/
@Deprecated
Event<ServerTickCallback> EVENT = EventFactory.createArrayBacked(ServerTickCallback.class,
(listeners) -> {
if (EventFactory.isProfilingEnabled()) {

View file

@ -18,10 +18,18 @@ package net.fabricmc.fabric.api.event.world;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@Deprecated
public interface WorldTickCallback {
/**
* @deprecated The new WorldTickCallback has been split into a client and server callback.
* Please use the {@link ServerTickEvents#END_WORLD_TICK server} or {@link ClientTickEvents#END_WORLD_TICK client} callbacks.
*/
@Deprecated
Event<WorldTickCallback> EVENT = EventFactory.createArrayBacked(WorldTickCallback.class,
(listeners) -> {
if (EventFactory.isProfilingEnabled()) {

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.impl.event.lifecycle;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.server.ServerStartCallback;
import net.fabricmc.fabric.api.event.server.ServerStopCallback;
import net.fabricmc.fabric.api.event.server.ServerTickCallback;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
public class LegacyEventInvokers implements ModInitializer {
@Override
public void onInitialize() {
// Allows deprecated events to still be invoked by the newer implementations
ServerLifecycleEvents.SERVER_STARTED.register(server -> ServerStartCallback.EVENT.invoker().onStartServer(server));
ServerLifecycleEvents.SERVER_STOPPING.register(server -> ServerStopCallback.EVENT.invoker().onStopServer(server));
ServerTickEvents.END_SERVER_TICK.register(server -> ServerTickCallback.EVENT.invoker().tick(server));
// Tick old events on ServerWorld
ServerTickEvents.END_WORLD_TICK.register(world -> WorldTickCallback.EVENT.invoker().tick(world));
}
}

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.impl.event.lifecycle.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.client.ClientTickCallback;
import net.fabricmc.fabric.api.event.client.ItemTooltipCallback;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
public class LegacyClientEventInvokers implements ClientModInitializer {
@Override
public void onInitializeClient() {
// Allows deprecated events to still be invoked by the newer implementations
ClientTickEvents.END_CLIENT_TICK.register(client -> ClientTickCallback.EVENT.invoker().tick(client));
// Tick old events on ClientWorld
ClientTickEvents.END_WORLD_TICK.register(world -> WorldTickCallback.EVENT.invoker().tick(world));
// This is part of item api now.
net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback.EVENT.register((stack, context, lines) -> ItemTooltipCallback.EVENT.invoker().getTooltip(stack, context, lines));
}
}

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.MinecraftClient;
import net.fabricmc.fabric.api.event.client.ClientTickCallback;
@Mixin(MinecraftClient.class)
public class MixinMinecraftClient {
@Inject(at = @At("RETURN"), method = "tick")
public void tick(CallbackInfo info) {
ClientTickCallback.EVENT.invoker().tick((MinecraftClient) (Object) this);
}
}

View file

@ -1,35 +0,0 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
@Mixin(World.class)
public class MixinWorld {
// TODO split into ClientWorld/ServerWorld ticks? mmm need more mappings
@Inject(at = @At("RETURN"), method = "tickBlockEntities")
public void tickBlockEntitiesAfter(CallbackInfo info) {
WorldTickCallback.EVENT.invoker().tick((World) (Object) this);
}
}

View file

@ -1,16 +0,0 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.event.lifecycle",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MixinMinecraftServer",
"MixinWorld"
],
"client": [
"MixinItemStack",
"MixinMinecraftClient"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -15,12 +15,19 @@
"authors": [
"FabricMC"
],
"entrypoints": {
"main": [
"net.fabricmc.fabric.impl.event.lifecycle.LegacyEventInvokers"
],
"client": [
"net.fabricmc.fabric.impl.event.lifecycle.client.LegacyClientEventInvokers"
]
},
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*"
"fabric-api-base": "*",
"fabric-item-api-v1": "*",
"fabric-lifecycle-events-v1": "*"
},
"description": "Events for the game's lifecycle.",
"mixins": [
"fabric-events-lifecycle-v0.mixins.json"
]
"description": "Legacy events for the game's lifecycle, superseded by fabric-lifecycle-events-v1 and fabric-item-api-v1."
}

View file

@ -0,0 +1,64 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.legacy;
import java.util.HashMap;
import java.util.Map;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.server.ServerStartCallback;
import net.fabricmc.fabric.api.event.server.ServerStopCallback;
import net.fabricmc.fabric.api.event.server.ServerTickCallback;
import net.fabricmc.fabric.api.event.world.WorldTickCallback;
public class LegacyLifecycleEventsTest implements ModInitializer {
public static final Logger LOGGER = LogManager.getLogger("LegacyLifecycleEventsTest");
private Map<RegistryKey<World>, Integer> tickTracker = new HashMap<>();
@Override
public void onInitialize() {
ServerTickCallback.EVENT.register(server -> {
if (server.getTicks() % 200 == 0) { // Log every 200 ticks to verify the tick callback works on the server
LOGGER.info("Ticked Server at " + server.getTicks() + " ticks. (Legacy)");
}
});
ServerStartCallback.EVENT.register(server -> {
LOGGER.info("Started Server! (Legacy)");
});
ServerStopCallback.EVENT.register(server -> {
LOGGER.info("Stopping Server! (Legacy)");
});
WorldTickCallback.EVENT.register(world -> {
final int worldTicks = tickTracker.computeIfAbsent(world.getRegistryKey(), k -> 0);
if (worldTicks % 200 == 0) { // Log every 200 ticks to verify the tick callback works on the server world
LOGGER.info("[LEGACY] Ticked World " + world.getRegistryKey().getValue() + " - " + worldTicks + " ticks: " + world.getClass().getName());
}
this.tickTracker.put(world.getRegistryKey(), worldTicks + 1);
});
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.legacy.client;
import net.minecraft.text.LiteralText;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.event.client.ClientTickCallback;
import net.fabricmc.fabric.api.event.client.ItemTooltipCallback;
import net.fabricmc.fabric.test.event.lifecycle.legacy.LegacyLifecycleEventsTest;
public class LegacyClientLifecycleEventsTest implements ClientModInitializer {
private int ticks;
@Override
public void onInitializeClient() {
ClientTickCallback.EVENT.register(client -> {
this.ticks++; // Just track our own tick since the client doesn't have a ticks value.
if (this.ticks % 200 == 0) {
LegacyLifecycleEventsTest.LOGGER.info("Ticked Client at " + this.ticks + " ticks. (Legacy)");
}
});
ItemTooltipCallback.EVENT.register((stack, context, lines) -> {
lines.add(new LiteralText("A Legacy Tooltip"));
});
}
}

View file

@ -0,0 +1,19 @@
{
"schemaVersion": 1,
"id": "fabric-events-lifecycle-v0-testmod",
"name": "Fabric Events Lifecycle (v0) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-events-lifecycle-v0": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.event.lifecycle.legacy.LegacyLifecycleEventsTest"
],
"client": [
"net.fabricmc.fabric.test.event.lifecycle.legacy.client.LegacyClientLifecycleEventsTest"
]
}
}

View file

@ -0,0 +1,6 @@
archivesBaseName = "fabric-item-api-v1"
version = getSubprojectVersion(project, "1.0.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.item.v1;
import java.util.List;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public interface ItemTooltipCallback {
/**
* Fired after the game has appended all base tooltip lines to the list.
*/
Event<ItemTooltipCallback> EVENT = EventFactory.createArrayBacked(ItemTooltipCallback.class, callbacks -> (stack, context, lines) -> {
for (ItemTooltipCallback callback : callbacks) {
callback.getTooltip(stack, context, lines);
}
});
/**
* Called when an item stack's tooltip is rendered. Text added to {@code lines} will be
* rendered with the tooltip.
*
* @param lines the list containing the lines of text displayed on the stack's tooltip
*/
void getTooltip(ItemStack stack, TooltipContext context, List<Text> lines);
}

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
package net.fabricmc.fabric.mixin.event.lifecycle;
package net.fabricmc.fabric.mixin.item.client;
import java.util.List;
@ -28,10 +28,10 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.fabricmc.fabric.api.event.client.ItemTooltipCallback;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
@Mixin(ItemStack.class)
public class MixinItemStack {
public abstract class ItemStackMixin {
@Inject(method = "getTooltip", at = @At("RETURN"))
private void getTooltip(PlayerEntity entity, TooltipContext tooltipContext, CallbackInfoReturnable<List<Text>> info) {
ItemTooltipCallback.EVENT.invoker().getTooltip((ItemStack) (Object) this, tooltipContext, info.getReturnValue());

Binary file not shown.

After

(image error) Size: 1.5 KiB

View file

@ -0,0 +1,11 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.item",
"compatibilityLevel": "JAVA_8",
"client": [
"client.ItemStackMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,26 @@
{
"schemaVersion": 1,
"id": "fabric-item-api-v1",
"name": "Fabric Item API (v1)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-item-api-v1/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
"issues": "https://github.com/FabricMC/fabric/issues",
"sources": "https://github.com/FabricMC/fabric"
},
"authors": [
"FabricMC"
],
"mixins": [
"fabric-item-api-v1.mixins.json"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*"
},
"description": "Hooks for items"
}

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.item.client;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Formatting;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback;
@Environment(EnvType.CLIENT)
public class TooltipTests implements ClientModInitializer {
@Override
public void onInitializeClient() {
// Adds a tooltip to all items so testing can be verified easily.
ItemTooltipCallback.EVENT.register((stack, context, lines) -> {
lines.add(new LiteralText("Fancy Tooltips").formatted(Formatting.LIGHT_PURPLE));
});
}
}

View file

@ -0,0 +1,16 @@
{
"schemaVersion": 1,
"id": "fabric-item-api-v1-testmod",
"name": "Fabric Item API (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-item-api-v1": "*"
},
"entrypoints": {
"client": [
"net.fabricmc.fabric.test.item.client.TooltipTests"
]
}
}

View file

@ -0,0 +1,6 @@
archivesBaseName = "fabric-lifecycle-events-v1"
version = getSubprojectVersion(project, "1.0.0")
dependencies {
compile project(path: ':fabric-api-base', configuration: 'dev')
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.event.lifecycle.v1;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public final class ClientBlockEntityEvents {
private ClientBlockEntityEvents() {
}
/**
* Called when a BlockEntity is loaded into a ClientWorld.
*
* <p>When this event is called, the block entity is already in the world.
*/
public static final Event<ClientBlockEntityEvents.Load> BLOCK_ENTITY_LOAD = EventFactory.createArrayBacked(ClientBlockEntityEvents.Load.class, callbacks -> (blockEntity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricClientBlockEntityLoad");
for (ClientBlockEntityEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onLoad(blockEntity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ClientBlockEntityEvents.Load callback : callbacks) {
callback.onLoad(blockEntity, world);
}
}
});
/**
* Called when a BlockEntity is about to be unloaded from a ClientWorld.
*
* <p>When this event is called, the block entity is still present on the world.
*/
public static final Event<ClientBlockEntityEvents.Unload> BLOCK_ENTITY_UNLOAD = EventFactory.createArrayBacked(ClientBlockEntityEvents.Unload.class, callbacks -> (blockEntity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricClientBlockEntityUnload");
for (ClientBlockEntityEvents.Unload callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onUnload(blockEntity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ClientBlockEntityEvents.Unload callback : callbacks) {
callback.onUnload(blockEntity, world);
}
}
});
public interface Load {
void onLoad(BlockEntity blockEntity, ClientWorld world);
}
public interface Unload {
void onUnload(BlockEntity blockEntity, ClientWorld world);
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.event.lifecycle.v1;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public final class ClientChunkEvents {
private ClientChunkEvents() {
}
/**
* Called when a chunk is loaded into a ClientWorld.
*
* <p>When this event is called, the chunk is already in the world.
*/
public static final Event<ClientChunkEvents.Load> CHUNK_LOAD = EventFactory.createArrayBacked(ClientChunkEvents.Load.class, callbacks -> (clientWorld, chunk) -> {
if (EventFactory.isProfilingEnabled()) {
Profiler profiler = clientWorld.getProfiler();
profiler.push("fabricClientChunkLoad");
for (ClientChunkEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onChunkLoad(clientWorld, chunk);
profiler.pop();
}
profiler.pop();
} else {
for (ClientChunkEvents.Load callback : callbacks) {
callback.onChunkLoad(clientWorld, chunk);
}
}
});
/**
* Called when a chunk is about to be unloaded from a ClientWorld.
*
* <p>When this event is called, the chunk is still present in the world.
*/
public static final Event<ClientChunkEvents.Unload> CHUNK_UNLOAD = EventFactory.createArrayBacked(ClientChunkEvents.Unload.class, callbacks -> (clientWorld, chunk) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = clientWorld.getProfiler();
profiler.push("fabricClientChunkUnload");
for (ClientChunkEvents.Unload callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onChunkUnload(clientWorld, chunk);
profiler.pop();
}
profiler.pop();
} else {
for (ClientChunkEvents.Unload callback : callbacks) {
callback.onChunkUnload(clientWorld, chunk);
}
}
});
public interface Load {
void onChunkLoad(ClientWorld world, WorldChunk chunk);
}
public interface Unload {
void onChunkUnload(ClientWorld world, WorldChunk chunk);
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.event.lifecycle.v1;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public final class ClientEntityEvents {
public ClientEntityEvents() {
}
/**
* Called when an Entity is loaded into a ClientWorld.
*
* <p>When this event is called, the chunk is already in the world.
*/
public static final Event<ClientEntityEvents.Load> ENTITY_LOAD = EventFactory.createArrayBacked(ClientEntityEvents.Load.class, callbacks -> (entity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricClientEntityLoad");
for (ClientEntityEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onLoad(entity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ClientEntityEvents.Load callback : callbacks) {
callback.onLoad(entity, world);
}
}
});
/**
* Called when an Entity is about to be unloaded from a ClientWorld.
*
* <p>When this event is called, the entity is still present in the world.
*/
public static final Event<ClientEntityEvents.Unload> ENTITY_UNLOAD = EventFactory.createArrayBacked(ClientEntityEvents.Unload.class, callbacks -> (entity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricClientEntityLoad");
for (ClientEntityEvents.Unload callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onUnload(entity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ClientEntityEvents.Unload callback : callbacks) {
callback.onUnload(entity, world);
}
}
});
public interface Load {
void onLoad(Entity entity, ClientWorld world);
}
public interface Unload {
void onUnload(Entity entity, ClientWorld world);
}
}

View file

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.event.lifecycle.v1;
import net.minecraft.client.MinecraftClient;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public final class ClientLifecycleEvents {
private ClientLifecycleEvents() {
}
/**
* Called when Minecraft has started and it's client about to tick for the first time.
*
* <p>This occurs while the splash screen is displayed.
*/
public static final Event<ClientStarted> CLIENT_STARTED = EventFactory.createArrayBacked(ClientStarted.class, callbacks -> client -> {
for (ClientStarted callback : callbacks) {
callback.onClientStarted(client);
}
});
/**
* Called when Minecraft's client begins to stop.
* This is caused by quitting while in game, or closing the game window.
*
* <p>This will be called before the integrated server is stopped if it is running.
*/
public static final Event<ClientStopping> CLIENT_STOPPING = EventFactory.createArrayBacked(ClientStopping.class, callbacks -> client -> {
for (ClientStopping callback : callbacks) {
callback.onClientStopping(client);
}
});
public interface ClientStarted {
void onClientStarted(MinecraftClient client);
}
public interface ClientStopping {
void onClientStopping(MinecraftClient client);
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.client.event.lifecycle.v1;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
@Environment(EnvType.CLIENT)
public final class ClientTickEvents {
public ClientTickEvents() {
}
/**
* Called at the start of the client tick.
*/
public static final Event<StartTick> START_CLIENT_TICK = EventFactory.createArrayBacked(StartTick.class, callbacks -> client -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = client.getProfiler();
profiler.push("fabricStartClientTick");
for (StartTick event : callbacks) {
profiler.push(EventFactory.getHandlerName(event));
event.onStartTick(client);
profiler.pop();
}
profiler.pop();
} else {
for (StartTick event : callbacks) {
event.onStartTick(client);
}
}
});
/**
* Called at the end of the client tick.
*/
public static final Event<EndTick> END_CLIENT_TICK = EventFactory.createArrayBacked(EndTick.class, callbacks -> client -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = client.getProfiler();
profiler.push("fabricEndClientTick");
for (EndTick event : callbacks) {
profiler.push(EventFactory.getHandlerName(event));
event.onEndTick(client);
profiler.pop();
}
profiler.pop();
} else {
for (EndTick event : callbacks) {
event.onEndTick(client);
}
}
});
/**
* Called at the start of a ClientWorld's tick.
*/
public static final Event<StartWorldTick> START_WORLD_TICK = EventFactory.createArrayBacked(StartWorldTick.class, callbacks -> world -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricStartClientWorldTick");
for (StartWorldTick callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onStartTick(world);
profiler.pop();
}
profiler.pop();
} else {
for (StartWorldTick callback : callbacks) {
callback.onStartTick(world);
}
}
});
/**
* Called at the end of a ClientWorld's tick.
*
* <p>End of world tick may be used to start async computations for the next tick.
*/
public static final Event<EndWorldTick> END_WORLD_TICK = EventFactory.createArrayBacked(EndWorldTick.class, callbacks -> world -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricEndClientWorldTick");
for (EndWorldTick callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onEndTick(world);
profiler.pop();
}
profiler.pop();
} else {
for (EndWorldTick callback : callbacks) {
callback.onEndTick(world);
}
}
});
public interface StartTick {
void onStartTick(MinecraftClient client);
}
public interface EndTick {
void onEndTick(MinecraftClient client);
}
public interface StartWorldTick {
void onStartTick(ClientWorld world);
}
public interface EndWorldTick {
void onEndTick(ClientWorld world);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.event.lifecycle.v1;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class ServerBlockEntityEvents {
private ServerBlockEntityEvents() {
}
/**
* Called when an BlockEntity is loaded into a ServerWorld.
*
* <p>When this is event is called, the block entity is already in the world.
*/
public static final Event<ServerBlockEntityEvents.Load> BLOCK_ENTITY_LOAD = EventFactory.createArrayBacked(ServerBlockEntityEvents.Load.class, callbacks -> (blockEntity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricServerBlockEntityLoad");
for (ServerBlockEntityEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onLoad(blockEntity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ServerBlockEntityEvents.Load callback : callbacks) {
callback.onLoad(blockEntity, world);
}
}
});
/**
* Called when an BlockEntity is about to be unloaded from a ServerWorld.
*
* <p>When this event is called, the block entity is still present on the world.
*/
public static final Event<Unload> BLOCK_ENTITY_UNLOAD = EventFactory.createArrayBacked(ServerBlockEntityEvents.Unload.class, callbacks -> (blockEntity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricServerBlockEntityUnload");
for (ServerBlockEntityEvents.Unload callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onUnload(blockEntity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ServerBlockEntityEvents.Unload callback : callbacks) {
callback.onUnload(blockEntity, world);
}
}
});
public interface Load {
void onLoad(BlockEntity blockEntity, ServerWorld world);
}
public interface Unload {
void onUnload(BlockEntity blockEntity, ServerWorld world);
}
}

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.event.lifecycle.v1;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class ServerChunkEvents {
private ServerChunkEvents() {
}
/**
* Called when an chunk is loaded into a ServerWorld.
*
* <p>When this event is called, the chunk is already in the world.
*/
public static final Event<ServerChunkEvents.Load> CHUNK_LOAD = EventFactory.createArrayBacked(ServerChunkEvents.Load.class, callbacks -> (serverWorld, chunk) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = serverWorld.getProfiler();
profiler.push("fabricServerChunkLoad");
for (ServerChunkEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onChunkLoad(serverWorld, chunk);
profiler.pop();
}
profiler.pop();
} else {
for (ServerChunkEvents.Load callback : callbacks) {
callback.onChunkLoad(serverWorld, chunk);
}
}
});
/**
* Called when an chunk is unloaded from a ServerWorld.
*
* <p>When this event is called, the chunk is still present in the world.
*/
public static final Event<ServerChunkEvents.Unload> CHUNK_UNLOAD = EventFactory.createArrayBacked(ServerChunkEvents.Unload.class, callbacks -> (serverWorld, chunk) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = serverWorld.getProfiler();
profiler.push("fabricServerChunkUnload");
for (ServerChunkEvents.Unload callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onChunkUnload(serverWorld, chunk);
profiler.pop();
}
profiler.pop();
} else {
for (ServerChunkEvents.Unload callback : callbacks) {
callback.onChunkUnload(serverWorld, chunk);
}
}
});
public interface Load {
void onChunkLoad(ServerWorld world, WorldChunk chunk);
}
public interface Unload {
void onChunkUnload(ServerWorld world, WorldChunk chunk);
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.event.lifecycle.v1;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class ServerEntityEvents {
private ServerEntityEvents() {
}
/**
* Called when an Entity is loaded into a ServerWorld.
*
* <p>When this event is called, the entity is already in the world.
*
* <p>Note there is no corresponding unload event because entity unloads cannot be reliably tracked.
*/
public static final Event<ServerEntityEvents.Load> ENTITY_LOAD = EventFactory.createArrayBacked(ServerEntityEvents.Load.class, callbacks -> (entity, world) -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricServerEntityLoad");
for (ServerEntityEvents.Load callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onLoad(entity, world);
profiler.pop();
}
profiler.pop();
} else {
for (ServerEntityEvents.Load callback : callbacks) {
callback.onLoad(entity, world);
}
}
});
public interface Load {
void onLoad(Entity entity, ServerWorld world);
}
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.event.lifecycle.v1;
import net.minecraft.server.MinecraftServer;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class ServerLifecycleEvents {
private ServerLifecycleEvents() {
}
/**
* Called when a Minecraft server has started and is about to tick for the first time.
*
* <p>At this stage, all worlds are live.
*/
public static final Event<ServerStarted> SERVER_STARTED = EventFactory.createArrayBacked(ServerStarted.class, (callbacks) -> (server) -> {
for (ServerStarted callback : callbacks) {
callback.onServerStarted(server);
}
});
/**
* Called when a Minecraft server has started shutting down.
* This occurs before the server's network channel is closed and before any players are disconnected.
*
* <p>For example, an integrated server will begin stopping, but it's client may continue to run.
*
* <p>All worlds are still present and can be modified.
*/
public static final Event<ServerStopping> SERVER_STOPPING = EventFactory.createArrayBacked(ServerStopping.class, (callbacks) -> (server) -> {
for (ServerStopping callback : callbacks) {
callback.onServerStopping(server);
}
});
/**
* Called when a Minecraft server has stopped.
* All worlds have been closed and all (block)entities and players have been unloaded.
*
* <p>For example, an {@link net.fabricmc.api.EnvType#CLIENT integrated server} will begin stopping, but it's client may continue to run.
* Meanwhile for a {@link net.fabricmc.api.EnvType#SERVER dedicated server}, this will be the last event called.
*/
public static final Event<ServerStopped> SERVER_STOPPED = EventFactory.createArrayBacked(ServerStopped.class, callbacks -> server -> {
for (ServerStopped callback : callbacks) {
callback.onServerStopped(server);
}
});
public interface ServerStarted {
void onServerStarted(MinecraftServer server);
}
public interface ServerStopping {
void onServerStopping(MinecraftServer server);
}
public interface ServerStopped {
void onServerStopped(MinecraftServer server);
}
}

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.api.event.lifecycle.v1;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory;
public final class ServerTickEvents {
private ServerTickEvents() {
}
/**
* Called at the start of the server tick.
*/
public static final Event<StartTick> START_SERVER_TICK = EventFactory.createArrayBacked(StartTick.class, callbacks -> server -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = server.getProfiler();
profiler.push("fabricStartServerTick");
for (StartTick event : callbacks) {
profiler.push(EventFactory.getHandlerName(event));
event.onStartTick(server);
profiler.pop();
}
profiler.pop();
} else {
for (StartTick event : callbacks) {
event.onStartTick(server);
}
}
});
/**
* Called at the end of the server tick.
*/
public static final Event<EndTick> END_SERVER_TICK = EventFactory.createArrayBacked(EndTick.class, callbacks -> server -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = server.getProfiler();
profiler.push("fabricEndServerTick");
for (EndTick event : callbacks) {
profiler.push(EventFactory.getHandlerName(event));
event.onEndTick(server);
profiler.pop();
}
profiler.pop();
} else {
for (EndTick event : callbacks) {
event.onEndTick(server);
}
}
});
/**
* Called at the start of a ServerWorld's tick.
*/
public static final Event<StartWorldTick> START_WORLD_TICK = EventFactory.createArrayBacked(StartWorldTick.class, callbacks -> world -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricStartServerWorldTick_" + world.getRegistryKey().getValue());
for (StartWorldTick callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onStartTick(world);
profiler.pop();
}
profiler.pop();
} else {
for (StartWorldTick callback : callbacks) {
callback.onStartTick(world);
}
}
});
/**
* Called at the end of a ServerWorld's tick.
*
* <p>End of world tick may be used to start async computations for the next tick.
*/
public static final Event<EndWorldTick> END_WORLD_TICK = EventFactory.createArrayBacked(EndWorldTick.class, callbacks -> world -> {
if (EventFactory.isProfilingEnabled()) {
final Profiler profiler = world.getProfiler();
profiler.push("fabricEndServerWorldTick_" + world.getRegistryKey().getValue());
for (EndWorldTick callback : callbacks) {
profiler.push(EventFactory.getHandlerName(callback));
callback.onEndTick(world);
profiler.pop();
}
profiler.pop();
} else {
for (EndWorldTick callback : callbacks) {
callback.onEndTick(world);
}
}
});
public interface StartTick {
void onStartTick(MinecraftServer server);
}
public interface EndTick {
void onEndTick(MinecraftServer server);
}
public interface StartWorldTick {
void onStartTick(ServerWorld world);
}
public interface EndWorldTick {
void onEndTick(ServerWorld world);
}
}

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import java.util.Iterator;
import java.util.List;
import java.util.function.BooleanSupplier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@Mixin(MinecraftServer.class)
public abstract class MinecraftServerMixin {
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;setFavicon(Lnet/minecraft/server/ServerMetadata;)V", ordinal = 0), method = "method_29741")
private void afterSetupServer(CallbackInfo info) {
ServerLifecycleEvents.SERVER_STARTED.invoker().onServerStarted((MinecraftServer) (Object) this);
}
@Inject(at = @At("HEAD"), method = "shutdown")
private void beforeShutdownServer(CallbackInfo info) {
ServerLifecycleEvents.SERVER_STOPPING.invoker().onServerStopping((MinecraftServer) (Object) this);
}
@Inject(at = @At("TAIL"), method = "shutdown")
private void afterShutdownServer(CallbackInfo info) {
ServerLifecycleEvents.SERVER_STOPPED.invoker().onServerStopped((MinecraftServer) (Object) this);
}
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;tickWorlds(Ljava/util/function/BooleanSupplier;)V"), method = "tick")
private void onStartTick(BooleanSupplier shouldKeepTicking, CallbackInfo ci) {
ServerTickEvents.START_SERVER_TICK.invoker().onStartTick((MinecraftServer) (Object) this);
}
@Inject(at = @At("TAIL"), method = "tick")
private void onEndTick(BooleanSupplier shouldKeepTicking, CallbackInfo info) {
ServerTickEvents.END_SERVER_TICK.invoker().onEndTick((MinecraftServer) (Object) this);
}
/**
* When a world is closed, it means the world will be unloaded.
*/
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;close()V"), method = "shutdown", locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void closeWorld(CallbackInfo ci, Iterator<ServerWorld> worlds, ServerWorld serverWorld) {
final List<Entity> entities = serverWorld.getEntities(null, entity -> true); // Get every single entity in the world
for (BlockEntity blockEntity : serverWorld.blockEntities) {
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, serverWorld);
}
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import java.util.function.BooleanSupplier;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.entity.Entity;
import net.minecraft.server.world.ServerWorld;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@Mixin(ServerWorld.class)
public abstract class ServerWorldMixin {
@Shadow
private boolean inEntityTick;
// Call our load event after vanilla has loaded the entity
@Inject(method = "loadEntityUnchecked", at = @At("TAIL"))
private void onLoadEntity(Entity entity, CallbackInfo ci) {
if (!this.inEntityTick) { // Copy vanilla logic, we cannot load entities while the game is ticking entities
ServerEntityEvents.ENTITY_LOAD.invoker().onLoad(entity, (ServerWorld) (Object) this);
}
}
// Make sure "insideBlockTick" is true before we call the start tick, so inject after it is set
@Inject(method = "tick", at = @At(value = "FIELD", target = "Lnet/minecraft/server/world/ServerWorld;inBlockTick:Z", opcode = Opcodes.PUTFIELD, ordinal = 0, shift = At.Shift.AFTER))
private void startWorldTick(BooleanSupplier shouldKeepTicking, CallbackInfo ci) {
ServerTickEvents.START_WORLD_TICK.invoker().onStartTick((ServerWorld) (Object) this);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import java.util.concurrent.CompletableFuture;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.server.world.ChunkHolder;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.server.world.ThreadedAnvilChunkStorage;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
@Mixin(ThreadedAnvilChunkStorage.class)
public abstract class ThreadedAnvilChunkStorageMixin {
@Shadow
@Final
private ServerWorld world;
// Chunk (Un)Load events, An explanation:
// Must of this code is wrapped inside of futures and consumers, so it's generally a mess.
/**
* Injection is inside of tryUnloadChunk.
* We inject just after "setLoadedToWorld" is made false, since here the WorldChunk is guaranteed to be unloaded.
*/
@Inject(method = "method_18843", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/WorldChunk;setLoadedToWorld(Z)V", shift = At.Shift.AFTER))
private void onChunkUnload(ChunkHolder chunkHolder, CompletableFuture<Chunk> chunkFuture, long pos, Chunk chunk, CallbackInfo ci) {
ServerChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload(this.world, (WorldChunk) chunk);
}
/**
* Injection is inside of convertToFullChunk?
*
* <p>The following is expected contractually
*
* <ul><li>the chunk being loaded MUST be a WorldChunk.
* <li>everything within the chunk has been loaded into the world. Entities, BlockEntities, etc.</ul>
*/
@Inject(method = "method_17227", at = @At("TAIL"))
private void onChunkLoad(ChunkHolder chunkHolder, Chunk protoChunk, CallbackInfoReturnable<Chunk> callbackInfoReturnable) {
// We fire the event at TAIL since the chunk is guaranteed to be a WorldChunk then.
ServerChunkEvents.CHUNK_LOAD.invoker().onChunkLoad(this.world, (WorldChunk) callbackInfoReturnable.getReturnValue());
}
}

View file

@ -0,0 +1,90 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.World;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@Mixin(World.class)
public abstract class WorldMixin {
@Shadow
public abstract boolean isClient();
@Shadow
public abstract Profiler getProfiler();
@Inject(method = "addBlockEntity", at = @At("TAIL"))
protected void onLoadBlockEntity(BlockEntity blockEntity, CallbackInfoReturnable<Boolean> cir) {
if (!this.isClient()) { // Only fire this event if we are a server world
ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.invoker().onLoad(blockEntity, (ServerWorld) (Object) this);
}
}
// Mojang what hell, why do you need three ways to unload block entities
@Inject(method = "removeBlockEntity", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z", ordinal = 1), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
protected void onUnloadBlockEntity(BlockPos pos, CallbackInfo ci, BlockEntity blockEntity) {
if (!this.isClient()) { // Only fire this event if we are a server world
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, (ServerWorld) (Object) this);
}
}
@Inject(method = "tickBlockEntities", at = @At(value = "INVOKE", target = "Ljava/util/List;remove(Ljava/lang/Object;)Z"), slice = @Slice(from = @At(value = "INVOKE", target = "Lnet/minecraft/util/profiler/Profiler;pop()V"), to = @At(value = "INVOKE", target = "Lnet/minecraft/world/chunk/WorldChunk;removeBlockEntity(Lnet/minecraft/util/math/BlockPos;)V")), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
protected void onRemoveBlockEntity(CallbackInfo ci, Profiler profiler, Iterator iterator, BlockEntity blockEntity) {
if (!this.isClient()) {
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, (ServerWorld) (Object) this);
}
}
@Redirect(method = "tickBlockEntities", at = @At(value = "INVOKE", target = "Ljava/util/List;removeAll(Ljava/util/Collection;)Z", ordinal = 1))
protected boolean onPurgeRemovedBlockEntities(List<BlockEntity> blockEntityList, Collection<BlockEntity> removals) {
if (!this.isClient()) {
for (BlockEntity removal : removals) {
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(removal, (ServerWorld) (Object) this);
}
}
// Mimic vanilla logic
return blockEntityList.removeAll(removals);
}
@Inject(at = @At("RETURN"), method = "tickBlockEntities")
protected void tickWorldAfterBlockEntities(CallbackInfo ci) {
if (!this.isClient()) {
ServerTickEvents.END_WORLD_TICK.invoker().onEndTick((ServerWorld) (Object) this);
}
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle.client;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import net.minecraft.client.world.ClientChunkManager;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.world.biome.source.BiomeArray;
import net.minecraft.world.chunk.WorldChunk;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
@Environment(EnvType.CLIENT)
@Mixin(ClientChunkManager.class)
public abstract class ClientChunkManagerMixin {
@Final
@Shadow
private ClientWorld world;
@Inject(method = "loadChunkFromPacket", at = @At("TAIL")) // 1.16 has a boolean param here. I think it means whether the packet is complete.
private void onChunkLoad(int chunkX, int chunkZ, BiomeArray biomes, PacketByteBuf buf, CompoundTag tag, int k, boolean complete, CallbackInfoReturnable<WorldChunk> cir) {
ClientChunkEvents.CHUNK_LOAD.invoker().onChunkLoad(this.world, cir.getReturnValue());
}
@Inject(method = "unload", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientChunkManager$ClientChunkMap;compareAndSet(ILnet/minecraft/world/chunk/WorldChunk;Lnet/minecraft/world/chunk/WorldChunk;)Lnet/minecraft/world/chunk/WorldChunk;", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILEXCEPTION)
private void onChunkUnload(int chunkX, int chunkZ, CallbackInfo ci, int i, WorldChunk chunk) {
ClientChunkEvents.CHUNK_UNLOAD.invoker().onChunkUnload(this.world, chunk);
}
}

View file

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
@Environment(EnvType.CLIENT)
@Mixin(ClientPlayNetworkHandler.class)
public abstract class ClientPlayNetworkHandlerMixin {
@Shadow
private ClientWorld world;
@Inject(method = "onPlayerRespawn", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
private void onPlayerRespawn(PlayerRespawnS2CPacket packet, CallbackInfo ci) {
// If a world already exists, we need to unload all (block)entities in the world.
if (this.world != null) {
for (Entity entity : world.getEntities()) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}
}
/**
* An explanation why we unload entities during onGameJoin:
* Proxies such as Waterfall may send another Game Join packet if entity meta rewrite is disabled, so we will cover ourselves.
* Velocity by default will send a Game Join packet when the player changes servers, which will create a new client world.
* Also anyone can send another GameJoinPacket at any time, so we need to watch out.
*/
@Inject(method = "onGameJoin", at = @At(value = "NEW", target = "net/minecraft/client/world/ClientWorld"))
private void onGameJoin(GameJoinS2CPacket packet, CallbackInfo ci) {
// If a world already exists, we need to unload all (block)entities in the world.
if (this.world != null) {
for (Entity entity : world.getEntities()) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}
}
// Called when the client disconnects from a server.
@Inject(method = "clearWorld", at = @At("HEAD"))
private void onClearWorld(CallbackInfo ci) {
// If a world already exists, we need to unload all (block)entities in the world.
if (this.world != null) {
for (Entity entity : world.getEntities()) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, this.world);
}
for (BlockEntity blockEntity : world.blockEntities) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, this.world);
// No need to clear the `tickingBlockEntities` list since it will be null in just an instant
}
}
}
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle.client;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.profiler.Profiler;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.mixin.event.lifecycle.WorldMixin;
@Environment(EnvType.CLIENT)
@Mixin(ClientWorld.class)
public abstract class ClientWorldMixin extends WorldMixin {
// Call our load event after vanilla has loaded the entity
@Inject(method = "addEntityPrivate", at = @At("TAIL"))
private void onEntityLoad(int id, Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_LOAD.invoker().onLoad(entity, (ClientWorld) (Object) this);
}
// Call our unload event before vanilla does.
@Inject(method = "finishRemovingEntity", at = @At("HEAD"))
private void onEntityUnload(Entity entity, CallbackInfo ci) {
ClientEntityEvents.ENTITY_UNLOAD.invoker().onUnload(entity, (ClientWorld) (Object) this);
}
// We override our injection on the clientworld so only the client's block entity invocations will run
@Override
protected void onLoadBlockEntity(BlockEntity blockEntity, CallbackInfoReturnable<Boolean> cir) {
ClientBlockEntityEvents.BLOCK_ENTITY_LOAD.invoker().onLoad(blockEntity, (ClientWorld) (Object) this);
}
// We override our injection on the clientworld so only the client's block entity invocations will run
@Override
protected void onUnloadBlockEntity(BlockPos pos, CallbackInfo ci, BlockEntity blockEntity) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, (ClientWorld) (Object) this);
}
@Override
protected void onRemoveBlockEntity(CallbackInfo ci, Profiler profiler, Iterator iterator, BlockEntity blockEntity) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(blockEntity, (ClientWorld) (Object) this);
}
@Override
protected boolean onPurgeRemovedBlockEntities(List<BlockEntity> blockEntityList, Collection<BlockEntity> removals) {
for (BlockEntity removal : removals) {
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.invoker().onUnload(removal, (ClientWorld) (Object) this);
}
return super.onPurgeRemovedBlockEntities(blockEntityList, removals); // Call super
}
// We override our injection on the clientworld so only the client world's tick invocations will run
@Override
protected void tickWorldAfterBlockEntities(CallbackInfo ci) {
ClientTickEvents.END_WORLD_TICK.invoker().onEndTick((ClientWorld) (Object) this);
}
@Inject(method = "tickEntities", at = @At("HEAD"))
private void startWorldTick(CallbackInfo ci) {
ClientTickEvents.START_WORLD_TICK.invoker().onStartTick((ClientWorld) (Object) this);
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.mixin.event.lifecycle.client;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.MinecraftClient;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
@Environment(EnvType.CLIENT)
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Inject(at = @At("HEAD"), method = "tick")
private void onStartTick(CallbackInfo info) {
ClientTickEvents.START_CLIENT_TICK.invoker().onStartTick((MinecraftClient) (Object) this);
}
@Inject(at = @At("RETURN"), method = "tick")
private void onEndTick(CallbackInfo info) {
ClientTickEvents.END_CLIENT_TICK.invoker().onEndTick((MinecraftClient) (Object) this);
}
@Inject(at = @At(value = "INVOKE", target = "Lorg/apache/logging/log4j/Logger;info(Ljava/lang/String;)V", shift = At.Shift.AFTER), method = "stop")
private void onStopping(CallbackInfo ci) {
ClientLifecycleEvents.CLIENT_STOPPING.invoker().onClientStopping((MinecraftClient) (Object) this);
}
// We inject after the thread field is set so `ThreadExecutor#getThread` will work
@Inject(at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;thread:Ljava/lang/Thread;", shift = At.Shift.AFTER), method = "run")
private void onStart(CallbackInfo ci) {
ClientLifecycleEvents.CLIENT_STARTED.invoker().onClientStarted((MinecraftClient) (Object) this);
}
}

Binary file not shown.

After

(image error) Size: 1.5 KiB

View file

@ -0,0 +1,20 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.event.lifecycle",
"compatibilityLevel": "JAVA_8",
"mixins": [
"MinecraftServerMixin",
"ServerWorldMixin",
"ThreadedAnvilChunkStorageMixin",
"WorldMixin"
],
"client": [
"client.ClientChunkManagerMixin",
"client.ClientPlayNetworkHandlerMixin",
"client.ClientWorldMixin",
"client.MinecraftClientMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,26 @@
{
"schemaVersion": 1,
"id": "fabric-lifecycle-events-v1",
"name": "Fabric Lifecycle Events (v1)",
"version": "${version}",
"environment": "*",
"license": "Apache-2.0",
"icon": "assets/fabric-lifecycle-events-v1/icon.png",
"contact": {
"homepage": "https://fabricmc.net",
"irc": "irc://irc.esper.net:6667/fabric",
"issues": "https://github.com/FabricMC/fabric/issues",
"sources": "https://github.com/FabricMC/fabric"
},
"authors": [
"FabricMC"
],
"mixins": [
"fabric-lifecycle-events-v1.mixins.json"
],
"depends": {
"fabricloader": ">=0.4.0",
"fabric-api-base": "*"
},
"description": "Events for the game's lifecycle."
}

View file

@ -0,0 +1,77 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerBlockEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
public class ServerBlockEntityLifecycleTests implements ModInitializer {
private List<BlockEntity> serverBlockEntities = new ArrayList<>();
@Override
public void onInitialize() {
final Logger logger = ServerLifecycleTests.LOGGER;
ServerBlockEntityEvents.BLOCK_ENTITY_LOAD.register((blockEntity, world) -> {
this.serverBlockEntities.add(blockEntity);
logger.info("[SERVER] LOADED " + Registry.BLOCK_ENTITY_TYPE.getId(blockEntity.getType()).toString() + " - BlockEntities: " + this.serverBlockEntities.size());
});
ServerBlockEntityEvents.BLOCK_ENTITY_UNLOAD.register((blockEntity, world) -> {
this.serverBlockEntities.remove(blockEntity);
logger.info("[SERVER] UNLOADED " + Registry.BLOCK_ENTITY_TYPE.getId(blockEntity.getType()).toString() + " - BlockEntities: " + this.serverBlockEntities.size());
});
ServerTickEvents.END_SERVER_TICK.register(minecraftServer -> {
if (minecraftServer.getTicks() % 200 == 0) {
int entities = 0;
logger.info("[SERVER] Tracked BlockEntities:" + this.serverBlockEntities.size() + " Ticked at: " + minecraftServer.getTicks() + "ticks");
for (ServerWorld world : minecraftServer.getWorlds()) {
int worldEntities = world.blockEntities.size();
logger.info("[SERVER] Tracked BlockEntities in " + world.getRegistryKey().toString() + " - " + worldEntities);
entities += worldEntities;
}
logger.info("[SERVER] Actual Total BlockEntities: " + entities);
if (entities != this.serverBlockEntities.size()) {
logger.error("[SERVER] Mismatch in tracked blockentities and actual blockentities");
}
}
});
ServerLifecycleEvents.SERVER_STOPPED.register(minecraftServer -> {
logger.info("[SERVER] Disconnected. Tracking: " + this.serverBlockEntities.size() + " blockentities");
if (this.serverBlockEntities.size() != 0) {
logger.error("[SERVER] Mismatch in tracked blockentities, expected 0");
}
});
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.entity.Entity;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
/**
* Tests related to the lifecycle of entities.
*/
public class ServerEntityLifecycleTests implements ModInitializer {
private List<Entity> serverEntities = new ArrayList<>();
@Override
public void onInitialize() {
final Logger logger = ServerLifecycleTests.LOGGER;
ServerEntityEvents.ENTITY_LOAD.register((entity, world) -> {
this.serverEntities.add(entity);
logger.info("[SERVER] LOADED " + entity.toString() + " - Entities: " + this.serverEntities.size());
});
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
/**
* Tests related to the lifecycle of a server.
*/
public class ServerLifecycleTests implements ModInitializer {
public static final Logger LOGGER = LogManager.getLogger("LifecycleEventsTest");
@Override
public void onInitialize() {
ServerLifecycleEvents.SERVER_STARTED.register(server -> {
LOGGER.info("Started Server!");
});
ServerLifecycleEvents.SERVER_STOPPING.register(server -> {
LOGGER.info("Stopping Server!");
});
ServerLifecycleEvents.SERVER_STOPPED.register(server -> {
LOGGER.info("Stopped Server!");
});
}
}

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
/**
* Test related to ticking events on the server.
*/
public class ServerTickTests implements ModInitializer {
private Map<RegistryKey<World>, Integer> tickTracker = new HashMap<>();
@Override
public void onInitialize() {
ServerTickEvents.END_SERVER_TICK.register(server -> {
if (server.getTicks() % 200 == 0) { // Log every 200 ticks to verify the tick callback works on the server
ServerLifecycleTests.LOGGER.info("Ticked Server at " + server.getTicks() + " ticks.");
}
});
ServerTickEvents.START_WORLD_TICK.register(world -> {
// Verify we are inside the tick
if (!world.isInBlockTick()) {
throw new AssertionError("Start tick event should be fired while ServerWorld is inside of block tick");
}
});
ServerTickEvents.END_WORLD_TICK.register(world -> {
final int worldTicks = tickTracker.computeIfAbsent(world.getRegistryKey(), k -> 0);
if (worldTicks % 200 == 0) { // Log every 200 ticks to verify the tick callback works on the server world
ServerLifecycleTests.LOGGER.info("Ticked Server World - " + worldTicks + " ticks:" + world.getRegistryKey().getValue());
}
this.tickTracker.put(world.getRegistryKey(), worldTicks + 1);
});
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.client;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.Logger;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.test.event.lifecycle.ServerLifecycleTests;
public class ClientBlockEntityLifecycleTests implements ClientModInitializer {
private List<BlockEntity> clientBlockEntities = new ArrayList<>();
private int clientTicks;
@Override
public void onInitializeClient() {
final Logger logger = ServerLifecycleTests.LOGGER;
ClientBlockEntityEvents.BLOCK_ENTITY_LOAD.register((blockEntity, world) -> {
this.clientBlockEntities.add(blockEntity);
logger.info("[CLIENT]" + " LOADED " + Registry.BLOCK_ENTITY_TYPE.getId(blockEntity.getType()).toString() + " - BlockEntities: " + this.clientBlockEntities.size());
});
ClientBlockEntityEvents.BLOCK_ENTITY_UNLOAD.register((blockEntity, world) -> {
this.clientBlockEntities.remove(blockEntity);
logger.info("[CLIENT]" + " UNLOADED " + Registry.BLOCK_ENTITY_TYPE.getId(blockEntity.getType()).toString() + " - BlockEntities: " + this.clientBlockEntities.size());
});
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (this.clientTicks++ % 200 == 0 && client.world != null) {
final int blockEntities = client.world.blockEntities.size();
logger.info("[CLIENT] Tracked BlockEntities:" + this.clientBlockEntities.size() + " Ticked at: " + this.clientTicks + "ticks");
logger.info("[CLIENT] Actual BlockEntities: " + client.world.blockEntities.size());
if (blockEntities != this.clientBlockEntities.size()) {
logger.error("[CLIENT] Mismatch in tracked blockentities and actual blockentities");
}
}
});
ServerLifecycleEvents.SERVER_STOPPED.register(minecraftServer -> {
if (!minecraftServer.isDedicated()) { // fixme: Use ClientNetworking#PLAY_DISCONNECTED instead of the server stop callback for testing.
logger.info("[CLIENT] Disconnected. Tracking: " + this.clientBlockEntities.size() + " blockentities");
if (this.clientBlockEntities.size() != 0) {
logger.error("[CLIENT] Mismatch in tracked blockentities, expected 0");
}
}
});
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.client;
import java.util.ArrayList;
import java.util.List;
import com.google.common.collect.Iterables;
import org.apache.logging.log4j.Logger;
import net.minecraft.entity.Entity;
import net.minecraft.util.registry.Registry;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.test.event.lifecycle.ServerLifecycleTests;
/**
* Tests related to the lifecycle of entities.
*/
@Environment(EnvType.CLIENT)
public class ClientEntityLifecycleTests implements ClientModInitializer {
private List<Entity> clientEntities = new ArrayList<>();
private int clientTicks;
@Override
public void onInitializeClient() {
final Logger logger = ServerLifecycleTests.LOGGER;
ClientEntityEvents.ENTITY_LOAD.register((entity, world) -> {
this.clientEntities.add(entity);
logger.info("[CLIENT]" + " LOADED " + Registry.ENTITY_TYPE.getId(entity.getType()).toString() + " - Entities: " + this.clientEntities.size());
});
ClientEntityEvents.ENTITY_UNLOAD.register((entity, world) -> {
this.clientEntities.remove(entity);
logger.info("[CLIENT]" + " UNLOADED " + Registry.ENTITY_TYPE.getId(entity.getType()).toString() + " - Entities: " + this.clientEntities.size());
});
ClientTickEvents.END_CLIENT_TICK.register(client -> {
if (this.clientTicks++ % 200 == 0 && client.world != null) {
final int entities = Iterables.toArray(client.world.getEntities(), Entity.class).length;
logger.info("[CLIENT] Tracked Entities:" + this.clientEntities.size() + " Ticked at: " + this.clientTicks + "ticks");
logger.info("[CLIENT] Actual Entities: " + entities);
if (entities != this.clientEntities.size()) {
logger.error("[CLIENT] Mismatch in tracked entities and actual entities");
}
}
});
ServerLifecycleEvents.SERVER_STOPPED.register(minecraftServer -> {
if (!minecraftServer.isDedicated()) { // fixme: Use ClientNetworking#PLAY_DISCONNECTED instead of the server stop callback for testing.
logger.info("[CLIENT] Disconnected. Tracking: " + this.clientEntities.size() + " entities");
if (this.clientEntities.size() != 0) {
logger.error("[CLIENT] Mismatch in tracked entities, expected 0");
}
}
});
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.client;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
@Environment(EnvType.CLIENT)
public class ClientLifecycleTests implements ClientModInitializer {
@Override
public void onInitializeClient() {
ClientLifecycleEvents.CLIENT_STARTED.register(client -> {
client.submitAndJoin(() -> { // This should fail if the client thread was not bound yet.
System.out.println("Started the client");
});
});
ClientLifecycleEvents.CLIENT_STOPPING.register(client -> {
System.out.println("Client has started stopping!");
});
}
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* 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 net.fabricmc.fabric.test.event.lifecycle.client;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.test.event.lifecycle.ServerLifecycleTests;
@Environment(EnvType.CLIENT)
public class ClientTickTests implements ClientModInitializer {
private Map<RegistryKey<World>, Integer> tickTracker = new HashMap<>();
private int ticks;
@Override
public void onInitializeClient() {
ClientTickEvents.END_CLIENT_TICK.register(client -> {
this.ticks++; // Just track our own tick since the client doesn't have a ticks value.
if (this.ticks % 200 == 0) {
ServerLifecycleTests.LOGGER.info("Ticked Client at " + this.ticks + " ticks.");
}
});
ClientTickEvents.END_WORLD_TICK.register(world -> {
final int worldTicks = this.tickTracker.computeIfAbsent(world.getRegistryKey(), k -> 0);
if (worldTicks % 200 == 0) { // Log every 200 ticks to verify the tick callback works on the client world
ServerLifecycleTests.LOGGER.info("Ticked Client World - " + worldTicks + " ticks:" + world.getRegistryKey());
}
this.tickTracker.put(world.getRegistryKey(), worldTicks + 1);
});
}
}

View file

@ -0,0 +1,25 @@
{
"schemaVersion": 1,
"id": "fabric-lifecycle-events-v1-testmod",
"name": "Fabric Lifecycle Events (v1) Test Mod",
"version": "1.0.0",
"environment": "*",
"license": "Apache-2.0",
"depends": {
"fabric-lifecycle-events-v1": "*"
},
"entrypoints": {
"main": [
"net.fabricmc.fabric.test.event.lifecycle.ServerBlockEntityLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.ServerEntityLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.ServerLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.ServerTickTests"
],
"client": [
"net.fabricmc.fabric.test.event.lifecycle.client.ClientBlockEntityLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.client.ClientEntityLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.client.ClientLifecycleTests",
"net.fabricmc.fabric.test.event.lifecycle.client.ClientTickTests",
]
}
}

View file

@ -24,9 +24,11 @@ include 'fabric-crash-report-info-v1'
include 'fabric-dimensions-v1'
include 'fabric-events-interaction-v0'
include 'fabric-events-lifecycle-v0'
include 'fabric-item-api-v1'
include 'fabric-item-groups-v0'
include 'fabric-keybindings-v0'
include 'fabric-key-binding-api-v1'
include 'fabric-lifecycle-events-v1'
include 'fabric-loot-tables-v1'
include 'fabric-mining-levels-v0'
include 'fabric-models-v0'