mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-12 16:27:47 +00:00
[1.20] Add events for checking spawn conditions (SpawnPlacementCheck and PositionCheck) (#9469)
This commit is contained in:
1 parent
7a25e83d34
commit
2d8d6b2a0a
6 files changed
+296
-16
No files matched your search
@@ -40,6 +40,7 @@ import net.minecraft.commands.Commands;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.SpawnGroupData;
|
||||
import net.minecraft.world.entity.SpawnPlacements;
|
||||
import net.minecraft.world.entity.EntityDimensions;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
@@ -75,6 +76,7 @@ import net.minecraft.world.level.Explosion;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraft.world.level.SpawnData;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
@@ -103,6 +105,8 @@ import net.minecraftforge.event.entity.living.LivingHealEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingPackSizeEvent;
|
||||
import net.minecraftforge.event.entity.living.MobSpawnEvent;
|
||||
import net.minecraftforge.event.entity.living.MobSpawnEvent.AllowDespawn;
|
||||
import net.minecraftforge.event.entity.living.MobSpawnEvent.PositionCheck;
|
||||
import net.minecraftforge.event.entity.living.MobSpawnEvent.SpawnPlacementCheck;
|
||||
import net.minecraftforge.event.entity.living.ZombieEvent.SummonAidEvent;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent.AdvancementEarnEvent;
|
||||
import net.minecraftforge.event.entity.player.AdvancementEvent.AdvancementProgressEvent;
|
||||
@@ -187,6 +191,55 @@ public class ForgeEventFactory
|
||||
MinecraftForge.EVENT_BUS.post(new PlayerDestroyItemEvent(player, stack, hand));
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal, should only be called via {@link SpawnPlacements#checkSpawnRules}.
|
||||
* @see SpawnPlacementCheck
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public static boolean checkSpawnPlacements(EntityType<?> entityType, ServerLevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random, boolean defaultResult)
|
||||
{
|
||||
var event = new SpawnPlacementCheck(entityType, level, spawnType, pos, random, defaultResult);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
return event.getResult() == Result.DEFAULT ? defaultResult : event.getResult() == Result.ALLOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current position of the passed mob is valid for spawning, by firing {@link PositionCheck}.<br>
|
||||
* The default check is to perform the logical and of {@link Mob#checkSpawnRules} and {@link Mob#checkSpawnObstruction}.<br>
|
||||
* @param mob The mob being spawned.
|
||||
* @param level The level the mob will be added to, if successful.
|
||||
* @param spawnType The spawn type of the spawn.
|
||||
* @return True, if the position is valid, as determined by the contract of {@link PositionCheck}.
|
||||
* @see PositionCheck
|
||||
*/
|
||||
public static boolean checkSpawnPosition(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType)
|
||||
{
|
||||
var event = new PositionCheck(mob, level, spawnType, null);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
if (event.getResult() == Result.DEFAULT)
|
||||
{
|
||||
return mob.checkSpawnRules(level, spawnType) && mob.checkSpawnObstruction(level);
|
||||
}
|
||||
return event.getResult() == Result.ALLOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized variant of {@link #checkSpawnPosition} for spawners, as they have slightly different checks.
|
||||
* @see #CheckSpawnPosition
|
||||
* @implNote See in-line comments about custom spawn rules.
|
||||
*/
|
||||
public static boolean checkSpawnPositionSpawner(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType, SpawnData spawnData, BaseSpawner spawner)
|
||||
{
|
||||
var event = new PositionCheck(mob, level, spawnType, null);
|
||||
MinecraftForge.EVENT_BUS.post(event);
|
||||
if (event.getResult() == Result.DEFAULT)
|
||||
{
|
||||
// Spawners do not evaluate Mob#checkSpawnRules if any custom rules are present. This is despite the fact that these two methods do not check the same things.
|
||||
return (spawnData.getCustomSpawnRules().isPresent() || mob.checkSpawnRules(level, spawnType)) && mob.checkSpawnObstruction(level);
|
||||
}
|
||||
return event.getResult() == Result.ALLOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vanilla calls to {@link Mob#finalizeSpawn} are replaced with calls to this method via coremod.<br>
|
||||
* Mods should call this method in place of calling {@link Mob#finalizeSpawn}. Super calls (from within overrides) should not be wrapped.
|
||||
@@ -236,7 +289,7 @@ public class ForgeEventFactory
|
||||
* Returns the FinalizeSpawn event instance, or null if it was canceled.<br>
|
||||
* This is separate since mob spawners perform special finalizeSpawn handling when NBT data is present, but we still want to fire the event.<br>
|
||||
* This overload is also the only way to pass through a {@link BaseSpawner} instance.
|
||||
* @see MobSpawnEvent.FinalizeSpawn
|
||||
* @see #onFinalizeSpawn
|
||||
*/
|
||||
@Nullable
|
||||
public static MobSpawnEvent.FinalizeSpawn onFinalizeSpawnSpawner(Mob mob, ServerLevelAccessor level, DifficultyInstance difficulty, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag spawnTag, BaseSpawner spawner)
|
||||
|
||||
@@ -8,24 +8,38 @@ package net.minecraftforge.event.entity.living;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.DifficultyInstance;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Mob;
|
||||
import net.minecraft.world.entity.MobSpawnType;
|
||||
import net.minecraft.world.entity.SpawnGroupData;
|
||||
import net.minecraft.world.entity.SpawnPlacements;
|
||||
import net.minecraft.world.level.BaseSpawner;
|
||||
import net.minecraft.world.level.ServerLevelAccessor;
|
||||
import net.minecraftforge.common.ForgeInternalHandler;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.event.entity.EntityEvent;
|
||||
import net.minecraftforge.event.entity.SpawnPlacementRegisterEvent;
|
||||
import net.minecraftforge.eventbus.api.Cancelable;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.LogicalSide;
|
||||
|
||||
/**
|
||||
* The subclasses of this event are fired whenever a mob performs a spawning-related action.
|
||||
* This class holds all events relating to the entire flow of mob spawns.<br>
|
||||
* Currently, the events have the following flow for any given mob spawn:
|
||||
* <p>
|
||||
* Before the spawn is attempted {@link SpawnPlacementCheck} is fired, to determine if the spawn may occur based on mob-specific rules.<br>
|
||||
* After the entity is created {@link PositionCheck} is fired, to determine if the selected position is legal for the entity.<br>
|
||||
* If both checks succeeded, {@link FinalizeSpawn} is fired, which performs initialization on the newly-spawned entity.<br>
|
||||
* Finally, if the spawn was not cancelled via {@link FinalizeSpawn#setSpawnCancelled}, then {@link EntityJoinLevelEvent} is fired as the entity enters the world.
|
||||
* <p>
|
||||
* {@link AllowDespawn} is not related to the mob spawn event flow, as it fires when a despawn is attempted.
|
||||
*/
|
||||
public abstract class MobSpawnEvent extends EntityEvent
|
||||
{
|
||||
@@ -34,11 +48,8 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
private final double y;
|
||||
private final double z;
|
||||
|
||||
/**
|
||||
* @apiNote Do not construct directly. Access via {@link ForgeEventFactory#onFinalizeSpawn}.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public MobSpawnEvent(Mob mob, ServerLevelAccessor level, double x, double y, double z)
|
||||
protected MobSpawnEvent(Mob mob, ServerLevelAccessor level, double x, double y, double z)
|
||||
{
|
||||
super(mob);
|
||||
this.level = level;
|
||||
@@ -85,6 +96,166 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
return this.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is fired {@linkplain SpawnPlacements#checkSpawnRules when Spawn Placements (aka Spawn Rules) are checked}, before a mob attempts to spawn.<br>
|
||||
* Spawn Placement checks include light levels, slime chunks, grass blocks for animals, and others in the same vein.<br>
|
||||
* The purpose of this event is to permit runtime changes to any or all spawn placement logic without having to wrap the placement for each entity.
|
||||
* <p>
|
||||
* This event {@linkplain HasResult has a result}.<br>
|
||||
* To change the result of this event, use {@link #setResult}. Results are interpreted in the following manner:
|
||||
* <ul>
|
||||
* <li>Allow - The check will succeed, and the spawn process will continue.</li>
|
||||
* <li>Default - The value of the vanilla check will be used to determine success.</li>
|
||||
* <li>Deny - The check will fail, and the spawn process will abort.</li>
|
||||
* </ul>
|
||||
* This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#SERVER logical server}.
|
||||
* <p>
|
||||
* This event is not fired for mob spawners which utilize {@link CustomSpawnRules}, as they do not check spawn placements.
|
||||
* @apiNote If your modifications are for a single entity, and do not vary at runtime, use {@link SpawnPlacementRegisterEvent}.
|
||||
* @see SpawnPlacementRegisterEvent
|
||||
*/
|
||||
@HasResult
|
||||
public static class SpawnPlacementCheck extends Event
|
||||
{
|
||||
private final EntityType<?> entityType;
|
||||
private final ServerLevelAccessor level;
|
||||
private final MobSpawnType spawnType;
|
||||
private final BlockPos pos;
|
||||
private final RandomSource random;
|
||||
private final boolean defaultResult;
|
||||
|
||||
/**
|
||||
* Internal.
|
||||
* @see {@link SpawnPlacements#checkSpawnRules} for the single call site of this event.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public SpawnPlacementCheck(EntityType<?> entityType, ServerLevelAccessor level, MobSpawnType spawnType, BlockPos pos, RandomSource random, boolean defaultResult)
|
||||
{
|
||||
this.entityType = entityType;
|
||||
this.level = level;
|
||||
this.spawnType = spawnType;
|
||||
this.pos = pos;
|
||||
this.random = random;
|
||||
this.defaultResult = defaultResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The type of entity that checks are being performed for.
|
||||
*/
|
||||
public EntityType<?> getEntityType()
|
||||
{
|
||||
return this.entityType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The level relating to the mob spawn action
|
||||
*/
|
||||
public ServerLevelAccessor getLevel()
|
||||
{
|
||||
return this.level;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of mob spawn that is happening.
|
||||
* @return The mob spawn type.
|
||||
* @see MobSpawnType
|
||||
*/
|
||||
public MobSpawnType getSpawnType()
|
||||
{
|
||||
return this.spawnType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The position where checks are being evaluated.
|
||||
*/
|
||||
public BlockPos getPos()
|
||||
{
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* In all vanilla cases, this is equal to {@link ServerLevelAccessor#getRandom()}.
|
||||
* @return The random source being used.
|
||||
*/
|
||||
public RandomSource getRandom()
|
||||
{
|
||||
return this.random;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default vanilla result is useful if an additional check wants to force {@link Result#ALLOW} only if the vanilla check would succeed.
|
||||
* @return The result of the vanilla spawn placement check.
|
||||
*/
|
||||
public boolean getDefaultResult()
|
||||
{
|
||||
return this.defaultResult;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is fired when a mob checks for a valid spawn position, after {@link SpawnPlacements#checkSpawnRules} has been evaluated.<br>
|
||||
* Conditions validated here include the following:
|
||||
* <ul>
|
||||
* <li>Obstruction - mobs inside blocks or fluids.</li>
|
||||
* <li>Pathfinding - if the spawn block is valid for pathfinding.</li>
|
||||
* <li>Sea Level - Ocelots check if the position is above sea level.</li>
|
||||
* <li>Spawn Block - Ocelots check if the below block is grass or leaves.</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* These checks are performed by the vanilla methods {@link Mob#checkSpawnRules} and {@link Mob#checkSpawnObstruction}.<br>
|
||||
* The logical-and of both methods forms the default result of this event.
|
||||
* <p>
|
||||
* This event {@linkplain HasResult has a result}.<br>
|
||||
* To change the result of this event, use {@link #setResult}. Results are interpreted in the following manner:
|
||||
* <ul>
|
||||
* <li>Allow - The position will be accepted, and the spawn process will continue.</li>
|
||||
* <li>Default - The position will be accepted if {@link Mob#checkSpawnRules} and {@link Mob#checkSpawnObstruction} are both true.</li>
|
||||
* <li>Deny - The position will not be accepted. The spawn process will abort, and further events will not be called.</li>
|
||||
* </ul>
|
||||
* This event is fired on the {@linkplain MinecraftForge#EVENT_BUS main Forge event bus},
|
||||
* only on the {@linkplain LogicalSide#SERVER logical server}.
|
||||
*
|
||||
* @apiNote This event fires after Spawn Placement checks, which are the primary set of spawn checks.
|
||||
* @see {@link SpawnPlacementRegisterEvent} To modify spawn placements statically at startup.
|
||||
* @see {@link SpawnPlacementCheck} To modify the result of spawn placements at runtime.
|
||||
*/
|
||||
@HasResult
|
||||
public static class PositionCheck extends MobSpawnEvent
|
||||
{
|
||||
@Nullable
|
||||
private final BaseSpawner spawner;
|
||||
private final MobSpawnType spawnType;
|
||||
|
||||
public PositionCheck(Mob mob, ServerLevelAccessor level, MobSpawnType spawnType, @Nullable BaseSpawner spawner)
|
||||
{
|
||||
super(mob, level, mob.getX(), mob.getY(), mob.getZ());
|
||||
this.spawnType = spawnType;
|
||||
this.spawner = spawner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the underlying {@link BaseSpawner} instance if this mob was created by a Mob Spawner of some form.
|
||||
* This is always null unless {@link #getSpawnType()} is {@link MobSpawnType#SPAWNER}, and may still be null even then.
|
||||
* @return The BaseSpawner responsible for triggering the spawn, or null if none is available.
|
||||
*/
|
||||
@Nullable
|
||||
public BaseSpawner getSpawner()
|
||||
{
|
||||
return spawner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of mob spawn that is happening.
|
||||
* @return The mob spawn type.
|
||||
* @see MobSpawnType
|
||||
*/
|
||||
public MobSpawnType getSpawnType()
|
||||
{
|
||||
return this.spawnType;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This event is fired before {@link Mob#finalizeSpawn} is called.<br>
|
||||
* This allows mods to control mob initialization.<br>
|
||||
@@ -101,12 +272,19 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
public static class FinalizeSpawn extends MobSpawnEvent
|
||||
{
|
||||
private final MobSpawnType spawnType;
|
||||
@Nullable private final BaseSpawner spawner;
|
||||
|
||||
private DifficultyInstance difficulty;
|
||||
@Nullable private SpawnGroupData spawnData;
|
||||
@Nullable private CompoundTag spawnTag;
|
||||
@Nullable
|
||||
private final BaseSpawner spawner;
|
||||
|
||||
private DifficultyInstance difficulty;
|
||||
@Nullable
|
||||
private SpawnGroupData spawnData;
|
||||
@Nullable
|
||||
private CompoundTag spawnTag;
|
||||
|
||||
/**
|
||||
* @apiNote Do not construct directly. Access via {@link ForgeEventFactory#onFinalizeSpawn} / {@link ForgeEventFactory#onFinalizeSpawnSpawner}.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
public FinalizeSpawn(Mob entity, ServerLevelAccessor level, double x, double y, double z, DifficultyInstance difficulty, MobSpawnType spawnType, @Nullable SpawnGroupData spawnData, @Nullable CompoundTag spawnTag, @Nullable BaseSpawner spawner)
|
||||
{
|
||||
super(entity, level, x, y, z);
|
||||
@@ -135,7 +313,7 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
{
|
||||
this.difficulty = inst;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the type of mob spawn that happened (the event that caused the spawn). The enum names are self-explanatory.
|
||||
* @return The mob spawn type.
|
||||
@@ -206,7 +384,8 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
* Usually that has no side effects, but callers should be aware.
|
||||
* @param cancel If the spawn should be cancelled (or not).
|
||||
*/
|
||||
public void setSpawnCancelled(boolean cancel) {
|
||||
public void setSpawnCancelled(boolean cancel)
|
||||
{
|
||||
this.getEntity().setSpawnCancelled(cancel);
|
||||
}
|
||||
|
||||
@@ -215,7 +394,8 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
* @return If this mob's spawn is cancelled or not.
|
||||
* @implNote This is enforced in {@link ForgeInternalHandler#builtinMobSpawnBlocker} and a patch in {@link WorldGenRegion#addEntity}
|
||||
*/
|
||||
public boolean isSpawnCancelled() {
|
||||
public boolean isSpawnCancelled()
|
||||
{
|
||||
return this.getEntity().isSpawnCancelled();
|
||||
}
|
||||
}
|
||||
@@ -236,6 +416,8 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
* @see LivingEntity#checkDespawn()
|
||||
* @author cpw
|
||||
*/
|
||||
// TODO: 1.20 Move to standalone class, as it is unrelated to the complex mob spawning flow.
|
||||
// Such a refactor will allow the BaseSpawner and MobSpawnType params to be hoisted to MobSpawnEvent.
|
||||
@HasResult
|
||||
public static class AllowDespawn extends MobSpawnEvent
|
||||
{
|
||||
@@ -244,4 +426,5 @@ public abstract class MobSpawnEvent extends EntityEvent
|
||||
super(mob, level, mob.getX(), mob.getY(), mob.getZ());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user