Overhauled Tick Sending by CommonTickHandler

Overhauled the way in which CommonTickHandler triggers tick-based
actions such as Limbo decay, spawning Monoliths, and regenerating rifts.
Now CommonTickHandler implements an interface called IRegularTickSender,
which indicates that it will periodically call on classes that implement
IRegulatTickReceiver to perform some task. I added classes for each
regularly scheduled task we were performing: MonolithSpawner and
RiftRegenerator, plus converted LimboDecay to a normal class instead of
a static class.  Modified several classes so that they have access to
the MonolithSpawner instance to request MonolithSpawning when needed.
This improves the structure of our code and gets us away from the way we
did things before, which was accessing a public static list inside
CommonTickHandler from other classes and adding arrays to specify chunk
coordinates. We should not be exposing the internal state of classes
like that! And we should be using clearly defined objects to pass
information.
This commit is contained in:
SenseiKiwi
2013-07-26 05:15:44 -04:00
parent bfc532da1f
commit 687a75f4d5
15 changed files with 461 additions and 366 deletions

View File

@@ -2,45 +2,29 @@ package StevenDimDoors.mod_pocketDim.ticking;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Random;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import StevenDimDoors.mod_pocketDim.DDProperties;
import StevenDimDoors.mod_pocketDim.DimData;
import StevenDimDoors.mod_pocketDim.LimboDecay;
import StevenDimDoors.mod_pocketDim.LinkData;
import StevenDimDoors.mod_pocketDim.TileEntityRift;
import StevenDimDoors.mod_pocketDim.mod_pocketDim;
import StevenDimDoors.mod_pocketDim.helpers.dimHelper;
import StevenDimDoors.mod_pocketDim.helpers.yCoordHelper;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.ITickHandler;
import cpw.mods.fml.common.TickType;
import cpw.mods.fml.relauncher.Side;
public class CommonTickHandler implements ITickHandler
public class CommonTickHandler implements ITickHandler, IRegularTickSender
{
private int tickCount = 0;
private static DDProperties properties = null;
public static ArrayList<int[]> chunksToPopulate = new ArrayList<int[]>();
private static final Random rand = new Random();
public static final int MAX_MONOLITH_SPAWNING_CHANCE = 100;
private static final String PROFILING_LABEL = "Dimensional Doors: Common Tick";
private static final String MOB_SPAWNING_RULE = "doMobSpawning";
private static final int MAX_MONOLITH_SPAWN_Y = 245;
private static final int CHUNK_SIZE = 16;
private static final int RIFT_REGENERATION_INTERVAL = 100; //Regenerate random rifts every 100 ticks
private static final int LIMBO_DECAY_INTERVAL = 10; //Apply spread decay every 10 ticks
private int tickCount = 0;
private ArrayList<RegularTickReceiverInfo> receivers;
public CommonTickHandler()
{
if (properties == null)
properties = DDProperties.instance();
this.receivers = new ArrayList<RegularTickReceiverInfo>();
}
@Override
public void registerForTicking(IRegularTickReceiver receiver, int interval, boolean onTickStart)
{
RegularTickReceiverInfo info = new RegularTickReceiverInfo(receiver, interval, onTickStart);
receivers.add(info);
}
@Override
@@ -48,35 +32,34 @@ public class CommonTickHandler implements ITickHandler
{
if (type.equals(EnumSet.of(TickType.SERVER)))
{
onServerTick();
for (RegularTickReceiverInfo info : receivers)
{
if (info.OnTickStart && tickCount % info.Interval == 0)
{
info.RegularTickReceiver.notifyTick();
}
}
}
//TODO: Stuck this in here because it's already rather hackish.
//We should standardize this as an IRegularTickReceiver in the future. ~SenseiKiwi
if (mod_pocketDim.teleTimer > 0)
{
mod_pocketDim.teleTimer--;
}
}
@Override
public void tickEnd(EnumSet<TickType> type, Object... tickData)
{
if (type.equals(EnumSet.of(TickType.SERVER)))
for (RegularTickReceiverInfo info : receivers)
{
if(!CommonTickHandler.chunksToPopulate.isEmpty() && IsMobSpawningAllowed())
if (!info.OnTickStart && tickCount % info.Interval == 0)
{
//TODO: This is bad. =/ We should not be passing around arrays of magic numbers.
//We should have an object that contains this information. ~SenseiKiwi
for (int[] chunkData : CommonTickHandler.chunksToPopulate)
{
if(chunkData[0] == properties.LimboDimensionID)
{
this.placeMonolithsInLimbo(chunkData[0], chunkData[1], chunkData[2]);
}
else
{
this.placeMonolithsInPockets(chunkData[0], chunkData[1], chunkData[2]);
}
}
info.RegularTickReceiver.notifyTick();
}
CommonTickHandler.chunksToPopulate.clear();
}
tickCount++; //There is no need to reset the counter. Let it overflow.
}
@Override
@@ -90,206 +73,4 @@ public class CommonTickHandler implements ITickHandler
{
return PROFILING_LABEL; //Used for profiling!
}
private void placeMonolithsInPockets(int worldID, int chunkX, int chunkZ)
{
World worldObj = dimHelper.getWorld(worldID);
DimData dimData = dimHelper.dimList.get(worldObj.provider.dimensionId);
int sanity = 0;
int blockID = 0;
boolean didSpawn=false;
if (dimData == null ||
dimData.dungeonGenerator == null ||
dimData.dungeonGenerator.isOpen)
{
return;
}
//The following initialization code is based on code from ChunkProviderGenerate.
//It makes our generation depend on the world seed.
Random random = new Random(worldObj.getSeed());
long factorA = random.nextLong() / 2L * 2L + 1L;
long factorB = random.nextLong() / 2L * 2L + 1L;
random.setSeed(chunkX * factorA + chunkZ * factorB ^ worldObj.getSeed());
int x, y, z;
do
{
//Select a random column within the chunk
x = chunkX * CHUNK_SIZE + random.nextInt(CHUNK_SIZE);
z = chunkZ * CHUNK_SIZE + random.nextInt(CHUNK_SIZE);
y = MAX_MONOLITH_SPAWN_Y;
blockID = worldObj.getBlockId(x, y, z);
while (blockID == 0 &&y>0)
{
y--;
blockID = worldObj.getBlockId(x, y, z);
}
while((blockID == mod_pocketDim.blockDimWall.blockID||blockID == mod_pocketDim.blockDimWallPerm.blockID)&&y>0)
{
y--;
blockID = worldObj.getBlockId(x, y, z);
}
while (blockID == 0 &&y>0)
{
y--;
blockID = worldObj.getBlockId(x, y, z);
}
if(y > 0)
{
int jumpSanity=0;
int jumpHeight=0;
do
{
jumpHeight = y+random.nextInt(10);
jumpSanity++;
}
while(!worldObj.isAirBlock(x,jumpHeight+6 , z)&&jumpSanity<20);
Entity mob = new MobObelisk(worldObj);
mob.setLocationAndAngles(x, jumpHeight, z, 1, 1);
worldObj.spawnEntityInWorld(mob);
didSpawn=true;
}
sanity++;
}
while (sanity<5&&!didSpawn);
}
private void placeMonolithsInLimbo(int worldID, int var2, int var3)
{
World world = dimHelper.getWorld(worldID);
if (rand.nextInt(MAX_MONOLITH_SPAWNING_CHANCE) < properties.MonolithSpawningChance)
{
int y =0;
int x = var2*16 + rand.nextInt(16);
int z = var3*16 + rand.nextInt(16);
int yTest;
do
{
x = var2*16 + rand.nextInt(16);
z = var3*16 + rand.nextInt(16);
while(world.getBlockId(x, y, z)==0&&y<255)
{
y++;
}
y = yCoordHelper.getFirstUncovered(world,x , y+2, z);
yTest=yCoordHelper.getFirstUncovered(world,x , y+5, z);
if(yTest>245)
{
return;
}
int jumpSanity=0;
int jumpHeight=0;
do
{
jumpHeight = y+rand.nextInt(25);
jumpSanity++;
}
while(!world.isAirBlock(x,jumpHeight+6 , z)&&jumpSanity<20);
Entity mob = new MobObelisk(world);
mob.setLocationAndAngles(x, jumpHeight, z, 1, 1);
world.spawnEntityInWorld(mob);
}
while (yTest > y);
}
}
private static boolean IsMobSpawningAllowed()
{
//This function is used to retrieve the value of doMobSpawning. The code is the same
//as the code used by Minecraft. Jaitsu requested this to make testing easier. ~SenseiKiwi
GameRules rules = MinecraftServer.getServer().worldServerForDimension(0).getGameRules();
return rules.getGameRuleBooleanValue(MOB_SPAWNING_RULE);
}
private void onServerTick()
{
tickCount++; //There is no need to reset the counter. Let it overflow. Really.
if (tickCount % RIFT_REGENERATION_INTERVAL == 0)
{
regenerateRifts();
}
if (tickCount % LIMBO_DECAY_INTERVAL == 0)
{
LimboDecay.applyRandomFastDecay();
}
if (mod_pocketDim.teleTimer > 0)
{
mod_pocketDim.teleTimer--;
}
}
private void regenerateRifts()
{
try
{
//Regenerate rifts that have been replaced (not permanently removed) by players
int i = 0;
while (i < 15 && FMLCommonHandler.instance().getEffectiveSide() == Side.SERVER)
{
i++;
LinkData link;
//actually gets the random rift based on the size of the list
link = (LinkData) dimHelper.instance.getRandomLinkData(true);
if(link!=null)
{
if (dimHelper.getWorld(link.locDimID)!=null)
{
World world = dimHelper.getWorld(link.locDimID);
int blocktoReplace = world.getBlockId(link.locXCoord, link.locYCoord, link.locZCoord);
if(!mod_pocketDim.blocksImmuneToRift.contains(blocktoReplace))//makes sure the rift doesn't replace a door or something
{
if(dimHelper.instance.getLinkDataFromCoords(link.locXCoord, link.locYCoord, link.locZCoord, link.locDimID) != null)
{
dimHelper.getWorld(link.locDimID).setBlock(link.locXCoord, link.locYCoord, link.locZCoord, properties.RiftBlockID);
TileEntityRift.class.cast(dimHelper.getWorld(link.locDimID).getBlockTileEntity(link.locXCoord, link.locYCoord, link.locZCoord)).hasGrownRifts=true;
}
}
}
}
}
}
catch (Exception e)
{
System.out.println("An exception occurred in CommonTickHandler.onServerTick():");
e.printStackTrace();
}
}
}