Skip to content

Commit

Permalink
Update entity tick semantics
Browse files Browse the repository at this point in the history
  • Loading branch information
Shadows-of-Fire committed Apr 22, 2024
1 parent d3e929e commit d8853c5
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 85 deletions.
10 changes: 7 additions & 3 deletions patches/net/minecraft/client/multiplayer/ClientLevel.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,18 @@
}

public void queueLightUpdate(Runnable p_194172_) {
@@ -282,6 +_,7 @@
@@ -282,7 +_,10 @@
p_104640_.setOldPosAndRot();
p_104640_.tickCount++;
this.getProfiler().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_104640_.getType()).toString());
+ if (p_104640_.canUpdate())
p_104640_.tick();
- p_104640_.tick();
+ // Neo: Permit cancellation of Entity#tick via EntityTickEvent.Pre
+ if (!net.neoforged.neoforge.event.EventHooks.fireEntityTickPre(p_104640_).isCanceled()) {
+ p_104640_.tick();
+ }
this.getProfiler().pop();

for (Entity entity : p_104640_.getPassengers()) {
@@ -330,8 +_,10 @@
}

Expand Down
18 changes: 11 additions & 7 deletions patches/net/minecraft/server/level/ServerLevel.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,18 @@
}
}

@@ -774,6 +_,7 @@
ProfilerFiller profilerfiller = this.getProfiler();
profilerfiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_8664_.getType()).toString());
profilerfiller.incrementCounter("tickPassenger");
+ if (p_8664_.canUpdate())
p_8664_.rideTick();
profilerfiller.pop();
@@ -757,7 +_,10 @@
p_8648_.tickCount++;
this.getProfiler().push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(p_8648_.getType()).toString());
profilerfiller.incrementCounter("tickNonPassenger");
- p_8648_.tick();
+ // Neo: Permit cancellation of Entity#tick via EntityTickEvent.Pre
+ if (!net.neoforged.neoforge.event.EventHooks.fireEntityTickPre(p_8648_).isCanceled()) {
+ p_8648_.tick();
+ }
this.getProfiler().pop();

for (Entity entity : p_8648_.getPassengers()) {
@@ -806,6 +_,7 @@
} else {
this.entityManager.autoSave();
Expand Down
33 changes: 9 additions & 24 deletions patches/net/minecraft/world/entity/Entity.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -198,15 +198,6 @@
}

public void moveRelative(float p_19921_, Vec3 p_19922_) {
@@ -1645,6 +_,8 @@
p_20241_.putBoolean("HasVisualFire", this.hasVisualFire);
}

+ p_20241_.putBoolean("CanUpdate", canUpdate);
+
if (!this.tags.isEmpty()) {
ListTag listtag = new ListTag();

@@ -1655,6 +_,10 @@
p_20241_.put("Tags", listtag);
}
Expand All @@ -218,12 +209,11 @@
this.addAdditionalSaveData(p_20241_);
if (this.isVehicle()) {
ListTag listtag1 = new ListTag();
@@ -1735,6 +_,9 @@
@@ -1735,6 +_,8 @@
this.setGlowingTag(p_20259_.getBoolean("Glowing"));
this.setTicksFrozen(p_20259_.getInt("TicksFrozen"));
this.hasVisualFire = p_20259_.getBoolean("HasVisualFire");
+ if (p_20259_.contains("NeoForgeData", 10)) persistentData = p_20259_.getCompound("NeoForgeData");
+ if (p_20259_.contains("CanUpdate", 99)) this.canUpdate(p_20259_.getBoolean("CanUpdate"));
+ if (p_20259_.contains(ATTACHMENTS_NBT_KEY, net.minecraft.nbt.Tag.TAG_COMPOUND)) deserializeAttachments(registryAccess(), p_20259_.getCompound(ATTACHMENTS_NBT_KEY));
if (p_20259_.contains("Tags", 9)) {
this.tags.clear();
Expand All @@ -237,14 +227,18 @@
this.level().addFreshEntity(itementity);
return itementity;
}
@@ -1865,6 +_,7 @@
@@ -1865,7 +_,10 @@

public void rideTick() {
this.setDeltaMovement(Vec3.ZERO);
+ if (canUpdate())
this.tick();
- this.tick();
+ // Neo: Permit cancellation of Entity#tick via EntityTickEvent.Pre
+ if (!net.neoforged.neoforge.event.EventHooks.fireEntityTickPre(this).isCanceled()) {
+ this.tick();
+ }
if (this.isPassenger()) {
this.getVehicle().positionRider(this);
}
@@ -1923,6 +_,7 @@
}
}
Expand Down Expand Up @@ -495,22 +489,13 @@
}

