More documentation, major refactoring of World structure, moving towards CLS-compliance.

This commit is contained in:
Justin Aquadro 2011-07-04 03:45:36 +00:00
parent 3387386295
commit b7b6f88fbe
18 changed files with 1124 additions and 582 deletions

View file

@ -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)]

View file

@ -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;
/// <summary>
/// Represents an Alpha-compatible (up to Beta 1.2) Minecraft world.
/// </summary>
public class AlphaWorld : NbtWorld
{
private const string _PLAYER_DIR = "players";
private string _levelFile = "level.dat";
private Level _level;
private Dictionary<int, ChunkFileManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs;
private PlayerManager _playerMan;
private AlphaWorld ()
{
_chunkMgrs = new Dictionary<int, ChunkFileManager>();
_blockMgrs = new Dictionary<int, BlockManager>();
}
/// <summary>
/// Gets a reference to this world's <see cref="Level"/> object.
/// </summary>
public override Level Level
{
get { return _level; }
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="ChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager ()
{
return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="ChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager (int dim)
{
return GetBlockManagerVirt(dim) as BlockManager;
}
/// <summary>
/// Gets a <see cref="ChunkManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="ChunkManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="ChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new ChunkFileManager GetChunkManager ()
{
return GetChunkManagerVirt(Dimension.DEFAULT) as ChunkFileManager;
}
/// <summary>
/// Gets a <see cref="ChunkManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="ChunkManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="ChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new ChunkFileManager GetChunkManager (int dim)
{
return GetChunkManagerVirt(dim) as ChunkFileManager;
}
/// <summary>
/// Gets a <see cref="PlayerManager"/> for maanging players on multiplayer worlds.
/// </summary>
/// <returns>A <see cref="PlayerManager"/> for this world.</returns>
/// <remarks>To manage the player of a single-player world, get a <see cref="Level"/> object for the world instead.</remarks>
public new PlayerManager GetPlayerManager ()
{
return GetPlayerManagerVirt() as PlayerManager;
}
/// <summary>
/// Saves the world's <see cref="Level"/> data, and any <see cref="Chunk"/> objects known to have unsaved changes.
/// </summary>
public void Save ()
{
_level.Save();
foreach (KeyValuePair<int, ChunkFileManager> cm in _chunkMgrs) {
cm.Value.Save();
}
}
/// <summary>
/// Opens an existing Alpha-compatible Minecraft world and returns a new <see cref="AlphaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory containing the world's level.dat, or the path to level.dat itself.</param>
/// <returns>A new <see cref="AlphaWorld"/> object representing an existing world on disk.</returns>
public static AlphaWorld Open (string path)
{
return new AlphaWorld().OpenWorld(path) as AlphaWorld;
}
/// <summary>
/// Creates a new Alpha-compatible Minecraft world and returns a new <see cref="AlphaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory where the new world should be stored.</param>
/// <returns>A new <see cref="AlphaWorld"/> object representing a new world.</returns>
/// <remarks>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.</remarks>
public static AlphaWorld Create (string path)
{
return new AlphaWorld().CreateWorld(path) as AlphaWorld;
}
/// <exclude/>
protected override IBlockManager GetBlockManagerVirt (int dim)
{
BlockManager rm;
if (_blockMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _blockMgrs[dim];
}
/// <exclude/>
protected override IChunkManager GetChunkManagerVirt (int dim)
{
ChunkFileManager rm;
if (_chunkMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _chunkMgrs[dim];
}
/// <exclude/>
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;
}
}
}
}

View file

@ -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;
/// <summary>
/// Represents a Beta-compatible (Beta 1.3 or higher) Minecraft world.
/// </summary>
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<int, RegionManager> _regionMgrs;
private Dictionary<int, ChunkManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs;
private PlayerManager _playerMan;
private BetaWorld ()
{
_regionMgrs = new Dictionary<int, RegionManager>();
_chunkMgrs = new Dictionary<int, ChunkManager>();
_blockMgrs = new Dictionary<int, BlockManager>();
}
/// <summary>
/// Gets a reference to this world's <see cref="Level"/> object.
/// </summary>
public override Level Level
{
get { return _level; }
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="BlockManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="ChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager ()
{
return GetBlockManagerVirt(Dimension.DEFAULT) as BlockManager;
}
/// <summary>
/// Gets a <see cref="BlockManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="BlockManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="BlockManager"/> 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
/// <see cref="ChunkManager"/> instead and working with blocks on a chunk-local level.</remarks>
public new BlockManager GetBlockManager (int dim)
{
return GetBlockManagerVirt(dim) as BlockManager;
}
/// <summary>
/// Gets a <see cref="ChunkManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="ChunkManager"/> tied to the default dimension in this world.</returns>
/// <remarks>Get a <see cref="ChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new ChunkManager GetChunkManager ()
{
return GetChunkManagerVirt(Dimension.DEFAULT) as ChunkManager;
}
/// <summary>
/// Gets a <see cref="ChunkManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="ChunkManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Get a <see cref="ChunkManager"/> if you you need to work with easily-digestible, bounded chunks of blocks.</remarks>
public new ChunkManager GetChunkManager (int dim)
{
return GetChunkManagerVirt(dim) as ChunkManager;
}
/// <summary>
/// Gets a <see cref="RegionManager"/> for the default dimension.
/// </summary>
/// <returns>A <see cref="RegionManager"/> tied to the defaul dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="ChunkManager"/> if you are interested in working with blocks.</remarks>
public RegionManager GetRegionManager ()
{
return GetRegionManager(Dimension.DEFAULT);
}
/// <summary>
/// Gets a <see cref="RegionManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>A <see cref="RegionManager"/> tied to the given dimension in this world.</returns>
/// <remarks>Regions are a higher-level unit of organization for blocks unique to worlds created in Beta 1.3 and beyond.
/// Consider using the <see cref="ChunkManager"/> if you are interested in working with blocks.</remarks>
public RegionManager GetRegionManager (int dim)
{
RegionManager rm;
if (_regionMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _regionMgrs[dim];
}
/// <summary>
/// Gets a <see cref="PlayerManager"/> for maanging players on multiplayer worlds.
/// </summary>
/// <returns>A <see cref="PlayerManager"/> for this world.</returns>
/// <remarks>To manage the player of a single-player world, get a <see cref="Level"/> object for the world instead.</remarks>
public new PlayerManager GetPlayerManager ()
{
return GetPlayerManagerVirt() as PlayerManager;
}
/// <summary>
/// Saves the world's <see cref="Level"/> data, and any <see cref="Chunk"/> objects known to have unsaved changes.
/// </summary>
public void Save ()
{
_level.Save();
foreach (KeyValuePair<int, ChunkManager> cm in _chunkMgrs) {
cm.Value.Save();
}
}
/// <summary>
/// Opens an existing Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory containing the world's level.dat, or the path to level.dat itself.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing an existing world on disk.</returns>
public static BetaWorld Open (string path)
{
return new BetaWorld().OpenWorld(path) as BetaWorld;
}
/// <summary>
/// Creates a new Beta-compatible Minecraft world and returns a new <see cref="BetaWorld"/> to represent it.
/// </summary>
/// <param name="path">The path to the directory where the new world should be stored.</param>
/// <returns>A new <see cref="BetaWorld"/> object representing a new world.</returns>
/// <remarks>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.</remarks>
public static BetaWorld Create (string path)
{
return new BetaWorld().CreateWorld(path) as BetaWorld;
}
/// <exclude/>
protected override IBlockManager GetBlockManagerVirt (int dim)
{
BlockManager rm;
if (_blockMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _blockMgrs[dim];
}
/// <exclude/>
protected override IChunkManager GetChunkManagerVirt (int dim)
{
ChunkManager rm;
if (_chunkMgrs.TryGetValue(dim, out rm)) {
return rm;
}
OpenDimension(dim);
return _chunkMgrs[dim];
}
/// <exclude/>
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;
}
}
}
}

