From ae15706b49b763bad609584b6b4f984ffc7e0d42 Mon Sep 17 00:00:00 2001 From: Svein Ove Aas Date: Fri, 15 Feb 2019 01:01:33 +0000 Subject: [PATCH] Replace the lamps' mob-killing logic with mob-blocking logic. --- src/main/java/mods/eln/Eln.java | 8 +- .../eln/eventhandlers/MonsterEventHandler.kt | 61 ++++++++ src/main/java/mods/eln/misc/ocTree.kt | 141 ++++++++++++++++++ .../java/mods/eln/server/ConsoleListener.java | 26 ---- .../mods/eln/server/ServerEventListener.java | 8 +- .../mods/eln/sim/MonsterPopFreeProcess.java | 56 ------- .../sixnode/lampsocket/LampSocketElement.java | 10 +- .../sixnode/lampsocket/LampSocketProcess.java | 25 ++++ 8 files changed, 245 insertions(+), 90 deletions(-) create mode 100644 src/main/java/mods/eln/eventhandlers/MonsterEventHandler.kt create mode 100644 src/main/java/mods/eln/misc/ocTree.kt delete mode 100644 src/main/java/mods/eln/sim/MonsterPopFreeProcess.java diff --git a/src/main/java/mods/eln/Eln.java b/src/main/java/mods/eln/Eln.java index 61ee66945..a5f021cf5 100644 --- a/src/main/java/mods/eln/Eln.java +++ b/src/main/java/mods/eln/Eln.java @@ -295,8 +295,7 @@ public class Eln { public double fuelHeatFurnacePowerFactor = 1; public int autominerRange = 10; - public boolean killMonstersAroundLamps; - public int killMonstersAroundLampsRange; + public int blockMonstersAroundLampsRange; double stdBatteryHalfLife = 2 * Utils.minecraftDay; double batteryCapacityFactor = 1.; @@ -311,6 +310,7 @@ public class Eln { public static double maxSoundDistance = 16; private double cablePowerFactor; + private int lampMonsterBlockRange = 8; @EventHandler public void preInit(FMLPreInitializationEvent event) { @@ -389,6 +389,7 @@ public void preInit(FMLPreInitializationEvent event) { fuelGeneratorPowerFactor = config.get("balancing", "fuelGeneratorPowerFactor", 1).getDouble(1); fuelHeatFurnacePowerFactor = config.get("balancing", "fuelHeatFurnacePowerFactor", 1.0).getDouble(); autominerRange = config.get("balancing", "autominerRange", 10, "Maximum horizontal distance from autominer that will be mined").getInt(10); + lampMonsterBlockRange = config.get("balancing", "lampMonsterBlockRange", 8, "Maximum distance for which lamps block monster spawning", 0, 32768).getInt(); Other.ElnToIc2ConversionRatio = config.get("balancing", "ElnToIndustrialCraftConversionRatio", 1.0 / 3.0).getDouble(1.0 / 3.0); Other.ElnToOcConversionRatio = config.get("balancing", "ElnToOpenComputerConversionRatio", 1.0 / 3.0 / 2.5).getDouble(1.0 / 3.0 / 2.5); @@ -405,8 +406,7 @@ public void preInit(FMLPreInitializationEvent event) { replicatorPop = config.get("entity", "replicatorPop", true).getBoolean(true); ReplicatorPopProcess.popPerSecondPerPlayer = config.get("entity", "replicatorPopWhenThunderPerSecond", 1.0 / 120).getDouble(1.0 / 120); replicatorRegistrationId = config.get("entity", "replicatorId", -1).getInt(-1); - killMonstersAroundLamps = config.get("entity", "killMonstersAroundLamps", true).getBoolean(true); - killMonstersAroundLampsRange = config.get("entity", "killMonstersAroundLampsRange", 9).getInt(9); + blockMonstersAroundLampsRange = config.get("entity", "blockMonstersAroundLampsRange", 12).getInt(12); forceOreRegen = config.get("mapGenerate", "forceOreRegen", false).getBoolean(false); genCopper = config.get("mapGenerate", "copper", true).getBoolean(true); diff --git a/src/main/java/mods/eln/eventhandlers/MonsterEventHandler.kt b/src/main/java/mods/eln/eventhandlers/MonsterEventHandler.kt new file mode 100644 index 000000000..e120081d1 --- /dev/null +++ b/src/main/java/mods/eln/eventhandlers/MonsterEventHandler.kt @@ -0,0 +1,61 @@ +package mods.eln.eventhandlers + +import mods.eln.entity.ReplicatorEntity +import mods.eln.misc.Coordonate +import mods.eln.misc.OctoTree +import net.minecraft.entity.boss.EntityWither +import net.minecraftforge.event.entity.EntityJoinWorldEvent +import kotlin.math.roundToInt + +/** + * This is not an efficient way to do anything. + * + * The octree is a good start. It's fine in terms of CPU use, more or less, but that memory use scares me and is + * also entirely pointless. I'll have to find (or invent) a better algorithm. Though, the octree implementation + * supports clusterization so... maybe that's at least partially fine? + * + * It's also far too easy to imagine this counter getting desynced from reality. + * Although it seems to be working so far. + */ + +object MonsterEventHandler { + private val trees: MutableMap> = HashMap() + + fun onSpawn(event: EntityJoinWorldEvent) { + val entity = event.entity + if (entity is ReplicatorEntity || entity is EntityWither) { + return + } + val x = entity.posX.roundToInt() + 32768 + val y = entity.posY.roundToInt() + 32768 + val z = entity.posZ.roundToInt() + 32768 + if (trees[entity.dimension]?.get(x, y, z) ?: 0 > 0) { + event.isCanceled = true + } + } + + private fun doCube(center: Coordonate, range: Int, f: (Int) -> Int) { + if (range <= 0) return + val xOffset = center.x + 32768 + val yOffset = center.y + 32768 + val zOffset = center.z + 32768 + val tree = trees.getOrPut(center.dimention) { OctoTree(16) } + for (x in (xOffset - range)..(xOffset + range)) { + for (y in (yOffset - range)..(yOffset + range)) { + for (z in (zOffset - range)..(zOffset + range)) { + tree.set(x, y, z, f(tree.get(x, y, z) ?: 0)) + } + } + } + } + + fun registerMonsterBlock(coord: Coordonate, range: Int) { + println("Registering block at $coord - $range") + doCube(coord, range) { it + 1 } + } + + fun unregisterMonsterBlock(coord: Coordonate, range: Int) { + println("Unregistering block at $coord - $range") + doCube(coord, range) { Math.max(0, it - 1) } + } +} diff --git a/src/main/java/mods/eln/misc/ocTree.kt b/src/main/java/mods/eln/misc/ocTree.kt new file mode 100644 index 000000000..51d5cbf7b --- /dev/null +++ b/src/main/java/mods/eln/misc/ocTree.kt @@ -0,0 +1,141 @@ +package mods.eln.misc + +// Licensed under Apache 2.0 +// Copyright JetBrains, 2017 + +class OctoTree(val depth: Int) { + + private var root: Node? = null + private var actual = false + + //-------------------------------------------------------------------------// + + fun get(x: Int, y: Int, z: Int): T? { + var dep = depth + var iter = root + while (true) { + if (iter == null) return null + else if (iter is Node.Leaf) return iter.value + + iter = (iter as Node.Branch).nodes[number(x, y, z, --dep)] + } + } + + //-------------------------------------------------------------------------// + + fun set(x: Int, y: Int, z: Int, value: T) { + if (root == null) root = Node.Branch() + if (root!!.set(x, y, z, value, depth - 1)) { + root = Node.Leaf(value) + } + actual = false + } + + //-------------------------------------------------------------------------// + + override fun toString(): String = root.toString() + + //-------------------------------------------------------------------------// + + sealed class Node { + + abstract fun set(x: Int, y: Int, z: Int, value: T, depth: Int): Boolean + + //---------------------------------------------------------------------// + + class Leaf(var value: T) : Node() { + + override fun set(x: Int, y: Int, z: Int, value: T, depth: Int): Boolean { + throw UnsupportedOperationException("set on Leaf element") + } + + override fun toString(): String = "L{$value}" + } + + //---------------------------------------------------------------------// + + class Branch() : Node() { + + constructor(value: T, exclude: Int) : this() { + + var i = 0 + while (i < 8) { + if (i != exclude) { + nodes[i] = Leaf(value) + } + i++ + } + } + + private fun canClusterize(value: T): Boolean { + var i = 0 + while (i < 8) { + val w = nodes[i] + if (w == null || w !is Leaf || value != w.value) { + return false + } + i++ + } + return true + } + + override fun set(x: Int, y: Int, z: Int, value: T, depth: Int): Boolean { + val branchIndex = number(x, y, z, depth) + val node = nodes[branchIndex] + when (node) { + null -> { + if (depth == 0) { + nodes[branchIndex] = Leaf(value) + return canClusterize(value) + } else { + nodes[branchIndex] = Branch() + } + } + is Leaf -> { + if (node.value == value) { + return false + } else if (depth == 0) { + node.value = value + return canClusterize(value) + } + nodes[branchIndex] = Branch(node.value, number(x, y, z, depth - 1)) + } + } + + if (nodes[branchIndex]!!.set(x, y, z, value, depth - 1)) { + nodes[branchIndex] = Leaf(value) + return canClusterize(value) + } + return false + } + + val nodes = arrayOfNulls>(8) + override fun toString(): String = nodes.joinToString(prefix = "[", postfix = "]") + } + } + //-------------------------------------------------------------------------// + + companion object { + fun number(x: Int, y: Int, z: Int, depth: Int): Int { + val mask = 1 shl depth + if (x and mask != 0) { + if (y and mask != 0) { + if (z and mask != 0) + return 7 + return 6 + } + if (z and mask != 0) + return 5 + return 4 + } + if (y and mask != 0) { + if (z and mask != 0) + return 3 + return 2 + } + if (z and mask != 0) + return 1 + return 0 + } + } +} diff --git a/src/main/java/mods/eln/server/ConsoleListener.java b/src/main/java/mods/eln/server/ConsoleListener.java index d7ba4b696..6f751d14f 100644 --- a/src/main/java/mods/eln/server/ConsoleListener.java +++ b/src/main/java/mods/eln/server/ConsoleListener.java @@ -30,7 +30,6 @@ public class ConsoleListener extends CommandBase { private final String cmdNameStr_newWind = "newWind"; private final String cmdNameStr_regenOre = "regenOre"; private final String cmdNameStr_generateLangFileTemplate = "generateLangFileTemplate"; - private final String cmdNameStr_killMonstersAroundLamps = "killMonstersAroundLamps"; private final String strOffsetL0 = " "; private final String strOffsetL1 = " "; @@ -50,7 +49,6 @@ public ConsoleListener() { cmdVisibleList.add(cmdNameStr_newWind); cmdVisibleList.add(cmdNameStr_regenOre); cmdVisibleList.add(cmdNameStr_generateLangFileTemplate); - cmdVisibleList.add(cmdNameStr_killMonstersAroundLamps); java.util.Collections.sort(cmdVisibleList); } @@ -214,24 +212,9 @@ public void processCommand(ICommandSender ics, String[] astring) { cprint(ics, Color.COLOR_DARK_CYAN + "ELN > " + Color.COLOR_DARK_YELLOW + cmdNameStr_generateLangFileTemplate); cprint(ics, strOffsetL0 + "New language system parses source code, see here how to generate language " + "files: https://github.com/Electrical-Age/ElectricalAge"); - } else if (cmd.equalsIgnoreCase(cmdNameStr_killMonstersAroundLamps)) { - cprint(ics, Color.COLOR_DARK_CYAN + "ELN > " + Color.COLOR_DARK_YELLOW + cmdNameStr_killMonstersAroundLamps); - if (!checkArgCount(ics, astring, 1)) - return; - ConsoleArg arg0 = getArgBool(ics, astring[1]); - if (!arg0.valid) - return; - Eln.instance.killMonstersAroundLamps = arg0.value; - cprint(ics, strOffsetL0 + "Avoid monsters spawning around lamps : " + Color.COLOR_DARK_GREEN + boolToStr(arg0.value)); - cprint(ics, strOffsetL0 + "Warning: Command effective to this game instance only."); } else { cprint(ics, Color.COLOR_DARK_CYAN + "ELN > " + Color.COLOR_DARK_RED + "Error: Unknown command."); } - - return; - - //Eln.simulator.setSimplify(!astring[1].equals("0")); - //Eln.simulator.pleaseCrash = true; } private boolean checkArgCount(ICommandSender ics, String[] args, int exceptedArgc) { @@ -343,15 +326,6 @@ private void commandMan(ICommandSender ics, String cmd) { cprint(ics, strOffsetL0 + "Parameters :"); cprint(ics, strOffsetL1 + "@0:string : full file path."); cprint(ics, ""); - } else if (cmd.equalsIgnoreCase(cmdNameStr_killMonstersAroundLamps)) { - cprint(ics, strOffsetL0 + "When set, monsters don't spawn around the lamps (default)."); - cprint(ics, strOffsetL0 + "When clear, leaving lights on in dark zones is recommended..."); - cprint(ics, strOffsetL0 + "Effective only during this game instance."); - cprint(ics, strOffsetL0 + "(See \"Eln.cfg\" for permanent effect.)"); - cprint(ics, ""); - cprint(ics, strOffsetL0 + "Parameters :"); - cprint(ics, strOffsetL1 + "@0:bool : Enable/disable."); - cprint(ics, ""); } else { cprint(ics, Color.COLOR_DARK_RED + strOffsetL0 + "Error : Unknown/Undocumented command."); } diff --git a/src/main/java/mods/eln/server/ServerEventListener.java b/src/main/java/mods/eln/server/ServerEventListener.java index a3a54b88d..e3c81d65c 100644 --- a/src/main/java/mods/eln/server/ServerEventListener.java +++ b/src/main/java/mods/eln/server/ServerEventListener.java @@ -5,16 +5,20 @@ import cpw.mods.fml.common.gameevent.TickEvent.Phase; import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent; import mods.eln.Eln; +import mods.eln.eventhandlers.MonsterEventHandler; import mods.eln.item.electricalitem.TreeCapitation; import mods.eln.misc.Coordonate; import mods.eln.misc.Utils; import mods.eln.node.NodeManager; +import net.minecraft.entity.EntityCreature; import net.minecraft.entity.effect.EntityLightningBolt; +import net.minecraft.entity.monster.EntityMob; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.world.World; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.EntityEvent.EntityConstructing; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.event.world.WorldEvent.Load; import net.minecraftforge.event.world.WorldEvent.Save; @@ -46,9 +50,11 @@ public void tick(ServerTickEvent event) { } @SubscribeEvent - public void onNewEntity(EntityConstructing event) { + public void onNewEntity(EntityJoinWorldEvent event) { if (event.entity instanceof EntityLightningBolt) { lightningListNext.add((EntityLightningBolt) event.entity); + } else if (event.entity instanceof EntityMob) { + MonsterEventHandler.INSTANCE.onSpawn(event); } } diff --git a/src/main/java/mods/eln/sim/MonsterPopFreeProcess.java b/src/main/java/mods/eln/sim/MonsterPopFreeProcess.java deleted file mode 100644 index a714e3b19..000000000 --- a/src/main/java/mods/eln/sim/MonsterPopFreeProcess.java +++ /dev/null @@ -1,56 +0,0 @@ -package mods.eln.sim; - -import mods.eln.Eln; -import mods.eln.entity.ReplicatorEntity; -import mods.eln.misc.Coordonate; -import mods.eln.misc.Utils; -import net.minecraft.entity.boss.EntityWither; -import net.minecraft.entity.monster.EntityEnderman; -import net.minecraft.entity.monster.EntityMob; - -import java.util.List; - -public class MonsterPopFreeProcess implements IProcess { - - private Coordonate coordonate; - private int range; - - double timerCounter = 0; - final double timerPeriod = 0.212; - - List oldList = null; - - public MonsterPopFreeProcess(Coordonate coordonate, int range) { - this.coordonate = coordonate; - this.range = range; - } - - @Override - public void process(double time) { - //Monster killing must be active before continuing : - if (!Eln.instance.killMonstersAroundLamps) - return; - - timerCounter += time; - if (timerCounter > timerPeriod) { - timerCounter -= Utils.rand(1, 1.5) * timerPeriod; - List list = coordonate.world().getEntitiesWithinAABB(EntityMob.class, coordonate.getAxisAlignedBB(range + 8)); - - for (Object o : list) { - //Utils.println("MonsterPopFreeProcess : In range"); - EntityMob mob = (EntityMob) o; - if (oldList == null || !oldList.contains(o)) { - if (coordonate.distanceTo(mob) < range) { - //Utils.println("MonsterPopFreeProcess : Must die"); - if (!(o instanceof ReplicatorEntity) && !(o instanceof EntityWither) && !(o instanceof EntityEnderman)) { - mob.setDead(); - Utils.println("MonsterPopFreeProcess : Dead"); - } - } - } - } - oldList = list; - } - } - -} diff --git a/src/main/java/mods/eln/sixnode/lampsocket/LampSocketElement.java b/src/main/java/mods/eln/sixnode/lampsocket/LampSocketElement.java index d9f5b7cd7..e88b5988d 100644 --- a/src/main/java/mods/eln/sixnode/lampsocket/LampSocketElement.java +++ b/src/main/java/mods/eln/sixnode/lampsocket/LampSocketElement.java @@ -1,6 +1,7 @@ package mods.eln.sixnode.lampsocket; import mods.eln.Eln; +import mods.eln.eventhandlers.MonsterEventHandler; import mods.eln.generic.GenericItemUsingDamageDescriptor; import mods.eln.i18n.I18N; import mods.eln.item.BrushDescriptor; @@ -15,7 +16,6 @@ import mods.eln.node.six.SixNodeElement; import mods.eln.node.six.SixNodeElementInventory; import mods.eln.sim.ElectricalLoad; -import mods.eln.sim.MonsterPopFreeProcess; import mods.eln.sim.ThermalLoad; import mods.eln.sim.mna.component.Resistor; import mods.eln.sim.nbt.NbtElectricalLoad; @@ -37,7 +37,6 @@ public class LampSocketElement extends SixNodeElement { LampSocketDescriptor socketDescriptor = null; - public MonsterPopFreeProcess monsterPopFreeProcess = new MonsterPopFreeProcess(sixNode.coordonate, Eln.instance.killMonstersAroundLampsRange); public NbtElectricalLoad positiveLoad = new NbtElectricalLoad("positiveLoad"); public LampSocketProcess lampProcess = new LampSocketProcess(this); @@ -64,13 +63,13 @@ public class LampSocketElement extends SixNodeElement { public int paintColor = 15; + public LampSocketElement(SixNode sixNode, Direction side, SixNodeDescriptor descriptor) { super(sixNode, side, descriptor); this.socketDescriptor = (LampSocketDescriptor) descriptor; lampProcess.alphaZ = this.socketDescriptor.alphaZBoot; slowProcessList.add(lampProcess); - slowProcessList.add(monsterPopFreeProcess); } @@ -100,6 +99,9 @@ public void readFromNBT(NBTTagCompound nbt) { //Of course, maps need to be loaded with this code before set an already existing lamp paintable. paintColor = 0x0F; } + + // Monster block stuff: + lampProcess.monsterBlockTicksWhilePowerLoss = nbt.getInteger("mbpl"); } @Override @@ -109,6 +111,8 @@ public void writeToNBT(NBTTagCompound nbt) { nbt.setBoolean("poweredByLampSupply", poweredByLampSupply); nbt.setString("channel", channel); nbt.setByte("color", (byte) (paintColor)); + // Monster block stuff: + nbt.setInteger("mbpl", lampProcess.monsterBlockTicksWhilePowerLoss); } public void networkUnserialize(DataInputStream stream) { diff --git a/src/main/java/mods/eln/sixnode/lampsocket/LampSocketProcess.java b/src/main/java/mods/eln/sixnode/lampsocket/LampSocketProcess.java index 8e2e62df1..08e05a173 100644 --- a/src/main/java/mods/eln/sixnode/lampsocket/LampSocketProcess.java +++ b/src/main/java/mods/eln/sixnode/lampsocket/LampSocketProcess.java @@ -1,6 +1,7 @@ package mods.eln.sixnode.lampsocket; import mods.eln.Eln; +import mods.eln.eventhandlers.MonsterEventHandler; import mods.eln.generic.GenericItemUsingDamage; import mods.eln.generic.GenericItemUsingDamageDescriptor; import mods.eln.item.LampDescriptor; @@ -23,6 +24,8 @@ import java.io.IOException; import java.util.List; +import static mods.eln.Eln.instance; + public class LampSocketProcess implements IProcess, INBTTReady /*,LightBlockObserver*/ { double time = 0; @@ -47,6 +50,11 @@ public class LampSocketProcess implements IProcess, INBTTReady /*,LightBlockObse Coordonate lbCoord; + final static int monsterBlockTimeout = 24000; // 1 day + int monsterBlockTicksWhilePowerLoss = 0; + boolean monsterBlockRegistered = false; + + public LampSocketProcess(LampSocketElement l) { this.lamp = l; lbCoord = new Coordonate(l.sixNode.coordonate); @@ -262,6 +270,23 @@ public void process(double time) { lampStackLast = lampStack; placeSpot(newLight); + setMonsterBlocking(newLight); + } + + private void setMonsterBlocking(int newLight) { + if (newLight > 0) { + monsterBlockTicksWhilePowerLoss = 0; + } else { + monsterBlockTicksWhilePowerLoss++; + } + if (!monsterBlockRegistered && monsterBlockTicksWhilePowerLoss < monsterBlockTimeout) { + MonsterEventHandler.INSTANCE.registerMonsterBlock(myCoord(), Eln.instance.blockMonstersAroundLampsRange); + monsterBlockRegistered = true; + } + if (monsterBlockRegistered && monsterBlockTicksWhilePowerLoss > monsterBlockTimeout) { + MonsterEventHandler.INSTANCE.unregisterMonsterBlock(myCoord(), Eln.instance.blockMonstersAroundLampsRange); + monsterBlockRegistered = false; + } } // ElectricalConnectionOneWay connection = null;