public void checkDespawn() {
@@ -3424,6 +_,126 @@
@@ -3424,6 +_,117 @@
public boolean mayInteract(Level p_146843_, BlockPos p_146844_) {
return true;
}
+
+ /* ================================== Forge Start =====================================*/
+
+ private boolean canUpdate = true;
+ @Override
+ public void canUpdate(boolean value) {
+ this.canUpdate = value;
+ }
+ @Override
+ public boolean canUpdate() {
+ return this.canUpdate;
+ }
+ @Nullable
+ private java.util.Collection<ItemEntity> captureDrops = null;
+ @Override
Expand Down
8 changes: 0 additions & 8 deletions patches/net/minecraft/world/entity/LivingEntity.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -430,14 +430,6 @@
d2 = 0.0;
}

@@ -2334,6 +_,7 @@

@Override
public void tick() {
+ if (net.neoforged.neoforge.common.CommonHooks.onLivingTick(this)) return;
super.tick();
this.updatingUsingItem();
this.updateSwimAmount();
@@ -2483,6 +_,7 @@
};
ItemStack itemstack1 = this.getItemBySlot(equipmentslot);
Expand Down
21 changes: 1 addition & 20 deletions src/main/java/net/neoforged/neoforge/common/CommonHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
import net.neoforged.neoforge.event.level.BlockEvent;
import net.neoforged.neoforge.event.level.NoteBlockEvent;
import net.neoforged.neoforge.event.tick.LivingTickEvent;
import net.neoforged.neoforge.event.tick.EntityTickEvent;
import net.neoforged.neoforge.fluids.FluidType;
import net.neoforged.neoforge.registries.NeoForgeRegistries;
import net.neoforged.neoforge.resource.ResourcePackLoader;
Expand Down Expand Up @@ -237,25 +237,6 @@ public static LivingChangeTargetEvent onLivingChangeTarget(LivingEntity entity,
return event;
}

/**
* Fires {@link LivingTickEvent.Pre}. Called from the head of {@link LivingEntity#tick()}.
*
* @param entity The entity being ticked
* @return The event
*/
public static LivingTickEvent.Pre fireLivingTickPre(LivingEntity entity) {
return NeoForge.EVENT_BUS.post(new LivingTickEvent.Pre(entity));
}

/**
* Fires {@link LivingTickEvent.Post}. Called from the tail of {@link LivingEntity#tick()}.
*
* @param entity The entity being ticked
*/
public static void fireLivingTickPost(LivingEntity entity) {
NeoForge.EVENT_BUS.post(new LivingTickEvent.Post(entity));
}

