Major restructuring in block/chunk/region handling. Abstracted block and chunk interfaces.

This commit is contained in:
Justin Aquadro 2011-03-11 02:36:00 +00:00
parent d094a3cb47
commit 951e912a57
18 changed files with 1084 additions and 50 deletions

View file

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBToolkit
{
public class BlockManager
{
public const int MIN_X = -32000000;
public const int MAX_X = 32000000;
public const int MIN_Y = 0;
public const int MAX_Y = 128;
public const int MIN_Z = -32000000;
public const int MAX_Z = 32000000;
public const int CHUNK_XLEN = 16;
public const int CHUNK_YLEN = 128;
public const int CHUNK_ZLEN = 16;
public const int CHUNK_XLOG = 4;
public const int CHUNK_YLOG = 7;
public const int CHUNK_ZLOG = 4;
public const int CHUNK_XMASK = 0xF;
public const int CHUNK_YMASK = 0x7F;
public const int CHUNK_ZMASK = 0xF;
protected ChunkManager _chunkMan;
public BlockManager (ChunkManager cm)
{
_chunkMan = cm;
}
public int GetBlockID (int x, int y, int z)
{
if (x < MIN_X || x >= MAX_X)
return 0;
if (y < MIN_Y || y >= MAX_Y)
return 0;
if (z < MIN_Z || z >= MAX_Z)
return 0;
Chunk c = GetChunk(x, y, z);
if (c == null) {
return 0;
}
return c.GetBlockID(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
}
int GetBlockData (int x, int y, int z) { return 0; }
int GetBlockLight (int x, int y, int z) { return 0; }
int GetBlockSkylight (int x, int y, int z) { return 0; }
void SetBlock (int x, int y, int z, int id, int data) { }
public bool SetBlockID (int x, int y, int z, int id)
{
if (x < MIN_X || x >= MAX_X)
return false;
if (y < MIN_Y || y >= MAX_Y)
return false;
if (z < MIN_Z || z >= MAX_Z)
return false;
Chunk c = GetChunk(x, y, z);
if (c == null) {
return false;
}
return c.SetBlockID(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, id);
}
void SetBlockData (int x, int y, int z, int data) { }
public Chunk GetChunk (int x, int y, int z)
{
x >>= CHUNK_XLOG;
z >>= CHUNK_ZLOG;
return _chunkMan.GetChunk(x, z);
}
}
}

View file

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Text;
using NBT;
namespace NBToolkit
{
public class Chunk
{
protected int _cx;
protected int _cz;
protected NBT_Tree _nbt = null;
protected NBT_ByteArray _blocks = null;
protected bool _dirty = false;
protected ChunkManager _chunkMan;
public Chunk (ChunkManager cm, int cx, int cz)
{
_chunkMan = cm;
_cx = cx;
_cz = cz;
}
public int X
{
get
{
return _cx;
}
}
public int Z
{
get
{
return _cz;
}
}
public bool Save ()
{
if (_dirty) {
if (SaveTree()) {
_dirty = false;
return true;
}
return false;
}
return true;
}
protected NBT_Tree GetTree ()
{
if (_nbt != null) {
return _nbt;
}
Region r = _chunkMan.GetRegion(_cx, _cz);
_nbt = r.GetChunkTree(_cx & ChunkManager.REGION_XMASK, _cz & ChunkManager.REGION_ZMASK);
return _nbt;
}
protected bool SaveTree ()
{
if (_nbt != null) {
_blocks = null;
Region r = _chunkMan.GetRegion(_cx, _cz);
return r.SaveChunkTree(_cx & ChunkManager.REGION_XMASK, _cz & ChunkManager.REGION_ZMASK, _nbt);
}
return false;
}
public int GetBlockID (int x, int y, int z)
{
if (_blocks == null) {
_blocks = GetTree().getRoot().findTagByName("Level").findTagByName("Blocks").value.toByteArray();
}
return _blocks.data[x << 11 | z << 7 | y];
}
public bool SetBlockID (int x, int y, int z, int id)
{
if (_blocks == null) {
_blocks = GetTree().getRoot().findTagByName("Level").findTagByName("Blocks").value.toByteArray();
}
int index = x << 11 | z << 7 | y;
if (_blocks.data[index] == id) {
return false;
}
_blocks.data[index] = (byte)id;
return true;
}
}
}

View file

@ -0,0 +1,145 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace NBToolkit
{
public class ChunkList : IEnumerable<Chunk>
{
//private List<Region> _regions;
private ChunkManager _cm = null;
private Region _region = null;
// Constructor to enumerate a single region
public ChunkList (ChunkManager cm, Region region)
{
_cm = cm;
_region = region;
}
// Constructor to enumerate all regions
public ChunkList (ChunkManager cm)
{
_cm = cm;
}
IEnumerator IEnumerable.GetEnumerator ()
{
return (IEnumerator)GetEnumerator();
}
IEnumerator<Chunk> IEnumerable<Chunk>.GetEnumerator ()
{
return (IEnumerator<Chunk>)GetEnumerator();
}
public ChunkEnumerator GetEnumerator ()
{
return new ChunkEnumerator(_cm, _region);
}
}
public class ChunkEnumerator : IEnumerator<Chunk>
{
protected Region _region;
protected ChunkManager _cm;
protected RegionEnumerator _enum = null;
protected int _x = 0;
protected int _z = -1;
public ChunkEnumerator (ChunkManager cm, Region region)
{
_cm = cm;
_region = region;
if (_region == null) {
_enum = new RegionEnumerator(_cm.GetRegionManager());
_enum.MoveNext();
_region = _enum.Current;
}
}
public bool MoveNext ()
{
if (_enum == null) {
return MoveNextInRegion();
}
else {
while (true) {
if (_x >= ChunkManager.REGION_XLEN) {
if (!_enum.MoveNext()) {
return false;
}
_x = 0;
_z = -1;
}
if (MoveNextInRegion()) {
return true;
}
}
}
}
protected bool MoveNextInRegion ()
{
for (; _x < ChunkManager.REGION_XLEN; _x++) {
for (_z++; _z < ChunkManager.REGION_ZLEN; _z++) {
if (_region.ChunkExists(_x, _z)) {
goto FoundNext;
}
}
_z = 0;
}
FoundNext:
return (_x < ChunkManager.REGION_XLEN);
}
public void Reset ()
{
if (_enum != null) {
_enum.Reset();
_enum.MoveNext();
_region = _enum.Current;
}
_x = 0;
_z = -1;
}
void IDisposable.Dispose () { }
object IEnumerator.Current
{
get
{
return Current;
}
}
Chunk IEnumerator<Chunk>.Current
{
get
{
return Current;
}
}
public Chunk Current
{
get
{
if (_x >= ChunkManager.REGION_XLEN) {
throw new InvalidOperationException();
}
return _cm.GetChunkInRegion(_region, _x, _z);
}
}
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBToolkit
{
public class ChunkKey : IEquatable<ChunkKey>
{
protected int cx;
protected int cz;
public ChunkKey (int _cx, int _cz)
{
cx = _cx;
cz = _cz;
}
public bool Equals (ChunkKey ck)
{
return this.cx == ck.cx && this.cz == ck.cz;
}
public override int GetHashCode ()
{
return cx ^ cz;
}
}
}

View file

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBToolkit
{
public class ChunkManager
{
public const int REGION_XLEN = 32;
public const int REGION_ZLEN = 32;
public const int REGION_XLOG = 5;
public const int REGION_ZLOG = 5;
public const int REGION_XMASK = 0x1F;
public const int REGION_ZMASK = 0x1F;
protected RegionManager _regionMan;
protected Dictionary<ChunkKey, WeakReference> _cache;
public ChunkManager (RegionManager rm)
{
_regionMan = rm;
_cache = new Dictionary<ChunkKey, WeakReference>();
}
public Chunk GetChunk (int cx, int cz)
{
ChunkKey k = new ChunkKey(cx, cz);
Chunk c = null;
if (_cache.ContainsKey(k)) {
c = _cache[k].Target as Chunk;
}
else {
_cache.Add(k, new WeakReference(null));
}
if (c != null) {
return c;
}
c = new Chunk(this, cx, cz);
_cache[k].Target = c;
return c;
}
public Chunk GetChunkInRegion (Region r, int lcx, int lcz)
{
int cx = r.X * REGION_XLEN + lcx;
int cz = r.Z * REGION_ZLEN + lcz;
return GetChunk(cx, cz);
}
public Region GetRegion (int cx, int cz)
{
cx >>= REGION_XLOG;
cz >>= REGION_ZLOG;
return _regionMan.GetRegion(cx, cz);
}
public RegionManager GetRegionManager ()
{
return _regionMan;
}
}
}

View file

@ -9,7 +9,22 @@ namespace NBToolkit
class Harness
{
public void Run (TKOptions opt, TKFilter filter) {
string[] regions = RegionFileCache.GetRegionFileList(opt.OPT_WORLD);
World world = new World(opt.OPT_WORLD);
RegionList regions = new RegionList(world.GetRegionManager());
foreach (Region region in regions) {
Console.WriteLine(region.GetFileName());
for (int x = 0; x < ChunkManager.REGION_XLEN; x++) {
for (int z = 0; z < ChunkManager.REGION_ZLEN; z++) {
if (!region.ChunkExists(x, z)) {
continue;
}
}
}
}
/*string[] regions = RegionFileCache.GetRegionFileList(opt.OPT_WORLD);
foreach (string p in regions) {
Console.WriteLine(p);
@ -85,7 +100,7 @@ namespace NBToolkit
zipStream.Close();
}
}
}
}*/
}
protected bool BlockScan (NBT_Tag level, int val)

View file

@ -10,7 +10,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>NBToolkit</RootNamespace>
<AssemblyName>NBToolkit</AssemblyName>
<TargetFrameworkVersion>v3.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
@ -53,10 +53,17 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BlockManager.cs" />
<Compile Include="Chunk.cs" />
<Compile Include="ChunkManager.cs" />
<Compile Include="Harness.cs" />
<Compile Include="NBT.cs" />
<Compile Include="NDesk\Options.cs" />
<Compile Include="Oregen.cs" />
<Compile Include="Region.cs" />
<Compile Include="RegionEnumerator.cs" />
<Compile Include="RegionKey.cs" />
<Compile Include="RegionManager.cs" />
<Compile Include="Replace.cs" />
<Compile Include="TKFilter.cs" />
<Compile Include="TKOptions.cs" />
@ -64,6 +71,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RegionFile.cs" />
<Compile Include="RegionFileCache.cs" />
<Compile Include="World.cs" />
<Compile Include="Zlib\Crc32.cs" />
<Compile Include="Zlib\Deflate.cs" />
<Compile Include="Zlib\DeflateStream.cs" />

Binary file not shown.

View file

@ -59,7 +59,7 @@ namespace NBToolkit
{ "max=", "Generates deposits no higher than depth {VAL} (0-127)",
v => OPT_MAX = Convert.ToInt32(v) % 128 },
{ "s|size=", "Generates deposits containing roughly up to {VAL} blocks",
v => OPT_MIN = Convert.ToInt32(v) % 128 },
v => OPT_SIZE = Convert.ToInt32(v) % 128 },
{ "oo=", "Generated deposits can replace other existing ores",
v => OPT_OO = true },
{ "oa=", "Generated deposits can replace any existing block (incl. air)",
@ -140,6 +140,95 @@ namespace NBToolkit
}
}
public class MathHelper
{
private static float[] trigTable = new float[65536];
static MathHelper ()
{
for (int i = 0; i < 65536; i++) {
trigTable[i] = (float)Math.Sin(i * Math.PI * 2.0D / 65536.0D);
}
}
public static float Sin(float angle)
{
return trigTable[((int)(angle * 10430.378F) & 0xFFFF)];
}
public static float Cos(float angle) {
return trigTable[((int)(angle * 10430.378F + 16384.0F) & 0xFFFF)];
}
}
public class NativeOreGen {
private int _blockId;
private int _size;
private static Random rand = new Random();
public NativeOreGen(int blockId, int size)
{
_blockId = blockId;
_size = size;
}
public bool GenerateDeposit (NBT_ByteArray blocks, NBT_ByteArray data, int x, int y, int z)
{
float rpi = (float)(rand.NextDouble() * Math.PI);
double x1 = x + 8 + MathHelper.Sin(rpi) * _size / 8.0F;
double x2 = x + 8 - MathHelper.Sin(rpi) * _size / 8.0F;
double z1 = z + 8 + MathHelper.Cos(rpi) * _size / 8.0F;
double z2 = z + 8 - MathHelper.Cos(rpi) * _size / 8.0F;
double y1 = y + rand.Next(3) + 2;
double y2 = y + rand.Next(3) + 2;
for (int i = 0; i <= _size; i++) {
double xPos = x1 + (x2 - x1) * i / _size;
double yPos = y1 + (y2 - y1) * i / _size;
double zPos = z1 + (z2 - z1) * i / _size;
double fuzz = rand.NextDouble() * _size / 16.0D;
double fuzzXZ = (MathHelper.Sin((float)(i * Math.PI / _size)) + 1.0F) * fuzz + 1.0D;
double fuzzY = (MathHelper.Sin((float)(i * Math.PI / _size)) + 1.0F) * fuzz + 1.0D;
int xStart = (int)(xPos - fuzzXZ / 2.0D);
int yStart = (int)(yPos - fuzzY / 2.0D);
int zStart = (int)(zPos - fuzzXZ / 2.0D);
int xEnd = (int)(xPos + fuzzXZ / 2.0D);
int yEnd = (int)(yPos + fuzzY / 2.0D);
int zEnd = (int)(zPos + fuzzXZ / 2.0D);
for (int ix = xStart; ix <= xEnd; ix++) {
double xThresh = (ix + 0.5D - xPos) / (fuzzXZ / 2.0D);
if (xThresh * xThresh < 1.0D) {
for (int iy = yStart; iy <= yEnd; iy++) {
double yThresh = (iy + 0.5D - yPos) / (fuzzY / 2.0D);
if (xThresh * xThresh + yThresh * yThresh < 1.0D) {
for (int iz = zStart; iz <= zEnd; iz++) {
double zThresh = (iz + 0.5D - zPos) / (fuzzXZ / 2.0D);
if (xThresh * xThresh + yThresh * yThresh + zThresh * zThresh < 1.0D) {
//Apply
}
}
}
}
}
}
}
return true;
}
int BlockIndex (int x, int y, int z)
{
return y + (z * 128 + x * 128 * 16);
}
}
public class Oregen : TKFilter
{
public const int BLOCK_STONE = 1;

View file

@ -8,9 +8,9 @@ using System.Runtime.InteropServices;
[assembly: AssemblyTitle("NBToolkit")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NBToolkit")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyCopyright("Copyright © Justin Aquadro 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

View file

@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
using NBT;
namespace NBToolkit
{
public class Region
{
protected int _rx;
protected int _rz;
protected RegionManager _regionMan;
protected static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
protected WeakReference _regionFile;
public Region (RegionManager rm, int rx, int rz)
{
_regionMan = rm;
_regionFile = new WeakReference(null);
_rx = rx;
_rz = rz;
if (!File.Exists(GetFilePath())) {
throw new FileNotFoundException();
}
}
public Region (RegionManager rm, string filename)
{
_regionMan = rm;
_regionFile = new WeakReference(null);
ParseFileName(filename, out _rx, out _rz);
if (!File.Exists(Path.Combine(_regionMan.GetRegionPath(), filename))) {
throw new FileNotFoundException();
}
}
public int X
{
get
{
return _rx;
}
}
public int Z
{
get
{
return _rz;
}
}
public string GetFileName ()
{
return "r." + _rx + "." + _rz + ".mcr";
}
public static bool TestFileName (string filename)
{
Match match = _namePattern.Match(filename);
if (!match.Success) {
return false;
}
return true;
}
public static bool ParseFileName (string filename, out int x, out int z)
{
x = 0;
z = 0;
Match match = _namePattern.Match(filename);
if (!match.Success) {
return false;
}
x = Convert.ToInt32(match.Groups[1].Value);
z = Convert.ToInt32(match.Groups[2].Value);
return true;
}
public string GetFilePath ()
{
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
}
protected RegionFile GetRegionFile ()
{
RegionFile rf = _regionFile.Target as RegionFile;
if (rf == null) {
rf = new RegionFile(GetFilePath());
_regionFile.Target = rf;
}
return rf;
}
public NBT_Tree GetChunkTree (int lcx, int lcz)
{
RegionFile rf = GetRegionFile();
Stream nbtstr = rf.GetChunkDataInputStream(lcx, lcz);
if (nbtstr == null) {
return null;
}
return new NBT_Tree(nbtstr);
}
public bool SaveChunkTree (int lcx, int lcz, NBT_Tree tree)
{
RegionFile rf = GetRegionFile();
Stream zipstr = rf.GetChunkDataOutputStream(lcx, lcz);
if (zipstr == null) {
return false;
}
tree.WriteTo(zipstr);
zipstr.Close();
return true;
}
public bool ChunkExists (int lcx, int lcz)
{
RegionFile rf = GetRegionFile();
return rf.HasChunk(lcx, lcz);
}
public int ChunkCount ()
{
RegionFile rf = GetRegionFile();
int count = 0;
for (int x = 0; x < ChunkManager.REGION_XLEN; x++) {
for (int z = 0; z < ChunkManager.REGION_ZLEN; z++) {
if (rf.HasChunk(x, z)) {
count++;
}
}
}
return count;
}
}
}

View file

@ -0,0 +1,131 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace NBToolkit
{
public class RegionList : IEnumerable<Region>
{
private List<Region> _regions;
public RegionList (List<Region> regs)
{
_regions = regs;
}
public RegionList (RegionManager rm)
{
_regions = new List<Region>();
if (!Directory.Exists(rm.GetRegionPath())) {
throw new DirectoryNotFoundException();
}
string[] files = Directory.GetFiles(rm.GetRegionPath());
_regions.Capacity = files.Length;
foreach (string file in files) {
try {
Region r = rm.GetRegion(file);
_regions.Add(r);
}
catch (ArgumentException) {
continue;
}
}
}
IEnumerator IEnumerable.GetEnumerator () {
return (IEnumerator)GetEnumerator();
}
IEnumerator<Region> IEnumerable<Region>.GetEnumerator ()
{
return (IEnumerator<Region>)GetEnumerator();
}
public RegionEnumerator GetEnumerator ()
{
return new RegionEnumerator(_regions);
}
}
public class RegionEnumerator : IEnumerator<Region>
{
protected List<Region> _regions;
protected int _pos = -1;
public RegionEnumerator (List<Region> regs)
{
_regions = regs;
}
public RegionEnumerator (RegionManager rm)
{
_regions = new List<Region>();
if (!Directory.Exists(rm.GetRegionPath())) {
throw new DirectoryNotFoundException();
}
string[] files = Directory.GetFiles(rm.GetRegionPath());
_regions.Capacity = files.Length;
foreach (string file in files) {
try {
Region r = rm.GetRegion(file);
_regions.Add(r);
}
catch (ArgumentException) {
continue;
}
}
}
public bool MoveNext ()
{
_pos++;
return (_pos < _regions.Count);
}
public void Reset ()
{
_pos = -1;
}
void IDisposable.Dispose () { }
object IEnumerator.Current
{
get
{
return Current;
}
}
Region IEnumerator<Region>.Current
{
get
{
return Current;
}
}
public Region Current
{
get
{
try {
return _regions[_pos];
}
catch (IndexOutOfRangeException) {
throw new InvalidOperationException();
}
}
}
}
}

View file

@ -5,18 +5,25 @@ using System.IO;
namespace NBToolkit
{
public class RegionFileCache
/*public class RegionFileCache
{
private const int MAX_CACHE_SIZE = 256;
private static Dictionary<string, WeakReference> cache = new Dictionary<string, WeakReference>();
private string _regionPath;
private Dictionary<string, WeakReference> cache = new Dictionary<string, WeakReference>();
private RegionFileCache() {
public RegionFileCache (string path) {
_regionPath = path;
if (!Directory.Exists(_regionPath)) {
throw new DirectoryNotFoundException();
}
}
public static RegionFile GetRegionFile(string basePath, string fileName) {
string regionDir = Path.Combine(basePath, "region");
string file = Path.Combine(regionDir, fileName);
public RegionFile GetRegionFile (string fileName) {
//string regionDir = basePath; // Path.Combine(basePath, "region");
string file = Path.Combine(_regionPath, fileName);
RegionFile rf = null;
if (cache.ContainsKey(file)) {
@ -27,10 +34,6 @@ namespace NBToolkit
return rf;
}
if (!Directory.Exists(regionDir)) {
Directory.CreateDirectory(regionDir);
}
if (cache.Count >= MAX_CACHE_SIZE) {
RegionFileCache.Clear();
}
@ -40,32 +43,11 @@ namespace NBToolkit
return reg;
}
public static RegionFile GetRegionFile (string basePath, int chunkX, int chunkZ)
public RegionFile GetRegionFile (int chunkX, int chunkZ)
{
string regionDir = Path.Combine(basePath, "region");
string fileName = Path.Combine(regionDir, "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mcr");
string fileName = "r." + (chunkX >> 5) + "." + (chunkZ >> 5) + ".mcr";
return GetRegionFile(basePath, fileName);
}
public static string[] GetRegionFileList (string basePath)
{
string regionDir = Path.Combine(basePath, "region");
if (!Directory.Exists(regionDir)) {
Directory.CreateDirectory(regionDir);
}
string[] files = Directory.GetFiles(regionDir);
List<string> valid = new List<string>();
foreach (string file in files) {
if (System.Text.RegularExpressions.Regex.IsMatch(file, "r\\.-?[0-9]+\\.-?[0-9]+\\.mcr$")) {
valid.Add(Path.GetFileName(file));
}
}
return valid.ToArray();
return GetRegionFile(fileName);
}
public static void Clear() {
@ -92,5 +74,5 @@ namespace NBToolkit
RegionFile r = GetRegionFile(basePath, chunkX, chunkZ);
return r.GetChunkDataOutputStream(chunkX & 31, chunkZ & 31);
}
}
}*/
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBToolkit
{
public class RegionKey : IEquatable<RegionKey>
{
protected int rx;
protected int rz;
public RegionKey (int _rx, int _rz)
{
rx = _rx;
rz = _rz;
}
public bool Equals (RegionKey ck)
{
return this.rx == ck.rx && this.rz == ck.rz;
}
public override int GetHashCode ()
{
return rx ^ rz;
}
}
}

View file

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace NBToolkit
{
public class RegionManager
{
protected string _regionPath;
protected Dictionary<RegionKey, Region> _cache;
public RegionManager (string regionDir)
{
_regionPath = regionDir;
_cache = new Dictionary<RegionKey, Region>();
}
public Region GetRegion (int rx, int rz)
{
RegionKey k = new RegionKey(rx, rz);
Region r;
if (_cache.TryGetValue(k, out r) == false) {
r = new Region(this, rx, rz);
_cache.Add(k, r);
}
return r;
}
public Region GetRegion (string filename)
{
int rx, rz;
if (!Region.ParseFileName(filename, out rx, out rz)) {
throw new ArgumentException();
}
return GetRegion(rx, rz);
}
public string GetRegionPath ()
{
return _regionPath;
}
}
}

View file

@ -16,6 +16,24 @@ namespace NBToolkit
public int? OPT_DATA = null;
public double? OPT_PROB = null;
// Block coordinate conditions
public int? BL_X_GE = null;
public int? BL_X_LE = null;
public int? BL_Y_GE = null;
public int? BL_Y_LE = null;
public int? BL_Z_GE = null;
public int? BL_Z_LE = null;
// Neighbor conditions
public int? OPT_NEIGHBOR = null;
public int? OPT_NEIGHBOR_SIDE = null;
public int? OPT_NEIGHBOR_E = null;
public int? OPT_NEIGHBOR_W = null;
public int? OPT_NEIGHBOR_N = null;
public int? OPT_NEIGHBOR_S = null;
public int? OPT_NEIGHBOR_T = null;
public int? OPT_NEIGHBOR_B = null;
public ReplaceOptions (string[] args) : base(args)
{
filterOpt = new OptionSet()
@ -30,6 +48,40 @@ namespace NBToolkit
v => { OPT_PROB = Convert.ToDouble(v);
OPT_PROB = Math.Max((double)OPT_PROB, 0.0);
OPT_PROB = Math.Min((double)OPT_PROB, 1.0); } },
{ "bxa=", "Update blocks with X-coord equal to or above {VAL}",
v => BL_X_GE = Convert.ToInt32(v) },
{ "bxb=", "Update blocks with X-coord equal to or below {VAL}",
v => BL_X_LE = Convert.ToInt32(v) },
{ "bxr=", "Update blocks with X-coord between {0:V1} and {1:V2} [inclusive]",
(v1, v2) => { BL_X_GE = Convert.ToInt32(v1); BL_X_LE = Convert.ToInt32(v2); } },
{ "bya=", "Update blocks with Y-coord equal to or above {VAL}",
v => BL_Y_GE = Convert.ToInt32(v) },
{ "byb=", "Update blocks with Y-coord equal to or below {VAL}",
v => BL_Y_LE = Convert.ToInt32(v) },
{ "byr=", "Update blocks with Y-coord between {0:V1} and {1:V2} [inclusive]",
(v1, v2) => { BL_Y_GE = Convert.ToInt32(v1); BL_Y_LE = Convert.ToInt32(v2); } },
{ "bza=", "Update blocks with Z-coord equal to or above {VAL}",
v => BL_Z_GE = Convert.ToInt32(v) },
{ "bzb=", "Update blocks with Z-coord equal to or below {VAL}",
v => BL_Z_LE = Convert.ToInt32(v) },
{ "bzr=", "Update blocks with Z-coord between {0:V1} and {1:V2} [inclusive]",
(v1, v2) => { BL_Z_GE = Convert.ToInt32(v1); BL_Z_LE = Convert.ToInt32(v2); } },
{ "nb=", "Update blocks that have block type {ID} as any neighbor",
v => OPT_NEIGHBOR = Convert.ToInt32(v) % 256 },
{ "nbs=", "Update blocks that have block type {ID} as any x/z neighbor",
v => OPT_NEIGHBOR_SIDE = Convert.ToInt32(v) % 256 },
{ "nbxa=", "Update blocks that have block type {ID} as their south neighbor",
v => OPT_NEIGHBOR_S = Convert.ToInt32(v) % 256 },
{ "nbxb=", "Update blocks that have block type {ID} as their north neighbor",
v => OPT_NEIGHBOR_N = Convert.ToInt32(v) % 256 },
{ "nbya=", "Update blocks that have block type {ID} as their top neighbor",
v => OPT_NEIGHBOR_T = Convert.ToInt32(v) % 256 },
{ "nbyb=", "Update blocks that have block type {ID} as their bottom neighbor",
v => OPT_NEIGHBOR_B = Convert.ToInt32(v) % 256 },
{ "nbza=", "Update blocks that have block type {ID} as their west neighbor",
v => OPT_NEIGHBOR_W = Convert.ToInt32(v) % 256 },
{ "nbzb=", "Update blocks that have block type {ID} as their east neighbor",
v => OPT_NEIGHBOR_E = Convert.ToInt32(v) % 256 },
};
filterOpt.Parse(args);
@ -94,15 +146,74 @@ namespace NBToolkit
return;
}
NBT_Tag xPos = level.findTagByName("xPos");
NBT_Tag zPos = level.findTagByName("zPos");
if (xPos == null || zPos == null || xPos.type != NBT_Type.TAG_INT || zPos.type != NBT_Type.TAG_INT) {
return;
}
int xBase = xPos.value.toInt().data * 16;
int zBase = zPos.value.toInt().data * 16;
NBT_ByteArray blocks_ary = blocks.value.toByteArray();
NBT_ByteArray data_ary = data.value.toByteArray();
// Determine X range
int xmin = 0;
int xmax = 15;
if (opt.BL_X_GE != null) {
xmin = (int)opt.BL_X_GE - xBase;
}
if (opt.BL_X_LE != null) {
xmax = (int)opt.BL_X_LE - xBase;
}
xmin = (xmin < 0) ? 0 : xmin;
xmax = (xmin > 15) ? 15 : xmax;
if (xmin > 15 || xmax < 0 || xmin > xmax) {
return;
}
// Determine Y range
int ymin = 0;
int ymax = 127;
if (opt.BL_Y_GE != null) {
ymin = (int)opt.BL_Y_GE;
}
if (opt.BL_Y_LE != null) {
ymax = (int)opt.BL_Y_LE;
}
if (ymin > ymax) {
return;
}
// Determine X range
int zmin = 0;
int zmax = 15;
if (opt.BL_Z_GE != null) {
zmin = (int)opt.BL_Z_GE - zBase;
}
if (opt.BL_Z_LE != null) {
zmax = (int)opt.BL_Z_LE - zBase;
}
zmin = (zmin < 0) ? 0 : zmin;
zmax = (zmin > 15) ? 15 : zmax;
if (zmin > 15 || zmax < 0 || zmin > zmax) {
return;
}
// Process Chunk
for (int y = ymin; y <= ymax; y++) {
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int x = xmin; x <= xmax; x++) {
for (int z = zmin; z <= zmax; z++) {
// Probability test
if (opt.OPT_PROB != null) {
double c = rand.NextDouble();
if (c > opt.OPT_PROB) {
@ -110,6 +221,7 @@ namespace NBToolkit
}
}
// Attempt to replace block
int index = BlockIndex(x, y, z);
if (blocks_ary.data[index] == opt.OPT_BEFORE) {
blocks_ary.data[index] = (byte)opt.OPT_AFTER;

View file

@ -27,12 +27,6 @@ namespace NBToolkit
public int? CH_Z_GE = null;
public int? CH_Z_LE = null;
// Block coordinate conditions
public int? BL_X_GE = null;
public int? BL_X_LE = null;
public int? BL_Z_GE = null;
public int? BL_Z_LE = null;
public TKOptions (string[] args)
{
commonOpt = new OptionSet()
@ -83,7 +77,7 @@ namespace NBToolkit
throw new TKOptionException();
}
if (!File.Exists(Path.Combine(OPT_WORLD, "Level.dat"))) {
if (!File.Exists(Path.Combine(OPT_WORLD, "level.dat"))) {
Console.WriteLine("Error: The supplied world path is invalid");
Console.WriteLine();
this.PrintUsage();

View file

@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace NBToolkit
{
class World
{
protected RegionManager _regionMan;
protected ChunkManager _chunkMan;
protected BlockManager _blockMan;
protected string _worldPath;
public World (string world)
{
_worldPath = world;
_regionMan = new RegionManager(Path.Combine(world, "region"));
_chunkMan = new ChunkManager(_regionMan);
_blockMan = new BlockManager(_chunkMan);
}
public RegionManager GetRegionManager ()
{
return _regionMan;
}
public ChunkManager GetChunkManager ()
{
return _chunkMan;
}
public BlockManager GetBlockManager ()
{
return _blockMan;
}
}
}