diff --git a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/EntityReviveEvents.java b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/EntityReviveEvents.java
index ec50d61935..2d17dbcd1c 100644
--- a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/EntityReviveEvents.java
+++ b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/EntityReviveEvents.java
@@ -71,10 +71,11 @@ public final class EntityReviveEvents {
@FunctionalInterface
public interface TryReviveBeforeTotem extends EventAwareListener {
/**
- * {@return {@code true} if the entity which has fatal damage should be revived, or {@code false} otherwise}
+ * Determines whether an entity should be revived.
*
* @param entity the entity
* @param damageSource the fatal damage source
+ * @return {@code true} if the entity which has fatal damage should be revived, or {@code false} otherwise
*/
boolean tryReviveBeforeTotem(LivingEntity entity, DamageSource damageSource);
}
@@ -82,10 +83,11 @@ public interface TryReviveBeforeTotem extends EventAwareListener {
@FunctionalInterface
public interface TryReviveAfterTotem extends EventAwareListener {
/**
- * {@return {@code true} if an entity which has fatal damage and which has not been saved by a totem should be revived, {@code false} otherwise}
+ * Determines whether an entity should be revived.
*
* @param entity the entity
* @param damageSource the fatal damage source
+ * @return {@code true} if the entity which has fatal damage should be revived, or {@code false} otherwise
*/
boolean tryReviveAfterTotem(LivingEntity entity, DamageSource damageSource);
}
diff --git a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/ServerEntityTickCallback.java b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/ServerEntityTickCallback.java
new file mode 100644
index 0000000000..05ba32dcc7
--- /dev/null
+++ b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/ServerEntityTickCallback.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2022 QuiltMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.quiltmc.qsl.entity_events.api;
+
+import net.minecraft.entity.Entity;
+
+import org.quiltmc.qsl.base.api.event.Event;
+import org.quiltmc.qsl.base.api.event.EventAwareListener;
+
+/**
+ * A callback that is invoked when an Entity is ticked on the logical server (nominally every 1/20 of a second).
+ *
+ * There are two types of entity tick - standalone ({@link Entity#tick()}) and riding ({@link Entity#tickRiding()}).
+ * This callback takes a parameter which specifies which type of tick it is.
+ */
+@FunctionalInterface
+public interface ServerEntityTickCallback extends EventAwareListener {
+ /**
+ * Called when an entity is ticked.
+ */
+ Event EVENT = Event.create(ServerEntityTickCallback.class, callbacks -> (entity, isPassengerTick) -> {
+ for (ServerEntityTickCallback callback : callbacks) {
+ callback.onServerEntityTick(entity, isPassengerTick);
+ }
+ });
+
+ /**
+ * Called when an entity is ticked on the logical server.
+ *
+ * @param entity the entity
+ * @param isPassengerTick whether the entity is being ticked as a passenger of another entity
+ */
+ void onServerEntityTick(Entity entity, boolean isPassengerTick);
+}
diff --git a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/client/ClientEntityTickCallback.java b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/client/ClientEntityTickCallback.java
new file mode 100644
index 0000000000..659fd461cd
--- /dev/null
+++ b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/api/client/ClientEntityTickCallback.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2022 QuiltMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.quiltmc.qsl.entity_events.api.client;
+
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+
+import net.minecraft.entity.Entity;
+
+import org.quiltmc.qsl.base.api.event.Event;
+import org.quiltmc.qsl.base.api.event.client.ClientEventAwareListener;
+
+/**
+ * A callback that is invoked when an Entity is ticked on the logical client (nominally every 1/20 of a second).
+ *
+ * There are two types of entity tick - standalone ({@link Entity#tick()}) and riding ({@link Entity#tickRiding()}).
+ * This callback takes a parameter which specifies which type of tick it is.
+ */
+@Environment(EnvType.CLIENT)
+@FunctionalInterface
+public interface ClientEntityTickCallback extends ClientEventAwareListener {
+ /**
+ * Called when an entity is ticked.
+ */
+ Event EVENT = Event.create(ClientEntityTickCallback.class, callbacks -> (entity, isPassengerTick) -> {
+ for (ClientEntityTickCallback callback : callbacks) {
+ callback.onClientEntityTick(entity, isPassengerTick);
+ }
+ });
+
+ /**
+ * Called when an entity is ticked on the logical client.
+ *
+ * @param entity the entity
+ * @param isPassengerTick whether the entity is being ticked as a passenger of another entity
+ */
+ void onClientEntityTick(Entity entity, boolean isPassengerTick);
+}
diff --git a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/ServerWorldMixin.java b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/ServerWorldMixin.java
new file mode 100644
index 0000000000..2018995095
--- /dev/null
+++ b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/ServerWorldMixin.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 QuiltMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.quiltmc.qsl.entity_events.mixin;
+
+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.entity.Entity;
+import net.minecraft.server.world.ServerWorld;
+
+import org.quiltmc.qsl.entity_events.api.ServerEntityTickCallback;
+
+@Mixin(ServerWorld.class)
+public abstract class ServerWorldMixin {
+ @Inject(method = "tickEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;tick()V"))
+ void invokeEntityTickEvent(Entity entity, CallbackInfo ci) {
+ ServerEntityTickCallback.EVENT.invoker().onServerEntityTick(entity, false);
+ }
+
+ @Inject(method = "tickPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;tickRiding()V"))
+ void invokePassengerEntityTickEvent(Entity vehicle, Entity passenger, CallbackInfo ci) {
+ ServerEntityTickCallback.EVENT.invoker().onServerEntityTick(passenger, true);
+ }
+}
diff --git a/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/client/ClientWorldMixin.java b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/client/ClientWorldMixin.java
new file mode 100644
index 0000000000..7344f94218
--- /dev/null
+++ b/library/entity/entity_events/src/main/java/org/quiltmc/qsl/entity_events/mixin/client/ClientWorldMixin.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 QuiltMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.quiltmc.qsl.entity_events.mixin.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.world.ClientWorld;
+import net.minecraft.entity.Entity;
+
+import org.quiltmc.qsl.entity_events.api.client.ClientEntityTickCallback;
+
+@Mixin(ClientWorld.class)
+public abstract class ClientWorldMixin {
+ @Inject(method = "tickEntity", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;tick()V"))
+ void invokeEntityTickEvent(Entity entity, CallbackInfo ci) {
+ ClientEntityTickCallback.EVENT.invoker().onClientEntityTick(entity, false);
+ }
+
+ @Inject(method = "tickPassenger", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;tickRiding()V"))
+ void invokePassengerEntityTickEvent(Entity vehicle, Entity passenger, CallbackInfo ci) {
+ ClientEntityTickCallback.EVENT.invoker().onClientEntityTick(passenger, true);
+ }
+}
diff --git a/library/entity/entity_events/src/main/resources/quilt_entity_events.mixins.json b/library/entity/entity_events/src/main/resources/quilt_entity_events.mixins.json
index 9ff3ec7b9e..f3307a6b1d 100644
--- a/library/entity/entity_events/src/main/resources/quilt_entity_events.mixins.json
+++ b/library/entity/entity_events/src/main/resources/quilt_entity_events.mixins.json
@@ -3,15 +3,17 @@
"package": "org.quiltmc.qsl.entity_events.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
- "LivingEntityDeathEventMixin",
"EntityMixin",
+ "LivingEntityDeathEventMixin",
"LivingEntityMixin",
"ServerEntityHandlerMixin",
"ServerPlayerEntityMixin",
+ "ServerWorldMixin",
"TeleportCommandMixin"
],
"client": [
- "client.ClientEntityHandlerMixin"
+ "client.ClientEntityHandlerMixin",
+ "client.ClientWorldMixin"
],
"injectors": {
"defaultRequire": 1
diff --git a/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/EntityEventsTestMod.java b/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/EntityEventsTestMod.java
index 1ebd3c8061..d754d39f3e 100644
--- a/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/EntityEventsTestMod.java
+++ b/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/EntityEventsTestMod.java
@@ -19,17 +19,20 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.mob.SkeletonEntity;
+import net.minecraft.entity.mob.ZombieEntity;
import net.minecraft.entity.passive.ChickenEntity;
import net.minecraft.item.Items;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
+import net.minecraft.util.math.Direction;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.dimension.DimensionTypes;
@@ -37,6 +40,7 @@
import org.quiltmc.qsl.entity_events.api.EntityWorldChangeEvents;
import org.quiltmc.qsl.entity_events.api.LivingEntityDeathCallback;
import org.quiltmc.qsl.entity_events.api.ServerEntityLoadEvents;
+import org.quiltmc.qsl.entity_events.api.ServerEntityTickCallback;
import org.quiltmc.qsl.entity_events.api.ServerPlayerEntityCopyCallback;
public class EntityEventsTestMod implements EntityReviveEvents.TryReviveAfterTotem,
@@ -46,7 +50,8 @@ public class EntityEventsTestMod implements EntityReviveEvents.TryReviveAfterTot
ServerEntityLoadEvents.AfterUnload,
EntityWorldChangeEvents.AfterPlayerWorldChange,
EntityWorldChangeEvents.AfterEntityWorldChange,
- ServerPlayerEntityCopyCallback {
+ ServerPlayerEntityCopyCallback,
+ ServerEntityTickCallback {
public static final Logger LOGGER = LoggerFactory.getLogger("quilt_entity_events_testmod");
// When an entity is holding an allium in its main hand at death and nothing else revives it, it will be
@@ -122,4 +127,17 @@ public void onPlayerCopy(ServerPlayerEntity newPlayer, ServerPlayerEntity origin
newPlayer.giveItemStack(Items.APPLE.getDefaultStack());
}
}
+
+ // Zombies will jump higher in a floaty way when it's raining,
+ // or place raw iron if they're riding something
+ @Override
+ public void onServerEntityTick(Entity entity, boolean isPassengerTick) {
+ if (entity.world.isRaining() && entity instanceof ZombieEntity zombie) {
+ if (isPassengerTick) {
+ entity.world.setBlockState(entity.getBlockPos().offset(Direction.UP, 3), Blocks.RAW_IRON_BLOCK.getDefaultState());
+ } else {
+ entity.setVelocity(entity.getVelocity().add(0.0, 0.05, 0.0));
+ }
+ }
+ }
}
diff --git a/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/client/EntityEventsTestModClient.java b/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/client/EntityEventsTestModClient.java
index 4af210ce46..64f075e63e 100644
--- a/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/client/EntityEventsTestModClient.java
+++ b/library/entity/entity_events/src/testmod/java/org/quiltmc/qsl/entity_events/test/client/EntityEventsTestModClient.java
@@ -19,12 +19,15 @@
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.SkeletonEntity;
+import net.minecraft.entity.mob.SlimeEntity;
import net.minecraft.entity.passive.ChickenEntity;
+import net.minecraft.particle.ParticleTypes;
import org.quiltmc.qsl.entity_events.api.client.ClientEntityLoadEvents;
+import org.quiltmc.qsl.entity_events.api.client.ClientEntityTickCallback;
import org.quiltmc.qsl.entity_events.test.EntityEventsTestMod;
-public class EntityEventsTestModClient implements ClientEntityLoadEvents.AfterLoad, ClientEntityLoadEvents.AfterUnload {
+public class EntityEventsTestModClient implements ClientEntityLoadEvents.AfterLoad, ClientEntityLoadEvents.AfterUnload, ClientEntityTickCallback {
// Chicken Loading is logged.
@Override
public void onLoadClient(Entity entity, ClientWorld world) {
@@ -40,4 +43,12 @@ public void onUnloadClient(Entity entity, ClientWorld world) {
EntityEventsTestMod.LOGGER.info("Skeleton unloaded, client");
}
}
+
+ // Slimes will emit explosion particles if they're riding something
+ @Override
+ public void onClientEntityTick(Entity entity, boolean isPassengerTick) {
+ if (entity instanceof SlimeEntity && isPassengerTick) {
+ entity.world.addParticle(ParticleTypes.EXPLOSION_EMITTER, entity.getX(), entity.getY(), entity.getZ(), 0.0, 0.0, 0.0);
+ }
+ }
}