package jas.spawner.refactor; import jas.common.global.BiomeBlacklist; import jas.common.helper.MVELHelper; import jas.spawner.modern.spawner.CountInfo; import jas.spawner.refactor.LivingTypeBuilder.LivingType; import jas.spawner.refactor.LivingTypeSpawnTriggerBuilder.SpawnProcess; import jas.spawner.refactor.LivingTypeSpawnTriggerBuilder.LivingTypeSpawnTrigger; import jas.spawner.refactor.LivingTypeSpawnTriggerBuilder.SPAWNER; import jas.spawner.refactor.LivingTypeSpawnTriggerBuilder.TRIGGER; import jas.spawner.refactor.SpawnerHelper.Counter; import jas.spawner.refactor.SpawnerHelper.SpawnerLogic; import jas.spawner.refactor.mvel.MVELExpression; import jas.spawner.refactor.spawning.WorldSpawningLogic; import java.util.ArrayList; import java.util.EnumMap; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.Table; import com.google.common.collect.Table.Cell; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraftforge.common.DimensionManager; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent; import cpw.mods.fml.relauncher.Side; public class SpawnerTicker { private BiomeBlacklist blacklist; private EnumMap<SPAWNER, SpawnerLogic> spawners; public SpawnerTicker(BiomeBlacklist blacklist) { this.blacklist = blacklist; this.spawners = new EnumMap(SPAWNER.class); for (SPAWNER spawner : SPAWNER.values()) { switch (spawner) { case CHUNK: spawners.put(SPAWNER.CHUNK, new WorldSpawningLogic()); break; case PLAYER: spawners.put(SPAWNER.PLAYER, new WorldSpawningLogic()); break; case WORLD: spawners.put(SPAWNER.WORLD, new WorldSpawningLogic()); break; } } } // Dimension, LivingType to ActiveSpawn // What if multiple chunk spawns happen? i.e. 2 Chunk Spawns + Block Break // List that allows == priority to be multiple times private Table<Integer, String, List<SpawnProcess>> activeSpawnProcesses; public synchronized void addActiveSpawn(World world, SpawnProcess activeSpawn) { addActiveSpawn(world.provider.dimensionId, activeSpawn); } public synchronized void addActiveSpawn(Integer dimension, SpawnProcess activeSpawn) { List<SpawnProcess> currentlyActive = activeSpawnProcesses.get(dimension, activeSpawn.livingType()); if (currentlyActive == null || currentlyActive.isEmpty() || currentlyActive.get(0).priority() < activeSpawn.priority()) { List<SpawnProcess> newList = new ArrayList(); newList.add(activeSpawn); activeSpawnProcesses.put(dimension, activeSpawn.livingType(), newList); } else if (currentlyActive.get(0).priority() == activeSpawn.priority()) { currentlyActive.add(activeSpawn); } } public synchronized void performActiveSpawns(World world) { Iterator<Cell<Integer, String, List<SpawnProcess>>> iterator = activeSpawnProcesses.cellSet().iterator(); while (iterator.hasNext()) { Cell<Integer, String, List<SpawnProcess>> cell = iterator.next(); for (SpawnProcess activeSpawn : cell.getValue()) { if (activeSpawn.performSpawnCycle(world)) { SpawnerLogic spawnLogic = spawners.get(activeSpawn.spawner()); Counter counter = spawnLogic.counter(world); counter.countEntities(world); } if (activeSpawn.isFinished(world)) { iterator.remove(); } activeSpawn.incremenetDuration(); } } } @SubscribeEvent public void serverTick(ServerTickEvent event) { if (event.side != Side.SERVER || event.phase == Phase.END) { return; } MinecraftServer server = MinecraftServer.getServer(); Integer[] ids = DimensionManager.getIDs(false); // PassiveTrigger: Triggered Every X Ticks; increase if performance is an issue // passive spawns that want to tick faster should create a ActiveSpawns that persists and is valid every TICK if (server.getTickCounter() % 1 == 0) { LivingTypes types = null; Set<Entry<String, ImmutableList<LivingTypeSpawnTrigger>>> triggerable = types .getTriggerableTypes(TRIGGER.PASSIVE); for (Entry<String, ImmutableList<LivingTypeSpawnTrigger>> entry : triggerable) { String livingTypeID = entry.getKey(); for (LivingTypeSpawnTrigger trigger : entry.getValue()) { // THID NEEDS TO BE DONE FOR EVERY DIMENSION; THIS FEELS BAD, THERE MUST BE A BETTER WAY // I think it is acceptable? Maybe make it process every 20 ticks or so // I think it is acceptable: Every ForEach Type (~7), ForEach Trigger(~1/2), ForEach Dim (~3-6, // Mystcraft??) == 7*2*6=84 MVEL expressions evaluated shouldn't cause issues, for (int x = 0; x < ids.length; x++) { int id = ids[x]; if (id == 0 || server.getAllowNether()) { Optional<Boolean> result = MVELExpression.execute(trigger.isTriggered, new Object(), "Error evaluating canBeTriggered expression " + trigger.isTriggered.expression + " of LivingType " + livingTypeID); if (result.isPresent() && result.get()) { List<SpawnProcess> currentlyActiveSpawns = activeSpawnProcesses.get(id, livingTypeID); if (currentlyActiveSpawns == null || currentlyActiveSpawns.isEmpty() || currentlyActiveSpawns.get(0).priority() <= trigger.triggerPriority) { // ActiveSpawn passiveActiveSpawn = new passiveActiveSpawn(trigger) // addActiveSpawn(id, activeSpawn); } } } } } } } /** Perform Spawning */ for (int x = 0; x < ids.length; x++) { int id = ids[x]; long j = System.nanoTime(); if (id == 0 || server.getAllowNether()) { WorldServer worldserver = DimensionManager.getWorld(id); // performSpawningInWorld(worldserver); if (!worldserver.getGameRules().hasRule("doCustomMobSpawning") || worldserver.getGameRules().getGameRuleBooleanValue("doCustomMobSpawning")) { performActiveSpawns(worldserver); } } } /** TODO: Perform Updates to WorldSettings Here */ // MVELProfile.worldSettings().applyChanges(); } // Alternative Spawner; spawn per player: allow acces to player specific Scoreboard:objectives. @Deprecated private void performSpawningInWorld(WorldServer world) { if (!world.getGameRules().hasRule("doCustomMobSpawning") || world.getGameRules().getGameRuleBooleanValue("doCustomMobSpawning")) { List<LivingType> readyLivingTypes = new ArrayList<LivingType>(); for (LivingType livingType : ExperimentalProfile.worldSettings().getSpawnSettings(world).livingTypes() .types().values()) { // TODO CreateContext for isReady // Tags tags = new Tags(world, countInfo, posX, posY, posZ); // Without POS, what tags are ACTUALLY useful? Object context = null; // creatureType.isReady(world) if (MVELHelper.executeExpression(livingType.isReadyToChnk.compiled.get(), context, "Error processing isReady compiled expression for " + livingType.livingTypeID + ": " + livingType.isReadyToChnk.expression)) { readyLivingTypes.add(livingType); } } if (readyLivingTypes.isEmpty()) { return; } CountInfo countInfo = SpawnerHelper.counter.countEntities(world); for (LivingType livingType : readyLivingTypes) { // Spawn entities // LivingHandlerRegistry livingHandlerRegistry = // MVELProfile.worldSettings().livingHandlerRegistry(); // BiomeSpawnListRegistry biomeSpawnListRegistry = MVELProfile.worldSettings() // .biomeSpawnListRegistry(); // CustomSpawner.spawnCreaturesInChunks(world, livingHandlerRegistry, biomeSpawnListRegistry, // creatureType, blacklist, countInfo); } } } }