SpawnSpan class

Пакет: com.hypixel.hytale.server.spawning

Файл: com/hypixel/hytale/server/spawning/SpawningContext.java

Поля (4)

МодификаторыТипИмя
int bottom
int groundLevel
int top
int waterLevel

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.server.spawning;

class="kw">import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
class="kw">import com.hypixel.hytale.component.ComponentAccessor;
class="kw">import com.hypixel.hytale.component.Ref;
class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.math.random.RandomExtra;
class="kw">import com.hypixel.hytale.math.shape.Box;
class="kw">import com.hypixel.hytale.math.util.ChunkUtil;
class="kw">import com.hypixel.hytale.math.util.MathUtil;
class="kw">import com.hypixel.hytale.math.vector.Rotation3f;
class="kw">import com.hypixel.hytale.protocol.BlockMaterial;
class="kw">import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
class="kw">import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
class="kw">import com.hypixel.hytale.server.core.asset.type.model.config.Model;
class="kw">import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
class="kw">import com.hypixel.hytale.server.core.modules.collision.BoxBlockIntersectionEvaluator;
class="kw">import com.hypixel.hytale.server.core.modules.collision.CollisionFilter;
class="kw">import com.hypixel.hytale.server.core.modules.collision.CollisionModule;
class="kw">import com.hypixel.hytale.server.core.modules.collision.CollisionResult;
class="kw">import com.hypixel.hytale.server.core.modules.collision.WorldUtil;
class="kw">import com.hypixel.hytale.server.core.universe.world.World;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.ChunkFlag;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
class="kw">import com.hypixel.hytale.server.core.universe.world.chunk.environment.EnvironmentColumn;
class="kw">import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
class="kw">import com.hypixel.hytale.server.npc.movement.MovementMode;
class="kw">import com.hypixel.hytale.server.npc.util.NPCPhysicsMath;
class="kw">import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
class="kw">import com.hypixel.hytale.server.npc.util.expression.Scope;
class="kw">import com.hypixel.hytale.server.spawning.suppression.SuppressionSpanHelper;
class="kw">import it.unimi.dsi.fastutil.ints.IntSet;
class="kw">import java.util.Arrays;
class="kw">import java.util.EnumSet;
class="kw">import java.util.Map;
class="kw">import java.util.Set;
class="kw">import java.util.Map.Entry;
class="kw">import java.util.concurrent.ThreadLocalRandom;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;
class="kw">import org.joml.Vector3d;

class="kw">public class SpawningContext {
   @Nonnull
   class="kw">private class="kw">static class="kw">final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
   @Nonnull
   class="kw">private class="kw">static class="kw">final BlockTypeAssetMap<String, BlockType> BLOCK_ASSET_MAP = BlockType.getAssetMap();
   @Nullable
   class="kw">private World world;
   @Nullable
   class="kw">private Ref<ChunkStore> chunkRef;
   @Nullable
   class="kw">private ComponentAccessor<ChunkStore> chunkStore;
   class="kw">public int xBlock;
   class="kw">public int zBlock;
   class="kw">public double ySpawnHint;
   class="kw">public int groundLevel;
   class="kw">public int groundBlockId;
   class="kw">public int groundRotation;
   @Nullable
   class="kw">public BlockType groundBlockType;
   class="kw">public int groundFluidId;
   @Nullable
   class="kw">public Fluid groundFluid;
   @Nullable
   class="kw">public MovementMode movementMode;
   class="kw">public boolean enableSafeSpawning = true;
   @Nullable
   class="kw">public String activeMotionControllerType;
   class="kw">public int ySpanMin;
   class="kw">public int ySpanMax;
   class="kw">public int yBlock;
   class="kw">public int waterLevel;
   class="kw">public int airHeight;
   class="kw">public double ySpawnMin;
   class="kw">public double xSpawn;
   class="kw">public double zSpawn;
   class="kw">public double ySpawn;
   class="kw">private int environmentIndex = Integer.MIN_VALUE;
   class="kw">private int minSpawnSpanHeight = Integer.MAX_VALUE;
   @Nonnull
   class="kw">public class="kw">final Rotation3f rotation = new Rotation3f();
   class="kw">public boolean breathesInAir = true;
   class="kw">public boolean breathesInWater;
   class="kw">public int invalidMaterials = -1;
   class="kw">public double swimDepth = Double.NaN;
   @Nullable
   class="kw">private ISpawnableWithModel spawnable;
   @Nullable
   class="kw">private Model spawnModel;
   @Nullable
   class="kw">private Scope modifierScope;
   @Nullable
   class="kw">private EnumSet<MovementMode> spawnMovementModes;
   @Nonnull
   class="kw">private class="kw">final EnumSet<MovementMode> supportedMovementModes = EnumSet.noneOf(MovementMode.class);
   @Nonnull
   class="kw">private class="kw">final EnumSet<MovementMode> safeMovementModes = EnumSet.noneOf(MovementMode.class);
   @Nonnull
   class="kw">private class="kw">final CollisionResult collisionResult = new CollisionResult();
   @Nonnull
   class="kw">private class="kw">final Vector3d position = new Vector3d();
   @Nonnull
   class="kw">private class="kw">final ExecutionContext executionContext = new ExecutionContext();
   class="kw">private SpawningContext.SpawnSpan[] spawnSpans = new SpawningContext.SpawnSpan[4];
   class="kw">private int spawnSpansUsed;
   class="kw">private int currentSpawnSpanIndex;
   class="kw">private class="kw">static class="kw">final int SOLID_BLOCK = -1;
   class="kw">private class="kw">static class="kw">final int EMPTY_BLOCK = 0;
   class="kw">private class="kw">static class="kw">final int FLUID_BLOCK = 1;

