Implement spawn rate limit and per-world spawn limit. Close #12.
This commit is contained in:
parent
9bc66e5552
commit
5158c40982
|
@ -26,4 +26,12 @@ public interface WorldBridge extends IWorldWriterBridge {
|
|||
TileEntity bridge$getTileEntity(BlockPos pos, boolean validate);
|
||||
|
||||
SpigotWorldConfig bridge$spigotConfig();
|
||||
|
||||
long bridge$ticksPerAnimalSpawns();
|
||||
|
||||
long bridge$ticksPerMonsterSpawns();
|
||||
|
||||
long bridge$ticksPerWaterSpawns();
|
||||
|
||||
long bridge$ticksPerAmbientSpawns();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package io.izzel.arclight.common.bridge.world.server;
|
||||
|
||||
import io.izzel.arclight.common.mod.util.ArclightCallbackExecutor;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.server.ChunkHolder;
|
||||
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
@ -9,6 +10,12 @@ public interface ChunkManagerBridge {
|
|||
|
||||
void bridge$tick(BooleanSupplier hasMoreTime);
|
||||
|
||||
Iterable<ChunkHolder> bridge$getLoadedChunksIterable();
|
||||
|
||||
boolean bridge$isOutsideSpawningRadius(ChunkPos chunkPosIn);
|
||||
|
||||
void bridge$tickEntityTracker();
|
||||
|
||||
ArclightCallbackExecutor bridge$getCallbackExecutor();
|
||||
|
||||
ChunkHolder bridge$chunkHolderAt(long chunkPos);
|
||||
|
|
|
@ -61,6 +61,10 @@ public abstract class WorldMixin implements WorldBridge {
|
|||
protected CraftWorld world;
|
||||
public boolean pvpMode;
|
||||
public boolean keepSpawnInMemory = true;
|
||||
public long ticksPerAnimalSpawns;
|
||||
public long ticksPerMonsterSpawns;
|
||||
public long ticksPerWaterSpawns;
|
||||
public long ticksPerAmbientSpawns;
|
||||
public boolean populating;
|
||||
public org.bukkit.generator.ChunkGenerator generator;
|
||||
protected org.bukkit.World.Environment environment;
|
||||
|
@ -70,6 +74,30 @@ public abstract class WorldMixin implements WorldBridge {
|
|||
private void arclight$init(WorldInfo info, DimensionType dimType, BiFunction<World, Dimension, AbstractChunkProvider> provider, IProfiler profilerIn, boolean remote, CallbackInfo ci) {
|
||||
spigotConfig = new SpigotWorldConfig(worldInfo.getWorldName());
|
||||
((WorldBorderBridge) this.worldBorder).bridge$setWorld((ServerWorld) (Object) this);
|
||||
this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns();
|
||||
this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns();
|
||||
this.ticksPerWaterSpawns = this.getServer().getTicksPerWaterSpawns();
|
||||
this.ticksPerAmbientSpawns = this.getServer().getTicksPerAmbientSpawns();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long bridge$ticksPerAnimalSpawns() {
|
||||
return ticksPerAnimalSpawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long bridge$ticksPerMonsterSpawns() {
|
||||
return ticksPerMonsterSpawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long bridge$ticksPerWaterSpawns() {
|
||||
return ticksPerWaterSpawns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long bridge$ticksPerAmbientSpawns() {
|
||||
return ticksPerAmbientSpawns;
|
||||
}
|
||||
|
||||
public void arclight$constructor(WorldInfo info, DimensionType dimType, BiFunction<World, Dimension, AbstractChunkProvider> provider, IProfiler profilerIn, boolean remote) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package io.izzel.arclight.common.mixin.core.world.server;
|
|||
|
||||
import io.izzel.arclight.common.bridge.world.server.ChunkManagerBridge;
|
||||
import io.izzel.arclight.common.mod.util.ArclightCallbackExecutor;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.server.ChunkHolder;
|
||||
import net.minecraft.world.server.ChunkManager;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
@ -16,6 +17,9 @@ public abstract class ChunkManagerMixin implements ChunkManagerBridge {
|
|||
|
||||
// @formatter:off
|
||||
@Shadow @Nullable protected abstract ChunkHolder func_219220_a(long chunkPosIn);
|
||||
@Shadow protected abstract Iterable<ChunkHolder> getLoadedChunksIterable();
|
||||
@Shadow abstract boolean isOutsideSpawningRadius(ChunkPos chunkPosIn);
|
||||
@Shadow protected abstract void tickEntityTracker();
|
||||
@Invoker("tick") public abstract void bridge$tick(BooleanSupplier hasMoreTime);
|
||||
// @formatter:on
|
||||
|
||||
|
@ -30,4 +34,19 @@ public abstract class ChunkManagerMixin implements ChunkManagerBridge {
|
|||
public ChunkHolder bridge$chunkHolderAt(long chunkPos) {
|
||||
return func_219220_a(chunkPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<ChunkHolder> bridge$getLoadedChunksIterable() {
|
||||
return this.getLoadedChunksIterable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bridge$isOutsideSpawningRadius(ChunkPos chunkPosIn) {
|
||||
return this.isOutsideSpawningRadius(chunkPosIn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bridge$tickEntityTracker() {
|
||||
this.tickEntityTracker();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package io.izzel.arclight.common.mixin.core.world.server;
|
||||
|
||||
import com.mojang.datafixers.util.Either;
|
||||
import io.izzel.arclight.common.bridge.world.WorldBridge;
|
||||
import io.izzel.arclight.common.bridge.world.server.ChunkHolderBridge;
|
||||
import io.izzel.arclight.common.bridge.world.server.ChunkManagerBridge;
|
||||
import io.izzel.arclight.common.bridge.world.server.ServerChunkProviderBridge;
|
||||
import io.izzel.arclight.common.bridge.world.server.TicketManagerBridge;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import net.minecraft.entity.EntityClassification;
|
||||
import net.minecraft.profiler.IProfiler;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.GameRules;
|
||||
import net.minecraft.world.WorldType;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.IChunk;
|
||||
import net.minecraft.world.gen.ChunkGenerator;
|
||||
import net.minecraft.world.server.ChunkHolder;
|
||||
import net.minecraft.world.server.ChunkManager;
|
||||
import net.minecraft.world.server.ServerChunkProvider;
|
||||
|
@ -16,6 +24,8 @@ import net.minecraft.world.server.ServerWorld;
|
|||
import net.minecraft.world.server.ServerWorldLightManager;
|
||||
import net.minecraft.world.server.TicketManager;
|
||||
import net.minecraft.world.server.TicketType;
|
||||
import net.minecraft.world.spawner.WorldEntitySpawner;
|
||||
import net.minecraft.world.storage.WorldInfo;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Overwrite;
|
||||
|
@ -27,6 +37,7 @@ import org.spongepowered.asm.mixin.injection.Redirect;
|
|||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Mixin(ServerChunkProvider.class)
|
||||
|
@ -42,6 +53,11 @@ public abstract class ServerChunkProviderMixin implements ServerChunkProviderBri
|
|||
@Shadow @Nullable protected abstract ChunkHolder func_217213_a(long chunkPosIn);
|
||||
@Shadow protected abstract boolean func_217235_l();
|
||||
@Shadow protected abstract boolean func_217224_a(@Nullable ChunkHolder chunkHolderIn, int p_217224_2_);
|
||||
@Shadow private long lastGameTime;
|
||||
@Shadow public boolean spawnHostiles;
|
||||
@Shadow public boolean spawnPassives;
|
||||
@Shadow @Final private static int field_217238_b;
|
||||
@Shadow @Final public ChunkGenerator<?> generator;
|
||||
@Invoker("func_217235_l") public abstract boolean bridge$tickDistanceManager();
|
||||
@Accessor("lightManager") public abstract ServerWorldLightManager bridge$getLightManager();
|
||||
// @formatter:on
|
||||
|
@ -89,6 +105,100 @@ public abstract class ServerChunkProviderMixin implements ServerChunkProviderBri
|
|||
return this.func_217224_a(chunkholder, j) ? ChunkHolder.MISSING_CHUNK_FUTURE : chunkholder.func_219276_a(requiredStatus, this.chunkManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @author IzzelAliz
|
||||
* @reason
|
||||
*/
|
||||
@Overwrite
|
||||
private void tickChunks() {
|
||||
long i = this.world.getGameTime();
|
||||
long j = i - this.lastGameTime;
|
||||
this.lastGameTime = i;
|
||||
WorldInfo worldinfo = this.world.getWorldInfo();
|
||||
boolean flag = worldinfo.getGenerator() == WorldType.DEBUG_ALL_BLOCK_STATES;
|
||||
boolean flag1 = this.world.getGameRules().getBoolean(GameRules.DO_MOB_SPAWNING) && !this.world.getPlayers().isEmpty();
|
||||
if (!flag) {
|
||||
this.world.getProfiler().startSection("pollingChunks");
|
||||
int k = this.world.getGameRules().getInt(GameRules.RANDOM_TICK_SPEED);
|
||||
BlockPos blockpos = this.world.getSpawnPoint();
|
||||
|
||||
boolean spawnAnimal = ((WorldBridge) this.world).bridge$ticksPerAnimalSpawns() != 0 && worldinfo.getGameTime() % ((WorldBridge) this.world).bridge$ticksPerAnimalSpawns() == 0;
|
||||
boolean spawnMonster = ((WorldBridge) this.world).bridge$ticksPerMonsterSpawns() != 0 && worldinfo.getGameTime() % ((WorldBridge) this.world).bridge$ticksPerMonsterSpawns() == 0;
|
||||
boolean spawnWater = ((WorldBridge) this.world).bridge$ticksPerWaterSpawns() != 0 && worldinfo.getGameTime() % ((WorldBridge) this.world).bridge$ticksPerWaterSpawns() == 0;
|
||||
boolean spawnAmbient = ((WorldBridge) this.world).bridge$ticksPerAmbientSpawns() != 0 && worldinfo.getGameTime() % ((WorldBridge) this.world).bridge$ticksPerAmbientSpawns() == 0;
|
||||
boolean flag2 = spawnAnimal;
|
||||
|
||||
this.world.getProfiler().startSection("naturalSpawnCount");
|
||||
int l = this.ticketManager.getSpawningChunksCount();
|
||||
EntityClassification[] aentityclassification = EntityClassification.values();
|
||||
Object2IntMap<EntityClassification> object2intmap = this.world.countEntities();
|
||||
this.world.getProfiler().endSection();
|
||||
((ChunkManagerBridge) this.chunkManager).bridge$getLoadedChunksIterable().forEach((p_223434_10_) -> {
|
||||
Optional<Chunk> optional = p_223434_10_.getEntityTickingFuture().getNow(ChunkHolder.UNLOADED_CHUNK).left();
|
||||
if (optional.isPresent()) {
|
||||
Chunk chunk = optional.get();
|
||||
this.world.getProfiler().startSection("broadcast");
|
||||
p_223434_10_.sendChanges(chunk);
|
||||
this.world.getProfiler().endSection();
|
||||
ChunkPos chunkpos = p_223434_10_.getPosition();
|
||||
if (!((ChunkManagerBridge) this.chunkManager).bridge$isOutsideSpawningRadius(chunkpos)) {
|
||||
chunk.setInhabitedTime(chunk.getInhabitedTime() + j);
|
||||
if (flag1 && (this.spawnHostiles || this.spawnPassives) && this.world.getWorldBorder().contains(chunk.getPos())) {
|
||||
this.world.getProfiler().startSection("spawner");
|
||||
|
||||
for (EntityClassification entityclassification : aentityclassification) {
|
||||
|
||||
boolean spawnThisTick = true;
|
||||
int limit = entityclassification.getMaxNumberOfCreature();
|
||||
switch (entityclassification) {
|
||||
case MONSTER:
|
||||
spawnThisTick = spawnMonster;
|
||||
limit = ((WorldBridge) world).bridge$getWorld().getMonsterSpawnLimit();
|
||||
break;
|
||||
case CREATURE:
|
||||
spawnThisTick = spawnAnimal;
|
||||
limit = ((WorldBridge) world).bridge$getWorld().getAnimalSpawnLimit();
|
||||
break;
|
||||
case WATER_CREATURE:
|
||||
spawnThisTick = spawnWater;
|
||||
limit = ((WorldBridge) world).bridge$getWorld().getWaterAnimalSpawnLimit();
|
||||
break;
|
||||
case AMBIENT:
|
||||
spawnThisTick = spawnAmbient;
|
||||
limit = ((WorldBridge) world).bridge$getWorld().getAmbientSpawnLimit();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!spawnThisTick || limit == 0) {
|
||||
continue;
|
||||
}
|
||||
if (entityclassification != EntityClassification.MISC && (!entityclassification.getPeacefulCreature() || this.spawnPassives) && (entityclassification.getPeacefulCreature() || this.spawnHostiles) && (!entityclassification.getAnimal() || flag2)) {
|
||||
int i1 = limit * l / field_217238_b;
|
||||
if (object2intmap.getInt(entityclassification) <= i1) {
|
||||
WorldEntitySpawner.spawnEntitiesInChunk(entityclassification, this.world, chunk, blockpos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.world.getProfiler().endSection();
|
||||
}
|
||||
|
||||
this.world.tickEnvironment(chunk, k);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.world.getProfiler().startSection("customSpawners");
|
||||
if (flag1) {
|
||||
this.generator.spawnMobs(this.world, this.spawnHostiles, this.spawnPassives);
|
||||
}
|
||||
|
||||
this.world.getProfiler().endSection();
|
||||
this.world.getProfiler().endSection();
|
||||
}
|
||||
|
||||
((ChunkManagerBridge) this.chunkManager).bridge$tickEntityTracker();
|
||||
}
|
||||
|
||||
public void close(boolean save) throws IOException {
|
||||
if (save) {
|
||||
this.save(true);
|
||||
|
|
Loading…
Reference in New Issue
Block a user