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 @@ +