   class="kw">public SpawningContext() {
      for (int var1 = 0; var1 < this.spawnSpans.length; var1++) {
         this.spawnSpans[var1] = new SpawningContext.SpawnSpan();
      }

      this.spawnSpansUsed = 0;
   }

   class="kw">public boolean setSpawnable(@Nonnull ISpawnableWithModel var1) {
      class="kw">return this.setSpawnable(var1, false);
   }

   class="kw">public boolean setSpawnable(@Nonnull ISpawnableWithModel var1, boolean var2) {
      if (var1 == this.spawnable) {
         class="kw">return true;
      }

      this.spawnable = var1;

      String var3;
      try {
         this.executionContext.setScope(var1.createExecutionScope());
         this.modifierScope = this.spawnable.createModifierScope(this.executionContext);
         this.breathesInAir = var1.breathesInAir(this.executionContext, this.modifierScope);
         this.breathesInWater = var1.breathesInWater(this.executionContext, this.modifierScope);
         this.movementMode = null;
         this.invalidMaterials = -1;
         this.swimDepth = Double.NaN;
         this.spawnMovementModes = null;
         var3 = var1.getSpawnModelName(this.executionContext, this.modifierScope);
      } catch (Throwable var5) {
         LOGGER.at(Level.WARNING).log("Can't set role in spawning context %s: %s", var1.getIdentifier(), var5.getMessage());
         var1.markNeedsReload();
         this.spawnable = null;
         class="kw">return false;
      }

      if (!this.setModel(var3, var2)) {
         LOGGER.at(Level.WARNING).log("Can't set model in spawning context %s: %s", var1.getIdentifier(), var3);
         var1.markNeedsReload();
         this.spawnable = null;
         class="kw">return false;
      } else {
         class="kw">return true;
      }
   }

   class="kw">private boolean setModel(@Nullable String var1, boolean var2) {
      if (var1 == null) {
         this.clearModel();
         class="kw">return false;
      }

      String var3 = this.spawnModel != null ? this.spawnModel.getModelAssetId() : null;
      if (var1.equals(var3)) {
         class="kw">return true;
      }

      ModelAsset var4 = ModelAsset.getAssetMap().getAsset(var1);
      Model var5 = null;
      if (var4 != null) {
         var5 = var2 ? Model.createScaledModel(var4, var4.getMaxScale()) : Model.createRandomScaleModel(var4);
      }

      if (var5 != null && var5.getBoundingBox() != null) {
         this.spawnModel = var5;
         this.minSpawnSpanHeight = MathUtil.ceil(var5.getBoundingBox().height() + 0.2F);
         class="kw">return true;
      } else {
         this.clearModel();
         class="kw">return false;
      }
   }

   class="kw">private void clearModel() {
      this.spawnModel = null;
      this.minSpawnSpanHeight = Integer.MAX_VALUE;
   }

   class="kw">public void newModel() {
      if (this.spawnModel != null) {
         ModelAsset var1 = ModelAsset.getAssetMap().getAsset(this.spawnModel.getModelAssetId());
         if (var1 != null) {
            this.spawnModel = Model.createRandomScaleModel(var1);
         }
      }
   }

   @Nullable
   class="kw">public Model getModel() {
      class="kw">return this.spawnModel;
   }

   @Nonnull
   class="kw">public Set<MovementMode> getSpawnMovementModes() {
      if (this.spawnMovementModes != null) {
         class="kw">return this.spawnMovementModes;
      }

