Improved mob spawn mechanics for PSPE w/ misc cleanup
This commit is contained in:
@@ -40,8 +40,8 @@ public class AkarinGlobalConfig {
|
||||
config.options().header(HEADER);
|
||||
config.options().copyDefaults(true);
|
||||
|
||||
version = getInt("config-version", 2);
|
||||
set("config-version", 2);
|
||||
version = getInt("config-version", 3);
|
||||
set("config-version", 3);
|
||||
readConfig(AkarinGlobalConfig.class, null);
|
||||
}
|
||||
|
||||
@@ -178,7 +178,11 @@ public class AkarinGlobalConfig {
|
||||
|
||||
public static double blockbreakAnimationVisibleDistance = 1024;
|
||||
private static void blockbreakAnimationVisibleDistance() {
|
||||
blockbreakAnimationVisibleDistance = Math.sqrt(getDouble("alternative.block-break-animation-visible-distance", 32.00));
|
||||
double def = 32.00;
|
||||
if (version == 2)
|
||||
def = getDouble("alternative.block-break-animation-visible-distance", def);
|
||||
|
||||
blockbreakAnimationVisibleDistance = Math.sqrt(getDouble("core.block-break-animation-visible-distance", def));
|
||||
}
|
||||
|
||||
public static boolean enableAsyncLighting = true;
|
||||
@@ -205,4 +209,9 @@ public class AkarinGlobalConfig {
|
||||
private static void ignoreRayTraceForSeatableBlocks() {
|
||||
ignoreRayTraceForSeatableBlocks = getBoolean("alternative.ignore-ray-trace-for-seatable-blocks", ignoreRayTraceForSeatableBlocks);
|
||||
}
|
||||
|
||||
public static boolean improvedMobSpawnMechanics = false;
|
||||
private static void improvedMobSpawnMechanics() {
|
||||
improvedMobSpawnMechanics = getBoolean("core.improved-mob-spawn-mechanics.enable", improvedMobSpawnMechanics);
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.NetworkManager;
|
||||
import net.minecraft.server.PacketPlayOutPlayerInfo;
|
||||
import net.minecraft.server.PacketPlayOutUpdateTime;
|
||||
import net.minecraft.server.World;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
public class AkarinAsyncScheduler extends Thread {
|
||||
|
||||
252
src/main/java/io/akarin/server/core/AkarinCreatureSpanwner.java
Normal file
252
src/main/java/io/akarin/server/core/AkarinCreatureSpanwner.java
Normal file
@@ -0,0 +1,252 @@
|
||||
package io.akarin.server.core;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent;
|
||||
import com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent;
|
||||
import com.destroystokyo.paper.exception.ServerInternalException;
|
||||
import com.koloboke.collect.set.hash.HashObjSets;
|
||||
|
||||
import io.akarin.server.misc.ChunkCoordOrdinalInt3Tuple;
|
||||
import net.minecraft.server.BiomeBase;
|
||||
import net.minecraft.server.BlockPosition;
|
||||
import net.minecraft.server.Chunk;
|
||||
import net.minecraft.server.ChunkCoordIntPair;
|
||||
import net.minecraft.server.EntityHuman;
|
||||
import net.minecraft.server.EntityInsentient;
|
||||
import net.minecraft.server.EntityPositionTypes;
|
||||
import net.minecraft.server.EntityTypes;
|
||||
import net.minecraft.server.EnumCreatureType;
|
||||
import net.minecraft.server.GroupDataEntity;
|
||||
import net.minecraft.server.MCUtil;
|
||||
import net.minecraft.server.MathHelper;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerChunk;
|
||||
import net.minecraft.server.SpawnerCreature;
|
||||
import net.minecraft.server.WorldServer;
|
||||
|
||||
/*
|
||||
* Reference on spawning mechanics by Colin Godsey <crgodsey@gmail.com>
|
||||
* https://github.com/yesdog/Paper/blob/0de3dd84b7e6688feb42af4fe6b4f323ce7e3013/Spigot-Server-Patches/0433-alternate-mob-spawning-mechanic.patch
|
||||
*/
|
||||
public class AkarinCreatureSpanwner {
|
||||
private static int getSpawnRange(WorldServer world, EntityHuman player) {
|
||||
byte mobSpawnRange = world.spigotConfig.mobSpawnRange;
|
||||
|
||||
mobSpawnRange = (mobSpawnRange > world.spigotConfig.viewDistance) ? (byte) world.spigotConfig.viewDistance : mobSpawnRange;
|
||||
mobSpawnRange = (mobSpawnRange > 8) ? 8 : mobSpawnRange;
|
||||
|
||||
if (PlayerNaturallySpawnCreaturesEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
PlayerNaturallySpawnCreaturesEvent event = new PlayerNaturallySpawnCreaturesEvent((Player) player.getBukkitEntity(), mobSpawnRange);
|
||||
new RuntimeException("Warning, one or more plugins is listening PlayerNaturallySpawnCreaturesEvent which is running asynchronously, this will may cause safe issue!").printStackTrace();
|
||||
synchronized (PlayerNaturallySpawnCreaturesEvent.class) {
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
return event.isCancelled() ? 0 : event.getSpawnRadius();
|
||||
}
|
||||
|
||||
return mobSpawnRange;
|
||||
}
|
||||
|
||||
private static int getCreatureLimit(WorldServer world, EnumCreatureType type) {
|
||||
switch (type) {
|
||||
case MONSTER:
|
||||
return world.getWorld().getMonsterSpawnLimit();
|
||||
case CREATURE:
|
||||
return world.getWorld().getAnimalSpawnLimit();
|
||||
case WATER_CREATURE:
|
||||
return world.getWorld().getWaterAnimalSpawnLimit();
|
||||
case AMBIENT:
|
||||
return world.getWorld().getAmbientSpawnLimit();
|
||||
}
|
||||
return type.spawnLimit();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static EntityInsentient createMob(WorldServer world, EnumCreatureType type, BlockPosition pos, BiomeBase.BiomeMeta biomeMeta) {
|
||||
if (!world.isBiomeMetaValidAt(type, biomeMeta, pos)) return null;
|
||||
|
||||
EntityTypes<? extends EntityInsentient> entityType = biomeMeta.entityType();
|
||||
org.bukkit.entity.EntityType bType = EntityTypes.clsToTypeMap.get(entityType.entityClass());
|
||||
if (bType != null) {
|
||||
if (PreCreatureSpawnEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
||||
PreCreatureSpawnEvent event = new PreCreatureSpawnEvent(
|
||||
MCUtil.toLocation(world, pos),
|
||||
bType, SpawnReason.NATURAL
|
||||
);
|
||||
new RuntimeException("Warning, one or more plugins is listening PlayerNaturallySpawnCreaturesEvent which is running asynchronously, this will may cause safe issue!").printStackTrace();
|
||||
synchronized (PreCreatureSpawnEvent.class) {
|
||||
Bukkit.getPluginManager().callEvent(event);
|
||||
}
|
||||
|
||||
if (!event.isCancelled() || event.shouldAbortSpawn())
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
EntityInsentient entity = null;
|
||||
|
||||
try {
|
||||
entity = entityType.create(world);
|
||||
} catch (Exception exception) {
|
||||
MinecraftServer.LOGGER.warn("Failed to create mob", exception);
|
||||
ServerInternalException.reportInternalException(exception);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static void spawnMob0(WorldServer world, Set<ChunkCoordIntPair> chunks, EnumCreatureType type, int amount) {
|
||||
if (chunks.isEmpty()) return;
|
||||
|
||||
final int maxPackIterations = 10; // X attempts per pack, 1 pack per chunk
|
||||
Iterator<ChunkCoordIntPair> iterator = chunks.iterator();
|
||||
BlockPosition worldSpawn = world.getSpawn();
|
||||
|
||||
int spawned = 0;
|
||||
|
||||
while (spawned < amount && iterator.hasNext()) {
|
||||
ChunkCoordIntPair chunkCoord = iterator.next();
|
||||
int packSize = world.random.nextInt(4) + 1;
|
||||
BlockPosition packCenter = SpawnerCreature.getRandomPosition(world, chunkCoord.x, chunkCoord.z);
|
||||
|
||||
if (world.getType(packCenter).isOccluding()) continue;
|
||||
|
||||
int x = packCenter.getX();
|
||||
int y = packCenter.getY();
|
||||
int z = packCenter.getZ();
|
||||
BlockPosition.MutableBlockPosition blockPointer = new BlockPosition.MutableBlockPosition();
|
||||
BiomeBase.BiomeMeta biomeMeta = null;
|
||||
GroupDataEntity group = null;
|
||||
EntityPositionTypes.Surface surfaceType = null;
|
||||
int iter = 0;
|
||||
int packSpawned = 0;
|
||||
|
||||
while (packSpawned < packSize && iter < maxPackIterations) {
|
||||
iter++;
|
||||
|
||||
// random walk
|
||||
x += world.random.nextInt(12) - 6;
|
||||
y += world.random.nextInt(2) - 1;
|
||||
z += world.random.nextInt(12) - 6;
|
||||
blockPointer.setValues(x, y, z);
|
||||
|
||||
if (worldSpawn.distanceSquared(x + 0.5, y, z + 0.5) < (24 * 24)) continue;
|
||||
|
||||
if (biomeMeta == null) {
|
||||
biomeMeta = world.getBiomeMetaAt(type, blockPointer);
|
||||
|
||||
if (biomeMeta == null) break;
|
||||
|
||||
int packRange = 1 + biomeMeta.getMaxPackSize() - biomeMeta.getMinPackSize();
|
||||
packSize = biomeMeta.getMinPackSize() + world.random.nextInt(packRange);
|
||||
surfaceType = EntityPositionTypes.a(biomeMeta.entityType());
|
||||
}
|
||||
|
||||
EntityInsentient entity = createMob(world, type, blockPointer, biomeMeta);
|
||||
|
||||
if (entity == null) continue;
|
||||
|
||||
entity.setPositionRotation(x + 0.5, y, z + 0.5, world.random.nextFloat() * 360.0F, 0.0F);
|
||||
|
||||
if (entity.canSpawnHere() && surfaceType != null
|
||||
&& SpawnerCreature.isValidSpawnSurface(surfaceType, world, blockPointer, biomeMeta.entityType())
|
||||
&& entity.isNotColliding(world) && !world.isPlayerNearby(x + 0.5, y, z + 0.5, 24)) {
|
||||
group = entity.prepare(world.getDamageScaler(new BlockPosition(entity)), group, null);
|
||||
|
||||
if (entity.isNotColliding(world) && world.addEntity(entity, SpawnReason.NATURAL))
|
||||
packSpawned++;
|
||||
|
||||
if (packSpawned >= entity.maxPackSize()) break;
|
||||
if ((packSpawned + spawned) >= amount) break;
|
||||
} else {
|
||||
entity.die();
|
||||
}
|
||||
}
|
||||
|
||||
spawned += packSpawned;
|
||||
}
|
||||
}
|
||||
|
||||
public static void spawnMobs(WorldServer world, boolean spawnMonsters, boolean spawnPassives, boolean spawnRare) {
|
||||
if(!spawnMonsters && !spawnPassives) return;
|
||||
|
||||
int hashOrdinal = world.random.nextInt();
|
||||
|
||||
Set<Chunk> rangeChunks = HashObjSets.newUpdatableSet();
|
||||
Map<EnumCreatureType, Set<ChunkCoordIntPair>> creatureChunks = new EnumMap<>(EnumCreatureType.class);
|
||||
int[] typeNumSpawn = new int[EnumCreatureType.values().length];
|
||||
|
||||
for (EnumCreatureType type : EnumCreatureType.values()) {
|
||||
if (type.passive() && !spawnPassives) continue;
|
||||
if (!type.passive() && !spawnMonsters) continue;
|
||||
if (type.rare() && !spawnRare) continue;
|
||||
if (getCreatureLimit(world, type) <= 0) continue;
|
||||
|
||||
creatureChunks.put(type, HashObjSets.newUpdatableSet());
|
||||
}
|
||||
|
||||
if (creatureChunks.isEmpty()) return;
|
||||
|
||||
for (EntityHuman player : world.players) {
|
||||
if (!player.affectsSpawning || player.isSpectator()) continue;
|
||||
|
||||
int spawnRange = getSpawnRange(world, player);
|
||||
if (spawnRange <= 0) continue;
|
||||
|
||||
int playerChunkX = MathHelper.floor(player.locX / 16.0);
|
||||
int playerChunkZ = MathHelper.floor(player.locZ / 16.0);
|
||||
|
||||
rangeChunks.clear();
|
||||
|
||||
for (int dX = -spawnRange; dX <= spawnRange; ++dX) {
|
||||
for (int dZ = -spawnRange; dZ <= spawnRange; ++dZ) {
|
||||
ChunkCoordIntPair chunkCoord = new ChunkCoordOrdinalInt3Tuple(dX + playerChunkX, dZ + playerChunkZ, hashOrdinal);
|
||||
|
||||
if (!world.getWorldBorder().isInBounds(chunkCoord)) continue;
|
||||
|
||||
PlayerChunk pChunk = world.getPlayerChunkMap().getChunk(chunkCoord.x, chunkCoord.z);
|
||||
|
||||
if (pChunk == null || !pChunk.isDone() || pChunk.chunk == null) continue;
|
||||
|
||||
rangeChunks.add(pChunk.chunk);
|
||||
}
|
||||
}
|
||||
|
||||
for (EnumCreatureType type : creatureChunks.keySet()) {
|
||||
int limit = getCreatureLimit(world, type);
|
||||
int creatureTotal = 0;
|
||||
|
||||
for (Chunk chunk : rangeChunks)
|
||||
creatureTotal += chunk.creatureCounts[type.ordinal()];
|
||||
|
||||
// if our local count is above the limit, dont qualify our chunks
|
||||
if (creatureTotal >= limit) continue;
|
||||
|
||||
Set<ChunkCoordIntPair> chunks = creatureChunks.get(type);
|
||||
for (Chunk chunk : rangeChunks)
|
||||
chunks.add(chunk.getPos());
|
||||
|
||||
// expect number is rather meaningless, just a ceil
|
||||
int expect = limit - creatureTotal;
|
||||
typeNumSpawn[type.ordinal()] = Math.max(typeNumSpawn[type.ordinal()], expect);
|
||||
}
|
||||
}
|
||||
|
||||
for (EnumCreatureType type : creatureChunks.keySet()) {
|
||||
Set<ChunkCoordIntPair> chunks = creatureChunks.get(type);
|
||||
|
||||
if (!chunks.isEmpty())
|
||||
spawnMob0(world, chunks, type, typeNumSpawn[type.ordinal()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package io.akarin.server.misc;
|
||||
|
||||
import net.minecraft.server.ChunkCoordIntPair;
|
||||
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.hash.HashFunction;
|
||||
|
||||
/*
|
||||
* Reference on spawning mechanics by Colin Godsey <crgodsey@gmail.com>
|
||||
* https://github.com/yesdog/Paper/blob/0de3dd84b7e6688feb42af4fe6b4f323ce7e3013/Spigot-Server-Patches/0433-alternate-mob-spawning-mechanic.patch
|
||||
*/
|
||||
public class ChunkCoordOrdinalInt3Tuple extends ChunkCoordIntPair {
|
||||
public static final HashFunction hashFunc = Hashing.murmur3_32("akarin".hashCode());
|
||||
|
||||
public final int ordinal;
|
||||
public final int cachedHashCode;
|
||||
|
||||
public ChunkCoordOrdinalInt3Tuple(int x, int z, int ord) {
|
||||
super(x, z);
|
||||
|
||||
this.ordinal = ord;
|
||||
|
||||
cachedHashCode = hashFunc.newHasher()
|
||||
.putInt(ordinal)
|
||||
.putInt(x)
|
||||
.putInt(z)
|
||||
.hash().asInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return cachedHashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else if (!(object instanceof ChunkCoordOrdinalInt3Tuple)) {
|
||||
return false;
|
||||
} else {
|
||||
ChunkCoordOrdinalInt3Tuple pair = (ChunkCoordOrdinalInt3Tuple) object;
|
||||
|
||||
return this.x == pair.x && this.z == pair.z && this.ordinal == pair.ordinal;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -587,9 +587,9 @@ public abstract class BiomeBase {
|
||||
|
||||
public static class BiomeMeta extends WeightedRandom.WeightedRandomChoice {
|
||||
|
||||
public EntityTypes<? extends EntityInsentient> b;
|
||||
public int c;
|
||||
public int d;
|
||||
public EntityTypes<? extends EntityInsentient> b; public EntityTypes<? extends EntityInsentient> entityType() { return b; } // Akarin
|
||||
public int c; public int getMinPackSize() { return c; } // Akarin - OBFHELPER
|
||||
public int d; public int getMaxPackSize() { return d; } // Akarin - OBFHELPER
|
||||
|
||||
public BiomeMeta(EntityTypes<? extends EntityInsentient> entitytypes, int i, int j, int k) {
|
||||
super(i);
|
||||
|
||||
@@ -75,6 +75,7 @@ public class Chunk implements IChunkAccess {
|
||||
private int D;
|
||||
private final AtomicInteger E;
|
||||
private final ChunkCoordIntPair F;
|
||||
public final int[] creatureCounts = new int[EnumCreatureType.values().length]; // Akarin
|
||||
|
||||
// CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking
|
||||
private volatile int neighbors = 0x1 << 12; // Akarin - volatile
|
||||
@@ -766,6 +767,12 @@ public class Chunk implements IChunkAccess {
|
||||
itemCounts[k]++;
|
||||
} else if (entity instanceof IInventory) {
|
||||
inventoryEntityCounts[k]++;
|
||||
// Akarin start
|
||||
} else if (entity instanceof IAnimal) {
|
||||
for (EnumCreatureType type : EnumCreatureType.values())
|
||||
if (type.matches(entity))
|
||||
creatureCounts[type.ordinal()]++;
|
||||
// Akarin end
|
||||
}
|
||||
// Paper end
|
||||
}
|
||||
@@ -800,6 +807,14 @@ public class Chunk implements IChunkAccess {
|
||||
itemCounts[i]--;
|
||||
} else if (entity instanceof IInventory) {
|
||||
inventoryEntityCounts[i]--;
|
||||
// Akarin start
|
||||
} else if (entity instanceof IAnimal) {
|
||||
for (EnumCreatureType type : EnumCreatureType.values())
|
||||
if (type.matches(entity)) {
|
||||
int typeCount = creatureCounts[type.ordinal()];
|
||||
creatureCounts[type.ordinal()] = typeCount > 1 ? --typeCount : 0;
|
||||
}
|
||||
// Akarin end
|
||||
}
|
||||
entityCounts.decrement(entity.getMinecraftKeyString());
|
||||
// Paper end
|
||||
|
||||
@@ -754,6 +754,7 @@ public abstract class EntityInsentient extends EntityLiving {
|
||||
return f + f3;
|
||||
}
|
||||
|
||||
public boolean canSpawnHere() { return a((GeneratorAccess) this.world, false); } // Akarin
|
||||
public boolean a(GeneratorAccess generatoraccess, boolean flag) {
|
||||
IBlockData iblockdata = generatoraccess.getType((new BlockPosition(this)).down());
|
||||
|
||||
@@ -764,10 +765,12 @@ public abstract class EntityInsentient extends EntityLiving {
|
||||
return this.a((IWorldReader) this.world);
|
||||
}
|
||||
|
||||
public boolean isNotColliding(IWorldReader iworldreader) { return a(iworldreader); } // Akarin
|
||||
public boolean a(IWorldReader iworldreader) {
|
||||
return !iworldreader.containsLiquid(this.getBoundingBox()) && iworldreader.getCubes(this, this.getBoundingBox()) && iworldreader.a_(this, this.getBoundingBox());
|
||||
}
|
||||
|
||||
public int maxPackSize() { return dg(); } // Akarin
|
||||
public int dg() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.koloboke.collect.map.hash.HashObjObjMaps;
|
||||
import com.mojang.datafixers.DataFixUtils;
|
||||
import com.mojang.datafixers.types.Type;
|
||||
|
||||
@@ -125,8 +126,8 @@ public class EntityTypes<T extends Entity> {
|
||||
EntityTypes<T> entitytypes = entitytypes_a.a(s);
|
||||
|
||||
// Paper start
|
||||
if (clsToKeyMap == null ) clsToKeyMap = new java.util.HashMap<>();
|
||||
if (clsToTypeMap == null ) clsToTypeMap = new java.util.HashMap<>();
|
||||
if (clsToKeyMap == null ) clsToKeyMap = HashObjObjMaps.newUpdatableMap(); // Akarin
|
||||
if (clsToTypeMap == null ) clsToTypeMap = HashObjObjMaps.newUpdatableMap(); // Akarin
|
||||
|
||||
MinecraftKey key = new MinecraftKey(s);
|
||||
Class<? extends T> entityClass = entitytypes_a.getEntityClass();
|
||||
@@ -249,6 +250,7 @@ public class EntityTypes<T extends Entity> {
|
||||
return this.aV;
|
||||
}
|
||||
|
||||
public Class<? extends T> entityClass() { return this.c(); } // Akarin
|
||||
public Class<? extends T> c() {
|
||||
return this.aS;
|
||||
}
|
||||
|
||||
@@ -22,14 +22,17 @@ public enum EnumCreatureType {
|
||||
return this.e;
|
||||
}
|
||||
|
||||
public int spawnLimit() { return this.b(); } // Akarin
|
||||
public int b() {
|
||||
return this.f;
|
||||
}
|
||||
|
||||
public boolean passive() { return c(); } // Akarin
|
||||
public boolean c() {
|
||||
return this.g;
|
||||
}
|
||||
|
||||
public boolean rare() { return d(); } // Akarin
|
||||
public boolean d() {
|
||||
return this.h;
|
||||
}
|
||||
|
||||
@@ -302,6 +302,7 @@ public class PlayerChunk {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isDone() { return e(); } // Paper - OBFHELPER
|
||||
public boolean e() {
|
||||
return this.done;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package net.minecraft.server;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import io.akarin.server.core.AkarinCreatureSpanwner;
|
||||
import io.akarin.server.core.AkarinGlobalConfig;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
@@ -25,6 +29,13 @@ public final class SpawnerCreature {
|
||||
public SpawnerCreature() {}
|
||||
|
||||
public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) {
|
||||
// Akarin start
|
||||
if (AkarinGlobalConfig.improvedMobSpawnMechanics) {
|
||||
int before = worldserver.entityList.size();
|
||||
AkarinCreatureSpanwner.spawnMobs(worldserver, flag, flag1, flag2);
|
||||
return worldserver.entityList.size() - before;
|
||||
}
|
||||
// Akarin end
|
||||
if (!flag && !flag1) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -253,7 +264,7 @@ public final class SpawnerCreature {
|
||||
}
|
||||
}
|
||||
|
||||
private static BlockPosition getRandomPosition(World world, int i, int j) {
|
||||
public static BlockPosition getRandomPosition(World world, int i, int j) { // Akarin - public
|
||||
Chunk chunk = world.getChunkAt(i, j);
|
||||
int k = i * 16 + world.random.nextInt(16);
|
||||
int l = j * 16 + world.random.nextInt(16);
|
||||
@@ -267,6 +278,7 @@ public final class SpawnerCreature {
|
||||
return iblockdata.k() ? false : (iblockdata.isPowerSource() ? false : (!fluid.e() ? false : !iblockdata.a(TagsBlock.RAILS)));
|
||||
}
|
||||
|
||||
public static boolean isValidSpawnSurface(EntityPositionTypes.Surface entitypositiontypes_surface, IWorldReader iworldreader, BlockPosition blockposition, @Nullable EntityTypes<? extends EntityInsentient> entitytypes) { return a(entitypositiontypes_surface, iworldreader, blockposition, entitytypes); } // Akarin
|
||||
public static boolean a(EntityPositionTypes.Surface entitypositiontypes_surface, IWorldReader iworldreader, BlockPosition blockposition, @Nullable EntityTypes<? extends EntityInsentient> entitytypes) {
|
||||
if (entitytypes != null && iworldreader.getWorldBorder().a(blockposition)) {
|
||||
IBlockData iblockdata = iworldreader.getType(blockposition);
|
||||
|
||||
@@ -341,6 +341,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
||||
return this.P;
|
||||
}
|
||||
|
||||
@Nullable public BiomeBase.BiomeMeta getBiomeMetaAt(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { return a(enumcreaturetype, blockposition); } // Akarin
|
||||
@Nullable
|
||||
public BiomeBase.BiomeMeta a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) {
|
||||
List<BiomeBase.BiomeMeta> list = this.getChunkProvider().a(enumcreaturetype, blockposition);
|
||||
@@ -348,6 +349,7 @@ public class WorldServer extends World implements IAsyncTaskHandler {
|
||||
return list.isEmpty() ? null : (BiomeBase.BiomeMeta) WeightedRandom.a(this.random, list);
|
||||
}
|
||||
|
||||
public boolean isBiomeMetaValidAt(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) { return this.a(enumcreaturetype, biomebase_biomemeta, blockposition); } // Akarin
|
||||
public boolean a(EnumCreatureType enumcreaturetype, BiomeBase.BiomeMeta biomebase_biomemeta, BlockPosition blockposition) {
|
||||
List<BiomeBase.BiomeMeta> list = this.getChunkProvider().a(enumcreaturetype, blockposition);
|
||||
|
||||
|
||||
Submodule work/Paper updated: 87a726351b...2cb75d6cce
Reference in New Issue
Block a user