rewrite zone planner map generation

This commit is contained in:
Adrian 2015-06-18 16:08:28 +02:00
parent 393337e7c1
commit f7e5e04244
5 changed files with 176 additions and 156 deletions

View file

@ -24,6 +24,7 @@ import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.event.FMLServerStartedEvent;
import cpw.mods.fml.common.event.FMLServerStartingEvent;
import cpw.mods.fml.common.event.FMLServerStoppingEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@ -183,14 +184,11 @@ public class BuildCraftRobotics extends BuildCraftMod {
public static MapManager manager;
private static Thread managerThread;
private boolean noThreadedZoneMapGen;
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent evt) {
new BCCreativeTab("boards");
BuildCraftCore.mainConfigManager.register("general", "boards.blacklist", new String[]{}, "Blacklisted robots boards", ConfigManager.RestartRequirement.GAME);
BuildCraftCore.mainConfigManager.register("experimental", "disableThreadedZoneMapGen", false, "If you're getting frequent EntityTracker crashes, report and turn this on! The option will be removed when we're sure we resolved the bug.\nDO NOT turn this option on if you're not experiencing any issues as it WILL cause slower game performance.", ConfigManager.RestartRequirement.GAME);
reloadConfig(ConfigManager.RestartRequirement.GAME);
@ -387,24 +385,26 @@ public class BuildCraftRobotics extends BuildCraftMod {
}
}
@Mod.EventHandler
public void serverUnload(FMLServerStoppingEvent event) {
private void stopMapManager() {
if (manager != null) {
manager.stop();
manager.saveAllWorlds();
}
if (managerThread != null) {
managerThread.interrupt();
MinecraftForge.EVENT_BUS.unregister(manager);
FMLCommonHandler.instance().bus().unregister(manager);
}
if (managerThread != null) {
managerThread.interrupt();
}
managerThread = null;
manager = null;
}
@Mod.EventHandler
public void serverUnload(FMLServerStoppingEvent event) {
stopMapManager();
}
@Mod.EventHandler
public void serverLoad(FMLServerStartingEvent event) {
File f = new File(DimensionManager.getCurrentSaveRootDirectory(), "buildcraft/zonemap");
@ -415,16 +415,21 @@ public class BuildCraftRobotics extends BuildCraftMod {
e.printStackTrace();
}
manager = new MapManager(f, !noThreadedZoneMapGen);
if (noThreadedZoneMapGen) {
managerThread = new Thread(manager);
managerThread.start();
}
stopMapManager();
manager = new MapManager(f);
managerThread = new Thread(manager);
managerThread.start();
MinecraftForge.EVENT_BUS.register(manager);
FMLCommonHandler.instance().bus().register(manager);
}
@Mod.EventHandler
public void serverLoadFinish(FMLServerStartedEvent event) {
manager.initialize();
}
@Mod.EventHandler
public void processRequests(FMLInterModComms.IMCEvent event) {
InterModComms.processIMC(event);
@ -432,7 +437,6 @@ public class BuildCraftRobotics extends BuildCraftMod {
public void reloadConfig(ConfigManager.RestartRequirement restartType) {
if (restartType == ConfigManager.RestartRequirement.GAME) {
noThreadedZoneMapGen = BuildCraftCore.mainConfigManager.get("experimental.disableThreadedZoneMapGen").getBoolean();
blacklistedRobots = new ArrayList<String>();
blacklistedRobots.addAll(Arrays.asList(BuildCraftCore.mainConfigManager.get("general",

View file

@ -9,16 +9,13 @@
package buildcraft.robotics;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import buildcraft.BuildCraftRobotics;
import buildcraft.core.GuiIds;
import buildcraft.core.lib.block.BlockBuildCraft;
import buildcraft.robotics.map.MapWorld;
public class BlockZonePlan extends BlockBuildCraft {
public BlockZonePlan() {
@ -45,23 +42,4 @@ public class BlockZonePlan extends BlockBuildCraft {
return true;
}
@Override
public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase entity, ItemStack stack) {
super.onBlockPlacedBy(world, x, y, z, entity, stack);
if (!world.isRemote) {
int r = TileZonePlan.RESOLUTION >> 4;
int cox = x >> 4;
int coz = z >> 4;
MapWorld w = BuildCraftRobotics.manager.getWorld(world);
for (int cx = -r; cx < r; cx++) {
for (int cz = -r; cz < r; cz++) {
int dist = cx * cx + cz * cz;
w.queueChunkForUpdateIfEmpty(cox + cx, coz + cz, dist);
}
}
}
}
}

View file

@ -6,30 +6,38 @@ import java.util.Date;
import com.google.common.collect.HashBiMap;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.chunk.IChunkProvider;
import net.minecraft.world.gen.ChunkProviderServer;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import cpw.mods.fml.relauncher.Side;
import net.minecraftforge.common.DimensionManager;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.event.world.ChunkEvent;
import net.minecraftforge.event.world.WorldEvent;
import buildcraft.core.lib.utils.Utils;
public class MapManager implements Runnable {
private static final int UPDATE_DELAY = 60000;
private final HashBiMap<World, MapWorld> worldMap = HashBiMap.create();
private final File location;
private final boolean isThreaded;
private boolean stop = false;
private long lastSaveTime;
public MapManager(File location, boolean isThreaded) {
public MapManager(File location) {
this.location = location;
this.isThreaded = isThreaded;
}
public void stop() {
stop = true;
saveAllWorlds();
}
public MapWorld getWorld(World world) {
if (world.isRemote) {
if (world == null || world.isRemote) {
return null;
}
@ -41,33 +49,65 @@ public class MapManager implements Runnable {
return worldMap.get(world);
}
private boolean doUpdate(MapWorld world, Chunk chunk) {
int x = chunk.xPosition;
int z = chunk.zPosition;
long updateTime = (new Date()).getTime() - UPDATE_DELAY;
return world.getUpdateTime(x, z) < updateTime || !world.hasChunk(x, z);
}
private void updateChunk(World rworld, Chunk chunk, boolean force) {
MapWorld world = getWorld(rworld);
if (world != null && (force || doUpdate(world, chunk))) {
world.updateChunk(chunk);
}
}
private void updateChunkDelayed(World rworld, Chunk chunk, boolean force, byte time) {
MapWorld world = getWorld(rworld);
if (world != null && (force || doUpdate(world, chunk))) {
world.updateChunkDelayed(chunk, time);
}
}
@SubscribeEvent
public void serverTick(TickEvent.ServerTickEvent event) {
if (!isThreaded && event.phase == TickEvent.Phase.END) {
public void tickDelayedWorlds(TickEvent.WorldTickEvent event) {
if (event.phase == TickEvent.Phase.END && event.side == Side.SERVER) {
MapWorld w = worldMap.get(event.world);
if (w != null) {
w.tick();
}
}
}
@SubscribeEvent
public void worldUnloaded(WorldEvent.Unload event) {
if (worldMap.containsKey(event.world)) {
worldMap.get(event.world).save();
synchronized (worldMap) {
for (MapWorld world : worldMap.values()) {
world.updateChunkInQueue();
}
worldMap.remove(event.world);
}
}
}
@SubscribeEvent
public void chunkLoaded(ChunkEvent.Load event) {
MapWorld world = getWorld(event.getChunk().worldObj);
if (world != null) {
world.queueChunkForUpdateIfEmpty(event.getChunk().xPosition, event.getChunk().zPosition, 99999);
}
updateChunkDelayed(event.world, event.getChunk(), false, (byte) (40 + Utils.RANDOM.nextInt(20)));
}
@SubscribeEvent
public void chunkUnloaded(ChunkEvent.Unload event) {
updateChunk(event.world, event.getChunk(), false);
}
@SubscribeEvent
public void blockPlaced(BlockEvent.PlaceEvent placeEvent) {
Chunk chunk = placeEvent.world.getChunkFromBlockCoords(placeEvent.x, placeEvent.z);
MapWorld world = getWorld(placeEvent.world);
if (world != null) {
if (world != null && doUpdate(world, chunk)) {
int hv = placeEvent.world.getHeightValue(placeEvent.x, placeEvent.z);
if (placeEvent.y >= (hv - 4)) {
world.queueChunkForUpdate(chunk.xPosition, chunk.zPosition, 512);
if (placeEvent.y >= (hv - 3)) {
world.updateChunk(chunk);
}
}
}
@ -76,15 +116,14 @@ public class MapManager implements Runnable {
public void blockBroken(BlockEvent.BreakEvent placeEvent) {
Chunk chunk = placeEvent.world.getChunkFromBlockCoords(placeEvent.x, placeEvent.z);
MapWorld world = getWorld(placeEvent.world);
if (world != null) {
if (world != null && doUpdate(world, chunk)) {
int hv = placeEvent.world.getHeightValue(placeEvent.x, placeEvent.z);
if (placeEvent.y >= (hv - 4)) {
world.queueChunkForUpdate(chunk.xPosition, chunk.zPosition, 512);
if (placeEvent.y >= (hv - 3)) {
world.updateChunk(chunk);
}
}
}
public void saveAllWorlds() {
synchronized (worldMap) {
for (MapWorld world : worldMap.values()) {
@ -98,12 +137,6 @@ public class MapManager implements Runnable {
lastSaveTime = (new Date()).getTime();
while (!stop) {
synchronized (worldMap) {
for (MapWorld world : worldMap.values()) {
world.updateChunkInQueue();
}
}
long now = (new Date()).getTime();
if (now - lastSaveTime > 120000) {
@ -112,10 +145,27 @@ public class MapManager implements Runnable {
}
try {
Thread.sleep(20 * worldMap.size());
Thread.sleep(4000);
} catch (Exception e) {
}
}
}
public void initialize() {
for (WorldServer ws : DimensionManager.getWorlds()) {
MapWorld mw = getWorld(ws);
IChunkProvider provider = ws.getChunkProvider();
if (provider instanceof ChunkProviderServer) {
for (Object o: ((ChunkProviderServer) provider).func_152380_a()) {
if (o != null && o instanceof Chunk) {
Chunk c = (Chunk) o;
if (!mw.hasChunk(c.xPosition, c.zPosition)) {
mw.updateChunkDelayed(c, (byte) (40 + Utils.RANDOM.nextInt(20)));
}
}
}
}
}
}
}

View file

@ -0,0 +1,25 @@
package buildcraft.robotics.map;
public final class MapUtils {
private MapUtils() {
}
public static long getIDFromCoords(int x, int z) {
return ((x & 0xFFFFFF) << 24) | (z & 0xFFFFFF);
}
public static int getXFromID(long id) {
return (int) ((id >> 24) & 0xFFFFFFFF);
}
public static int getZFromID(long id) {
int z = (int) (id & 0xFFFFFF);
if (z >= 0x800000) {
return -(z ^ 0xFFFFFF);
} else {
return z;
}
}
}

View file

@ -5,60 +5,35 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Set;
import gnu.trove.map.hash.TLongObjectHashMap;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.map.hash.TLongLongHashMap;
import gnu.trove.set.hash.TLongHashSet;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import buildcraft.core.lib.utils.NBTUtils;
import buildcraft.core.lib.utils.ThreadSafeUtils;
public class MapWorld {
private final World world;
private final TLongObjectHashMap<MapRegion> regionMap;
private final Set<QueuedXZ> regionUpdateSet = new HashSet<QueuedXZ>();
private final Queue<QueuedXZ> queuedChunks;
private final LongHashMap regionMap;
private final HashMap<Chunk, Integer> timeToUpdate = new HashMap<Chunk, Integer>();
private final TLongLongHashMap regionUpdateTime;
private final TLongHashSet updatedChunks;
private final File location;
private class QueuedXZ {
int x, z, p;
QueuedXZ(int x, int z, int p) {
this.x = x;
this.z = z;
this.p = p;
}
@Override
public boolean equals(Object other) {
if (other == null || !(other instanceof QueuedXZ)) {
return false;
}
return ((QueuedXZ) other).x == x && ((QueuedXZ) other).z == z;
}
@Override
public int hashCode() {
return x * 31 + z;
}
}
public MapWorld(World world, File location) {
this.world = world;
regionMap = new TLongObjectHashMap<MapRegion>();
queuedChunks = new PriorityQueue<QueuedXZ>(11, new Comparator<QueuedXZ>() {
@Override
public int compare(QueuedXZ c1, QueuedXZ c2) {
return (c1 != null ? c1.p : 0) - (c2 != null ? c2.p : 0);
}
});
regionMap = new LongHashMap();
regionUpdateTime = new TLongLongHashMap();
updatedChunks = new TLongHashSet();
String saveFolder = world.provider.getSaveFolder();
if (saveFolder == null) {
@ -72,13 +47,9 @@ public class MapWorld {
}
}
private long getXzId(int x, int z) {
return (x << 24) | z;
}
private MapRegion getRegion(int x, int z) {
long id = getXzId(x, z);
MapRegion region = regionMap.get(id);
long id = MapUtils.getIDFromCoords(x, z);
MapRegion region = (MapRegion) regionMap.getValueByKey(id);
if (region == null) {
region = new MapRegion(x, z);
@ -99,7 +70,7 @@ public class MapWorld {
}
}
regionMap.put(id, region);
regionMap.add(id, region);
}
return region;
}
@ -114,46 +85,14 @@ public class MapWorld {
return region.hasChunk(x & 15, z & 15);
}
public void queueChunkForUpdate(int x, int z, int priority) {
synchronized (queuedChunks) {
queuedChunks.add(new QueuedXZ(x, z, priority));
}
}
public void queueChunkForUpdateIfEmpty(int x, int z, int priority) {
if (!hasChunk(x, z)) {
queueChunkForUpdate(x, z, priority);
}
}
public void updateChunkInQueue() {
synchronized (queuedChunks) {
if (queuedChunks.size() == 0) {
return;
}
QueuedXZ q = queuedChunks.remove();
if (q == null) {
return;
}
if (world.getChunkProvider().chunkExists(q.x, q.z)) {
updateChunk(q.x, q.z);
}
}
}
public void save() {
Iterator<QueuedXZ> i = regionUpdateSet.iterator();
TLongIterator i = updatedChunks.iterator();
while (i.hasNext()) {
QueuedXZ id = i.next();
long id = i.next();
i.remove();
if (id == null) {
continue;
}
MapRegion region = regionMap.get(getXzId(id.x, id.z));
MapRegion region = (MapRegion) regionMap.getValueByKey(id);
if (region == null) {
continue;
}
@ -161,7 +100,7 @@ public class MapWorld {
NBTTagCompound output = new NBTTagCompound();
region.writeToNBT(output);
byte[] data = NBTUtils.save(output);
File file = new File(location, "r" + id.x + "," + id.z + ".nbt");
File file = new File(location, "r" + MapUtils.getXFromID(id) + "," + MapUtils.getZFromID(id) + ".nbt");
try {
FileOutputStream f = new FileOutputStream(file);
@ -178,14 +117,38 @@ public class MapWorld {
return chunk.getColor(x & 15, z & 15);
}
private void updateChunk(int x, int z) {
MapChunk chunk = getChunk(x, z);
chunk.update(ThreadSafeUtils.getChunk(world, x, z));
regionUpdateSet.add(new QueuedXZ(x >> 4, z >> 4, 0));
// priority does not matter - see equals
synchronized (queuedChunks) {
queuedChunks.remove(new QueuedXZ(x, z, 0));
public void tick() {
if (timeToUpdate.size() > 0) {
synchronized (timeToUpdate) {
Set<Chunk> chunks = new HashSet<Chunk>();
chunks.addAll(timeToUpdate.keySet());
for (Chunk c : chunks) {
int v = timeToUpdate.get(c);
if (v > 1) {
timeToUpdate.put(c, v - 1);
} else {
timeToUpdate.remove(c);
updateChunk(c);
}
}
}
}
}
public void updateChunk(Chunk rchunk) {
long id = MapUtils.getIDFromCoords(rchunk.xPosition, rchunk.zPosition);
MapChunk chunk = getChunk(rchunk.xPosition, rchunk.zPosition);
chunk.update(rchunk);
updatedChunks.add(id);
timeToUpdate.remove(rchunk);
regionUpdateTime.put(id, (new Date()).getTime());
}
public long getUpdateTime(int x, int z) {
return regionUpdateTime.get(MapUtils.getIDFromCoords(x, z));
}
public void updateChunkDelayed(Chunk chunk, byte time) {
timeToUpdate.put(chunk, new Integer(time));
}
}