      if (this.spawnable == null) {
         throw new IllegalStateException("SpawningContext not initialised");
      }

      this.supportedMovementModes.clear();
      this.safeMovementModes.clear();
      EnumSet var1 = EnumSet.noneOf(MovementMode.class);
      this.spawnable.getMovementModes(this, this.supportedMovementModes, var1, this.safeMovementModes);
      var1.retainAll(this.safeMovementModes);
      if (!var1.isEmpty()) {
         this.spawnMovementModes = var1;
      }

      class="kw">return var1;
   }

   class="kw">public boolean setMovementMode(MovementMode var1, boolean var2) {
      if (this.spawnable == null) {
         class="kw">return false;
      }

      this.movementMode = var1;
      this.enableSafeSpawning = var2;
      if (!var2) {
         class="kw">return true;
      }

      class="kw">return class="kw">switch (var1) {
         case WALK, WADE, FLY -> this.breathesInAir;
         case UNDERWATER_WALK, DIVE -> this.breathesInWater;
      };
   }

   class="kw">public void setChunk(@Nonnull World var1, @Nonnull Ref<ChunkStore> var2, int var3) {
      this.world = var1;
      this.chunkRef = var2;
      this.chunkStore = var1.getChunkStore().getStore();
      this.environmentIndex = var3;
      this.commonInit();
   }

   class="kw">public boolean setColumn(int var1, int var2, int var3, @Nonnull int[] var4) {
      if (this.chunkRef != null && this.chunkStore != null) {
         this.xBlock = var1;
         this.zBlock = var2;
         this.ySpawnHint = -1.0;
         this.spawnSpansUsed = 0;
         int var5 = Math.max(0, var3 + var4[0]);
         int var6 = Math.min(319, var3 + var4[1]);
         this.splitRangeToSpawnSpans(var5, var6);
         class="kw">return this.spawnSpansUsed > 0;
      } else {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }
   }

   class="kw">public boolean setColumn(int var1, int var2, int var3, @Nonnull int[] var4, @Nonnull SuppressionSpanHelper var5) {
      if (this.chunkRef != null && this.chunkStore != null) {
         this.xBlock = var1;
         this.zBlock = var2;
         this.ySpawnHint = -1.0;
         this.spawnSpansUsed = 0;
         int var6 = Math.max(0, var3 + var4[0]);
         int var7 = Math.min(319, var3 + var4[1]);

         while (var6 <= var7) {
            int var8 = var5.adjustSpawnRangeMin(var6);
            if (var8 >= var7) {
               break;
            }

            int var9 = Math.min(var5.adjustSpawnRangeMax(var8, var7), var7);
            var6 = var9 + 1;
            this.splitRangeToSpawnSpans(var8, var9);
         }

         class="kw">return this.spawnSpansUsed > 0;
      } else {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }
   }

   class="kw">public void setColumn(int var1, int var2, @Nonnull SuppressionSpanHelper var3) {
      if (this.chunkRef != null && this.chunkStore != null) {
         this.xBlock = var1;
         this.zBlock = var2;
         this.ySpawnHint = -1.0;
         this.spawnSpansUsed = 0;
         BlockChunk var4 = this.chunkStore.getComponent(this.chunkRef, BlockChunk.getComponentType());
         if (var4 != null) {
            EnvironmentColumn var5 = var4.getEnvironmentColumn(this.xBlock, this.zBlock);

            for (int var6 = var5.indexOf(0); var6 < var5.size(); var6++) {
               int var7 = var5.getValue(var6);
               if (var7 == this.environmentIndex) {
                  int var8 = Math.max(0, var5.getValueMin(var6));
                  if (var8 > 320) {
                     break;
                  }

                  int var9 = var3.adjustSpawnRangeMin(var8);
                  int var10 = var5.getValueMax(var6);
                  if (var9 > var10) {
                     var6 = var5.indexOf(var9) - 1;
                  } else {
                     int var11 = var3.adjustSpawnRangeMax(var9, var10);
                     this.splitRangeToSpawnSpans(var9, Math.min(var11, 319));
                  }
               }
            }
         }
      } else {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }
   }

   @Nullable
   class="kw">public Scope getModifierScope() {
      class="kw">return this.modifierScope;
   }

   @Nullable
   class="kw">public World getWorld() {
      class="kw">return this.world;
   }

   @Nullable
   class="kw">public Ref<ChunkStore> getChunkRef() {
      class="kw">return this.chunkRef;
   }

