rewrite zone planner map generation
This commit is contained in:
parent
393337e7c1
commit
f7e5e04244
5 changed files with 176 additions and 156 deletions
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
25
common/buildcraft/robotics/map/MapUtils.java
Normal file
25
common/buildcraft/robotics/map/MapUtils.java
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue