Skip to content

Commit

Permalink
refactor: introduced EntityStatus
Browse files Browse the repository at this point in the history
  • Loading branch information
smartcmd committed Feb 7, 2025
1 parent 0bce082 commit d2c0bc5
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 50 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ Unless otherwise specified, any version comparison below is the comparison of se
- (API) Added `ChunkSection`, chunk section can be obtained from chunk.
- (API) Added `EnchantmentType#canBeAppliedTo`, `EnchantmentType#getAppliableType` and `ApplicableType`, these methods can be used to
check if an enchantment type can be applied to a specific item type.
- (API) Introduce new item tag `allay:head` and helper method `ItemHelper#isHead` which can be used to check if an item is a head item.
- (API) Introduce `ItemStack#isAllEnchantmentsAvailableInEnchantTable` method. This method is used in book item.
- (API) Introduced new item tag `allay:head` and helper method `ItemHelper#isHead` which can be used to check if an item is a head item.
- (API) Introduced `ItemStack#isAllEnchantmentsAvailableInEnchantTable` method. This method is used in book item.
- (API) Introduced `EntityBaseComponent#getStatus` which can get the status of an entity. This status replaced the old boolean flags
such as `spawned`, `dead`, `willBeSpawnedNextTick` and provide better stability and extensibility.
- Implemented reeds (also called sugar cane) and cactus.
- Implemented `UpdateSubChunkBlocksPacket` related logic, which will make client load large range block updates much quicker (e.g.
using `/fill` command to fill a large area).
Expand Down
49 changes: 49 additions & 0 deletions api/src/main/java/org/allaymc/api/entity/EntityStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.allaymc.api.entity;

