diff --git a/Substrate/SubstrateCS/Properties/AssemblyInfo.cs b/Substrate/SubstrateCS/Properties/AssemblyInfo.cs
index ba49779..f1d9fff 100644
--- a/Substrate/SubstrateCS/Properties/AssemblyInfo.cs
+++ b/Substrate/SubstrateCS/Properties/AssemblyInfo.cs
@@ -1,4 +1,5 @@
-using System.Reflection;
+using System;
+using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -6,7 +7,7 @@ using System.Runtime.InteropServices;
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Substrate")]
-[assembly: AssemblyDescription("Minecraft map SDK")]
+[assembly: AssemblyDescription("SDK for loading and modifying Minecraft worlds")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Substrate")]
@@ -31,3 +32,6 @@ using System.Runtime.InteropServices;
//
[assembly: AssemblyVersion("0.7.0.0")]
[assembly: AssemblyFileVersion("0.7.0.0")]
+
+// This library is compatible with all CLS-compliant .NET programming languages.
+[assembly: CLSCompliant(true)]
\ No newline at end of file
diff --git a/Substrate/SubstrateCS/Source/AlphaWorld.cs b/Substrate/SubstrateCS/Source/AlphaWorld.cs
new file mode 100644
index 0000000..04ae168
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/AlphaWorld.cs
@@ -0,0 +1,260 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using Substrate.Core;
+using Substrate.Nbt;
+
+namespace Substrate
+{
+ using IO = System.IO;
+
+ ///
+ /// Represents an Alpha-compatible (up to Beta 1.2) Minecraft world.
+ ///
+ public class AlphaWorld : NbtWorld
+ {
+ private const string _PLAYER_DIR = "players";
+ private string _levelFile = "level.dat";
+
+ private Level _level;
+
+ private Dictionary _chunkMgrs;
+ private Dictionary _blockMgrs;
+
+ private PlayerManager _playerMan;
+
+ private AlphaWorld ()
+ {
+ _chunkMgrs = new Dictionary();
+ _blockMgrs = new Dictionary();
+ }
+
+ ///
+ /// Gets a reference to this world's object.
+ ///
+ public override Level Level
+ {
+ get { return _level; }
+ }
+
+ ///
+ /// Gets a for the default dimension.
+ ///
+ /// A tied to the default dimension in this world.
+ /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away
+ /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
+ /// instead and working with blocks on a chunk-local level.
+ public new BlockManager GetBlockManager ()
+ {
+ return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
+ }
+
+ ///
+ /// Gets a for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// A tied to the given dimension in this world.
+ /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away
+ /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
+ /// instead and working with blocks on a chunk-local level.
+ public new BlockManager GetBlockManager (int dim)
+ {
+ return GetBlockManagerVirt(dim) as BlockManager;
+ }
+
+ ///
+ /// Gets a for the default dimension.
+ ///
+ /// A tied to the default dimension in this world.
+ /// Get a if you you need to work with easily-digestible, bounded chunks of blocks.
+ public new ChunkFileManager GetChunkManager ()
+ {
+ return GetChunkManagerVirt(Dimension.DEFAULT) as ChunkFileManager;
+ }
+
+ ///
+ /// Gets a for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// A tied to the given dimension in this world.
+ /// Get a if you you need to work with easily-digestible, bounded chunks of blocks.
+ public new ChunkFileManager GetChunkManager (int dim)
+ {
+ return GetChunkManagerVirt(dim) as ChunkFileManager;
+ }
+
+ ///
+ /// Gets a for maanging players on multiplayer worlds.
+ ///
+ /// A for this world.
+ /// To manage the player of a single-player world, get a object for the world instead.
+ public new PlayerManager GetPlayerManager ()
+ {
+ return GetPlayerManagerVirt() as PlayerManager;
+ }
+
+ ///
+ /// Saves the world's data, and any objects known to have unsaved changes.
+ ///
+ public void Save ()
+ {
+ _level.Save();
+
+ foreach (KeyValuePair cm in _chunkMgrs) {
+ cm.Value.Save();
+ }
+ }
+
+ ///
+ /// Opens an existing Alpha-compatible Minecraft world and returns a new to represent it.
+ ///
+ /// The path to the directory containing the world's level.dat, or the path to level.dat itself.
+ /// A new object representing an existing world on disk.
+ public static AlphaWorld Open (string path)
+ {
+ return new AlphaWorld().OpenWorld(path) as AlphaWorld;
+ }
+
+ ///
+ /// Creates a new Alpha-compatible Minecraft world and returns a new to represent it.
+ ///
+ /// The path to the directory where the new world should be stored.
+ /// A new object representing a new world.
+ /// This method will attempt to create the specified directory immediately if it does not exist, but will not
+ /// write out any world data unless it is explicitly saved at a later time.
+ public static AlphaWorld Create (string path)
+ {
+ return new AlphaWorld().CreateWorld(path) as AlphaWorld;
+ }
+
+ ///
+ protected override IBlockManager GetBlockManagerVirt (int dim)
+ {
+ BlockManager rm;
+ if (_blockMgrs.TryGetValue(dim, out rm)) {
+ return rm;
+ }
+
+ OpenDimension(dim);
+ return _blockMgrs[dim];
+ }
+
+ ///
+ protected override IChunkManager GetChunkManagerVirt (int dim)
+ {
+ ChunkFileManager rm;
+ if (_chunkMgrs.TryGetValue(dim, out rm)) {
+ return rm;
+ }
+
+ OpenDimension(dim);
+ return _chunkMgrs[dim];
+ }
+
+ ///
+ protected override IPlayerManager GetPlayerManagerVirt ()
+ {
+ if (_playerMan != null) {
+ return _playerMan;
+ }
+
+ string path = IO.Path.Combine(Path, _PLAYER_DIR);
+
+ _playerMan = new PlayerManager(path);
+ return _playerMan;
+ }
+
+ private void OpenDimension (int dim)
+ {
+ string path = Path;
+ if (dim != Dimension.DEFAULT) {
+ path = IO.Path.Combine(path, "DIM" + dim);
+ }
+
+ if (!Directory.Exists(path)) {
+ Directory.CreateDirectory(path);
+ }
+
+ ChunkFileManager cm = new ChunkFileManager(path);
+ BlockManager bm = new BlockManager(cm);
+
+ _chunkMgrs[dim] = cm;
+ _blockMgrs[dim] = bm;
+ }
+
+ private AlphaWorld OpenWorld (string path)
+ {
+ if (!Directory.Exists(path)) {
+ if (File.Exists(path)) {
+ _levelFile = IO.Path.GetFileName(path);
+ path = IO.Path.GetDirectoryName(path);
+ }
+ else {
+ throw new DirectoryNotFoundException("Directory '" + path + "' not found");
+ }
+ }
+
+ Path = path;
+
+ string ldat = IO.Path.Combine(path, _levelFile);
+ if (!File.Exists(ldat)) {
+ throw new FileNotFoundException("Data file '" + _levelFile + "' not found in '" + path + "'", ldat);
+ }
+
+ if (!LoadLevel()) {
+ throw new Exception("Failed to load '" + _levelFile + "'");
+ }
+
+ return this;
+ }
+
+ private AlphaWorld CreateWorld (string path)
+ {
+ if (!Directory.Exists(path)) {
+ throw new DirectoryNotFoundException("Directory '" + path + "' not found");
+ }
+
+ Path = path;
+ _level = new Level(this);
+
+ return this;
+ }
+
+ private bool LoadLevel ()
+ {
+ NBTFile nf = new NBTFile(IO.Path.Combine(Path, _levelFile));
+ Stream nbtstr = nf.GetDataInputStream();
+ if (nbtstr == null) {
+ return false;
+ }
+
+ NbtTree tree = new NbtTree(nbtstr);
+
+ _level = new Level(this);
+ _level = _level.LoadTreeSafe(tree.Root);
+
+ return _level != null;
+ }
+
+
+ internal static void OnResolveOpen (object sender, OpenWorldEventArgs e)
+ {
+ try {
+ AlphaWorld world = new AlphaWorld().OpenWorld(e.Path);
+ if (world == null) {
+ return;
+ }
+
+ if (world.Level.Version != 0) {
+ return;
+ }
+
+ e.AddHandler(Open);
+ }
+ catch (Exception) {
+ return;
+ }
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/BetaWorld.cs b/Substrate/SubstrateCS/Source/BetaWorld.cs
new file mode 100644
index 0000000..e72866c
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/BetaWorld.cs
@@ -0,0 +1,310 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Substrate.Core;
+using Substrate.Nbt;
+
+//TODO: Exceptions (+ Alpha)
+
+namespace Substrate
+{
+ using IO = System.IO;
+
+ ///
+ /// Represents a Beta-compatible (Beta 1.3 or higher) Minecraft world.
+ ///
+ public class BetaWorld : NbtWorld
+ {
+ private const string _REGION_DIR = "region";
+ private const string _PLAYER_DIR = "players";
+ private string _levelFile = "level.dat";
+
+ private Level _level;
+
+ private Dictionary _regionMgrs;
+ private Dictionary _chunkMgrs;
+ private Dictionary _blockMgrs;
+
+ private PlayerManager _playerMan;
+
+ private BetaWorld ()
+ {
+ _regionMgrs = new Dictionary();
+ _chunkMgrs = new Dictionary();
+ _blockMgrs = new Dictionary();
+ }
+
+ ///
+ /// Gets a reference to this world's object.
+ ///
+ public override Level Level
+ {
+ get { return _level; }
+ }
+
+ ///
+ /// Gets a for the default dimension.
+ ///
+ /// A tied to the default dimension in this world.
+ /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away
+ /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
+ /// instead and working with blocks on a chunk-local level.
+ public new BlockManager GetBlockManager ()
+ {
+ return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
+ }
+
+ ///
+ /// Gets a for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// A tied to the given dimension in this world.
+ /// Get a if you need to manage blocks as a global, unbounded matrix. This abstracts away
+ /// any higher-level organizational divisions. If your task is going to be heavily performance-bound, consider getting a
+ /// instead and working with blocks on a chunk-local level.
+ public new BlockManager GetBlockManager (int dim)
+ {
+ return GetBlockManagerVirt(dim) as BlockManager;
+ }
+
+ ///
+ /// Gets a for the default dimension.
+ ///
+ /// A tied to the default dimension in this world.
+ /// Get a if you you need to work with easily-digestible, bounded chunks of blocks.
+ public new ChunkManager GetChunkManager ()
+ {
+ return GetChunkManagerVirt(Dimension.DEFAULT) as ChunkManager;
+ }
+
+ ///
+ /// Gets a for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// A tied to the given dimension in this world.
+ /// Get a if you you need to work with easily-digestible, bounded chunks of blocks.
+ public new ChunkManager GetChunkManager (int dim)
+ {
+ return GetChunkManagerVirt(dim) as ChunkManager;
+ }
+
+ ///
+ /// Gets a for the default dimension.
+ ///
+ /// A tied to the defaul dimension in this world.
+ /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
+ /// Consider using the if you are interested in working with blocks.
+ public RegionManager GetRegionManager ()
+ {
+ return GetRegionManager(Dimension.DEFAULT);
+ }
+
+ ///
+ /// Gets a for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// A tied to the given dimension in this world.
+ /// Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
+ /// Consider using the if you are interested in working with blocks.
+ public RegionManager GetRegionManager (int dim)
+ {
+ RegionManager rm;
+ if (_regionMgrs.TryGetValue(dim, out rm)) {
+ return rm;
+ }
+
+ OpenDimension(dim);
+ return _regionMgrs[dim];
+ }
+
+ ///
+ /// Gets a for maanging players on multiplayer worlds.
+ ///
+ /// A for this world.
+ /// To manage the player of a single-player world, get a object for the world instead.
+ public new PlayerManager GetPlayerManager ()
+ {
+ return GetPlayerManagerVirt() as PlayerManager;
+ }
+
+ ///
+ /// Saves the world's data, and any objects known to have unsaved changes.
+ ///
+ public void Save ()
+ {
+ _level.Save();
+
+ foreach (KeyValuePair cm in _chunkMgrs) {
+ cm.Value.Save();
+ }
+ }
+
+ ///
+ /// Opens an existing Beta-compatible Minecraft world and returns a new to represent it.
+ ///
+ /// The path to the directory containing the world's level.dat, or the path to level.dat itself.
+ /// A new object representing an existing world on disk.
+ public static BetaWorld Open (string path)
+ {
+ return new BetaWorld().OpenWorld(path) as BetaWorld;
+ }
+
+ ///
+ /// Creates a new Beta-compatible Minecraft world and returns a new to represent it.
+ ///
+ /// The path to the directory where the new world should be stored.
+ /// A new object representing a new world.
+ /// This method will attempt to create the specified directory immediately if it does not exist, but will not
+ /// write out any world data unless it is explicitly saved at a later time.
+ public static BetaWorld Create (string path)
+ {
+ return new BetaWorld().CreateWorld(path) as BetaWorld;
+ }
+
+ ///
+ protected override IBlockManager GetBlockManagerVirt (int dim)
+ {
+ BlockManager rm;
+ if (_blockMgrs.TryGetValue(dim, out rm)) {
+ return rm;
+ }
+
+ OpenDimension(dim);
+ return _blockMgrs[dim];
+ }
+
+ ///
+ protected override IChunkManager GetChunkManagerVirt (int dim)
+ {
+ ChunkManager rm;
+ if (_chunkMgrs.TryGetValue(dim, out rm)) {
+ return rm;
+ }
+
+ OpenDimension(dim);
+ return _chunkMgrs[dim];
+ }
+
+ ///
+ protected override IPlayerManager GetPlayerManagerVirt ()
+ {
+ if (_playerMan != null) {
+ return _playerMan;
+ }
+
+ string path = IO.Path.Combine(Path, _PLAYER_DIR);
+
+ _playerMan = new PlayerManager(path);
+ return _playerMan;
+ }
+
+ private void OpenDimension (int dim)
+ {
+ string path = Path;
+ if (dim == Dimension.DEFAULT) {
+ path = IO.Path.Combine(path, _REGION_DIR);
+ }
+ else {
+ path = IO.Path.Combine(path, "DIM" + dim);
+ path = IO.Path.Combine(path, _REGION_DIR);
+ }
+
+ if (!Directory.Exists(path)) {
+ Directory.CreateDirectory(path);
+ }
+
+ ChunkCache cc = new ChunkCache();
+
+ RegionManager rm = new RegionManager(path, cc);
+ ChunkManager cm = new ChunkManager(rm, cc);
+ BlockManager bm = new BlockManager(cm);
+
+ _regionMgrs[dim] = rm;
+ _chunkMgrs[dim] = cm;
+ _blockMgrs[dim] = bm;
+ }
+
+ private BetaWorld OpenWorld (string path)
+ {
+ if (!Directory.Exists(path)) {
+ if (File.Exists(path)) {
+ _levelFile = IO.Path.GetFileName(path);
+ path = IO.Path.GetDirectoryName(path);
+ }
+ else {
+ throw new DirectoryNotFoundException("Directory '" + path + "' not found");
+ }
+ }
+
+ Path = path;
+
+ string ldat = IO.Path.Combine(path, _levelFile);
+ if (!File.Exists(ldat)) {
+ throw new FileNotFoundException("Data file '" + _levelFile + "' not found in '" + path + "'", ldat);
+ }
+
+ if (!LoadLevel()) {
+ throw new Exception("Failed to load '" + _levelFile + "'");
+ }
+
+ return this;
+ }
+
+ private BetaWorld CreateWorld (string path)
+ {
+ if (!Directory.Exists(path)) {
+ throw new DirectoryNotFoundException("Directory '" + path + "' not found");
+ }
+
+ string regpath = IO.Path.Combine(path, _REGION_DIR);
+ if (!Directory.Exists(regpath)) {
+ Directory.CreateDirectory(regpath);
+ }
+
+ Path = path;
+ _level = new Level(this);
+
+ return this;
+ }
+
+ private bool LoadLevel ()
+ {
+ NBTFile nf = new NBTFile(IO.Path.Combine(Path, _levelFile));
+ Stream nbtstr = nf.GetDataInputStream();
+ if (nbtstr == null) {
+ return false;
+ }
+
+ NbtTree tree = new NbtTree(nbtstr);
+
+ _level = new Level(this);
+ _level = _level.LoadTreeSafe(tree.Root);
+
+ return _level != null;
+ }
+
+ internal static void OnResolveOpen (object sender, OpenWorldEventArgs e)
+ {
+ try {
+ BetaWorld world = new BetaWorld().OpenWorld(e.Path);
+ if (world == null) {
+ return;
+ }
+
+ string regPath = IO.Path.Combine(e.Path, _REGION_DIR);
+ if (!Directory.Exists(regPath)) {
+ return;
+ }
+
+ if (world.Level.Version < 19132) {
+ return;
+ }
+
+ e.AddHandler(Open);
+ }
+ catch (Exception) {
+ return;
+ }
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/BlockManager.cs b/Substrate/SubstrateCS/Source/BlockManager.cs
index 0d8a5a8..56d4711 100644
--- a/Substrate/SubstrateCS/Source/BlockManager.cs
+++ b/Substrate/SubstrateCS/Source/BlockManager.cs
@@ -12,20 +12,19 @@ namespace Substrate
public const int MIN_Z = -32000000;
public const int MAX_Z = 32000000;
- protected int _chunkXDim;
- protected int _chunkYDim;
- protected int _chunkZDim;
- protected int _chunkXMask;
- protected int _chunkYMask;
- protected int _chunkZMask;
- protected int _chunkXLog;
- protected int _chunkYLog;
- protected int _chunkZLog;
+ protected int chunkXDim;
+ protected int chunkYDim;
+ protected int chunkZDim;
+ protected int chunkXMask;
+ protected int chunkYMask;
+ protected int chunkZMask;
+ protected int chunkXLog;
+ protected int chunkYLog;
+ protected int chunkZLog;
- protected IChunkManager _chunkMan;
+ protected IChunkManager chunkMan;
- protected ChunkRef _cache;
- protected AlphaBlockCollection _blocks;
+ protected ChunkRef cache;
private bool _autoLight = true;
private bool _autoFluid = false;
@@ -44,56 +43,56 @@ namespace Substrate
public BlockManager (IChunkManager cm)
{
- _chunkMan = cm;
+ chunkMan = cm;
Chunk c = Chunk.Create(0, 0);
- _chunkXDim = c.Blocks.XDim;
- _chunkYDim = c.Blocks.YDim;
- _chunkZDim = c.Blocks.ZDim;
- _chunkXMask = _chunkXDim - 1;
- _chunkYMask = _chunkYDim - 1;
- _chunkZMask = _chunkZDim - 1;
- _chunkXLog = Log2(_chunkXDim);
- _chunkYLog = Log2(_chunkYDim);
- _chunkZLog = Log2(_chunkZDim);
+ chunkXDim = c.Blocks.XDim;
+ chunkYDim = c.Blocks.YDim;
+ chunkZDim = c.Blocks.ZDim;
+ chunkXMask = chunkXDim - 1;
+ chunkYMask = chunkYDim - 1;
+ chunkZMask = chunkZDim - 1;
+ chunkXLog = Log2(chunkXDim);
+ chunkYLog = Log2(chunkYDim);
+ chunkZLog = Log2(chunkZDim);
}
public AlphaBlock GetBlock (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return null;
}
- return _cache.Blocks.GetBlock(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetBlock(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public AlphaBlockRef GetBlockRef (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return new AlphaBlockRef();
}
- return _cache.Blocks.GetBlockRef(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetBlockRef(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void SetBlock (int x, int y, int z, AlphaBlock block)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.SetBlock(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, block);
+ cache.Blocks.SetBlock(x & chunkXMask, y & chunkYMask, z & chunkZMask, block);
}
protected ChunkRef GetChunk (int x, int y, int z)
{
- x >>= _chunkXLog;
- z >>= _chunkZLog;
- return _chunkMan.GetChunkRef(x, z);
+ x >>= chunkXLog;
+ z >>= chunkZLog;
+ return chunkMan.GetChunkRef(x, z);
}
private int Log2 (int x)
@@ -132,46 +131,46 @@ namespace Substrate
public void SetBlock (int x, int y, int z, IBlock block)
{
- _cache.Blocks.SetBlock(x, y, z, block);
+ cache.Blocks.SetBlock(x, y, z, block);
}
public BlockInfo GetInfo (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return null;
}
- return _cache.Blocks.GetInfo(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetInfo(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public int GetID (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null) {
+ cache = GetChunk(x, y, z);
+ if (cache == null) {
return 0;
}
- return _cache.Blocks.GetID(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetID(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void SetID (int x, int y, int z, int id)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- bool autolight = _cache.Blocks.AutoLight;
- bool autofluid = _cache.Blocks.AutoFluid;
+ bool autolight = cache.Blocks.AutoLight;
+ bool autofluid = cache.Blocks.AutoFluid;
- _cache.Blocks.AutoLight = _autoLight;
- _cache.Blocks.AutoFluid = _autoFluid;
+ cache.Blocks.AutoLight = _autoLight;
+ cache.Blocks.AutoFluid = _autoFluid;
- _cache.Blocks.SetID(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, id);
+ cache.Blocks.SetID(x & chunkXMask, y & chunkYMask, z & chunkZMask, id);
- _cache.Blocks.AutoFluid = autofluid;
- _cache.Blocks.AutoLight = autolight;
+ cache.Blocks.AutoFluid = autofluid;
+ cache.Blocks.AutoLight = autolight;
}
#endregion
@@ -191,27 +190,27 @@ namespace Substrate
public void SetBlock (int x, int y, int z, IDataBlock block)
{
- _cache.Blocks.SetBlock(x, y, z, block);
+ cache.Blocks.SetBlock(x, y, z, block);
}
public int GetData (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null) {
+ cache = GetChunk(x, y, z);
+ if (cache == null) {
return 0;
}
- return _cache.Blocks.GetData(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetData(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void SetData (int x, int y, int z, int data)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.SetData(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, data);
+ cache.Blocks.SetData(x & chunkXMask, y & chunkYMask, z & chunkZMask, data);
}
#endregion
@@ -231,87 +230,87 @@ namespace Substrate
public void SetBlock (int x, int y, int z, ILitBlock block)
{
- _cache.Blocks.SetBlock(x, y, z, block);
+ cache.Blocks.SetBlock(x, y, z, block);
}
public int GetBlockLight (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null) {
+ cache = GetChunk(x, y, z);
+ if (cache == null) {
return 0;
}
- return _cache.Blocks.GetBlockLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetBlockLight(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public int GetSkyLight (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null) {
+ cache = GetChunk(x, y, z);
+ if (cache == null) {
return 0;
}
- return _cache.Blocks.GetSkyLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetSkyLight(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void SetBlockLight (int x, int y, int z, int light)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.SetBlockLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, light);
+ cache.Blocks.SetBlockLight(x & chunkXMask, y & chunkYMask, z & chunkZMask, light);
}
public void SetSkyLight (int x, int y, int z, int light)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.SetSkyLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, light);
+ cache.Blocks.SetSkyLight(x & chunkXMask, y & chunkYMask, z & chunkZMask, light);
}
public int GetHeight (int x, int z)
{
- _cache = GetChunk(x, 0, z);
- if (_cache == null || !Check(x, 0, z)) {
+ cache = GetChunk(x, 0, z);
+ if (cache == null || !Check(x, 0, z)) {
return 0;
}
- return _cache.Blocks.GetHeight(x & _chunkXMask, z & _chunkZMask);
+ return cache.Blocks.GetHeight(x & chunkXMask, z & chunkZMask);
}
public void SetHeight (int x, int z, int height)
{
- _cache = GetChunk(x, 0, z);
- if (_cache == null || !Check(x, 0, z)) {
+ cache = GetChunk(x, 0, z);
+ if (cache == null || !Check(x, 0, z)) {
return;
}
- _cache.Blocks.SetHeight(x & _chunkXMask, z & _chunkZMask, height);
+ cache.Blocks.SetHeight(x & chunkXMask, z & chunkZMask, height);
}
public void UpdateBlockLight (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.UpdateBlockLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ cache.Blocks.UpdateBlockLight(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void UpdateSkyLight (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.UpdateBlockLight(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ cache.Blocks.UpdateBlockLight(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
#endregion
@@ -331,47 +330,47 @@ namespace Substrate
public void SetBlock (int x, int y, int z, IPropertyBlock block)
{
- _cache.Blocks.SetBlock(x, y, z, block);
+ cache.Blocks.SetBlock(x, y, z, block);
}
public TileEntity GetTileEntity (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return null;
}
- return _cache.Blocks.GetTileEntity(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ return cache.Blocks.GetTileEntity(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void SetTileEntity (int x, int y, int z, TileEntity te)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.SetTileEntity(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask, te);
+ cache.Blocks.SetTileEntity(x & chunkXMask, y & chunkYMask, z & chunkZMask, te);
}
public void CreateTileEntity (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.CreateTileEntity(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ cache.Blocks.CreateTileEntity(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
public void ClearTileEntity (int x, int y, int z)
{
- _cache = GetChunk(x, y, z);
- if (_cache == null || !Check(x, y, z)) {
+ cache = GetChunk(x, y, z);
+ if (cache == null || !Check(x, y, z)) {
return;
}
- _cache.Blocks.ClearTileEntity(x & _chunkXMask, y & _chunkYMask, z & _chunkZMask);
+ cache.Blocks.ClearTileEntity(x & chunkXMask, y & chunkYMask, z & chunkZMask);
}
#endregion
diff --git a/Substrate/SubstrateCS/Source/Chunk.cs b/Substrate/SubstrateCS/Source/Chunk.cs
index aabddf5..a05b972 100644
--- a/Substrate/SubstrateCS/Source/Chunk.cs
+++ b/Substrate/SubstrateCS/Source/Chunk.cs
@@ -124,7 +124,6 @@ namespace Substrate
c._cz = z;
c.BuildNBTTree();
-
return c;
}
diff --git a/Substrate/SubstrateCS/Source/Core/ByteArray.cs b/Substrate/SubstrateCS/Source/Core/ByteArray.cs
index 6112335..1908eef 100644
--- a/Substrate/SubstrateCS/Source/Core/ByteArray.cs
+++ b/Substrate/SubstrateCS/Source/Core/ByteArray.cs
@@ -6,29 +6,29 @@ namespace Substrate.Core
{
public class ByteArray : ICopyable
{
- protected readonly byte[] _data;
+ protected readonly byte[] dataArray;
public ByteArray (byte[] data)
{
- _data = data;
+ dataArray = data;
}
public byte this[int i]
{
- get { return _data[i]; }
- set { _data[i] = value; }
+ get { return dataArray[i]; }
+ set { dataArray[i] = value; }
}
public int Length
{
- get { return _data.Length; }
+ get { return dataArray.Length; }
}
public void Clear ()
{
- for (int i = 0; i < _data.Length; i++)
+ for (int i = 0; i < dataArray.Length; i++)
{
- _data[i] = 0;
+ dataArray[i] = 0;
}
}
@@ -36,8 +36,8 @@ namespace Substrate.Core
public virtual ByteArray Copy ()
{
- byte[] data = new byte[_data.Length];
- _data.CopyTo(data, 0);
+ byte[] data = new byte[dataArray.Length];
+ dataArray.CopyTo(data, 0);
return new ByteArray(data);
}
@@ -69,13 +69,13 @@ namespace Substrate.Core
get
{
int index = _ydim * (x * _zdim + z) + y;
- return _data[index];
+ return dataArray[index];
}
set
{
int index = _ydim * (x * _zdim + z) + y;
- _data[index] = value;
+ dataArray[index] = value;
}
}
@@ -113,8 +113,8 @@ namespace Substrate.Core
public override ByteArray Copy ()
{
- byte[] data = new byte[_data.Length];
- _data.CopyTo(data, 0);
+ byte[] data = new byte[dataArray.Length];
+ dataArray.CopyTo(data, 0);
return new XZYByteArray(_xdim, _ydim, _zdim, data);
}
@@ -144,13 +144,13 @@ namespace Substrate.Core
get
{
int index = z * _xdim + x;
- return _data[index];
+ return dataArray[index];
}
set
{
int index = z * _xdim + x;
- _data[index] = value;
+ dataArray[index] = value;
}
}
@@ -168,8 +168,8 @@ namespace Substrate.Core
public override ByteArray Copy ()
{
- byte[] data = new byte[_data.Length];
- _data.CopyTo(data, 0);
+ byte[] data = new byte[dataArray.Length];
+ dataArray.CopyTo(data, 0);
return new ZXByteArray(_xdim, _zdim, data);
}
diff --git a/Substrate/SubstrateCS/Source/Core/ChunkFileManager.cs b/Substrate/SubstrateCS/Source/Core/ChunkFileManager.cs
index 1d78226..8af6070 100644
--- a/Substrate/SubstrateCS/Source/Core/ChunkFileManager.cs
+++ b/Substrate/SubstrateCS/Source/Core/ChunkFileManager.cs
@@ -224,7 +224,7 @@ namespace Substrate.Core
public IEnumerator GetEnumerator ()
{
- return new ChunkEnumerator(this);
+ return new Enumerator(this);
}
#endregion
@@ -233,13 +233,13 @@ namespace Substrate.Core
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
{
- return new ChunkEnumerator(this);
+ return new Enumerator(this);
}
#endregion
- public class ChunkEnumerator : IEnumerator
+ private class Enumerator : IEnumerator
{
protected ChunkFileManager _cm;
protected Queue _tld;
@@ -250,7 +250,7 @@ namespace Substrate.Core
private string _cursld;
private ChunkRef _curchunk;
- public ChunkEnumerator (ChunkFileManager cfm)
+ public Enumerator (ChunkFileManager cfm)
{
_cm = cfm;
diff --git a/Substrate/SubstrateCS/Source/Core/OpenWorldEvent.cs b/Substrate/SubstrateCS/Source/Core/OpenWorldEvent.cs
new file mode 100644
index 0000000..8ac75d7
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/Core/OpenWorldEvent.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+
+namespace Substrate.Core
+{
+ ///
+ /// A callback function to open a world and return it as an instance of a concrete derivative of .
+ ///
+ /// The path to the directory of the world to open.
+ /// An instance of a concrete derivative of .
+ public delegate NbtWorld OpenWorldCallback (string path);
+
+ ///
+ /// Event arugments and response data for any handlers trying to determine if they can open a given world.
+ ///
+ public class OpenWorldEventArgs : EventArgs
+ {
+ private List _handlers;
+ private string _path;
+
+ ///
+ /// Create a new instance of event arguments.
+ ///
+ /// The path to the directory of a world.
+ public OpenWorldEventArgs (string path)
+ : base()
+ {
+ _path = path;
+ _handlers = new List();
+ }
+
+ ///
+ /// Gets the path to the directory of a world being investigated.
+ ///
+ public string Path
+ {
+ get { return _path; }
+ }
+
+ ///
+ /// Adds an delegate that can open a world and return a corresponding object.
+ ///
+ /// The delegate to return to the code that raised the event.
+ public void AddHandler (OpenWorldCallback callback)
+ {
+ _handlers.Add(callback);
+ }
+
+ internal int HandlerCount
+ {
+ get { return _handlers.Count; }
+ }
+
+ internal ICollection Handlers
+ {
+ get { return _handlers; }
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Entity.cs b/Substrate/SubstrateCS/Source/Entity.cs
index af55d44..369b887 100644
--- a/Substrate/SubstrateCS/Source/Entity.cs
+++ b/Substrate/SubstrateCS/Source/Entity.cs
@@ -300,7 +300,7 @@ namespace Substrate
///
/// Gets a representing the basic schema of an Entity.
///
- public static SchemaNodeCompound Schema
+ public static new SchemaNodeCompound Schema
{
get { return _schema; }
}
diff --git a/Substrate/SubstrateCS/Source/Level.cs b/Substrate/SubstrateCS/Source/Level.cs
index ddfa442..f11fb8d 100644
--- a/Substrate/SubstrateCS/Source/Level.cs
+++ b/Substrate/SubstrateCS/Source/Level.cs
@@ -5,9 +5,12 @@ using Substrate.Nbt;
namespace Substrate
{
+ ///
+ /// Represents general data and metadata of a single world.
+ ///
public class Level : INbtObject, ICopyable
{
- public static SchemaNodeCompound LevelSchema = new SchemaNodeCompound()
+ private static SchemaNodeCompound _schema = new SchemaNodeCompound()
{
new SchemaNodeCompound("Data")
{
@@ -28,7 +31,7 @@ namespace Substrate
},
};
- private INBTWorld _world;
+ private NbtWorld _world;
private long _time;
private long _lastPlayed;
@@ -49,17 +52,27 @@ namespace Substrate
private int? _rainTime;
private int? _thunderTime;
+ ///
+ /// Gets or sets the creation time of the world as a long timestamp.
+ ///
public long Time
{
get { return _time; }
set { _time = value; }
}
+ ///
+ /// Gets or sets the time that the world was last played as a long timestamp.
+ ///
public long LastPlayed
{
get { return _lastPlayed; }
+ set { _lastPlayed = value; }
}
+ ///
+ /// Gets or sets the player for single-player worlds.
+ ///
public Player Player
{
get { return _player; }
@@ -70,41 +83,51 @@ namespace Substrate
}
}
- public int SpawnX
+ ///
+ /// Gets or sets the world's spawn point.
+ ///
+ public SpawnPoint Spawn
{
- get { return _spawnX; }
- set { _spawnX = value; }
- }
-
- public int SpawnY
- {
- get { return _spawnY; }
- set { _spawnY = value; }
- }
-
- public int SpawnZ
- {
- get { return _spawnZ; }
- set { _spawnZ = value; }
+ get { return new SpawnPoint(_spawnX, _spawnY, _spawnZ); }
+ set
+ {
+ _spawnX = value.X;
+ _spawnY = value.Y;
+ _spawnZ = value.Z;
+ }
}
+ ///
+ /// Gets the estimated size of the world in bytes.
+ ///
public long SizeOnDisk
{
get { return _sizeOnDisk; }
}
+ ///
+ /// Gets or sets the world's random seed.
+ ///
public long RandomSeed
{
get { return _randomSeed; }
set { _randomSeed = value; }
}
+ ///
+ /// Gets or sets the world's version number.
+ ///
public int Version
{
get { return _version ?? 0; }
set { _version = value; }
}
+ ///
+ /// Gets or sets the name of the world.
+ ///
+ /// If there is a object attached to this world, the player's world field
+ /// will also be updated.
public string LevelName
{
get { return _name; }
@@ -117,31 +140,55 @@ namespace Substrate
}
}
+ ///
+ /// Gets or sets a value indicating that it is raining in the world.
+ ///
public bool IsRaining
{
get { return (_raining ?? 0) == 1; }
set { _raining = value ? (byte)1 : (byte)0; }
}
+ ///
+ /// Gets or sets a value indicating that it is thunderstorming in the world.
+ ///
public bool IsThundering
{
get { return (_thundering ?? 0) == 1; }
set { _thundering = value ? (byte)1 : (byte)0; }
}
+ ///
+ /// Gets or sets the timer value for controlling rain.
+ ///
public int RainTime
{
get { return _rainTime ?? 0; }
set { _rainTime = value; }
}
+ ///
+ /// Gets or sets the timer value for controlling thunderstorms.
+ ///
public int ThunderTime
{
get { return _thunderTime ?? 0; }
set { _thunderTime = value; }
}
- public Level (INBTWorld world)
+ ///
+ /// Gets a representing the schema of a level.
+ ///
+ public static SchemaNodeCompound Schema
+ {
+ get { return _schema; }
+ }
+
+ ///
+ /// Creates a new object with reasonable defaults tied to the given world.
+ ///
+ /// The world that the should be tied to.
+ public Level (NbtWorld world)
{
_world = world;
@@ -157,7 +204,11 @@ namespace Substrate
_name = "Untitled";
}
- public Level (Level p)
+ ///
+ /// Creates a copy of an existing object.
+ ///
+ /// The object to copy.
+ protected Level (Level p)
{
_world = p._world;
@@ -181,6 +232,9 @@ namespace Substrate
}
}
+ ///
+ /// Creates a default player entry for this world.
+ ///
public void SetDefaultPlayer ()
{
_player = new Player();
@@ -191,27 +245,46 @@ namespace Substrate
_player.Position.Z = _spawnZ;
}
+ ///
+ /// Saves a object to disk as a standard compressed NBT stream.
+ ///
+ /// True if the level was saved; false otherwise.
+ /// Thrown when an error is encountered writing out the level.
public bool Save ()
{
if (_world == null) {
return false;
}
- NBTFile nf = new NBTFile(Path.Combine(_world.WorldPath, "level.dat"));
- Stream zipstr = nf.GetDataOutputStream();
- if (zipstr == null) {
- return false;
+ try {
+ NBTFile nf = new NBTFile(Path.Combine(_world.Path, "level.dat"));
+ Stream zipstr = nf.GetDataOutputStream();
+ if (zipstr == null) {
+ NbtIOException nex = new NbtIOException("Failed to initialize compressed NBT stream for output");
+ nex.Data["Level"] = this;
+ throw nex;
+ }
+
+ new NbtTree(BuildTree() as TagNodeCompound).WriteTo(zipstr);
+ zipstr.Close();
+
+ return true;
+ }
+ catch (Exception ex) {
+ LevelIOException lex = new LevelIOException("Could not save level file.", ex);
+ lex.Data["Level"] = this;
+ throw lex;
}
-
- new NbtTree(BuildTree() as TagNodeCompound).WriteTo(zipstr);
- zipstr.Close();
-
- return true;
}
#region INBTObject Members
+ ///
+ /// Attempt to load a Level subtree into the without validation.
+ ///
+ /// The root node of a Level subtree.
+ /// The returns itself on success, or null if the tree was unparsable.
public virtual Level LoadTree (TagNode tree)
{
TagNodeCompound dtree = tree as TagNodeCompound;
@@ -264,6 +337,11 @@ namespace Substrate
return this;
}
+ ///
+ /// Attempt to load a Level subtree into the with validation.
+ ///
+ /// The root node of a Level subtree.
+ /// The returns itself on success, or null if the tree failed validation.
public virtual Level LoadTreeSafe (TagNode tree)
{
if (!ValidateTree(tree)) {
@@ -273,6 +351,10 @@ namespace Substrate
return LoadTree(tree);
}
+ ///
+ /// Builds a Level subtree from the current data.
+ ///
+ /// The root node of a Level subtree representing the current data.
public virtual TagNode BuildTree ()
{
TagNodeCompound data = new TagNodeCompound();
@@ -316,9 +398,14 @@ namespace Substrate
return tree;
}
+ ///
+ /// Validate a Level subtree against a schema defintion.
+ ///
+ /// The root node of a Level subtree.
+ /// Status indicating whether the tree was valid against the internal schema.
public virtual bool ValidateTree (TagNode tree)
{
- return new NbtVerifier(tree, LevelSchema).Verify();
+ return new NbtVerifier(tree, _schema).Verify();
}
#endregion
@@ -326,6 +413,10 @@ namespace Substrate
#region ICopyable Members
+ ///
+ /// Creates a deep-copy of the .
+ ///
+ /// A deep-copy of the , including a copy of the , if one is attached.
public virtual Level Copy ()
{
return new Level(this);
diff --git a/Substrate/SubstrateCS/Source/LevelIOException.cs b/Substrate/SubstrateCS/Source/LevelIOException.cs
new file mode 100644
index 0000000..709315a
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/LevelIOException.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Substrate
+{
+ ///
+ /// The exception that is thrown when IO errors occur during level management operations.
+ ///
+ [Serializable]
+ public class LevelIOException : SubstrateException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public LevelIOException ()
+ : base()
+ { }
+
+ ///
+ /// Initializes a new instance of the class with a custom error message.
+ ///
+ /// A custom error message.
+ public LevelIOException (string message)
+ : base(message)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with a custom error message and a reference to
+ /// an InnerException representing the original cause of the exception.
+ ///
+ /// A custom error message.
+ /// A reference to the original exception that caused the error.
+ public LevelIOException (string message, Exception innerException)
+ : base(message, innerException)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with serialized data.
+ ///
+ /// The object that holds the serialized object data.
+ /// The contextual information about the source or destination.
+ protected LevelIOException (SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ { }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Nbt/NbtIOException.cs b/Substrate/SubstrateCS/Source/Nbt/NbtIOException.cs
index 0074b75..ed62e31 100644
--- a/Substrate/SubstrateCS/Source/Nbt/NbtIOException.cs
+++ b/Substrate/SubstrateCS/Source/Nbt/NbtIOException.cs
@@ -9,7 +9,7 @@ namespace Substrate.Nbt
/// In most cases, the property will contain more detailed information on the
/// error that occurred.
[Serializable]
- public class NbtIOException : Exception
+ public class NbtIOException : SubstrateException
{
///
/// Initializes a new instance of the class.
diff --git a/Substrate/SubstrateCS/Source/NbtWorld.cs b/Substrate/SubstrateCS/Source/NbtWorld.cs
new file mode 100644
index 0000000..eb7d8fc
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/NbtWorld.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using Substrate.Core;
+using Substrate.Nbt;
+
+namespace Substrate
+{
+ ///
+ /// An abstract representation of any conforming chunk-based world.
+ ///
+ /// By default, NbtWorld registers handlers to check if a given world can be opened as an or
+ /// a , which are used by 's generic method to automatically
+ /// detect a world's type and open it.
+ /// Advanced implementors can support loading other Nbt-compatible world formats by extending and registering
+ /// an event handler with the event, which will allow the generic method to
+ /// open worlds of the new format.
+ public abstract class NbtWorld
+ {
+ private string _path;
+
+ ///
+ /// Creates a new instance of an object.
+ ///
+ protected NbtWorld () { }
+
+ ///
+ /// Gets or sets the path to the directory containing the world.
+ ///
+ public string Path
+ {
+ get { return _path; }
+ set { _path = value; }
+ }
+
+ ///
+ /// Gets a reference to this world's object.
+ ///
+ public abstract Level Level { get; }
+
+ ///
+ /// Gets an for the default dimension.
+ ///
+ /// An tied to the default dimension in this world.
+ public IBlockManager GetBlockManager ()
+ {
+ return GetBlockManagerVirt(Dimension.DEFAULT);
+ }
+
+ ///
+ /// Gets an for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// An tied to the given dimension in this world.
+ public IBlockManager GetBlockManager (int dim)
+ {
+ return GetBlockManagerVirt(dim);
+ }
+
+ ///
+ /// Gets an for the default dimension.
+ ///
+ /// An tied to the default dimension in this world.
+ public IChunkManager GetChunkManager ()
+ {
+ return GetChunkManagerVirt(Dimension.DEFAULT);
+ }
+
+ ///
+ /// Gets an for the given dimension.
+ ///
+ /// The id of the dimension to look up.
+ /// An tied to the given dimension in this world.
+ public IChunkManager GetChunkManager (int dim)
+ {
+ return GetChunkManagerVirt(dim);
+ }
+
+ ///
+ /// Gets an for maanging players on multiplayer worlds.
+ ///
+ /// An for this world.
+ public IPlayerManager GetPlayerManager ()
+ {
+ return GetPlayerManagerVirt();
+ }
+
+ ///
+ /// Attempts to determine the best matching world type of the given path, and open the world as that type.
+ ///
+ /// The path to the directory containing the world.
+ /// A concrete type, or null if the world cannot be opened or is ambiguos.
+ public static NbtWorld Open (string path)
+ {
+ if (ResolveOpen == null) {
+ return null;
+ }
+
+ OpenWorldEventArgs eventArgs = new OpenWorldEventArgs(path);
+ ResolveOpen(null, eventArgs);
+
+ if (eventArgs.HandlerCount != 1) {
+ return null;
+ }
+
+
+ foreach (OpenWorldCallback callback in eventArgs.Handlers) {
+ return callback(path);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Raised when is called, used to find a concrete type that can open the world.
+ ///
+ protected static event EventHandler ResolveOpen;
+
+ #region Covariant Return-Type Helpers
+
+ ///
+ /// Virtual implementor of .
+ ///
+ /// The given dimension to fetch an for.
+ /// An for the given dimension in the world.
+ protected abstract IBlockManager GetBlockManagerVirt (int dim);
+
+ ///
+ /// Virtual implementor of .
+ ///
+ /// The given dimension to fetch an for.
+ /// An for the given dimension in the world.
+ protected abstract IChunkManager GetChunkManagerVirt (int dim);
+
+ ///
+ /// Virtual implementor of .
+ ///
+ /// An for the given dimension in the world.
+ protected abstract IPlayerManager GetPlayerManagerVirt ();
+
+ #endregion
+
+ static NbtWorld ()
+ {
+ ResolveOpen += AlphaWorld.OnResolveOpen;
+ ResolveOpen += BetaWorld.OnResolveOpen;
+ }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/Player.cs b/Substrate/SubstrateCS/Source/Player.cs
index 58a8795..984002a 100644
--- a/Substrate/SubstrateCS/Source/Player.cs
+++ b/Substrate/SubstrateCS/Source/Player.cs
@@ -197,7 +197,7 @@ namespace Substrate
///
/// Gets a representing the schema of a Player.
///
- public static SchemaNodeCompound Schema
+ public static new SchemaNodeCompound Schema
{
get { return _schema; }
}
diff --git a/Substrate/SubstrateCS/Source/PlayerIOException.cs b/Substrate/SubstrateCS/Source/PlayerIOException.cs
index eab4e22..a6816e7 100644
--- a/Substrate/SubstrateCS/Source/PlayerIOException.cs
+++ b/Substrate/SubstrateCS/Source/PlayerIOException.cs
@@ -7,7 +7,7 @@ namespace Substrate
/// The exception that is thrown when IO errors occur during high-level player management operations.
///
[Serializable]
- public class PlayerIOException : Exception
+ public class PlayerIOException : SubstrateException
{
///
/// Initializes a new instance of the class.
diff --git a/Substrate/SubstrateCS/Source/SubstrateException.cs b/Substrate/SubstrateCS/Source/SubstrateException.cs
new file mode 100644
index 0000000..1c05b43
--- /dev/null
+++ b/Substrate/SubstrateCS/Source/SubstrateException.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Substrate
+{
+ ///
+ /// A base class for all Substrate-related exception classes.
+ ///
+ [Serializable]
+ public class SubstrateException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SubstrateException ()
+ : base()
+ { }
+
+ ///
+ /// Initializes a new instance of the class with a custom error message.
+ ///
+ /// A custom error message.
+ public SubstrateException (string message)
+ : base(message)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with a custom error message and a reference to
+ /// an InnerException representing the original cause of the exception.
+ ///
+ /// A custom error message.
+ /// A reference to the original exception that caused the error.
+ public SubstrateException (string message, Exception innerException)
+ : base(message, innerException)
+ { }
+
+ ///
+ /// Initializes a new instance of the class with serialized data.
+ ///
+ /// The object that holds the serialized object data.
+ /// The contextual information about the source or destination.
+ protected SubstrateException (SerializationInfo info, StreamingContext context)
+ : base(info, context)
+ { }
+ }
+}
diff --git a/Substrate/SubstrateCS/Source/World.cs b/Substrate/SubstrateCS/Source/World.cs
index 11bad2b..00774cb 100644
--- a/Substrate/SubstrateCS/Source/World.cs
+++ b/Substrate/SubstrateCS/Source/World.cs
@@ -13,433 +13,5 @@ namespace Substrate
public const int DEFAULT = 0;
}
- public interface INBTWorld
- {
- string WorldPath { get; }
-
- Level Level { get; }
-
- void Save ();
-
- IBlockManager GetBlockManager ();
- IBlockManager GetBlockManager (int dim);
-
- IChunkManager GetChunkManager ();
- IChunkManager GetChunkManager (int dim);
-
- PlayerManager GetPlayerManager ();
- }
-
- public class AlphaWorld : INBTWorld
- {
- private const string _PLAYER_DIR = "players";
- protected string _path;
- protected string _levelFile = "level.dat";
-
- protected Level _level;
-
- private Dictionary _chunkMgrs;
- private Dictionary _blockMgrs;
-
- private PlayerManager _playerMan;
-
- private AlphaWorld ()
- {
- _chunkMgrs = new Dictionary();
- _blockMgrs = new Dictionary();
- }
-
- public BlockManager GetBlockManager ()
- {
- return GetBlockManager(Dimension.DEFAULT);
- }
-
- public BlockManager GetBlockManager (int dim)
- {
- BlockManager rm;
- if (_blockMgrs.TryGetValue(dim, out rm)) {
- return rm;
- }
-
- OpenDimension(dim);
- return _blockMgrs[dim];
- }
-
- public ChunkFileManager GetChunkManager ()
- {
- return GetChunkManager(Dimension.DEFAULT);
- }
-
- public ChunkFileManager GetChunkManager (int dim)
- {
- ChunkFileManager rm;
- if (_chunkMgrs.TryGetValue(dim, out rm)) {
- return rm;
- }
-
- OpenDimension(dim);
- return _chunkMgrs[dim];
- }
-
- public PlayerManager GetPlayerManager ()
- {
- if (_playerMan != null) {
- return _playerMan;
- }
-
- string path = Path.Combine(_path, _PLAYER_DIR);
-
- _playerMan = new PlayerManager(path);
- return _playerMan;
- }
-
- public static AlphaWorld Open (string path)
- {
- return new AlphaWorld().OpenWorld(path) as AlphaWorld;
- }
-
- public static AlphaWorld Create (string path)
- {
- return new AlphaWorld().CreateWorld(path) as AlphaWorld;
- }
-
- public void Save ()
- {
- _level.Save();
-
- foreach (KeyValuePair cm in _chunkMgrs) {
- cm.Value.Save();
- }
- }
-
- private void OpenDimension (int dim)
- {
- string path = _path;
- if (dim != Dimension.DEFAULT) {
- path = Path.Combine(path, "DIM" + dim);
- }
-
- if (!Directory.Exists(path)) {
- Directory.CreateDirectory(path);
- }
-
- ChunkFileManager cm = new ChunkFileManager(path);
- BlockManager bm = new BlockManager(cm);
-
- _chunkMgrs[dim] = cm;
- _blockMgrs[dim] = bm;
- }
-
- private AlphaWorld OpenWorld (string path)
- {
- if (!Directory.Exists(path)) {
- if (File.Exists(path)) {
- _levelFile = Path.GetFileName(path);
- path = Path.GetDirectoryName(path);
- }
- else {
- throw new DirectoryNotFoundException("Directory '" + path + "' not found");
- }
- }
-
- _path = path;
-
- string ldat = Path.Combine(path, _levelFile);
- if (!File.Exists(ldat)) {
- throw new FileNotFoundException("Data file '" + _levelFile + "' not found in '" + path + "'", ldat);
- }
-
- if (!LoadLevel()) {
- throw new Exception("Failed to load '" + _levelFile + "'");
- }
-
- return this;
- }
-
- private AlphaWorld CreateWorld (string path)
- {
- if (!Directory.Exists(path)) {
- throw new DirectoryNotFoundException("Directory '" + path + "' not found");
- }
-
- _path = path;
- _level = new Level(this);
-
- return this;
- }
-
- private bool LoadLevel ()
- {
- NBTFile nf = new NBTFile(Path.Combine(_path, _levelFile));
- Stream nbtstr = nf.GetDataInputStream();
- if (nbtstr == null) {
- return false;
- }
-
- NbtTree tree = new NbtTree(nbtstr);
-
- _level = new Level(this);
- _level = _level.LoadTreeSafe(tree.Root);
-
- return _level != null;
- }
-
-
- #region INBTWorld Members
-
- public string WorldPath
- {
- get { return _path; }
- }
-
- public Level Level
- {
- get { return _level; }
- }
-
- IBlockManager INBTWorld.GetBlockManager ()
- {
- return GetBlockManager();
- }
-
- IBlockManager INBTWorld.GetBlockManager (int dim)
- {
- return GetBlockManager(dim);
- }
-
- IChunkManager INBTWorld.GetChunkManager ()
- {
- return GetChunkManager();
- }
-
- IChunkManager INBTWorld.GetChunkManager (int dim)
- {
- return GetChunkManager(dim);
- }
-
- #endregion
- }
-
- public class BetaWorld : INBTWorld {
- private const string _REGION_DIR = "region";
- private const string _PLAYER_DIR = "players";
- protected string _path;
- protected string _levelFile = "level.dat";
-
- protected Level _level;
-
- private Dictionary _regionMgrs;
- private Dictionary _chunkMgrs;
- private Dictionary _blockMgrs;
-
- private PlayerManager _playerMan;
-
- private BetaWorld ()
- {
- _regionMgrs = new Dictionary();
- _chunkMgrs = new Dictionary();
- _blockMgrs = new Dictionary();
- }
-
- public BlockManager GetBlockManager ()
- {
- return GetBlockManager(Dimension.DEFAULT);
- }
-
- public BlockManager GetBlockManager (int dim)
- {
- BlockManager rm;
- if (_blockMgrs.TryGetValue(dim, out rm)) {
- return rm;
- }
-
- OpenDimension(dim);
- return _blockMgrs[dim];
- }
-
- public ChunkManager GetChunkManager ()
- {
- return GetChunkManager(Dimension.DEFAULT);
- }
-
- public ChunkManager GetChunkManager (int dim)
- {
- ChunkManager rm;
- if (_chunkMgrs.TryGetValue(dim, out rm)) {
- return rm;
- }
-
- OpenDimension(dim);
- return _chunkMgrs[dim];
- }
-
- public RegionManager GetRegionManager ()
- {
- return GetRegionManager(Dimension.DEFAULT);
- }
-
- public RegionManager GetRegionManager (int dim)
- {
- RegionManager rm;
- if (_regionMgrs.TryGetValue(dim, out rm)) {
- return rm;
- }
-
- OpenDimension(dim);
- return _regionMgrs[dim];
- }
-
- public PlayerManager GetPlayerManager ()
- {
- if (_playerMan != null) {
- return _playerMan;
- }
-
- string path = Path.Combine(_path, _PLAYER_DIR);
-
- _playerMan = new PlayerManager(path);
- return _playerMan;
- }
-
- public static BetaWorld Open (string path)
- {
- return new BetaWorld().OpenWorld(path) as BetaWorld;
- }
-
- public static BetaWorld Create (string path)
- {
- return new BetaWorld().CreateWorld(path) as BetaWorld;
- }
-
- public void Save ()
- {
- _level.Save();
-
- foreach (KeyValuePair cm in _chunkMgrs) {
- cm.Value.Save();
- }
- }
-
- private void OpenDimension (int dim)
- {
- string path = _path;
- if (dim == Dimension.DEFAULT) {
- path = Path.Combine(path, _REGION_DIR);
- }
- else {
- path = Path.Combine(path, "DIM" + dim);
- path = Path.Combine(path, _REGION_DIR);
- }
-
- if (!Directory.Exists(path)) {
- Directory.CreateDirectory(path);
- }
-
- ChunkCache cc = new ChunkCache();
-
- RegionManager rm = new RegionManager(path, cc);
- ChunkManager cm = new ChunkManager(rm, cc);
- BlockManager bm = new BlockManager(cm);
-
- _regionMgrs[dim] = rm;
- _chunkMgrs[dim] = cm;
- _blockMgrs[dim] = bm;
- }
-
- private BetaWorld OpenWorld (string path)
- {
- if (!Directory.Exists(path)) {
- if (File.Exists(path)) {
- _levelFile = Path.GetFileName(path);
- path = Path.GetDirectoryName(path);
- }
- else {
- throw new DirectoryNotFoundException("Directory '" + path + "' not found");
- }
- }
-
- _path = path;
-
- string ldat = Path.Combine(path, _levelFile);
- if (!File.Exists(ldat)) {
- throw new FileNotFoundException("Data file '" + _levelFile + "' not found in '" + path + "'", ldat);
- }
-
- if (!LoadLevel()) {
- throw new Exception("Failed to load '" + _levelFile + "'");
- }
-
- return this;
- }
-
- private BetaWorld CreateWorld (string path)
- {
- if (!Directory.Exists(path)) {
- throw new DirectoryNotFoundException("Directory '" + path + "' not found");
- }
-
- string regpath = Path.Combine(path, _REGION_DIR);
- if (!Directory.Exists(regpath)) {
- Directory.CreateDirectory(regpath);
- }
-
- _path = path;
- _level = new Level(this);
-
- return this;
- }
-
- private bool LoadLevel ()
- {
- NBTFile nf = new NBTFile(Path.Combine(_path, _levelFile));
- Stream nbtstr = nf.GetDataInputStream();
- if (nbtstr == null) {
- return false;
- }
-
- NbtTree tree = new NbtTree(nbtstr);
-
- _level = new Level(this);
- _level = _level.LoadTreeSafe(tree.Root);
-
- return _level != null;
- }
-
-
- #region INBTWorld Members
-
- public string WorldPath
- {
- get { return _path; }
- }
-
- public Level Level
- {
- get { return _level; }
- }
-
- IBlockManager INBTWorld.GetBlockManager ()
- {
- return GetBlockManager();
- }
-
- IBlockManager INBTWorld.GetBlockManager (int dim)
- {
- return GetBlockManager(dim);
- }
-
- IChunkManager INBTWorld.GetChunkManager ()
- {
- return GetChunkManager();
- }
-
- IChunkManager INBTWorld.GetChunkManager (int dim)
- {
- return GetChunkManager(dim);
- }
-
- #endregion
- }
-
public class DimensionNotFoundException : Exception { }
}
diff --git a/Substrate/SubstrateCS/Substrate.csproj b/Substrate/SubstrateCS/Substrate.csproj
index 615d8e2..2e68610 100644
--- a/Substrate/SubstrateCS/Substrate.csproj
+++ b/Substrate/SubstrateCS/Substrate.csproj
@@ -43,7 +43,7 @@
DEBUG;TRACE
prompt
4
- Substrate.xml
+ bin\Debug\Substrate.XML
pdbonly
@@ -52,7 +52,7 @@
TRACE
prompt
4
- Substrate.xml
+ bin\Release\Substrate.XML
@@ -62,6 +62,10 @@
+
+
+
+
@@ -76,6 +80,7 @@
+
Code
@@ -159,6 +164,7 @@
+