View file

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

View file

@ -124,7 +124,6 @@ namespace Substrate
c._cz = z;
c.BuildNBTTree();
return c;
}

View file

@ -6,29 +6,29 @@ namespace Substrate.Core
{
public class ByteArray : ICopyable<ByteArray>
{
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);
}

View file

@ -224,7 +224,7 @@ namespace Substrate.Core
public IEnumerator<ChunkRef> 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<ChunkRef>
private class Enumerator : IEnumerator<ChunkRef>
{
protected ChunkFileManager _cm;
protected Queue<string> _tld;
@ -250,7 +250,7 @@ namespace Substrate.Core
private string _cursld;
private ChunkRef _curchunk;
public ChunkEnumerator (ChunkFileManager cfm)
public Enumerator (ChunkFileManager cfm)
{
_cm = cfm;

View file

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
namespace Substrate.Core
{
/// <summary>
/// A callback function to open a world and return it as an instance of a concrete derivative of <see cref="NbtWorld"/>.
/// </summary>
/// <param name="path">The path to the directory of the world to open.</param>
/// <returns>An instance of a concrete derivative of <see cref="NbtWorld"/>.</returns>
public delegate NbtWorld OpenWorldCallback (string path);
/// <summary>
/// Event arugments and response data for any handlers trying to determine if they can open a given world.
/// </summary>
public class OpenWorldEventArgs : EventArgs
{
private List<OpenWorldCallback> _handlers;
private string _path;
/// <summary>
/// Create a new instance of event arguments.
/// </summary>
/// <param name="path">The path to the directory of a world.</param>
public OpenWorldEventArgs (string path)
: base()
{
_path = path;
_handlers = new List<OpenWorldCallback>();
}
/// <summary>
/// Gets the path to the directory of a world being investigated.
/// </summary>
public string Path
{
get { return _path; }
}
/// <summary>
/// Adds an <see cref="OpenWorldCallback"/> delegate that can open a world and return a corresponding <see cref="NbtWorld"/> object.
/// </summary>
/// <param name="callback">The delegate to return to the code that raised the event.</param>
public void AddHandler (OpenWorldCallback callback)
{
_handlers.Add(callback);
}
internal int HandlerCount
{
get { return _handlers.Count; }
}
internal ICollection<OpenWorldCallback> Handlers
{
get { return _handlers; }
}
}
}

View file

@ -300,7 +300,7 @@ namespace Substrate
/// <summary>
/// Gets a <see cref="SchemaNode"/> representing the basic schema of an Entity.
/// </summary>
public static SchemaNodeCompound Schema
public static new SchemaNodeCompound Schema
{
get { return _schema; }
}

View file

@ -5,9 +5,12 @@ using Substrate.Nbt;
namespace Substrate
{
/// <summary>
/// Represents general data and metadata of a single world.
/// </summary>
public class Level : INbtObject<Level>, ICopyable<Level>
{
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;
/// <summary>
/// Gets or sets the creation time of the world as a long timestamp.
/// </summary>
public long Time
{
get { return _time; }
set { _time = value; }
}
/// <summary>
/// Gets or sets the time that the world was last played as a long timestamp.
/// </summary>
public long LastPlayed
{
get { return _lastPlayed; }
set { _lastPlayed = value; }
}
/// <summary>
/// Gets or sets the player for single-player worlds.
/// </summary>
public Player Player
{
get { return _player; }
@ -70,41 +83,51 @@ namespace Substrate
}
}
public int SpawnX
/// <summary>
/// Gets or sets the world's spawn point.
/// </summary>
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;
}
}
/// <summary>
/// Gets the estimated size of the world in bytes.
/// </summary>
public long SizeOnDisk
{
get { return _sizeOnDisk; }
}
/// <summary>
/// Gets or sets the world's random seed.
/// </summary>
public long RandomSeed
{
get { return _randomSeed; }
set { _randomSeed = value; }
}
/// <summary>
/// Gets or sets the world's version number.
/// </summary>
public int Version
{
get { return _version ?? 0; }
set { _version = value; }
}
/// <summary>
/// Gets or sets the name of the world.
/// </summary>
/// <remarks>If there is a <see cref="Player"/> object attached to this world, the player's world field
/// will also be updated.</remarks>
public string LevelName
{
get { return _name; }
@ -117,31 +140,55 @@ namespace Substrate
}
}
/// <summary>
/// Gets or sets a value indicating that it is raining in the world.
/// </summary>
public bool IsRaining
{
get { return (_raining ?? 0) == 1; }
set { _raining = value ? (byte)1 : (byte)0; }
}
/// <summary>
/// Gets or sets a value indicating that it is thunderstorming in the world.
/// </summary>
public bool IsThundering
{
get { return (_thundering ?? 0) == 1; }
set { _thundering = value ? (byte)1 : (byte)0; }
}
/// <summary>
/// Gets or sets the timer value for controlling rain.
/// </summary>
public int RainTime
{
get { return _rainTime ?? 0; }
set { _rainTime = value; }
}
/// <summary>
/// Gets or sets the timer value for controlling thunderstorms.
/// </summary>
public int ThunderTime
{
get { return _thunderTime ?? 0; }
set { _thunderTime = value; }
}
public Level (INBTWorld world)
/// <summary>
/// Gets a <see cref="SchemaNode"/> representing the schema of a level.
/// </summary>
public static SchemaNodeCompound Schema
{
get { return _schema; }
}
/// <summary>
/// Creates a new <see cref="Level"/> object with reasonable defaults tied to the given world.
/// </summary>
/// <param name="world">The world that the <see cref="Level"/> should be tied to.</param>
public Level (NbtWorld world)
{
_world = world;
@ -157,7 +204,11 @@ namespace Substrate
_name = "Untitled";
}
public Level (Level p)
/// <summary>
/// Creates a copy of an existing <see cref="Level"/> object.
/// </summary>
/// <param name="p">The <see cref="Level"/> object to copy.</param>
protected Level (Level p)
{
_world = p._world;
@ -181,6 +232,9 @@ namespace Substrate
}
}
/// <summary>
/// Creates a default player entry for this world.
/// </summary>
public void SetDefaultPlayer ()
{
_player = new Player();
@ -191,16 +245,24 @@ namespace Substrate
_player.Position.Z = _spawnZ;
}
/// <summary>
/// Saves a <see cref="Level"/> object to disk as a standard compressed NBT stream.
/// </summary>
/// <returns>True if the level was saved; false otherwise.</returns>
/// <exception cref="LevelIOException">Thrown when an error is encountered writing out the level.</exception>
public bool Save ()
{
if (_world == null) {
return false;
}
NBTFile nf = new NBTFile(Path.Combine(_world.WorldPath, "level.dat"));
try {
NBTFile nf = new NBTFile(Path.Combine(_world.Path, "level.dat"));
Stream zipstr = nf.GetDataOutputStream();
if (zipstr == null) {
return false;
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);
@ -208,10 +270,21 @@ namespace Substrate
return true;
}
catch (Exception ex) {
LevelIOException lex = new LevelIOException("Could not save level file.", ex);
lex.Data["Level"] = this;
throw lex;
}
}
#region INBTObject<Player> Members
/// <summary>
/// Attempt to load a Level subtree into the <see cref="Level"/> without validation.
/// </summary>
/// <param name="tree">The root node of a Level subtree.</param>
/// <returns>The <see cref="Level"/> returns itself on success, or null if the tree was unparsable.</returns>
public virtual Level LoadTree (TagNode tree)
{
TagNodeCompound dtree = tree as TagNodeCompound;
@ -264,6 +337,11 @@ namespace Substrate
return this;
}
/// <summary>
/// Attempt to load a Level subtree into the <see cref="Level"/> with validation.
/// </summary>
/// <param name="tree">The root node of a Level subtree.</param>
/// <returns>The <see cref="Level"/> returns itself on success, or null if the tree failed validation.</returns>
public virtual Level LoadTreeSafe (TagNode tree)
{
if (!ValidateTree(tree)) {
@ -273,6 +351,10 @@ namespace Substrate
return LoadTree(tree);
}
/// <summary>
/// Builds a Level subtree from the current data.
/// </summary>
/// <returns>The root node of a Level subtree representing the current data.</returns>
public virtual TagNode BuildTree ()
{
TagNodeCompound data = new TagNodeCompound();
@ -316,9 +398,14 @@ namespace Substrate
return tree;
}
/// <summary>
/// Validate a Level subtree against a schema defintion.
/// </summary>
/// <param name="tree">The root node of a Level subtree.</param>
/// <returns>Status indicating whether the tree was valid against the internal schema.</returns>
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<Entity> Members
/// <summary>
/// Creates a deep-copy of the <see cref="Level"/>.
/// </summary>
/// <returns>A deep-copy of the <see cref="Level"/>, including a copy of the <see cref="Player"/>, if one is attached.</returns>
public virtual Level Copy ()
{
return new Level(this);

View file

@ -0,0 +1,46 @@
using System;
using System.Runtime.Serialization;
namespace Substrate
{
/// <summary>
/// The exception that is thrown when IO errors occur during level management operations.
/// </summary>
[Serializable]
public class LevelIOException : SubstrateException
{
/// <summary>
/// Initializes a new instance of the <see cref="LevelIOException"/> class.
/// </summary>
public LevelIOException ()
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LevelIOException"/> class with a custom error message.
/// </summary>
/// <param name="message">A custom error message.</param>
public LevelIOException (string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LevelIOException"/> class with a custom error message and a reference to
/// an InnerException representing the original cause of the exception.
/// </summary>
/// <param name="message">A custom error message.</param>
/// <param name="innerException">A reference to the original exception that caused the error.</param>
public LevelIOException (string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="LevelIOException"/> class with serialized data.
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source or destination.</param>
protected LevelIOException (SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View file

@ -9,7 +9,7 @@ namespace Substrate.Nbt
/// <remarks>In most cases, the <see cref="InnerException"/> property will contain more detailed information on the
/// error that occurred.</remarks>
[Serializable]
public class NbtIOException : Exception
public class NbtIOException : SubstrateException
{
/// <summary>
/// Initializes a new instance of the <see cref="NbtIOException"/> class.

View file

@ -0,0 +1,150 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using Substrate.Core;
using Substrate.Nbt;
namespace Substrate
{
/// <summary>
/// An abstract representation of any conforming chunk-based world.
/// </summary>
/// <remarks><para>By default, NbtWorld registers handlers to check if a given world can be opened as an <see cref="AlphaWorld"/> or
/// a <see cref="BetaWorld"/>, which are used by <see cref="NbtWorld"/>'s generic <see cref="Open(string)"/> method to automatically
/// detect a world's type and open it.</para>
/// <para>Advanced implementors can support loading other Nbt-compatible world formats by extending <see cref="NbtWorld"/> and registering
/// an event handler with the <see cref="ResolveOpen"/> event, which will allow the generic <see cref="Open(string)"/> method to
/// open worlds of the new format.</para></remarks>
public abstract class NbtWorld
{
private string _path;
/// <summary>
/// Creates a new instance of an <see cref="NbtWorld"/> object.
/// </summary>
protected NbtWorld () { }
/// <summary>
/// Gets or sets the path to the directory containing the world.
/// </summary>
public string Path
{
get { return _path; }
set { _path = value; }
}
/// <summary>
/// Gets a reference to this world's <see cref="Level"/> object.
/// </summary>
public abstract Level Level { get; }
/// <summary>
/// Gets an <see cref="IBlockManager"/> for the default dimension.
/// </summary>
/// <returns>An <see cref="IBlockManager"/> tied to the default dimension in this world.</returns>
public IBlockManager GetBlockManager ()
{
return GetBlockManagerVirt(Dimension.DEFAULT);
}
/// <summary>
/// Gets an <see cref="IBlockManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>An <see cref="IBlockManager"/> tied to the given dimension in this world.</returns>
public IBlockManager GetBlockManager (int dim)
{
return GetBlockManagerVirt(dim);
}
/// <summary>
/// Gets an <see cref="IChunkManager"/> for the default dimension.
/// </summary>
/// <returns>An <see cref="IChunkManager"/> tied to the default dimension in this world.</returns>
public IChunkManager GetChunkManager ()
{
return GetChunkManagerVirt(Dimension.DEFAULT);
}
/// <summary>
/// Gets an <see cref="IChunkManager"/> for the given dimension.
/// </summary>
/// <param name="dim">The id of the dimension to look up.</param>
/// <returns>An <see cref="IChunkManager"/> tied to the given dimension in this world.</returns>
public IChunkManager GetChunkManager (int dim)
{
return GetChunkManagerVirt(dim);
}
/// <summary>
/// Gets an <see cref="IPlayerManager"/> for maanging players on multiplayer worlds.
/// </summary>
/// <returns>An <see cref="IPlayerManager"/> for this world.</returns>
public IPlayerManager GetPlayerManager ()
{
return GetPlayerManagerVirt();
}
/// <summary>
/// Attempts to determine the best matching world type of the given path, and open the world as that type.
/// </summary>
/// <param name="path">The path to the directory containing the world.</param>
/// <returns>A concrete <see cref="NbtWorld"/> type, or null if the world cannot be opened or is ambiguos.</returns>
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;
}
/// <summary>
/// Raised when <see cref="Open"/> is called, used to find a concrete <see cref="NbtWorld"/> type that can open the world.
/// </summary>
protected static event EventHandler<OpenWorldEventArgs> ResolveOpen;
#region Covariant Return-Type Helpers
/// <summary>
/// Virtual implementor of <see cref="GetBlockManager(int)"/>.
/// </summary>
/// <param name="dim">The given dimension to fetch an <see cref="IBlockManager"/> for.</param>
/// <returns>An <see cref="IBlockManager"/> for the given dimension in the world.</returns>
protected abstract IBlockManager GetBlockManagerVirt (int dim);
/// <summary>
/// Virtual implementor of <see cref="GetChunkManager(int)"/>.
/// </summary>
/// <param name="dim">The given dimension to fetch an <see cref="IChunkManager"/> for.</param>
/// <returns>An <see cref="IChunkManager"/> for the given dimension in the world.</returns>
protected abstract IChunkManager GetChunkManagerVirt (int dim);
/// <summary>
/// Virtual implementor of <see cref="GetPlayerManager"/>.
/// </summary>
/// <returns>An <see cref="IPlayerManager"/> for the given dimension in the world.</returns>
protected abstract IPlayerManager GetPlayerManagerVirt ();
#endregion
static NbtWorld ()
{
ResolveOpen += AlphaWorld.OnResolveOpen;
ResolveOpen += BetaWorld.OnResolveOpen;
}
}
}

View file

@ -197,7 +197,7 @@ namespace Substrate
/// <summary>
/// Gets a <see cref="SchemaNode"/> representing the schema of a Player.
/// </summary>
public static SchemaNodeCompound Schema
public static new SchemaNodeCompound Schema
{
get { return _schema; }
}

View file

@ -7,7 +7,7 @@ namespace Substrate
/// The exception that is thrown when IO errors occur during high-level player management operations.
/// </summary>
[Serializable]
public class PlayerIOException : Exception
public class PlayerIOException : SubstrateException
{
/// <summary>
/// Initializes a new instance of the <see cref="PlayerIOException"/> class.

View file

@ -0,0 +1,46 @@
using System;
using System.Runtime.Serialization;
namespace Substrate
{
/// <summary>
/// A base class for all Substrate-related exception classes.
/// </summary>
[Serializable]
public class SubstrateException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="SubstrateException"/> class.
/// </summary>
public SubstrateException ()
: base()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SubstrateException"/> class with a custom error message.
/// </summary>
/// <param name="message">A custom error message.</param>
public SubstrateException (string message)
: base(message)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SubstrateException"/> class with a custom error message and a reference to
/// an InnerException representing the original cause of the exception.
/// </summary>
/// <param name="message">A custom error message.</param>
/// <param name="innerException">A reference to the original exception that caused the error.</param>
public SubstrateException (string message, Exception innerException)
: base(message, innerException)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="SubstrateException"/> class with serialized data.
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source or destination.</param>
protected SubstrateException (SerializationInfo info, StreamingContext context)
: base(info, context)
{ }
}
}

View file

@ -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<int, ChunkFileManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs;
private PlayerManager _playerMan;
private AlphaWorld ()
{
_chunkMgrs = new Dictionary<int, ChunkFileManager>();
_blockMgrs = new Dictionary<int, BlockManager>();
}
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<int, ChunkFileManager> 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<int, RegionManager> _regionMgrs;
private Dictionary<int, ChunkManager> _chunkMgrs;
private Dictionary<int, BlockManager> _blockMgrs;
private PlayerManager _playerMan;
private BetaWorld ()
{
_regionMgrs = new Dictionary<int, RegionManager>();
_chunkMgrs = new Dictionary<int, ChunkManager>();
_blockMgrs = new Dictionary<int, BlockManager>();
}
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<int, ChunkManager> 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 { }
}

View file

@ -43,7 +43,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>Substrate.xml</DocumentationFile>
<DocumentationFile>bin\Debug\Substrate.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -52,7 +52,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>Substrate.xml</DocumentationFile>
<DocumentationFile>bin\Release\Substrate.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="Ionic.Zlib, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
@ -62,6 +62,10 @@
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Source\AlphaWorld.cs" />
<Compile Include="Source\BetaWorld.cs" />
<Compile Include="Source\Core\OpenWorldEvent.cs" />
<Compile Include="Source\LevelIOException.cs" />
<Compile Include="Source\AlphaBlock.cs" />
<Compile Include="Source\AlphaBlockRef.cs" />
<Compile Include="Source\CacheTable.cs" />
@ -76,6 +80,7 @@
<Compile Include="Source\Core\BlockLight.cs" />
<Compile Include="Source\Core\BlockTileEntities.cs" />
<Compile Include="Source\Level.cs" />
<Compile Include="Source\NbtWorld.cs" />
<Compile Include="Source\NBT\INBTObject.cs">
<SubType>Code</SubType>
</Compile>
@ -159,6 +164,7 @@
<Compile Include="Source\Core\RegionKey.cs" />
<Compile Include="Source\RegionManager.cs" />
<Compile Include="Source\SpawnPoint.cs" />
<Compile Include="Source\SubstrateException.cs" />
<Compile Include="Source\TileEntities\TileEntityChest.cs" />
<Compile Include="Source\TileEntities\TileEntityFurnace.cs" />
<Compile Include="Source\TileEntities\TileEntityMobSpawner.cs" />