2011-04-06 04:43:54 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.IO;
|
2011-06-30 04:41:29 +00:00
|
|
|
|
using Substrate.Nbt;
|
2011-06-29 02:58:34 +00:00
|
|
|
|
using Substrate.Core;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
2011-04-06 21:20:35 +00:00
|
|
|
|
namespace Substrate
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
2011-04-14 07:04:13 +00:00
|
|
|
|
public class Region : IDisposable, IChunkContainer
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
private const int XDIM = 32;
|
|
|
|
|
private const int ZDIM = 32;
|
|
|
|
|
private const int XMASK = XDIM - 1;
|
|
|
|
|
private const int ZMASK = ZDIM - 1;
|
|
|
|
|
private const int XLOG = 5;
|
|
|
|
|
private const int ZLOG = 5;
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
protected int _rx;
|
|
|
|
|
protected int _rz;
|
|
|
|
|
protected bool _disposed = false;
|
|
|
|
|
|
|
|
|
|
protected RegionManager _regionMan;
|
|
|
|
|
|
|
|
|
|
protected static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
|
|
|
|
|
|
|
|
|
|
protected WeakReference _regionFile;
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
//protected Dictionary<ChunkKey, WeakReference> _cache;
|
|
|
|
|
//protected Dictionary<ChunkKey, ChunkRef> _dirty;
|
|
|
|
|
|
|
|
|
|
protected ChunkCache _cache;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
|
|
|
|
public int X
|
|
|
|
|
{
|
|
|
|
|
get { return _rx; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int Z
|
|
|
|
|
{
|
|
|
|
|
get { return _rz; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-11 08:40:48 +00:00
|
|
|
|
public int XDim
|
|
|
|
|
{
|
|
|
|
|
get { return XDIM; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ZDim
|
|
|
|
|
{
|
|
|
|
|
get { return ZDIM; }
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
public Region (RegionManager rm, ChunkCache cache, int rx, int rz)
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
|
|
|
|
_regionMan = rm;
|
2011-04-14 07:04:13 +00:00
|
|
|
|
_cache = cache;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
_regionFile = new WeakReference(null);
|
|
|
|
|
_rx = rx;
|
|
|
|
|
_rz = rz;
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
//_cache = new Dictionary<ChunkKey, WeakReference>();
|
|
|
|
|
//_dirty = new Dictionary<ChunkKey, ChunkRef>();
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
|
|
|
|
if (!File.Exists(GetFilePath())) {
|
|
|
|
|
throw new FileNotFoundException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
public Region (RegionManager rm, ChunkCache cache, string filename)
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
|
|
|
|
_regionMan = rm;
|
2011-04-14 07:04:13 +00:00
|
|
|
|
_cache = cache;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
_regionFile = new WeakReference(null);
|
|
|
|
|
|
|
|
|
|
ParseFileName(filename, out _rx, out _rz);
|
|
|
|
|
|
|
|
|
|
if (!File.Exists(Path.Combine(_regionMan.GetRegionPath(), filename))) {
|
|
|
|
|
throw new FileNotFoundException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~Region ()
|
|
|
|
|
{
|
|
|
|
|
Dispose(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose ()
|
|
|
|
|
{
|
|
|
|
|
Dispose(true);
|
|
|
|
|
System.GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose (bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if (!_disposed) {
|
|
|
|
|
if (disposing) {
|
|
|
|
|
// Cleanup managed resources
|
|
|
|
|
RegionFile rf = _regionFile.Target as RegionFile;
|
|
|
|
|
if (rf != null) {
|
|
|
|
|
rf.Dispose();
|
|
|
|
|
rf = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cleanup unmanaged resources
|
|
|
|
|
}
|
|
|
|
|
_disposed = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetFileName ()
|
|
|
|
|
{
|
|
|
|
|
return "r." + _rx + "." + _rz + ".mcr";
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool TestFileName (string filename)
|
|
|
|
|
{
|
|
|
|
|
Match match = _namePattern.Match(filename);
|
|
|
|
|
if (!match.Success) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool ParseFileName (string filename, out int x, out int z)
|
|
|
|
|
{
|
|
|
|
|
x = 0;
|
|
|
|
|
z = 0;
|
|
|
|
|
|
|
|
|
|
Match match = _namePattern.Match(filename);
|
|
|
|
|
if (!match.Success) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x = Convert.ToInt32(match.Groups[1].Value);
|
|
|
|
|
z = Convert.ToInt32(match.Groups[2].Value);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public string GetFilePath ()
|
|
|
|
|
{
|
|
|
|
|
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected RegionFile GetRegionFile ()
|
|
|
|
|
{
|
|
|
|
|
RegionFile rf = _regionFile.Target as RegionFile;
|
|
|
|
|
if (rf == null) {
|
|
|
|
|
rf = new RegionFile(GetFilePath());
|
|
|
|
|
_regionFile.Target = rf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rf;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-30 04:41:29 +00:00
|
|
|
|
public NbtTree GetChunkTree (int lcx, int lcz)
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? null : alt.GetChunkTree(ForeignX(lcx), ForeignZ(lcz));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
Stream nbtstr = rf.GetChunkDataInputStream(lcx, lcz);
|
|
|
|
|
if (nbtstr == null) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-30 04:41:29 +00:00
|
|
|
|
NbtTree tree = new NbtTree(nbtstr);
|
2011-04-08 06:48:27 +00:00
|
|
|
|
|
|
|
|
|
nbtstr.Close();
|
|
|
|
|
return tree;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-06-30 04:41:29 +00:00
|
|
|
|
public bool SaveChunkTree (int lcx, int lcz, NbtTree tree)
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? false : alt.SaveChunkTree(ForeignX(lcx), ForeignZ(lcz), tree);
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
Stream zipstr = rf.GetChunkDataOutputStream(lcx, lcz);
|
|
|
|
|
if (zipstr == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tree.WriteTo(zipstr);
|
|
|
|
|
zipstr.Close();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Stream GetChunkOutStream (int lcx, int lcz)
|
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? null : alt.GetChunkOutStream(ForeignX(lcx), ForeignZ(lcz));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
return rf.GetChunkDataOutputStream(lcx, lcz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ChunkCount ()
|
|
|
|
|
{
|
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
for (int x = 0; x < ChunkManager.REGION_XLEN; x++) {
|
|
|
|
|
for (int z = 0; z < ChunkManager.REGION_ZLEN; z++) {
|
|
|
|
|
if (rf.HasChunk(x, z)) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
public ChunkRef GetChunkRef (int lcx, int lcz)
|
2011-04-06 04:43:54 +00:00
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
2011-04-14 07:04:13 +00:00
|
|
|
|
return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz));
|
2011-04-11 08:17:52 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
int cx = lcx + _rx * ChunkManager.REGION_XLEN;
|
|
|
|
|
int cz = lcz + _rz * ChunkManager.REGION_ZLEN;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
ChunkKey k = new ChunkKey(cx, cz);
|
|
|
|
|
ChunkRef c = _cache.Fetch(k);
|
2011-04-06 04:43:54 +00:00
|
|
|
|
if (c != null) {
|
|
|
|
|
return c;
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-13 03:09:57 +00:00
|
|
|
|
c = ChunkRef.Create(this, lcx, lcz);
|
2011-04-19 01:24:26 +00:00
|
|
|
|
if (c != null) {
|
2011-04-14 07:04:13 +00:00
|
|
|
|
_cache.Insert(c);
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
2011-04-19 01:24:26 +00:00
|
|
|
|
|
|
|
|
|
return c;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-09 04:52:43 +00:00
|
|
|
|
public ChunkRef CreateChunk (int lcx, int lcz)
|
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
2011-04-14 07:04:13 +00:00
|
|
|
|
return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
|
2011-04-11 08:40:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-09 04:52:43 +00:00
|
|
|
|
DeleteChunk(lcx, lcz);
|
|
|
|
|
|
2011-04-13 08:46:06 +00:00
|
|
|
|
int cx = lcx + _rx * ChunkManager.REGION_XLEN;
|
|
|
|
|
int cz = lcz + _rz * ChunkManager.REGION_ZLEN;
|
|
|
|
|
|
2011-04-19 07:50:17 +00:00
|
|
|
|
Chunk c = Chunk.Create(cx, cz);
|
2011-04-09 04:52:43 +00:00
|
|
|
|
c.Save(GetChunkOutStream(lcx, lcz));
|
|
|
|
|
|
2011-05-13 03:09:57 +00:00
|
|
|
|
ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
|
2011-04-14 07:04:13 +00:00
|
|
|
|
_cache.Insert(cr);
|
2011-04-09 04:52:43 +00:00
|
|
|
|
|
|
|
|
|
return cr;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
|
|
|
|
#region IChunkCollection Members
|
|
|
|
|
|
|
|
|
|
public int ChunkGlobalX (int cx)
|
|
|
|
|
{
|
|
|
|
|
return _rx * ChunkManager.REGION_XLEN + cx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ChunkGlobalZ (int cz)
|
|
|
|
|
{
|
|
|
|
|
return _rz * ChunkManager.REGION_ZLEN + cz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ChunkLocalX (int cx)
|
|
|
|
|
{
|
|
|
|
|
return cx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public int ChunkLocalZ (int cz)
|
|
|
|
|
{
|
|
|
|
|
return cz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Chunk GetChunk (int lcx, int lcz)
|
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? null : alt.GetChunk(ForeignX(lcx), ForeignZ(lcz));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
if (!ChunkExists(lcx, lcz)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-19 07:50:17 +00:00
|
|
|
|
return Chunk.CreateVerified(GetChunkTree(lcx, lcz));
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool ChunkExists (int lcx, int lcz)
|
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? false : alt.ChunkExists(ForeignX(lcx), ForeignZ(lcz));
|
2011-04-11 06:13:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
return rf.HasChunk(lcx, lcz);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool DeleteChunk (int lcx, int lcz)
|
|
|
|
|
{
|
2011-04-11 08:40:48 +00:00
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? false : alt.DeleteChunk(ForeignX(lcx), ForeignZ(lcz));
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
RegionFile rf = GetRegionFile();
|
|
|
|
|
if (!rf.HasChunk(lcx, lcz)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rf.DeleteChunk(lcx, lcz);
|
|
|
|
|
|
|
|
|
|
ChunkKey k = new ChunkKey(lcx, lcz);
|
|
|
|
|
_cache.Remove(k);
|
|
|
|
|
|
|
|
|
|
if (ChunkCount() == 0) {
|
|
|
|
|
_regionMan.DeleteRegion(X, Z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-02 20:08:06 +00:00
|
|
|
|
public ChunkRef SetChunk (int lcx, int lcz, Chunk chunk)
|
|
|
|
|
{
|
|
|
|
|
if (!LocalBoundsCheck(lcx, lcz)) {
|
|
|
|
|
Region alt = GetForeignRegion(lcx, lcz);
|
|
|
|
|
return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DeleteChunk(lcx, lcz);
|
|
|
|
|
|
|
|
|
|
int cx = lcx + _rx * ChunkManager.REGION_XLEN;
|
|
|
|
|
int cz = lcz + _rz * ChunkManager.REGION_ZLEN;
|
|
|
|
|
|
|
|
|
|
chunk.SetLocation(cx, cz);
|
|
|
|
|
chunk.Save(GetChunkOutStream(lcx, lcz));
|
|
|
|
|
|
|
|
|
|
ChunkRef cr = ChunkRef.Create(this, lcx, lcz);
|
|
|
|
|
_cache.Insert(cr);
|
|
|
|
|
|
|
|
|
|
return cr;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
public int Save ()
|
|
|
|
|
{
|
2011-05-13 03:09:57 +00:00
|
|
|
|
_cache.SyncDirty();
|
|
|
|
|
|
2011-04-06 04:43:54 +00:00
|
|
|
|
int saved = 0;
|
2011-04-14 07:04:13 +00:00
|
|
|
|
IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator();
|
|
|
|
|
while (en.MoveNext()) {
|
|
|
|
|
ChunkRef chunk = en.Current;
|
2011-04-06 04:43:54 +00:00
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
if (!ChunkExists(chunk.LocalX, chunk.LocalZ)) {
|
2011-04-06 04:43:54 +00:00
|
|
|
|
throw new MissingChunkException();
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
if (chunk.Save(GetChunkOutStream(chunk.LocalX, chunk.LocalZ))) {
|
2011-04-06 04:43:54 +00:00
|
|
|
|
saved++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-14 07:04:13 +00:00
|
|
|
|
_cache.ClearDirty();
|
2011-04-06 04:43:54 +00:00
|
|
|
|
return saved;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SaveChunk (Chunk chunk)
|
|
|
|
|
{
|
2011-06-04 16:16:01 +00:00
|
|
|
|
//Console.WriteLine("Region[{0}, {1}].Save({2}, {3})", _rx, _rz, ForeignX(chunk.X),ForeignZ(chunk.Z));
|
2011-06-02 20:08:06 +00:00
|
|
|
|
return chunk.Save(GetChunkOutStream(ForeignX(chunk.X), ForeignZ(chunk.Z)));
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
2011-04-11 08:40:48 +00:00
|
|
|
|
private bool LocalBoundsCheck (int lcx, int lcz)
|
|
|
|
|
{
|
|
|
|
|
return (lcx >= 0 && lcx < XDIM && lcz >= 0 && lcz < ZDIM);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Region GetForeignRegion (int lcx, int lcz)
|
|
|
|
|
{
|
|
|
|
|
return _regionMan.GetRegion(_rx + (lcx >> XLOG), _rz + (lcz >> ZLOG));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int ForeignX (int lcx)
|
|
|
|
|
{
|
|
|
|
|
return (lcx + XDIM * 10000) & XMASK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int ForeignZ (int lcz)
|
|
|
|
|
{
|
|
|
|
|
return (lcz + ZDIM * 10000) & ZMASK;
|
|
|
|
|
}
|
2011-04-06 04:43:54 +00:00
|
|
|
|
}
|
|
|
|
|
}
|