   @Nullable
   class="kw">public Ref<ChunkStore> getSectionRefAtBlockY(int var1) {
      if (this.world != null && this.chunkRef != null && this.chunkStore != null) {
         BlockChunk var2 = this.chunkStore.getComponent(this.chunkRef, BlockChunk.getComponentType());
         if (var2 == null) {
            class="kw">return null;
         }

         Ref var3 = this.world.getChunkStore().getChunkSectionReference(var2.getX(), ChunkUtil.chunkCoordinate(var1), var2.getZ());
         class="kw">return var3 != null && var3.isValid() ? var3 : null;
      } else {
         class="kw">return null;
      }
   }

   @Nullable
   class="kw">public ComponentAccessor<ChunkStore> getChunkStore() {
      class="kw">return this.chunkStore;
   }

   class="kw">public boolean set(@Nonnull World var1, double var2, double var4, double var6) {
      if (this.minSpawnSpanHeight == Integer.MAX_VALUE) {
         throw new IllegalStateException("minSpawnSpanHeight not set - forgot to set model or role?");
      }

      this.xBlock = MathUtil.floor(var2);
      this.zBlock = MathUtil.floor(var6);
      this.ySpawnHint = var4;
      ChunkStore var8 = var1.getChunkStore();
      Ref var9 = resolveTickingChunk(var8, this.xBlock, this.zBlock);
      if (var9 != null && isTickingSection(var8, this.xBlock, MathUtil.floor(var4), this.zBlock)) {
         this.xBlock = ChunkUtil.localCoordinate(this.xBlock);
         this.zBlock = ChunkUtil.localCoordinate(this.zBlock);
         this.world = var1;
         this.chunkRef = var9;
         this.chunkStore = var8.getStore();
         this.environmentIndex = Integer.MIN_VALUE;
         this.commonInit();
         int var10 = MathUtil.floor(var4);
         this.spawnSpansUsed = 0;
         BlockChunk var11 = this.chunkStore.getComponent(var9, BlockChunk.getComponentType());
         if (var11 == null) {
            class="kw">return false;
         }

         EnvironmentColumn var12 = var11.getEnvironmentColumn(this.xBlock, this.zBlock);
         int var13 = Math.max(0, var12.getMin(var10));
         int var14 = Math.min(var12.getMax(var10), 319);
         this.splitRangeToSpawnSpans(var13, var14);
         if (this.spawnSpansUsed == 0) {
            class="kw">return false;
         }

         int var15 = Integer.MAX_VALUE;
         int var16 = -1;

         for (int var17 = 0; var17 < this.spawnSpansUsed; var17++) {
            SpawningContext.SpawnSpan var19 = this.spawnSpans[var17];
            int var18;
            if (var19.top < var10) {
               var18 = var10 - var19.top;
            } else {
               if (var19.bottom <= var10) {
                  var16 = var17;
                  break;
               }

               var18 = var19.bottom - var10;
            }

            if (var18 < var15) {
               var16 = var17;
               var15 = var18;
            }
         }

         class="kw">return this.selectSpawnSpan(var16);
      } else {
         class="kw">return false;
      }
   }

   class="kw">public boolean setExact(@Nonnull World var1, double var2, double var4, double var6) {
      if (this.spawnModel == null) {
         throw new IllegalStateException("spawnModel not set - forgot to set model or role?");
      } else {
         this.xBlock = MathUtil.floor(var2);
         this.zBlock = MathUtil.floor(var6);
         this.ySpawnHint = var4;
         ChunkStore var8 = var1.getChunkStore();
         Ref var9 = resolveTickingChunk(var8, this.xBlock, this.zBlock);
         if (var9 != null && isTickingSection(var8, this.xBlock, MathUtil.floor(var4), this.zBlock)) {
            this.xBlock = ChunkUtil.localCoordinate(this.xBlock);
            this.zBlock = ChunkUtil.localCoordinate(this.zBlock);
            this.world = var1;
            this.chunkRef = var9;
            this.chunkStore = var8.getStore();
            this.environmentIndex = Integer.MIN_VALUE;
            this.commonInit();
            this.xSpawn = var2;
            this.ySpawn = var4;
            this.zSpawn = var6;
            class="kw">return true;
         } else {
            class="kw">return false;
         }
      }
   }

   class="kw">public void deleteCurrentSpawnSpan() {
      if (--this.spawnSpansUsed > this.currentSpawnSpanIndex) {
         SpawningContext.SpawnSpan var1 = this.spawnSpans[this.currentSpawnSpanIndex];
         System.arraycopy(
            this.spawnSpans, this.currentSpawnSpanIndex + 1, this.spawnSpans, this.currentSpawnSpanIndex, this.spawnSpansUsed - this.currentSpawnSpanIndex
         );
         this.spawnSpans[this.spawnSpansUsed] = var1;
      }
   }

