Skip to content

Commit

Permalink
Entity Tick Events (#201)
Browse files Browse the repository at this point in the history
* Create entity tick events

* Apply licenses to entity tick events

* Rename entity tick event Event fields

* Fix javadocs / imports

* Unfix some entity event javadoc

* Change a comment in EntityEventsTestMod

Co-authored-by: Ennui Langeweile <[email protected]>

* Invert slime tick event test condition

Co-authored-by: Ennui Langeweile <[email protected]>
  • Loading branch information
williambl and EnnuiL authored Dec 5, 2022
1 parent 78bd427 commit 2e7e9d8
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,21 +71,23 @@ 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);
}

@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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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).
* <p>
* 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<ServerEntityTickCallback> 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);
}
Original file line number Diff line number Diff line change
@@ -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).
* <p>
* 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<ClientEntityTickCallback> 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);
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,28 @@
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;

import org.quiltmc.qsl.entity_events.api.EntityReviveEvents;
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,
Expand All @@ -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
Expand Down Expand Up @@ -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));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
}
}
}

0 comments on commit 2e7e9d8

Please sign in to comment.