Per chunk spawner cooldowns

This commit is contained in:
Daniel Mills 2021-08-06 22:11:17 -04:00
parent d1e8d52f5c
commit 9d251f94e5
12 changed files with 259 additions and 19 deletions

View File

@ -134,6 +134,7 @@ public class Iris extends VolmitPlugin implements Listener {
super.onEnable(); super.onEnable();
Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(this, this);
J.s(this::lateBind); J.s(this::lateBind);
splash();
} }
public static void callEvent(Event e) { public static void callEvent(Event e) {
@ -165,7 +166,6 @@ public class Iris extends VolmitPlugin implements Listener {
J.a(() -> PaperLib.suggestPaper(this)); J.a(() -> PaperLib.suggestPaper(this));
J.a(() -> IO.delete(getTemp())); J.a(() -> IO.delete(getTemp()));
J.a(this::bstats); J.a(this::bstats);
J.a(this::splash, 20);
J.ar(this::checkConfigHotload, 60); J.ar(this::checkConfigHotload, 60);
J.sr(this::tickQueue, 0); J.sr(this::tickQueue, 0);
J.s(this::setupPapi); J.s(this::setupPapi);

View File

@ -21,10 +21,14 @@ package com.volmit.iris.core.command;
import com.volmit.iris.Iris; import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings; import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.plugin.Command;
import com.volmit.iris.util.plugin.MortarCommand; import com.volmit.iris.util.plugin.MortarCommand;
import com.volmit.iris.util.plugin.VolmitSender; import com.volmit.iris.util.plugin.VolmitSender;
public class CommandIrisDebug extends MortarCommand { public class CommandIrisDebug extends MortarCommand {
@Command
private CommandIrisDebugSpawnerBoost boost;
public CommandIrisDebug() { public CommandIrisDebug() {
super("debug", "dbg"); super("debug", "dbg");
requiresPermission(Iris.perm.studio); requiresPermission(Iris.perm.studio);

View File

@ -0,0 +1,54 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.core.command;
import com.volmit.iris.Iris;
import com.volmit.iris.core.IrisSettings;
import com.volmit.iris.core.tools.IrisToolbelt;
import com.volmit.iris.engine.IrisEngine;
import com.volmit.iris.util.collection.KList;
import com.volmit.iris.util.plugin.MortarCommand;
import com.volmit.iris.util.plugin.VolmitSender;
public class CommandIrisDebugSpawnerBoost extends MortarCommand {
public CommandIrisDebugSpawnerBoost() {
super("charge", "zzt");
requiresPermission(Iris.perm.studio);
setDescription("Charge spawner energy");
setCategory("Studio");
}
@Override
public void addTabOptions(VolmitSender sender, String[] args, KList<String> list) {
}
@Override
public boolean handle(VolmitSender sender, String[] args) {
((IrisEngine)
IrisToolbelt.access(sender.player().getWorld()).getEngineAccess(sender.player().getLocation().getBlockY())).getWorldManager().chargeEnergy();
return true;
}
@Override
protected String getArgsUsage() {
return "<number> [|,&,^,>>,<<,%] <other>";
}
}

View File

@ -25,6 +25,7 @@ import com.volmit.iris.engine.framework.EngineAssignedWorldManager;
import com.volmit.iris.engine.object.biome.IrisBiome; import com.volmit.iris.engine.object.biome.IrisBiome;
import com.volmit.iris.engine.object.block.IrisBlockDrops; import com.volmit.iris.engine.object.block.IrisBlockDrops;
import com.volmit.iris.engine.object.common.IRare; import com.volmit.iris.engine.object.common.IRare;
import com.volmit.iris.engine.object.engine.IrisEngineChunkData;
import com.volmit.iris.engine.object.engine.IrisEngineData; import com.volmit.iris.engine.object.engine.IrisEngineData;
import com.volmit.iris.engine.object.engine.IrisEngineSpawnerCooldown; import com.volmit.iris.engine.object.engine.IrisEngineSpawnerCooldown;
import com.volmit.iris.engine.object.entity.IrisEntitySpawn; import com.volmit.iris.engine.object.entity.IrisEntitySpawn;
@ -61,6 +62,8 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
private int entityCount = 0; private int entityCount = 0;
private final ChronoLatch cl; private final ChronoLatch cl;
private final ChronoLatch ecl; private final ChronoLatch ecl;
private final ChronoLatch cln;
private long charge = 0;
private int actuallySpawned = 0; private int actuallySpawned = 0;
private int cooldown = 0; private int cooldown = 0;
private List<Entity> precount = new KList<>(); private List<Entity> precount = new KList<>();
@ -69,12 +72,14 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
super(null); super(null);
cl = null; cl = null;
ecl = null; ecl = null;
cln = null;
chunkCooldowns = null; chunkCooldowns = null;
looper = null; looper = null;
} }
public IrisWorldManager(Engine engine) { public IrisWorldManager(Engine engine) {
super(engine); super(engine);
cln = new ChronoLatch(60000);
cl = new ChronoLatch(3000); cl = new ChronoLatch(3000);
ecl = new ChronoLatch(250); ecl = new ChronoLatch(250);
chunkCooldowns = new KMap<>(); chunkCooldowns = new KMap<>();
@ -82,6 +87,16 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
looper = new Looper() { looper = new Looper() {
@Override @Override
protected long loop() { protected long loop() {
if(M.ms() < charge)
{
energy += 70;
}
if(cln.flip())
{
engine.getEngineData().cleanup(getEngine());
}
if (precount != null) { if (precount != null) {
entityCount = 0; entityCount = 0;
for (Entity i : precount) { for (Entity i : precount) {
@ -137,7 +152,6 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
J.s(() -> precount = getEngine().getWorld().realWorld().getEntities()); J.s(() -> precount = getEngine().getWorld().realWorld().getEntities());
} }
int maxGroups = 1;
int chunkCooldownSeconds = 60; int chunkCooldownSeconds = 60;
for (Long i : chunkCooldowns.k()) { for (Long i : chunkCooldowns.k()) {
@ -158,7 +172,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
Chunk c = cc[RNG.r.nextInt(cc.length)]; Chunk c = cc[RNG.r.nextInt(cc.length)];
IrisBiome biome = getEngine().getSurfaceBiome(c); IrisBiome biome = getEngine().getSurfaceBiome(c);
IrisRegion region = getEngine().getRegion(c); IrisRegion region = getEngine().getRegion(c);
spawnIn(c, biome, region, maxGroups); spawnIn(c, biome, region);
chunkCooldowns.put(Cache.key(c), M.ms()); chunkCooldowns.put(Cache.key(c), M.ms());
} }
@ -170,15 +184,9 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
energy = M.clip(energy, 1D, 1000D); energy = M.clip(energy, 1D, 1000D);
} }
private void spawnIn(Chunk c, IrisBiome biome, IrisRegion region, int max) { private void spawnIn(Chunk c, IrisBiome biome, IrisRegion region) {
for (Entity i : c.getEntities()) {
if (i instanceof LivingEntity) {
return;
}
}
//@builder //@builder
spawnRandomly(Stream.concat(Stream.concat( IrisEntitySpawn v = spawnRandomly(Stream.concat(Stream.concat(
getData().getSpawnerLoader() getData().getSpawnerLoader()
.loadAll(getDimension().getEntitySpawners()) .loadAll(getDimension().getEntitySpawners())
.shuffleCopy(RNG.r).stream().filter(this::canSpawn), .shuffleCopy(RNG.r).stream().filter(this::canSpawn),
@ -197,11 +205,59 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
.shuffleCopy(RNG.r).stream().filter(this::canSpawn) .shuffleCopy(RNG.r).stream().filter(this::canSpawn)
.flatMap(this::stream))) .flatMap(this::stream)))
.collect(Collectors.toList())) .collect(Collectors.toList()))
.popRandom(RNG.r, max).forEach((i) -> spawn(c, i)); .popRandom(RNG.r);
if(v != null && v.getReferenceSpawner() != null)
{
int maxEntCount = v.getReferenceSpawner().getMaxEntitiesPerChunk();
for (Entity i : c.getEntities()) {
if (i instanceof LivingEntity) {
if(-maxEntCount <= 0)
{
return;
}
}
}
spawn(c, v);
}
//@done //@done
} }
private void spawn(Chunk c, IrisEntitySpawn i) { private void spawn(Chunk c, IrisEntitySpawn i) {
boolean allow = true;
if(!i.getReferenceSpawner().getMaximumRatePerChunk().isInfinite())
{
allow = false;
IrisEngineChunkData cd = getEngine().getEngineData().getChunk(c.getX(), c.getZ());
IrisEngineSpawnerCooldown sc = null;
for(IrisEngineSpawnerCooldown j : cd.getCooldowns())
{
if(j.getSpawner().equals(i.getReferenceSpawner().getLoadKey()))
{
sc = j;
break;
}
}
if(sc == null)
{
sc = new IrisEngineSpawnerCooldown();
sc.setSpawner(i.getReferenceSpawner().getLoadKey());
cd.getCooldowns().add(sc);
}
if(sc.canSpawn(i.getReferenceSpawner().getMaximumRatePerChunk()))
{
sc.spawn(getEngine());
allow = true;
}
}
if(allow)
{
int s = i.spawn(getEngine(), c, RNG.r); int s = i.spawn(getEngine(), c, RNG.r);
actuallySpawned += s; actuallySpawned += s;
if (s > 0) { if (s > 0) {
@ -209,6 +265,7 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
energy -= s * ((i.getEnergyMultiplier() * i.getReferenceSpawner().getEnergyMultiplier() * 1)); energy -= s * ((i.getEnergyMultiplier() * i.getReferenceSpawner().getEnergyMultiplier() * 1));
} }
} }
}
private Stream<IrisEntitySpawn> stream(IrisSpawner s) { private Stream<IrisEntitySpawn> stream(IrisSpawner s) {
for (IrisEntitySpawn i : s.getSpawns()) { for (IrisEntitySpawn i : s.getSpawns()) {
@ -278,6 +335,11 @@ public class IrisWorldManager extends EngineAssignedWorldManager {
fixEnergy(); fixEnergy();
} }
@Override
public void chargeEnergy() {
charge = M.ms() + 3000;
}
@Override @Override
public void onBlockBreak(BlockBreakEvent e) { public void onBlockBreak(BlockBreakEvent e) {
if (e.getBlock().getWorld().equals(getTarget().getWorld().realWorld()) && getEngine().contains(e.getBlock().getLocation())) { if (e.getBlock().getWorld().equals(getTarget().getWorld().realWorld()) && getEngine().contains(e.getBlock().getLocation())) {

View File

@ -43,4 +43,6 @@ public interface EngineWorldManager {
void onBlockPlace(BlockPlaceEvent e); void onBlockPlace(BlockPlaceEvent e);
void onChunkLoad(Chunk e, boolean generated); void onChunkLoad(Chunk e, boolean generated);
void chargeEnergy();
} }

View File

@ -48,4 +48,8 @@ public class IrisRate {
public ChronoLatch toChronoLatch() { public ChronoLatch toChronoLatch() {
return new ChronoLatch(getInterval()); return new ChronoLatch(getInterval());
} }
public boolean isInfinite() {
return per.toMilliseconds() == 0;
}
} }

View File

@ -0,0 +1,47 @@
/*
* Iris is a World Generator for Minecraft Bukkit Servers
* Copyright (c) 2021 Arcane Arts (Volmit Software)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.volmit.iris.engine.object.engine;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.spawners.IrisSpawner;
import com.volmit.iris.util.collection.KList;
import lombok.Data;
@Data
public class IrisEngineChunkData {
private long chunk;
private KList<IrisEngineSpawnerCooldown> cooldowns = new KList<>();
public void cleanup(Engine engine)
{
for(IrisEngineSpawnerCooldown i : getCooldowns().copy())
{
IrisSpawner sp = engine.getData().getSpawnerLoader().load(i.getSpawner());
if(sp == null || i.canSpawn(sp.getMaximumRate()))
{
getCooldowns().remove(i);
}
}
}
public boolean isEmpty() {
return cooldowns.isEmpty();
}
}

View File

@ -18,6 +18,10 @@
package com.volmit.iris.engine.object.engine; package com.volmit.iris.engine.object.engine;
import com.volmit.iris.engine.data.cache.Cache;
import com.volmit.iris.engine.framework.Engine;
import com.volmit.iris.engine.object.meta.IrisEnchantment;
import com.volmit.iris.engine.object.spawners.IrisSpawner;
import com.volmit.iris.util.collection.KList; import com.volmit.iris.util.collection.KList;
import lombok.Data; import lombok.Data;
@ -25,4 +29,51 @@ import lombok.Data;
public class IrisEngineData { public class IrisEngineData {
private IrisEngineStatistics statistics = new IrisEngineStatistics(); private IrisEngineStatistics statistics = new IrisEngineStatistics();
private KList<IrisEngineSpawnerCooldown> spawnerCooldowns = new KList<>(); private KList<IrisEngineSpawnerCooldown> spawnerCooldowns = new KList<>();
private KList<IrisEngineChunkData> chunks = new KList<>();
public void removeChunk(int x, int z)
{
long k = Cache.key(x, z);
chunks.removeWhere((i) -> i.getChunk() == k);
}
public IrisEngineChunkData getChunk(int x, int z)
{
long k = Cache.key(x, z);
for(IrisEngineChunkData i : chunks)
{
if(i.getChunk() == k)
{
return i;
}
}
IrisEngineChunkData c = new IrisEngineChunkData();
c.setChunk(k);
chunks.add(c);
return c;
}
public void cleanup(Engine engine) {
for(IrisEngineSpawnerCooldown i : getSpawnerCooldowns().copy())
{
IrisSpawner sp = engine.getData().getSpawnerLoader().load(i.getSpawner());
if(sp == null || i.canSpawn(sp.getMaximumRate()))
{
getSpawnerCooldowns().remove(i);
}
}
for(IrisEngineChunkData i : chunks.copy())
{
i.cleanup(engine);
if(i.isEmpty())
{
getChunks().remove(i);
}
}
}
} }

View File

@ -25,7 +25,7 @@ import lombok.Data;
@Data @Data
public class IrisEngineSpawnerCooldown { public class IrisEngineSpawnerCooldown {
private long lastSpawn; private long lastSpawn = 0;
private String spawner; private String spawner;
public void spawn(Engine engine) { public void spawn(Engine engine) {

View File

@ -48,6 +48,9 @@ public class IrisSpawner extends IrisRegistrant {
@Desc("The energy multiplier when calculating spawn energy usage") @Desc("The energy multiplier when calculating spawn energy usage")
private double energyMultiplier = 1; private double energyMultiplier = 1;
@Desc("This spawner will not spawn in a given chunk if that chunk has more than the defined amount of living entities.")
private int maxEntitiesPerChunk = 1;
@Desc("The block of 24 hour time to contain this spawn in.") @Desc("The block of 24 hour time to contain this spawn in.")
private IrisTimeBlock timeBlock = new IrisTimeBlock(); private IrisTimeBlock timeBlock = new IrisTimeBlock();
@ -57,6 +60,9 @@ public class IrisSpawner extends IrisRegistrant {
@Desc("The maximum rate this spawner can fire") @Desc("The maximum rate this spawner can fire")
private IrisRate maximumRate = new IrisRate(); private IrisRate maximumRate = new IrisRate();
@Desc("The maximum rate this spawner can fire on a specific chunk")
private IrisRate maximumRatePerChunk = new IrisRate();
@Desc("Where should these spawns be placed") @Desc("Where should these spawns be placed")
private IrisSpawnGroup group = IrisSpawnGroup.NORMAL; private IrisSpawnGroup group = IrisSpawnGroup.NORMAL;

View File

@ -91,7 +91,7 @@ public abstract class MortarCommand implements ICommand {
b = true; b = true;
sender.sendMessage("" + C.GREEN + i.getNode() + " " + C.WHITE + "<font:minecraft:uniform>" + i.getArgsUsage() + C.GRAY + " - " + i.getDescription()); sender.sendMessage("" + C.GREEN + i.getNode() + " " + "<font:minecraft:uniform>" + (getArgsUsage().trim().isEmpty() ? "" : (C.WHITE + i.getArgsUsage())) + C.GRAY + " - " + i.getDescription());
} }
if (!b) { if (!b) {

View File

@ -188,6 +188,12 @@ public class VolmitSender implements CommandSender {
@Override @Override
public void sendMessage(String message) { public void sendMessage(String message) {
if(message.contains("<NOMINI>"))
{
s.sendMessage(message.replaceAll("\\Q<NOMINI>\\E", ""));
return;
}
try try
{ {
String t = C.translateAlternateColorCodes('&', getTag() + message); String t = C.translateAlternateColorCodes('&', getTag() + message);
@ -198,6 +204,10 @@ public class VolmitSender implements CommandSender {
catch(Throwable e) catch(Throwable e)
{ {
String t = C.translateAlternateColorCodes('&', getTag() + message);
String a = C.aura(t, IrisSettings.get().getGeneral().getSpinh(), IrisSettings.get().getGeneral().getSpins(), IrisSettings.get().getGeneral().getSpinb());
Iris.debug("<NOMINI>Failure to parse " + a);
s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message)); s.sendMessage(C.translateAlternateColorCodes('&', getTag() + message));
} }
} }