   class="kw">public boolean selectRandomSpawnSpan() {
      class="kw">return this.spawnSpansUsed > 0 && this.selectSpawnSpan(ThreadLocalRandom.current().nextInt(0, this.spawnSpansUsed));
   }

   class="kw">private boolean selectSpawnSpan(int var1) {
      if (this.chunkRef == null || this.chunkStore == null) {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }

      if (var1 >= 0 && var1 < this.spawnSpansUsed) {
         this.currentSpawnSpanIndex = var1;
         SpawningContext.SpawnSpan var2 = this.spawnSpans[this.currentSpawnSpanIndex];
         this.ySpanMin = var2.bottom;
         this.ySpanMax = var2.top;
         this.waterLevel = var2.waterLevel;
         this.groundLevel = var2.groundLevel;
         BlockChunk var3 = this.chunkStore.getComponent(this.chunkRef, BlockChunk.getComponentType());
         ChunkColumn var4 = this.chunkStore.getComponent(this.chunkRef, ChunkColumn.getComponentType());
         if (var3 != null && var4 != null) {
            if (this.waterLevel != -1) {
               this.airHeight = -1;
               if (this.waterLevel < 319) {
                  int var5 = var3.getBlock(this.xBlock, this.waterLevel + 1, this.zBlock);
                  int var6 = WorldUtil.getFluidIdAtPosition(this.chunkStore, var4, this.xBlock, this.waterLevel + 1, this.zBlock);
                  if (var5 == 0 && var6 == 0 || BLOCK_ASSET_MAP.getAsset(var5).getMaterial() == BlockMaterial.Empty && var6 == 0) {
                     this.airHeight = this.waterLevel + 1;
                  }
               }
            } else {
               this.airHeight = this.groundLevel + 1;
            }

            this.yBlock = this.groundLevel;
            this.groundBlockId = var3.getBlock(this.xBlock, this.groundLevel, this.zBlock);
            this.groundRotation = var3.getSectionAtBlockY(this.groundLevel).getRotationIndex(this.xBlock, this.groundLevel, this.zBlock);
            this.groundBlockType = BLOCK_ASSET_MAP.getAsset(this.groundBlockId);
            this.groundFluidId = WorldUtil.getFluidIdAtPosition(this.chunkStore, var4, this.xBlock, this.groundLevel, this.zBlock);
            this.groundFluid = Fluid.getAssetMap().getAsset(this.groundFluidId);
            this.ySpawnMin = this.yBlock + NPCPhysicsMath.blockHeight(this.groundBlockType, this.groundRotation);
            this.xSpawn = ChunkUtil.minBlock(var3.getX()) + this.xBlock + 0.5;
            this.zSpawn = ChunkUtil.minBlock(var3.getZ()) + this.zBlock + 0.5;
            this.ySpawn = this.ySpawnMin - this.spawnModel.getBoundingBox().min.y;
            class="kw">return true;
         } else {
            class="kw">return false;
         }
      } else {
         class="kw">return false;
      }
   }

   class="kw">private void splitRangeToSpawnSpans(int var1, int var2) {
      if (this.chunkRef != null && this.chunkStore != null) {
         BlockChunk var3 = this.chunkStore.getComponent(this.chunkRef, BlockChunk.getComponentType());
         ChunkColumn var4 = this.chunkStore.getComponent(this.chunkRef, ChunkColumn.getComponentType());
         if (var3 != null && var4 != null) {
            int var6 = 0;
            int var7 = -1;
            int var8 = -1;

            while (var1 <= var2) {
               int var5 = isSpawnSpanBlock(this.xBlock, var1, this.zBlock, this.chunkStore, var3, var4);
               if (var5 != -1) {
                  if (var5 == 1) {
                     var7 = var1;
                  }

                  var6++;
               } else {
                  if (var6 > this.minSpawnSpanHeight) {
                     this.addSpawnSpan(var1, var6, var8, var7, this.chunkStore, var3, var4);
                  }

                  var6 = 0;
                  var7 = -1;
                  var8 = var1;
               }

               var1++;
            }

            if (var6 > this.minSpawnSpanHeight) {
               this.addSpawnSpan(var1, var6, var8, var7, this.chunkStore, var3, var4);
            }
         }
      } else {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }
   }

