Fixed a serious cache consistency bug. The design is not terribly satisfying and may be reviewed later, but Beta worlds should live once more. Also including relighting example.

This commit is contained in:
Justin Aquadro 2011-04-14 07:04:13 +00:00
parent 66bd79646c
commit e27595271b
10 changed files with 310 additions and 81 deletions

View file

@ -13,7 +13,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MoveSpawn", "MoveSpawn\Move
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Skyscraper", "Skyscraper\Skyscraper.csproj", "{83F55F54-7253-4B4D-BC37-E9D1CB63E0B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{B34A66B6-6A3B-42FC-AF39-74A30F487840}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Relight", "Relight\Relight.csproj", "{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -45,10 +45,10 @@ Global
{83F55F54-7253-4B4D-BC37-E9D1CB63E0B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83F55F54-7253-4B4D-BC37-E9D1CB63E0B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83F55F54-7253-4B4D-BC37-E9D1CB63E0B8}.Release|Any CPU.Build.0 = Release|Any CPU
{B34A66B6-6A3B-42FC-AF39-74A30F487840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B34A66B6-6A3B-42FC-AF39-74A30F487840}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B34A66B6-6A3B-42FC-AF39-74A30F487840}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B34A66B6-6A3B-42FC-AF39-74A30F487840}.Release|Any CPU.Build.0 = Release|Any CPU
{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,49 @@
using System;
using Substrate;
namespace Relight
{
class Program
{
static void Main (string[] args)
{
if (args.Length < 1) {
Console.WriteLine("You must specify a target directory");
return;
}
string dest = args[0];
// Load the world, supporting either alpha or beta format
INBTWorld world;
if (args.Length >= 2 && args[1] == "alpha") {
world = AlphaWorld.Open(dest);
}
else {
world = BetaWorld.Open(dest);
}
// Grab a generic chunk manager reference
IChunkManager cm = world.GetChunkManager();
// First blank out all of the lighting in all of the chunks
foreach (ChunkRef chunk in cm) {
chunk.ResetBlockLight();
chunk.ResetSkyLight();
cm.Save();
Console.WriteLine("Reset Chunk {0},{1}", chunk.X, chunk.Z);
}
// In a separate pass, reconstruct the light
foreach (ChunkRef chunk in cm) {
chunk.RebuildBlockLight();
chunk.RebuildSkyLight();
// Save the chunk to disk so it doesn't hang around in RAM
cm.Save();
Console.WriteLine("Lit Chunk {0},{1}", chunk.X, chunk.Z);
}
}
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Relight")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Microsoft")]
[assembly: AssemblyProduct("Relight")]
[assembly: AssemblyCopyright("Copyright © Microsoft 2011")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("b21635dd-f744-4740-842a-f86781787961")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{EBDD447B-01FA-4A29-B4AB-380EC4379B5F}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Relight</RootNamespace>
<AssemblyName>Relight</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Substrate, Version=0.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\bin\Release\Substrate.dll</HintPath>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Collections;
namespace Substrate
{
public class ChunkCache : IChunkCache
{
private Dictionary<ChunkKey, WeakReference> _cache;
private Dictionary<ChunkKey, ChunkRef> _dirty;
public ChunkCache ()
{
_cache = new Dictionary<ChunkKey, WeakReference>();
_dirty = new Dictionary<ChunkKey, ChunkRef>();
}
#region IChunkCache Members
public bool Insert (ChunkRef chunk)
{
ChunkKey key = new ChunkKey(chunk.X, chunk.Z);
WeakReference wref;
if (!_cache.TryGetValue(key, out wref)) {
_cache[key] = new WeakReference(chunk);
return true;
}
if (!wref.IsAlive) {
wref.Target = chunk;
return true;
}
return false;
}
public bool Remove (ChunkKey key)
{
_dirty.Remove(key);
return _cache.Remove(key);
}
public ChunkRef Fetch (ChunkKey key)
{
WeakReference wref;
if (!_cache.TryGetValue(key, out wref)) {
return null;
}
return wref.Target as ChunkRef;
}
public IEnumerator<ChunkRef> GetDirtyEnumerator ()
{
return _dirty.Values.GetEnumerator();
}
public void ClearDirty ()
{
_dirty.Clear();
}
public bool MarkChunkDirty (ChunkRef chunk)
{
int cx = chunk.X;
int cz = chunk.Z;
ChunkKey k = new ChunkKey(cx, cz);
if (!_dirty.ContainsKey(k)) {
_dirty.Add(k, chunk);
return true;
}
return false;
}
public bool MarkChunkClean (ChunkRef chunk)
{
int cx = chunk.X;
int cz = chunk.Z;
ChunkKey k = new ChunkKey(cx, cz);
if (_dirty.ContainsKey(k)) {
_dirty.Remove(k);
return true;
}
return false;
}
#endregion
}
}

View file

@ -5,7 +5,7 @@ using System.Collections;
namespace Substrate
{
public class ChunkManager : IChunkManager, IChunkCache, IEnumerable<ChunkRef>
public class ChunkManager : IChunkManager, IEnumerable<ChunkRef>
{
public const int REGION_XLEN = 32;
public const int REGION_ZLEN = 32;
@ -18,21 +18,25 @@ namespace Substrate
protected RegionManager _regionMan;
protected Dictionary<RegionKey, Region> _cache;
protected Dictionary<RegionKey, Region> _dirty;
//protected Dictionary<RegionKey, Region> _cache;
//protected Dictionary<RegionKey, Region> _dirty;
public ChunkManager (RegionManager rm)
protected ChunkCache _cache;
public ChunkManager (RegionManager rm, ChunkCache cache)
{
_regionMan = rm;
_cache = new Dictionary<RegionKey, Region>();
_dirty = new Dictionary<RegionKey, Region>();
_cache = cache;
//_cache = new Dictionary<RegionKey, Region>();
//_dirty = new Dictionary<RegionKey, Region>();
}
public ChunkManager (ChunkManager cm)
{
_regionMan = cm._regionMan;
_cache = new Dictionary<RegionKey, Region>();
_dirty = new Dictionary<RegionKey, Region>();
_cache = cm._cache;
//_cache = new Dictionary<RegionKey, Region>();
//_dirty = new Dictionary<RegionKey, Region>();
}
public int ChunkGlobalX (int cx)
@ -72,7 +76,7 @@ namespace Substrate
return null;
}
return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK, this);
return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK);
}
public bool ChunkExists (int cx, int cz)
@ -94,10 +98,10 @@ namespace Substrate
r = _regionMan.CreateRegion(rx, rz);
}
return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK, this);
return r.CreateChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
}
public bool MarkChunkDirty (ChunkRef chunk)
/*public bool MarkChunkDirty (ChunkRef chunk)
{
Region r = GetRegion(chunk.X, chunk.Z);
if (r == null) {
@ -125,16 +129,25 @@ namespace Substrate
r.MarkChunkClean(chunk);
return true;
}
}*/
public int Save ()
{
int saved = 0;
foreach (Region r in _dirty.Values) {
saved += r.Save();
IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator();
while (en.MoveNext()) {
ChunkRef chunk = en.Current;
Region r = GetRegion(chunk.X, chunk.Z);
if (r == null) {
continue;
}
_dirty.Clear();
chunk.Save(r.GetChunkOutStream(chunk.LocalX, chunk.LocalZ));
saved++;
}
_cache.ClearDirty();
return saved;
}
@ -160,10 +173,6 @@ namespace Substrate
}
if (r.ChunkCount() == 0) {
RegionKey k = new RegionKey(r.X, r.Z);
_cache.Remove(k);
_dirty.Remove(k);
_regionMan.DeleteRegion(r.X, r.Z);
}
@ -245,7 +254,7 @@ namespace Substrate
_region = _enum.Current;
}
if (MoveNextInRegion()) {
_chunk = _region.GetChunkRef(_x, _z, _cm);
_chunk = _region.GetChunkRef(_x, _z);
return true;
}
}

