package com.hypixel.hytale.server.spawning.world.system;
import com.hypixel.fastutil.longs.Long2ObjectConcurrentHashMap;
import com.hypixel.hytale.component.AddReason;
import com.hypixel.hytale.component.Archetype;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.component.Holder;
import com.hypixel.hytale.component.NonTicking;
import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.RemoveReason;
import com.hypixel.hytale.component.ResourceType;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.HolderSystem;
import com.hypixel.hytale.component.system.RefChangeSystem;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.function.consumer.TriConsumer;
import com.hypixel.hytale.logger.HytaleLogger;
import com.hypixel.hytale.math.vector.Vector3d;
import com.hypixel.hytale.math.vector.Vector3f;
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
import com.hypixel.hytale.server.core.entity.Frozen;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import com.hypixel.hytale.server.flock.FlockPlugin;
import com.hypixel.hytale.server.npc.NPCPlugin;
import com.hypixel.hytale.server.npc.asset.builder.Builder;
import com.hypixel.hytale.server.npc.entities.NPCEntity;
import com.hypixel.hytale.server.npc.role.Role;
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
import com.hypixel.hytale.server.spawning.SpawnRejection;
import com.hypixel.hytale.server.spawning.SpawnTestResult;
import com.hypixel.hytale.server.spawning.SpawningContext;
import com.hypixel.hytale.server.spawning.suppression.SuppressionSpanHelper;
import com.hypixel.hytale.server.spawning.suppression.component.ChunkSuppressionEntry;
import com.hypixel.hytale.server.spawning.suppression.component.SpawnSuppressionController;
import com.hypixel.hytale.server.spawning.util.RandomChunkColumnIterator;
import com.hypixel.hytale.server.spawning.world.ChunkEnvironmentSpawnData;
import com.hypixel.hytale.server.spawning.world.WorldEnvironmentSpawnData;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnData;
import com.hypixel.hytale.server.spawning.world.component.ChunkSpawnedNPCData;
import com.hypixel.hytale.server.spawning.world.component.SpawnJobData;
import com.hypixel.hytale.server.spawning.world.component.WorldSpawnData;
import it.unimi.dsi.fastutil.Pair;
import it.unimi.dsi.fastutil.ints.IntBinaryOperator;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import java.util.logging.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class WorldSpawnJobSystems {
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
private static final Query<ChunkStore> QUERY = Archetype.of(SpawnJobData.getComponentType(), WorldChunk.getComponentType());
private static final Query<ChunkStore> TICKING_QUERY;
private static final int JOB_BUDGET = 64;
public WorldSpawnJobSystems() {
}
@Nonnull
private static Result run(@Nonnull SpawnJobData spawnJobData, @Nonnull WorldChunk chunk, @Nonnull ChunkEnvironmentSpawnData chunkEnvironmentSpawnData, @Nonnull WorldSpawnData worldSpawnData, @Nonnull SpawnSuppressionController spawnSuppressionController) {
int roleIndex = spawnJobData.getRoleIndex();
ISpawnableWithModel spawnable;
try {
spawnable = getSpawnable(roleIndex);
} catch (IllegalArgumentException e) {
endProbing(WorldSpawnJobSystems.Result.PERMANENT_FAILURE, spawnJobData, chunk, worldSpawnData);
throw e;
}
if (spawnable == null) {
HytaleLogger.Api context = LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("Spawn job %s: Terminated, spawnable %s gone", spawnJobData.getJobId(), getSpawnableName(roleIndex));
}
return endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, chunk, worldSpawnData);
} else {
SpawningContext spawningContext = spawnJobData.getSpawningContext();
if (!spawningContext.setSpawnable(spawnable)) {
HytaleLogger.Api context = LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("Spawn job %s: Terminated, Unable to set spawnable %s", spawnJobData.getJobId(), getSpawnableName(roleIndex));
}
return endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, chunk, worldSpawnData);
} else {
spawningContext.setChunk(chunk, spawnJobData.getEnvironmentIndex());
SuppressionSpanHelper suppressionSpanHelper = spawnJobData.getSuppressionSpanHelper();
Long2ObjectConcurrentHashMap<ChunkSuppressionEntry> chunkSuppressionMap = spawnSuppressionController.getChunkSuppressionMap();
suppressionSpanHelper.optimiseSuppressedSpans(roleIndex, chunkSuppressionMap.get(chunk.getIndex()));
Result var14;
try {
IntSet spawnBlockSet = spawnJobData.getSpawnConfig().getSpawnBlockSet(roleIndex);
int spawnFluidTag = spawnJobData.getSpawnConfig().getSpawnFluidTag(roleIndex);
RandomChunkColumnIterator iterator = chunkEnvironmentSpawnData.getRandomChunkColumnIterator();
spawnJobData.setBudgetUsed(0);
Result result;
do {
if (spawnJobData.getBudgetUsed() >= 64) {
return WorldSpawnJobSystems.Result.TRY_AGAIN;
}
iterator.nextPositionAvoidBorders();
spawnJobData.adjustBudgetUsed(3);
spawningContext.setColumn(iterator.getCurrentX(), iterator.getCurrentZ(), suppressionSpanHelper);
result = trySpawn(spawnable, spawnBlockSet, spawnFluidTag, spawnJobData, chunk, chunkEnvironmentSpawnData, worldSpawnData);
} while(result == WorldSpawnJobSystems.Result.TRY_AGAIN);
var14 = result;
} finally {
spawningContext.release();
}
return var14;
}
}
}
@Nullable
private static ISpawnableWithModel getSpawnable(int roleIndex) {
Builder<Role> role = NPCPlugin.get().tryGetCachedValidRole(roleIndex);
if (role == null) {
return null;
} else if (!role.isSpawnable()) {
throw new IllegalArgumentException("Spawn job: Role must be a spawnable (non-abstract) type for spawning: " + NPCPlugin.get().getName(roleIndex));
} else if (!(role instanceof ISpawnableWithModel)) {
throw new IllegalArgumentException("Spawn job: Need ISpawnableWithModel interface for spawning: " + NPCPlugin.get().getName(roleIndex));
} else {
return (ISpawnableWithModel)role;
}
}
@Nonnull
private static Result trySpawn(@Nonnull ISpawnableWithModel spawnable, IntSet spawnBlockSet, int spawnFluidTag, @Nonnull SpawnJobData spawnJobData, @Nonnull WorldChunk worldChunk, @Nonnull ChunkEnvironmentSpawnData environmentSpawnData, @Nonnull WorldSpawnData worldSpawnData) {
spawnJobData.incrementTotalColumnsTested();
SpawningContext spawningContext = spawnJobData.getSpawningContext();
try {
int spansBlocked = 0;
int spansTested;
for(spansTested = 0; spawningContext.selectRandomSpawnSpan(); spawningContext.deleteCurrentSpawnSpan()) {
spawnJobData.adjustBudgetUsed(1);
spawnJobData.incrementSpansTried();
++spansTested;
if (!spawnJobData.getSpawnConfig().withinLightRange(spawningContext)) {
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.OUTSIDE_LIGHT_RANGE);
} else if (!canSpawnOnBlock(spawnBlockSet, spawnFluidTag, spawningContext)) {
++spansBlocked;
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.INVALID_SPAWN_BLOCK);
} else {
SpawnTestResult spawnTestResult = spawningContext.canSpawn();
if (spawnTestResult == SpawnTestResult.TEST_OK) {
return spawn(spawnJobData, worldChunk, worldSpawnData);
}
if (spawnTestResult == SpawnTestResult.FAIL_INVALID_POSITION) {
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.INVALID_POSITION);
++spansBlocked;
} else if (spawnTestResult == SpawnTestResult.FAIL_NO_POSITION) {
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.NO_POSITION);
++spansBlocked;
} else if (spawnTestResult == SpawnTestResult.FAIL_NOT_BREATHABLE) {
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.NOT_BREATHABLE);
++spansBlocked;
} else {
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.OTHER);
}
}
}
if (spansBlocked > 0 && spansTested == spansBlocked) {
spawnJobData.incrementTotalColumnsBlocked();
}
} catch (NullPointerException | IllegalStateException e) {
LOGGER.at(Level.WARNING).log("%s with spawnable=%s spwnCfg=%s X/Y/Z=%s/%s/%s", ((RuntimeException)e).getMessage(), getSpawnableName(spawnJobData.getRoleIndex()), spawnJobData.getSpawnConfig().getSpawn().getId(), spawningContext.xSpawn, spawningContext.ySpawn, spawningContext.zSpawn);
spawnable.markNeedsReload();
return endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
}
if (environmentSpawnData.getRandomChunkColumnIterator().isAtSavedIteratorPosition()) {
if (spawnJobData.getTotalColumnsBlocked() == spawnJobData.getTotalColumnsTested()) {
environmentSpawnData.markRoleAsUnspawnable(spawnJobData.getRoleIndex());
HytaleLogger.Api context = LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("Spawn job %s: No column to create %s (env %s) at chunk %s/%s, columns probed %s", spawnJobData.getJobId(), getSpawnableName(spawnJobData.getRoleIndex()), spawnJobData.getEnvironment().getId(), worldChunk.getX(), worldChunk.getZ(), spawnJobData.getTotalColumnsTested());
}
if (environmentSpawnData.allRolesUnspawnable()) {
worldSpawnData.queueUnspawnableChunk(spawnJobData.getEnvironmentIndex(), worldChunk.getIndex());
if (context.isEnabled()) {
context.log("Spawn job %s: All roles unspawnable. Queued for processing.", spawnJobData.getJobId());
}
}
}
return endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
} else {
return WorldSpawnJobSystems.Result.TRY_AGAIN;
}
}
@Nonnull
private static Result spawn(@Nonnull SpawnJobData spawnJobData, @Nonnull WorldChunk worldChunk, @Nonnull WorldSpawnData worldSpawnData) {
NPCPlugin npcModule = NPCPlugin.get();
SpawningContext spawningContext = spawnJobData.getSpawningContext();
Vector3d position = spawningContext.newPosition();
Vector3f rotation = spawningContext.newRotation();
int roleIndex = spawnJobData.getRoleIndex();
try {
Store<EntityStore> store = spawningContext.world.getEntityStore().getStore();
Pair<Ref<EntityStore>, NPCEntity> npcPair = npcModule.spawnEntity(store, roleIndex, position, rotation, spawningContext.getModel(), (_npc, _holder, _store) -> preAddToWorld(_npc, _holder, roleIndex, spawnJobData), (TriConsumer)null);
NPCEntity npcComponent = npcPair.right();
Ref<EntityStore> npcRef = npcPair.left();
FlockPlugin.trySpawnFlock(npcRef, npcComponent, roleIndex, position, rotation, spawnJobData.getFlockSize(), spawnJobData.getFlockAsset(), (_npc, _holder, _store) -> preAddToWorld(_npc, _holder, roleIndex, spawnJobData), (TriConsumer)null, store);
} catch (RuntimeException e) {
((HytaleLogger.Api)LOGGER.at(Level.SEVERE).withCause(e)).log("Spawn job %s: Failed to create %s: %s", spawnJobData.getJobId(), npcModule.getName(roleIndex), e.getMessage());
rejectSpan(spawnJobData.getRejectionMap(), SpawnRejection.OTHER);
return endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
}
HytaleLogger.Api context = LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("Spawn job %s: Created %s with flock size %s (env %s) at chunk %s/%s, columns probed %s", spawnJobData.getJobId(), NPCPlugin.get().getName(roleIndex), spawnJobData.getFlockSize(), spawnJobData.getEnvironment().getId(), worldChunk.getX(), worldChunk.getZ(), spawnJobData.getTotalColumnsTested());
}
spawnJobData.incrementSpansSuccess();
return endProbing(WorldSpawnJobSystems.Result.SUCCESS, spawnJobData, worldChunk, worldSpawnData);
}
private static void preAddToWorld(@Nonnull NPCEntity npc, @Nonnull Holder<EntityStore> holder, int roleIndex, @Nonnull SpawnJobData spawnJobData) {
npc.setSpawnRoleIndex(roleIndex);
if (spawnJobData.isSpawnFrozen()) {
holder.ensureComponent(Frozen.getComponentType());
}
npc.setEnvironment(spawnJobData.getEnvironmentIndex());
npc.setSpawnConfiguration(spawnJobData.getSpawnConfigIndex());
}
private static boolean canSpawnOnBlock(@Nullable IntSet spawnBlockSet, int spawnFluidTag, @Nonnull SpawningContext spawningContext) {
if (spawnBlockSet == null && spawnFluidTag == -2147483648) {
return true;
} else if (spawnBlockSet != null && spawnBlockSet.contains(spawningContext.groundBlockId)) {
return true;
} else {
return spawnFluidTag != -2147483648 && Fluid.getAssetMap().getIndexesForTag(spawnFluidTag).contains(spawningContext.groundFluidId);
}
}
private static void rejectSpan(@Nonnull Object2IntMap<SpawnRejection> rejectionMap, SpawnRejection rejection) {
rejectionMap.mergeInt(rejection, 1, (IntBinaryOperator)(Integer::sum));
}
protected static Result endProbing(Result result, @Nonnull SpawnJobData spawnJobData, @Nonnull WorldChunk worldChunk, @Nonnull WorldSpawnData worldSpawnData) {
HytaleLogger.Api context = LOGGER.at(Level.FINEST);
if (context.isEnabled()) {
context.log("Term Spawnjob id=%s env=%s role=%s chunk=[%s/%s] tested=%s result=%s budgetUsed=%s", spawnJobData.getJobId(), spawnJobData.getEnvironment().getId(), getSpawnableName(spawnJobData.getRoleIndex()), worldChunk.getX(), worldChunk.getZ(), spawnJobData.getTotalColumnsTested(), result, spawnJobData.getTotalBudgetUsed());
}
worldSpawnData.untrackNPC(spawnJobData.getEnvironmentIndex(), spawnJobData.getRoleIndex(), spawnJobData.getFlockSize());
updateSpawnStats(worldSpawnData, spawnJobData, result);
worldSpawnData.adjustActiveSpawnJobs(-1, -spawnJobData.getFlockSize());
return result;
}
private static void updateSpawnStats(@Nonnull WorldSpawnData worldSpawnData, @Nonnull SpawnJobData spawnJobData, Result result) {
boolean success = result == WorldSpawnJobSystems.Result.SUCCESS;
WorldEnvironmentSpawnData worldEnvironmentSpawnStats = worldSpawnData.getWorldEnvironmentSpawnData(spawnJobData.getEnvironmentIndex());
worldEnvironmentSpawnStats.updateSpawnStats(spawnJobData.getRoleIndex(), spawnJobData.getSpansTried(), spawnJobData.getSpansSuccess(), spawnJobData.getTotalBudgetUsed(), spawnJobData.getRejectionMap(), success);
worldSpawnData.addCompletedSpawnJob(spawnJobData.getTotalBudgetUsed());
}
@Nullable
private static String getSpawnableName(int roleIndex) {
return NPCPlugin.get().getName(roleIndex);
}
static {
TICKING_QUERY = Query.<ChunkStore>and(Query.not(ChunkStore.REGISTRY.getNonTickingComponentType()), QUERY);
}
public static class EntityRemoved extends HolderSystem<ChunkStore> {
private final ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType;
private final ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType;
private final ComponentType<ChunkStore, WorldChunk> worldChunkComponentType;
public EntityRemoved(ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType, ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType) {
this.worldSpawnDataResourceType = worldSpawnDataResourceType;
this.spawnJobDataComponentType = spawnJobDataComponentType;
this.worldChunkComponentType = WorldChunk.getComponentType();
}
@Nonnull
public Query<ChunkStore> getQuery() {
return WorldSpawnJobSystems.TICKING_QUERY;
}
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
}
public void onEntityRemoved(@Nonnull Holder<ChunkStore> entityHolder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
SpawnJobData spawnJobData = (SpawnJobData)entityHolder.getComponent(this.spawnJobDataComponentType);
WorldChunk worldChunk = (WorldChunk)entityHolder.getComponent(this.worldChunkComponentType);
WorldSpawnData worldSpawnData = (WorldSpawnData)((ChunkStore)store.getExternalData()).getWorld().getEntityStore().getStore().getResource(this.worldSpawnDataResourceType);
WorldSpawnJobSystems.endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
entityHolder.removeComponent(this.spawnJobDataComponentType);
}
}
public static class TickingState extends RefChangeSystem<ChunkStore, NonTicking<ChunkStore>> {
private final ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType;
private final ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType;
private final ComponentType<ChunkStore, WorldChunk> worldChunkComponentType;
public TickingState(ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType, ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType) {
this.worldSpawnDataResourceType = worldSpawnDataResourceType;
this.spawnJobDataComponentType = spawnJobDataComponentType;
this.worldChunkComponentType = WorldChunk.getComponentType();
}
public Query<ChunkStore> getQuery() {
return WorldSpawnJobSystems.QUERY;
}
@Nonnull
public ComponentType<ChunkStore, NonTicking<ChunkStore>> componentType() {
return ChunkStore.REGISTRY.getNonTickingComponentType();
}
public void onComponentAdded(@Nonnull Ref<ChunkStore> ref, @Nonnull NonTicking<ChunkStore> component, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer) {
SpawnJobData spawnJobData = (SpawnJobData)store.getComponent(ref, this.spawnJobDataComponentType);
WorldChunk worldChunk = (WorldChunk)store.getComponent(ref, this.worldChunkComponentType);
WorldSpawnData worldSpawnData = (WorldSpawnData)((ChunkStore)store.getExternalData()).getWorld().getEntityStore().getStore().getResource(this.worldSpawnDataResourceType);
WorldSpawnJobSystems.endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
commandBuffer.removeComponent(ref, this.spawnJobDataComponentType);
}
public void onComponentSet(@Nonnull Ref<ChunkStore> ref, NonTicking<ChunkStore> oldComponent, @Nonnull NonTicking<ChunkStore> newComponent, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer) {
}
public void onComponentRemoved(@Nonnull Ref<ChunkStore> ref, @Nonnull NonTicking<ChunkStore> component, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer) {
}
}
public static class Ticking extends EntityTickingSystem<ChunkStore> {
private final ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType;
private final ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType;
private final ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType;
private final ComponentType<ChunkStore, WorldChunk> worldChunkComponentType;
private final ComponentType<ChunkStore, ChunkSpawnData> chunkSpawnDataComponentType;
private final ComponentType<ChunkStore, ChunkSpawnedNPCData> chunkSpawnedNPCDataComponentType;
public Ticking(ResourceType<EntityStore, WorldSpawnData> worldSpawnDataResourceType, ResourceType<EntityStore, SpawnSuppressionController> spawnSuppressionControllerResourceType, ComponentType<ChunkStore, SpawnJobData> spawnJobDataComponentType, ComponentType<ChunkStore, ChunkSpawnData> chunkSpawnDataComponentType, ComponentType<ChunkStore, ChunkSpawnedNPCData> chunkSpawnedNPCDataComponentType) {
this.worldSpawnDataResourceType = worldSpawnDataResourceType;
this.spawnSuppressionControllerResourceType = spawnSuppressionControllerResourceType;
this.spawnJobDataComponentType = spawnJobDataComponentType;
this.worldChunkComponentType = WorldChunk.getComponentType();
this.chunkSpawnDataComponentType = chunkSpawnDataComponentType;
this.chunkSpawnedNPCDataComponentType = chunkSpawnedNPCDataComponentType;
}
public Query<ChunkStore> getQuery() {
return WorldSpawnJobSystems.QUERY;
}
public boolean isParallel(int archetypeChunkSize, int taskCount) {
return false;
}
public void tick(float dt, int index, @Nonnull ArchetypeChunk<ChunkStore> archetypeChunk, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer) {
World world = ((ChunkStore)store.getExternalData()).getWorld();
Store<EntityStore> entityStore = world.getEntityStore().getStore();
SpawnJobData spawnJobData = (SpawnJobData)archetypeChunk.getComponent(index, this.spawnJobDataComponentType);
WorldSpawnData worldSpawnData = (WorldSpawnData)entityStore.getResource(this.worldSpawnDataResourceType);
WorldChunk worldChunk = (WorldChunk)archetypeChunk.getComponent(index, this.worldChunkComponentType);
if (!spawnJobData.isTerminated() && !((double)worldSpawnData.getActualNPCs() > worldSpawnData.getExpectedNPCs())) {
ChunkSpawnData chunkSpawnData = (ChunkSpawnData)archetypeChunk.getComponent(index, this.chunkSpawnDataComponentType);
ChunkSpawnedNPCData chunkSpawnedNPCData = (ChunkSpawnedNPCData)archetypeChunk.getComponent(index, this.chunkSpawnedNPCDataComponentType);
int environmentIndex = spawnJobData.getEnvironmentIndex();
double spawnedNPCs = chunkSpawnedNPCData.getEnvironmentSpawnCount(environmentIndex);
ChunkEnvironmentSpawnData chunkEnvironmentSpawnData = chunkSpawnData.getEnvironmentSpawnData(environmentIndex);
if (!chunkEnvironmentSpawnData.allRolesUnspawnable() && (spawnJobData.isIgnoreFullyPopulated() || !chunkEnvironmentSpawnData.isFullyPopulated(spawnedNPCs))) {
SpawnSuppressionController spawnSuppressionController = (SpawnSuppressionController)entityStore.getResource(this.spawnSuppressionControllerResourceType);
Result result = WorldSpawnJobSystems.run(spawnJobData, worldChunk, chunkEnvironmentSpawnData, worldSpawnData, spawnSuppressionController);
if (result == WorldSpawnJobSystems.Result.SUCCESS) {
chunkSpawnData.setLastSpawn(System.nanoTime());
}
if (result != WorldSpawnJobSystems.Result.TRY_AGAIN) {
commandBuffer.removeComponent(archetypeChunk.getReferenceTo(index), this.spawnJobDataComponentType);
}
} else {
WorldSpawnJobSystems.endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
commandBuffer.removeComponent(archetypeChunk.getReferenceTo(index), this.spawnJobDataComponentType);
}
} else {
WorldSpawnJobSystems.endProbing(WorldSpawnJobSystems.Result.FAILED, spawnJobData, worldChunk, worldSpawnData);
commandBuffer.removeComponent(archetypeChunk.getReferenceTo(index), this.spawnJobDataComponentType);
}
}
}
protected static enum Result {
SUCCESS,
FAILED,
TRY_AGAIN,
PERMANENT_FAILURE;
private Result() {
}
}
}