   class="kw">private void addSpawnSpan(
      int var1, int var2, int var3, int var4, @Nonnull ComponentAccessor<ChunkStore> var5, @Nonnull BlockChunk var6, @Nonnull ChunkColumn var7
   ) {
      if (var3 == -1) {
         var3 = var1 - var2;

         for (int var8 = isSpawnSpanBlock(this.xBlock, var3, this.zBlock, var5, var6, var7);
            var8 != -1;
            var8 = isSpawnSpanBlock(this.xBlock, --var3, this.zBlock, var5, var6, var7)
         ) {
            if (var4 == -1 && var8 == 1) {
               var4 = var3;
            }

            if (var3 <= 0) {
               class="kw">return;
            }
         }
      }

      if (var4 == var1 - 1) {
         while (var4 < 319 && isSpawnSpanBlock(this.xBlock, var4 + 1, this.zBlock, var5, var6, var7) == 1) {
            var4++;
         }
      }

      if (this.spawnSpans.length <= this.spawnSpansUsed) {
         SpawningContext.SpawnSpan[] var10 = new SpawningContext.SpawnSpan[this.spawnSpansUsed + 4];
         System.arraycopy(this.spawnSpans, 0, var10, 0, this.spawnSpansUsed);

         for (int var9 = this.spawnSpansUsed; var9 < var10.length; var9++) {
            var10[var9] = new SpawningContext.SpawnSpan();
         }

         this.spawnSpans = var10;
      }

      SpawningContext.SpawnSpan var11 = this.spawnSpans[this.spawnSpansUsed++];
      var11.bottom = var1 - var2;
      var11.top = var1 - 1;
      var11.waterLevel = var4;
      var11.groundLevel = var3;
   }

   class="kw">private class="kw">static int isSpawnSpanBlock(
      int var0, int var1, int var2, @Nonnull ComponentAccessor<ChunkStore> var3, @Nonnull BlockChunk var4, @Nonnull ChunkColumn var5
   ) {
      int var6 = var4.getBlock(var0, var1, var2);
      if (var6 != 0 && BLOCK_ASSET_MAP.getAsset(var6).getMaterial() != BlockMaterial.Empty) {
         class="kw">return -1;
      } else {
         class="kw">return WorldUtil.getFluidIdAtPosition(var3, var5, var0, var1, var2) == 0 ? 0 : 1;
      }
   }

   class="kw">private void commonInit() {
      this.rotation.set(0.0F, RandomExtra.randomRange(0.0F, (float) (Math.PI * 2)), 0.0F);
   }

   @Nullable
   class="kw">private class="kw">static Ref<ChunkStore> resolveTickingChunk(@Nonnull ChunkStore var0, int var1, int var2) {
      long var3 = ChunkUtil.indexChunkFromBlock(var1, var2);
      Ref var5 = var0.getChunkReference(var3);
      if (var5 != null && var5.isValid()) {
         WorldChunk var6 = var0.getStore().getComponent(var5, WorldChunk.getComponentType());
         class="kw">return var6 != null && var6.is(ChunkFlag.TICKING) ? var5 : null;
      } else {
         class="kw">return null;
      }
   }

   class="kw">private class="kw">static boolean isTickingSection(@Nonnull ChunkStore var0, int var1, int var2, int var3) {
      Ref var4 = var0.getChunkSectionReferenceAtBlock(var1, var2, var3);
      class="kw">return var4 != null && var4.isValid() ? !var0.getStore().getArchetype(var4).contains(ChunkStore.REGISTRY.getNonTickingComponentType()) : false;
   }

   @Nonnull
   class="kw">public SpawnTestResult canSpawn(boolean var1, boolean var2) {
      SpawnTestResult var3 = SpawnTestResult.TEST_OK;
      if (var1) {
         var3 = this.intersectsBlock();
         if (var3 != SpawnTestResult.TEST_OK) {
            class="kw">return var3;
         }
      }

      if (var2) {
         var3 = intersectsEntity();
      }

      class="kw">return var3;
   }

   @Nonnull
   class="kw">public SpawnTestResult canSpawn() {
      class="kw">return this.canSpawn(true, true);
   }

   class="kw">public boolean canSpawnOnBlock(@Nullable IntSet var1, int var2) {
      if (this.chunkRef == null || this.chunkStore == null) {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }

      if (var1 != null && !var1.contains(this.groundBlockId)) {
         class="kw">return false;
      }

      if (this.waterLevel != -1 && var2 != Integer.MIN_VALUE) {
         ChunkColumn var3 = this.chunkStore.getComponent(this.chunkRef, ChunkColumn.getComponentType());
         if (var3 == null) {
            class="kw">return false;
         }

         int var4 = WorldUtil.getFluidIdAtPosition(this.chunkStore, var3, this.xBlock, this.waterLevel, this.zBlock);
         class="kw">return Fluid.getAssetMap().getIndexesForTag(var2).contains(var4);
      } else {
         class="kw">return true;
      }
   }