public static boolean onLivingAttack(LivingEntity entity, DamageSource src, float amount) {
return entity instanceof Player || !NeoForge.EVENT_BUS.post(new LivingAttackEvent(entity, src, amount)).isCanceled();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ default CompoundTag serializeNBT(HolderLookup.Provider provider) {
return self().saveWithoutId(ret);
}

boolean canUpdate();

void canUpdate(boolean value);

@Nullable
Collection<ItemEntity> captureDrops();

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/net/neoforged/neoforge/event/EventHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@
import net.neoforged.neoforge.event.level.PistonEvent;
import net.neoforged.neoforge.event.level.SaplingGrowTreeEvent;
import net.neoforged.neoforge.event.level.SleepFinishedTimeEvent;
import net.neoforged.neoforge.event.tick.EntityTickEvent;
import net.neoforged.neoforge.event.tick.LevelTickEvent;
import net.neoforged.neoforge.event.tick.PlayerTickEvent;
import net.neoforged.neoforge.event.tick.ServerTickEvent;
Expand Down Expand Up @@ -812,6 +813,25 @@ public static void firePlayerSmeltedEvent(Player player, ItemStack smelted) {
NeoForge.EVENT_BUS.post(new PlayerEvent.ItemSmeltedEvent(player, smelted));
}

/**
* Fires {@link EntityTickEvent.Pre}. Called from the head of {@link LivingEntity#tick()}.
*
* @param entity The entity being ticked
* @return The event
*/
public static EntityTickEvent.Pre fireEntityTickPre(Entity entity) {
return NeoForge.EVENT_BUS.post(new EntityTickEvent.Pre(entity));
}

/**
* Fires {@link EntityTickEvent.Post}. Called from the tail of {@link LivingEntity#tick()}.
*
* @param entity The entity being ticked
*/
public static void fireEntityTickPost(Entity entity) {
NeoForge.EVENT_BUS.post(new EntityTickEvent.Post(entity));
}

/**
* Fires {@link PlayerTickEvent.Pre}. Called from the head of {@link Player#tick()}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,38 @@

package net.neoforged.neoforge.event.tick;

import net.minecraft.world.entity.LivingEntity;
import net.neoforged.bus.api.ICancellableEvent;
import net.neoforged.neoforge.event.entity.living.LivingEvent;
import org.jetbrains.annotations.ApiStatus;

import net.minecraft.world.entity.Entity;
import net.neoforged.bus.api.ICancellableEvent;
import net.neoforged.neoforge.event.entity.EntityEvent;

/**
* Base class of the two player tick events.
* Base class of the two entity tick events.
*
* @see Pre
* @see Post
*/
public abstract class LivingTickEvent extends LivingEvent {
public abstract class EntityTickEvent extends EntityEvent {
@ApiStatus.Internal
public LivingTickEvent(LivingEntity entity) {
public EntityTickEvent(Entity entity) {
super(entity);
}

/**
* {@link LivingTickEvent.Pre} is fired once per game tick, per entity, before the entity performs work for the current tick.
* {@link EntityTickEvent.Pre} is fired once per game tick, per entity, before the entity performs work for the current tick.
* <p>
* This event fires on both the logical server and logical client.
*/
public static class Pre extends LivingTickEvent implements ICancellableEvent {
public Pre(LivingEntity entity) {
public static class Pre extends EntityTickEvent implements ICancellableEvent {
public Pre(Entity entity) {
super(entity);
}

/**
* Cancels this event, preventing the current tick from being executed for the entity.
* <p>
* Additionally, if this event is canceled, then {@link LivingTickEvent.Post} will not be fired for the current tick.
* Additionally, if this event is canceled, then {@link EntityTickEvent.Post} will not be fired for the current tick.
*/
@Override
public void setCanceled(boolean canceled) {
Expand All @@ -44,14 +45,14 @@ public void setCanceled(boolean canceled) {
}

/**
* {@link LivingTickEvent.Post} is fired once per game tick, per entity, after the entity performs work for the current tick.
* {@link EntityTickEvent.Post} is fired once per game tick, per entity, after the entity performs work for the current tick.
* <p>
* If {@link LivingTickEvent.Pre} was canceled for the current tick, this event will not fire.
* If {@link EntityTickEvent.Pre} was canceled for the current tick, this event will not fire.
* <p>
* This event fires on both the logical server and logical client.
*/
public static class Post extends LivingTickEvent {
public Post(LivingEntity entity) {
public static class Post extends EntityTickEvent {
public Post(Entity entity) {
super(entity);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@

package net.neoforged.neoforge.event.tick;

import org.jetbrains.annotations.ApiStatus;

import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import org.jetbrains.annotations.ApiStatus;

/**
* Base class of the two player tick events.
* <p>
* These events are separate from {@link LivingTickEvent} due to the semantics of player ticks.
* On the client, players tick from the usual {@link Entity#tick()} method, but on the server, they rely
* on {@link ServerPlayer#doTick()} which is called from {@link ServerGamePacketListenerImpl#tick()}.
* <p>
* Use of these events should only be necessary if you rely on this specific timing.
*
* @see Pre
* @see Post
Expand All @@ -23,7 +34,7 @@ protected PlayerTickEvent(Player player) {
/**
* {@link PlayerTickEvent.Pre} is fired once per game tick, per player, before the player performs work for the current tick.
* <p>
* This event will fire on both the logical server and logical client, for all subclasses of {@link Player} on their respective sides.
* This event will fire on both the logical server and logical client, for subclasses of {@link Player} on their respective sides.
* <p>
* As such, be sure to check {@link Level#isClientSide()} before performing any operations.
*/
Expand All @@ -37,7 +48,7 @@ public Pre(Player player) {
/**
* {@link PlayerTickEvent.Post} is fired once per game tick, per player, after the player performs work for the current tick.
* <p>
* This event will fire on both the logical server and logical client, for all subclasses of {@link Player} on their respective sides.
* This event will fire on both the logical server and logical client, for subclasses of {@link Player} on their respective sides.
* <p>
* As such, be sure to check {@link Level#isClientSide()} before performing any operations.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;
import net.neoforged.neoforge.event.tick.LivingTickEvent;
import net.neoforged.neoforge.event.tick.EntityTickEvent;
import net.neoforged.testframework.DynamicTest;
import net.neoforged.testframework.annotation.ForEachTest;
import net.neoforged.testframework.annotation.TestHolder;
Expand Down Expand Up @@ -49,7 +49,7 @@ static void customGameRule(final DynamicTest test) {
final GameRules.Key<GameRules.BooleanValue> booleanGameRule = GameRules.register("%s:custom_boolean_game_rule".formatted(test.createModId()), GameRules.Category.MISC, GameRules.BooleanValue.create(true));
final GameRules.Key<GameRules.IntegerValue> integerGameRule = GameRules.register("%s:custom_integer_game_rule".formatted(test.createModId()), GameRules.Category.MISC, GameRules.IntegerValue.create(1337));

test.eventListeners().forge().addListener((LivingTickEvent.Pre event) -> {
test.eventListeners().forge().addListener((EntityTickEvent.Pre event) -> {
if (event.getEntity() instanceof ServerPlayer player && player.getGameProfile().getName().equals("test-mock-player")) {
if (player.level().getGameRules().getBoolean(booleanGameRule)) {
player.setHealth(player.getHealth() - player.level().getGameRules().getInt(integerGameRule));
Expand Down

0 comments on commit d8853c5

Please sign in to comment.