import lombok.Getter;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/**
* Represents the status of an entity.
*
* @author daoge_cmd
*/
public enum EntityStatus {
/**
* The entity is despawned.
*/
DESPAWNED(false),
/**
* The entity will be spawn in the next tick.
*/
SPAWNED_NEXT_TICK(false, DESPAWNED),
/**
* The entity is alive.
*/
ALIVE(true, SPAWNED_NEXT_TICK),
/**
* The entity is dead.
*/
DEAD(true, ALIVE),
/**
* The entity will be despawned in the next tick.
*/
DESPAWNED_NEXT_TICK(false, DEAD, ALIVE);

@Getter
private final boolean spawned;
/**
* The possible previous statuses of the entity. Can be {@code null} if previous status is not exist.
*/
@Getter
private final Set<EntityStatus> previousStatuses;

EntityStatus(boolean spawned, EntityStatus... previousStatuses) {
this.spawned = spawned;
this.previousStatuses = new HashSet<>();
this.previousStatuses.addAll(Arrays.asList(previousStatuses));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.allaymc.api.block.type.BlockTypes;
import org.allaymc.api.command.CommandSender;
import org.allaymc.api.entity.Entity;
import org.allaymc.api.entity.EntityStatus;
import org.allaymc.api.entity.effect.EffectInstance;
import org.allaymc.api.entity.effect.EffectType;
import org.allaymc.api.entity.effect.type.EffectTypes;
Expand Down Expand Up @@ -136,41 +137,56 @@ default World getWorld() {
return getLocation().dimension() != null ? getLocation().dimension().getWorld() : null;
}

/**
* Get the status of the entity.
*
* @return the status of the entity.
*/
EntityStatus getStatus();

/**
* Check if the entity will be spawned in the next tick.
*
* @return {@code true} if the entity will be spawned in the next tick.
*/
boolean willBeSpawnedNextTick();
default boolean willBeSpawnedNextTick() {
return getStatus() == EntityStatus.SPAWNED_NEXT_TICK;
}

/**
* Check if the entity will be despawned in the next tick.
*
* @return {@code true} if the entity will be despawned in the next tick.
*/
boolean willBeDespawnedNextTick();
default boolean willBeDespawnedNextTick() {
return getStatus() == EntityStatus.DESPAWNED_NEXT_TICK;
}

/**
* Check if the entity is dead.
*
* @return {@code true} if the entity is dead.
*/
boolean isDead();
default boolean isDead() {
return getStatus() == EntityStatus.DEAD;
}

/**
* Check if the entity is spawned.
*
* @return {@code true} if the entity is spawned.
*/
boolean isSpawned();
default boolean isSpawned() {
return getStatus().isSpawned();
}

/**
* Check if the entity is alive.
*
* @return {@code true} if the entity is alive.
*/
default boolean isAlive() {
return isSpawned() && !isDead();
return getStatus() == EntityStatus.ALIVE;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public enum ClientStatus {
IN_GAME(LOGGED_IN);

/**
* The previous status of the client.
* The previous status of the client. Can be {@code null} if previous status is not exist.
*/
@Getter
private final ClientStatus previousStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public boolean place(Dimension dimension, BlockState blockState, Vector3ic place
processBlockProperties(blockState, placeBlockPos, placementInfo),
placementInfo
);

return true;
}

Expand Down Expand Up @@ -120,7 +121,9 @@ public void afterNeighborLayerReplace(BlockStateWithPos currentBlockState, Block

@Override
public void onBreak(BlockStateWithPos blockState, ItemStack usedItem, Entity entity) {
if (!isDroppable(blockState, usedItem, entity)) return;
if (!isDroppable(blockState, usedItem, entity)) {
return;
}

var dropPos = MathUtils.center(blockState.pos());
var dimension = blockState.pos().dimension();
Expand All @@ -138,7 +141,10 @@ public void onBreak(BlockStateWithPos blockState, ItemStack usedItem, Entity ent

@Override
public boolean isDroppable(BlockStateWithPos blockState, ItemStack usedItem, Entity entity) {
if (entity instanceof EntityPlayer player && player.getGameType() == GameType.CREATIVE) return false;
if (entity instanceof EntityPlayer player && player.getGameType() == GameType.CREATIVE) {
return false;
}

return !blockState.blockState().getBlockStateData().requiresCorrectToolForDrops() || (usedItem != null && usedItem.isCorrectToolFor(blockState.blockState()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.allaymc.api.command.CommandSender;
import org.allaymc.api.component.interfaces.ComponentManager;
import org.allaymc.api.entity.Entity;
import org.allaymc.api.entity.EntityStatus;
import org.allaymc.api.entity.component.EntityBaseComponent;
import org.allaymc.api.entity.component.attribute.AttributeType;
import org.allaymc.api.entity.component.attribute.EntityAttributeComponent;
Expand Down Expand Up @@ -116,15 +117,8 @@ public class EntityBaseComponentImpl implements EntityBaseComponent {
protected Vector3d lastMotion = new Vector3d();
@Getter
protected boolean onGround = true;
@Setter
protected boolean willBeDespawnedNextTick = false;
@Setter
protected boolean willBeSpawnedNextTick = false;
@Getter
@Setter
protected boolean spawned;
@Getter
protected boolean dead;
protected EntityStatus status = EntityStatus.DESPAWNED;
protected int deadTimer;
@Getter
protected double fallDistance;
Expand Down Expand Up @@ -237,20 +231,23 @@ protected void tickEffects() {

protected void checkDead() {
// TODO: move these code to EntityAttributeComponentImpl
if (attributeComponent == null || !attributeComponent.supportAttribute(AttributeType.HEALTH)) return;
if (attributeComponent.getHealth() == 0 && !dead) {
if (attributeComponent == null || !attributeComponent.supportAttribute(AttributeType.HEALTH)) {
return;
}

if (attributeComponent.getHealth() == 0 && !isDead()) {
onDie();
}
if (dead) {
if (isDead()) {
if (hasDeadTimer()) {
if (deadTimer > 0) deadTimer--;
if (deadTimer == 0) {
// Spawn dead particle
spawnDeadParticle();
getDimension().getEntityService().removeEntity(thisEntity, () -> dead = false);
getDimension().getEntityService().removeEntity(thisEntity);
}
} else {
getDimension().getEntityService().removeEntity(thisEntity, () -> dead = false);
getDimension().getEntityService().removeEntity(thisEntity);
}
}
}
Expand All @@ -263,7 +260,7 @@ protected void onDie() {
new EntityDieEvent(thisEntity).call();

manager.callEvent(CEntityDieEvent.INSTANCE);
dead = true;
setStatus(EntityStatus.DEAD);
if (hasDeadTimer()) {
deadTimer = DEFAULT_DEAD_TIMER;
}
Expand All @@ -289,6 +286,7 @@ public void setLocationBeforeSpawn(Location3dc location) {
if (!canBeSpawnedIgnoreLocation()) {
throw new IllegalStateException("Trying to set location of an entity which cannot being spawned!");
}

setLocation(location, false);
}

Expand Down Expand Up @@ -343,9 +341,14 @@ public void despawn() {
getDimension().getEntityService().removeEntity(thisEntity);
}

@Override
public boolean willBeDespawnedNextTick() {
return willBeDespawnedNextTick;
public synchronized boolean setStatus(EntityStatus status) {
if (!status.getPreviousStatuses().isEmpty() && !status.getPreviousStatuses().contains(this.status)) {
log.warn("Trying to set status of entity {} to {} but the current status is {}", thisEntity, status, this.status);
return false;
}

this.status = status;
return true;
}

@Override
Expand All @@ -358,12 +361,7 @@ public boolean canBeSpawned() {
}

protected boolean canBeSpawnedIgnoreLocation() {
return !spawned && !willBeSpawnedNextTick;
}

@Override
public boolean willBeSpawnedNextTick() {
return willBeSpawnedNextTick;
return status == EntityStatus.DESPAWNED;
}

public boolean setLocationAndCheckChunk(Location3dc newLoc) {
Expand Down Expand Up @@ -426,7 +424,7 @@ public void teleport(Location3dc target, EntityTeleportEvent.Reason reason) {
return;
}

if (!this.spawned) {
if (!this.isSpawned()) {
log.warn("Trying to teleport an entity which is not spawned! Entity: {}", thisEntity);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,9 @@ protected void syncData() {
}

protected void tryPickUpItems() {
if (dead || !spawned || willBeDespawnedNextTick) return;
if (isDead() || !isSpawned() || willBeDespawnedNextTick()) {
return;
}

var dimension = location.dimension();
// pick up items
Expand Down Expand Up @@ -654,7 +656,7 @@ protected void sendSimpleMessage(String message, TextPacket.Type type) {

@Override
public boolean isLoaderActive() {
return spawned;
return status.isSpawned();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public synchronized boolean setClientStatus(ClientStatus clientStatus, boolean w
}
return false;
}

this.lastClientStatus = this.clientStatus;
this.clientStatus = clientStatus;
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.netty.util.internal.PlatformDependent;
import lombok.extern.slf4j.Slf4j;
import org.allaymc.api.entity.Entity;
import org.allaymc.api.entity.EntityStatus;
import org.allaymc.api.eventbus.event.entity.EntityDespawnEvent;
import org.allaymc.api.eventbus.event.entity.EntitySpawnEvent;
import org.allaymc.api.world.service.EntityService;
Expand Down Expand Up @@ -50,9 +51,7 @@ private void removeEntityImmediately(Entity entity) {
entityPhysicsService.removeEntity(entity);
entity.despawnFromAll();

var baseComponent = ((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent());
baseComponent.setWillBeDespawnedNextTick(false);
baseComponent.setSpawned(false);
((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setStatus(EntityStatus.DESPAWNED);
}

private void addEntityImmediately(Entity entity) {
Expand All @@ -67,9 +66,7 @@ private void addEntityImmediately(Entity entity) {
entityPhysicsService.addEntity(entity);
entity.spawnTo(unsafeChunk.getPlayerChunkLoaders());

var baseComponent = ((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent());
baseComponent.setWillBeSpawnedNextTick(false);
baseComponent.setSpawned(true);
((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setStatus(EntityStatus.ALIVE);
}

@Override
Expand All @@ -79,19 +76,16 @@ public void addEntity(Entity entity, Runnable callback) {
return;
}

((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setWillBeSpawnedNextTick(true);
entityUpdateOperationQueue.add(new EntityUpdateOperation(entity, EntityUpdateType.ADD, callback));
if (((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setStatus(EntityStatus.SPAWNED_NEXT_TICK)) {
entityUpdateOperationQueue.add(new EntityUpdateOperation(entity, EntityUpdateType.ADD, callback));
}
}

@Override
public void removeEntity(Entity entity, Runnable callback) {
if (entity.willBeDespawnedNextTick()) {
log.warn("Trying to remove an entity twice! Entity: {}", entity);
return;
if (((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setStatus(EntityStatus.DESPAWNED_NEXT_TICK)) {
entityUpdateOperationQueue.add(new EntityUpdateOperation(entity, EntityUpdateType.REMOVE, callback));
}

((EntityBaseComponentImpl) ((EntityImpl) entity).getBaseComponent()).setWillBeDespawnedNextTick(true);
entityUpdateOperationQueue.add(new EntityUpdateOperation(entity, EntityUpdateType.REMOVE, callback));
}

protected enum EntityUpdateType {
Expand Down

0 comments on commit d2c0bc5

Please sign in to comment.