   @Nonnull
   class="kw">private class="kw">static SpawnTestResult intersectsEntity() {
      class="kw">return SpawnTestResult.TEST_OK;
   }

   @Nonnull
   class="kw">private SpawnTestResult intersectsBlock() {
      if (this.spawnModel != null && this.spawnable != null) {
         class="kw">return this.spawnable.canSpawn(this);
      } else {
         throw new IllegalStateException("SpawningContext not initialised");
      }
   }

   class="kw">public class="kw">static boolean isWaterBlock(int var0) {
      class="kw">return var0 != 0;
   }

   class="kw">public int getWaterLevel() {
      class="kw">return this.waterLevel;
   }

   class="kw">public int getAirHeight() {
      class="kw">return this.airHeight;
   }

   class="kw">public boolean isInsideSpan(double var1) {
      class="kw">return var1 >= this.ySpanMin && var1 < this.ySpanMax + 1;
   }

   class="kw">public boolean isInWater(float var1) {
      if (this.spawnModel != null && this.waterLevel >= 0) {
         int var2 = this.waterLevel - this.groundLevel - 1;
         int var3 = Math.max(0, MathUtil.fastCeil(var1));
         if (var2 < var3) {
            class="kw">return false;
         }

         double var4 = this.waterLevel - var3;
         if (!this.isInsideSpan(var4)) {
            class="kw">return false;
         }

         this.ySpawn = var4 - this.spawnModel.getBoundingBox().min.y;
         class="kw">return true;
      } else {
         class="kw">return false;
      }
   }

   class="kw">public boolean isOnSolidGround() {
      if (this.chunkRef == null || this.chunkStore == null) {
         throw new IllegalStateException("SpawningContext not bound to a chunk");
      }

      if (this.waterLevel == -1 && isWaterBlock(this.groundFluidId)) {
         class="kw">return false;
      }

      this.ySpawn = this.ySpawnMin - this.spawnModel.getBoundingBox().min.y;
      int var1 = MathUtil.floor(this.ySpawnMin);
      if (var1 != this.yBlock) {
         BlockChunk var2 = this.chunkStore.getComponent(this.chunkRef, BlockChunk.getComponentType());
         ChunkColumn var3 = this.chunkStore.getComponent(this.chunkRef, ChunkColumn.getComponentType());
         if (var2 != null && var3 != null) {
            BlockType var4 = BLOCK_ASSET_MAP.getAsset(var2.getBlock(this.xBlock, var1, this.zBlock));
            int var5 = WorldUtil.getFluidIdAtPosition(this.chunkStore, var3, this.xBlock, var1, this.zBlock);
            int var6 = var2.getSectionAtBlockY(var1).getRotationIndex(this.xBlock, var1, this.zBlock);
            if (WorldUtil.isEmptyOnlyBlock(var4, var5) || var5 != 0) {
               class="kw">return this.isInsideSpan(this.ySpawn);
            } else if (WorldUtil.isSolidOnlyBlock(var4, var5)) {
               this.ySpawn = var1 + NPCPhysicsMath.blockHeight(var4, var6) - this.spawnModel.getBoundingBox().min.y;
               class="kw">return this.isInsideSpan(this.ySpawn);
            } else {
               class="kw">return false;
            }
         } else {
            class="kw">return false;
         }
      } else {
         class="kw">return this.ySpawn >= this.ySpanMin - 1 && this.ySpawn < this.ySpanMax + 1;
      }
   }

   class="kw">public boolean isInAir(double var1) {
      if (this.spawnModel != null && this.airHeight >= 0) {
         Box var3 = this.spawnModel.getBoundingBox();
         double var4 = this.airHeight - var3.min.y;
         double var6 = this.ySpanMax + 1 - var3.max.y;
         if (var6 < var4) {
            class="kw">return false;
         }

         double var8 = Math.min(var4 + Math.max(0.0, var1), var6);
         if (!this.isInsideSpan(var8)) {
            class="kw">return false;
         }

         this.ySpawn = var8;
         class="kw">return true;
      } else {
         class="kw">return false;
      }
   }

   class="kw">public boolean validatePosition(int var1) {
      class="kw">return this.validatePosition(var1, false);
   }

   class="kw">public boolean validatePosition(int var1, boolean var2) {
      if (this.world == null) {
         throw new IllegalStateException("SpawningContext not bound to a world");
      }

      if (this.spawnModel == null) {
         class="kw">return false;
      }