View file

@ -8,7 +8,7 @@ namespace Substrate
{
using NBT;
public class Region : IDisposable, IChunkContainer, IChunkCache
public class Region : IDisposable, IChunkContainer
{
private const int XDIM = 32;
private const int ZDIM = 32;
@ -27,8 +27,10 @@ namespace Substrate
protected WeakReference _regionFile;
protected Dictionary<ChunkKey, WeakReference> _cache;
protected Dictionary<ChunkKey, ChunkRef> _dirty;
//protected Dictionary<ChunkKey, WeakReference> _cache;
//protected Dictionary<ChunkKey, ChunkRef> _dirty;
protected ChunkCache _cache;
public int X
{
@ -50,24 +52,26 @@ namespace Substrate
get { return ZDIM; }
}
public Region (RegionManager rm, int rx, int rz)
public Region (RegionManager rm, ChunkCache cache, int rx, int rz)
{
_regionMan = rm;
_cache = cache;
_regionFile = new WeakReference(null);
_rx = rx;
_rz = rz;
_cache = new Dictionary<ChunkKey, WeakReference>();
_dirty = new Dictionary<ChunkKey, ChunkRef>();
//_cache = new Dictionary<ChunkKey, WeakReference>();
//_dirty = new Dictionary<ChunkKey, ChunkRef>();
if (!File.Exists(GetFilePath())) {
throw new FileNotFoundException();
}
}
public Region (RegionManager rm, string filename)
public Region (RegionManager rm, ChunkCache cache, string filename)
{
_regionMan = rm;
_cache = cache;
_regionFile = new WeakReference(null);
ParseFileName(filename, out _rx, out _rz);
@ -217,32 +221,25 @@ namespace Substrate
return count;
}
public ChunkRef GetChunkRef (int lcx, int lcz, IChunkCache cache)
public ChunkRef GetChunkRef (int lcx, int lcz)
{
if (!LocalBoundsCheck(lcx, lcz)) {
Region alt = GetForeignRegion(lcx, lcz);
return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz), cache);
return (alt == null) ? null : alt.GetChunkRef(ForeignX(lcx), ForeignZ(lcz));
}
ChunkKey k = new ChunkKey(lcx, lcz);
ChunkRef c = null;
WeakReference chunkref = null;
if (_cache.TryGetValue(k, out chunkref)) {
c = chunkref.Target as ChunkRef;
}
else {
_cache.Add(k, new WeakReference(null));
}
int cx = lcx + _rx * ChunkManager.REGION_XLEN;
int cz = lcz + _rz * ChunkManager.REGION_ZLEN;
ChunkKey k = new ChunkKey(cx, cz);
ChunkRef c = _cache.Fetch(k);
if (c != null) {
return c;
}
try {
c = new ChunkRef(this, cache, lcx, lcz);
_cache[k].Target = c;
c = new ChunkRef(this, _cache, lcx, lcz);
_cache.Insert(c);
return c;
}
catch (MissingChunkException) {
@ -251,15 +248,10 @@ namespace Substrate
}
public ChunkRef CreateChunk (int lcx, int lcz)
{
return CreateChunk(lcx, lcz, this);
}
public ChunkRef CreateChunk (int lcx, int lcz, IChunkCache cache)
{
if (!LocalBoundsCheck(lcx, lcz)) {
Region alt = GetForeignRegion(lcx, lcz);
return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz), cache);
return (alt == null) ? null : alt.CreateChunk(ForeignX(lcx), ForeignZ(lcz));
}
DeleteChunk(lcx, lcz);
@ -270,9 +262,8 @@ namespace Substrate
Chunk c = new Chunk(cx, cz);
c.Save(GetChunkOutStream(lcx, lcz));
ChunkRef cr = new ChunkRef(this, cache, lcx, lcz);
ChunkKey k = new ChunkKey(lcx, lcz);
_cache[k] = new WeakReference(cr);
ChunkRef cr = new ChunkRef(this, _cache, lcx, lcz);
_cache.Insert(cr);
return cr;
}
@ -314,11 +305,6 @@ namespace Substrate
return new Chunk(GetChunkTree(lcx, lcz));
}
public ChunkRef GetChunkRef (int lcx, int lcz)
{
return GetChunkRef(lcx, lcz, this);
}
public bool ChunkExists (int lcx, int lcz)
{
if (!LocalBoundsCheck(lcx, lcz)) {
@ -346,7 +332,6 @@ namespace Substrate
ChunkKey k = new ChunkKey(lcx, lcz);
_cache.Remove(k);
_dirty.Remove(k);
if (ChunkCount() == 0) {
_regionMan.DeleteRegion(X, Z);
@ -358,22 +343,20 @@ namespace Substrate
public int Save ()
{
int saved = 0;
foreach (ChunkRef c in _dirty.Values) {
int cx = c.X;
int cz = c.Z;
int lcx = cx - _rx * ChunkManager.REGION_XLEN;
int lcz = cz - _rz * ChunkManager.REGION_ZLEN;
IEnumerator<ChunkRef> en = _cache.GetDirtyEnumerator();
while (en.MoveNext()) {
ChunkRef chunk = en.Current;
if (!ChunkExists(lcx, lcz)) {
if (!ChunkExists(chunk.LocalX, chunk.LocalZ)) {
throw new MissingChunkException();
}
if (c.Save(GetChunkOutStream(lcx, lcz))) {
if (chunk.Save(GetChunkOutStream(chunk.LocalX, chunk.LocalZ))) {
saved++;
}
}
_dirty.Clear();
_cache.ClearDirty();
return saved;
}
@ -387,7 +370,7 @@ namespace Substrate
#region IChunkCache Members
public bool MarkChunkDirty (ChunkRef chunk)
/*public bool MarkChunkDirty (ChunkRef chunk)
{
int cx = chunk.X;
int cz = chunk.Z;
@ -415,7 +398,7 @@ namespace Substrate
return true;
}
return false;
}
}*/
#endregion

View file

@ -25,9 +25,12 @@ namespace Substrate
protected Dictionary<RegionKey, Region> _cache;
public RegionManager (string regionDir)
protected ChunkCache _chunkCache;
public RegionManager (string regionDir, ChunkCache cache)
{
_regionPath = regionDir;
_chunkCache = cache;
_cache = new Dictionary<RegionKey, Region>();
}
@ -38,7 +41,7 @@ namespace Substrate
try {
if (_cache.TryGetValue(k, out r) == false) {
r = new Region(this, rx, rz);
r = new Region(this, _chunkCache, rx, rz);
_cache.Add(k, r);
}
return r;
@ -64,7 +67,7 @@ namespace Substrate
}
r = new Region(this, rx, rz);
r = new Region(this, _chunkCache, rx, rz);
RegionKey k = new RegionKey(rx, rz);
_cache[k] = r;
@ -113,7 +116,7 @@ namespace Substrate
return true;
}
public int Save ()
/*public int Save ()
{
int saved = 0;
foreach (Region r in _cache.Values) {
@ -121,7 +124,7 @@ namespace Substrate
}
return saved;
}
}*/
#region IEnumerable<Region> Members

View file

@ -281,8 +281,8 @@ namespace Substrate
{
_level.Save();
foreach (KeyValuePair<int, RegionManager> rm in _regionMgrs) {
rm.Value.Save();
foreach (KeyValuePair<int, ChunkManager> cm in _chunkMgrs) {
cm.Value.Save();
}
}
@ -301,8 +301,10 @@ namespace Substrate
Directory.CreateDirectory(path);
}
RegionManager rm = new RegionManager(path);
ChunkManager cm = new ChunkManager(rm);
ChunkCache cc = new ChunkCache();
RegionManager rm = new RegionManager(path, cc);
ChunkManager cm = new ChunkManager(rm, cc);
BlockManager bm = new BlockManager(cm);
_regionMgrs[dim] = rm;

View file

@ -59,8 +59,11 @@
<HintPath>Assemblies\Ionic.Zlib.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Source\ChunkCache.cs" />
<Compile Include="Source\Level.cs" />
<Compile Include="Source\PlayerManager.cs" />
<Compile Include="Source\PlayerFile.cs" />