      this.position.set(this.xSpawn, this.ySpawn, this.zSpawn);
      CollisionFilter var3 = var2
         ? (var0, var1x, var2x, var3x) -> var3x.blockId != -1 && var3x.blockType != null && var3x.blockType.getHitboxTypeIndex() == 0
         : (var0, var1x, var2x, var3x) -> var3x.blockId != -1;
      class="kw">return CollisionModule.get().validatePosition(this.world, this.spawnModel.getBoundingBox(), this.position, var1, null, var3, this.collisionResult) != -1;
   }

   class="kw">public boolean canBreathe(boolean var1, boolean var2) {
      if (this.spawnModel == null) {
         class="kw">return false;
      }

      if (var1 && var2) {
         class="kw">return true;
      }

      if (!var1 && !var2) {
         class="kw">return false;
      }

      float var3 = this.spawnModel.getEyeHeight();
      class="kw">return var1 ? this.waterLevel + 1 - this.ySpawn < var3 : this.ySpawn + var3 < this.airHeight;
   }

   class="kw">public void release() {
      this.world = null;
      this.chunkRef = null;
      this.chunkStore = null;
      this.groundBlockType = null;
   }

   class="kw">public void releaseFull() {
      this.release();
      this.spawnable = null;
      this.movementMode = null;
      this.enableSafeSpawning = true;
      this.activeMotionControllerType = null;
      this.breathesInAir = true;
      this.breathesInWater = false;
      this.invalidMaterials = -1;
      this.swimDepth = Double.NaN;
      this.modifierScope = null;
      this.spawnMovementModes = null;
      this.spawnModel = null;
      this.executionContext.setScope(null);
   }

   @Nullable
   class="kw">public class="kw">static double[] buildMovementModeWeights(
      @Nullable Map<MovementMode, Double> var0, @Nonnull Set<MovementMode> var1, @Nonnull Set<MovementMode> var2, @Nullable Set<MovementMode> var3
   ) {
      double[] var4 = new double[MovementMode.values().length];
      class="kw">return fillMovementModeWeights(var4, var0, var1, var2, var3) ? var4 : null;
   }

   class="kw">private class="kw">static boolean fillMovementModeWeights(
      @Nonnull double[] var0,
      @Nullable Map<MovementMode, Double> var1,
      @Nonnull Set<MovementMode> var2,
      @Nonnull Set<MovementMode> var3,
      @Nullable Set<MovementMode> var4
   ) {
      assert var0.length == MovementMode.values().length : "weights array length must match number of MovementMode values";
      Arrays.fill(var0, 0.0);
      if (var1 != null && !var1.isEmpty()) {
         for (Entry var14 : var1.entrySet()) {
            var0[var14.getKey().ordinal()] = var14.getValue();
         }
      } else {
         for (MovementMode var6 : var3) {
            var0[var6.ordinal()] = 1.0;
         }
      }

      for (MovementMode var8 : MovementMode.values()) {
         if (!var2.contains(var8)) {
            var0[var8.ordinal()] = 0.0;
         }
      }

      if (var4 != null) {
         for (MovementMode var20 : MovementMode.values()) {
            if (!var4.contains(var20)) {
               var0[var20.ordinal()] = 0.0;
            }
         }
      }

      for (double var21 : var0) {
         if (var21 != 0.0) {
            class="kw">return true;
         }
      }

      class="kw">return false;
   }

   @Nonnull
   class="kw">public ExecutionContext getExecutionContext() {
      class="kw">return this.executionContext;
   }

   @Nonnull
   class="kw">public Vector3d newPosition() {
      class="kw">return new Vector3d(this.xSpawn, this.ySpawn, this.zSpawn);
   }

   @Nonnull
   class="kw">public Rotation3f newRotation() {
      class="kw">return new Rotation3f(this.rotation);
   }

   @Nonnull
   @Override
   class="kw">public String toString() {
      class="kw">return "SpawningContext{xBlock="
         + this.xBlock
         + ", zBlock="
         + this.zBlock
         + ", yBlock="
         + this.yBlock
         + ", ySpawnMin="
         + this.ySpawnMin
         + ", xSpawn="
         + this.xSpawn
         + ", zSpawn="
         + this.zSpawn
         + ", ySpawn="
         + this.ySpawn
         + ", rotation="
         + this.rotation
         + ", groundBlockId="
         + this.groundBlockId
         + "}";
   }

   class="kw">private class="kw">static class SpawnSpan {
      int bottom;
      int top;
      int waterLevel;
      int groundLevel;

      class="kw">private SpawnSpan() {
      }
   }
}