forked from mirrors/NBTExplorer
Early fluid recalculation support (for water)
This commit is contained in:
parent
da05301983
commit
ff0140d60d
9 changed files with 371 additions and 7 deletions
|
@ -57,6 +57,7 @@ namespace Substrate
|
|||
private TagList _tileEntities;
|
||||
|
||||
private BlockLight _lightManager;
|
||||
private BlockFluid _fluidManager;
|
||||
private BlockTileEntities _tileEntityManager;
|
||||
|
||||
private bool _dirty = false;
|
||||
|
@ -71,11 +72,16 @@ namespace Substrate
|
|||
_lightManager.ResolveNeighbor += delegate(int relx, int rely, int relz) {
|
||||
return value(relx, rely, relz);
|
||||
};
|
||||
_fluidManager.ResolveNeighbor += delegate(int relx, int rely, int relz)
|
||||
{
|
||||
return value(relx, rely, relz);
|
||||
};
|
||||
}
|
||||
|
||||
remove
|
||||
{
|
||||
_lightManager = new BlockLight(this);
|
||||
_fluidManager = new BlockFluid(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -107,6 +113,7 @@ namespace Substrate
|
|||
_tileEntities = properties.tileEntities;
|
||||
|
||||
_lightManager = new BlockLight(this);
|
||||
_fluidManager = new BlockFluid(this);
|
||||
_tileEntityManager = new BlockTileEntities(_blocks, _tileEntities);
|
||||
}
|
||||
|
||||
|
@ -452,6 +459,16 @@ namespace Substrate
|
|||
|
||||
#endregion
|
||||
|
||||
public void ResetBlockFluid ()
|
||||
{
|
||||
_fluidManager.ResetWater();
|
||||
_dirty = true;
|
||||
}
|
||||
|
||||
public void RebuildBlockFluid ()
|
||||
{
|
||||
_fluidManager.RebuildWater();
|
||||
_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
321
Substrate/SubstrateCS/Source/BlockFluid.cs
Normal file
321
Substrate/SubstrateCS/Source/BlockFluid.cs
Normal file
|
@ -0,0 +1,321 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Substrate
|
||||
{
|
||||
using Utility;
|
||||
|
||||
public class BlockFluid
|
||||
{
|
||||
private IBoundedDataBlockCollection _blockset;
|
||||
|
||||
private readonly int _xdim;
|
||||
private readonly int _ydim;
|
||||
private readonly int _zdim;
|
||||
|
||||
private Dictionary<ChunkKey, IBoundedDataBlockCollection> _chunks;
|
||||
|
||||
public delegate IBoundedDataBlockCollection NeighborLookupHandler (int relx, int rely, int relz);
|
||||
|
||||
public event NeighborLookupHandler ResolveNeighbor;
|
||||
|
||||
internal class BlockCoord
|
||||
{
|
||||
internal IBoundedDataBlockCollection chunk;
|
||||
internal int lx;
|
||||
internal int ly;
|
||||
internal int lz;
|
||||
|
||||
internal BlockCoord (IBoundedDataBlockCollection _chunk, int _lx, int _ly, int _lz)
|
||||
{
|
||||
chunk = _chunk;
|
||||
lx = _lx;
|
||||
ly = _ly;
|
||||
lz = _lz;
|
||||
}
|
||||
}
|
||||
|
||||
public BlockFluid (IBoundedDataBlockCollection blockset)
|
||||
{
|
||||
_blockset = blockset;
|
||||
|
||||
_xdim = _blockset.XDim;
|
||||
_ydim = _blockset.YDim;
|
||||
_zdim = _blockset.ZDim;
|
||||
|
||||
_chunks = new Dictionary<ChunkKey,IBoundedDataBlockCollection>();
|
||||
}
|
||||
|
||||
public BlockFluid (BlockFluid bl)
|
||||
{
|
||||
_blockset = bl._blockset;
|
||||
|
||||
_xdim = bl._xdim;
|
||||
_ydim = bl._ydim;
|
||||
_zdim = bl._zdim;
|
||||
|
||||
_chunks = new Dictionary<ChunkKey, IBoundedDataBlockCollection>();
|
||||
}
|
||||
|
||||
public void ResetWater ()
|
||||
{
|
||||
int xdim = _xdim;
|
||||
int ydim = _ydim;
|
||||
int zdim = _zdim;
|
||||
|
||||
for (int x = 0; x < xdim; x++) {
|
||||
for (int z = 0; z < zdim; z++) {
|
||||
for (int y = 0; y < ydim; y++) {
|
||||
IDataBlock block = _blockset.GetBlockRef(x, y, z);
|
||||
if ((block.ID == BlockType.STATIONARY_WATER || block.ID == BlockType.WATER) && block.Data != (int)WaterFlow.FULL) {
|
||||
block.ID = BlockType.AIR;
|
||||
block.Data = 0;
|
||||
}
|
||||
else if (block.ID == BlockType.WATER) {
|
||||
block.ID = BlockType.STATIONARY_WATER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RebuildWater ()
|
||||
{
|
||||
int xdim = _xdim;
|
||||
int ydim = _ydim;
|
||||
int zdim = _zdim;
|
||||
|
||||
List<BlockKey> buildQueue = new List<BlockKey>();
|
||||
|
||||
for (int x = 0; x < xdim; x++) {
|
||||
for (int z = 0; z < zdim; z++) {
|
||||
for (int y = 0; y < ydim; y++) {
|
||||
BlockInfo info = _blockset.GetInfo(x, y, z);
|
||||
if (info.ID == BlockType.STATIONARY_WATER && _blockset.GetData(x, y, z) == (int)WaterFlow.FULL) {
|
||||
buildQueue.Add(new BlockKey(x, y, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (BlockKey key in buildQueue) {
|
||||
SpreadWater(key.x, key.y, key.z);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpreadWater (int x, int y, int z)
|
||||
{
|
||||
Queue<KeyValuePair<BlockKey, int>> targets = new Queue<KeyValuePair<BlockKey, int>>();
|
||||
targets.Enqueue(new KeyValuePair<BlockKey, int>(new BlockKey(x, y, z), 0));
|
||||
|
||||
while (targets.Count > 0) {
|
||||
KeyValuePair<BlockKey, int> skey = targets.Dequeue();
|
||||
|
||||
BlockKey source = skey.Key;
|
||||
int sourceDist = skey.Value;
|
||||
|
||||
int nearestDrop = 7;
|
||||
bool foundDrop = false;
|
||||
|
||||
Queue<BlockKey> searchQueue = new Queue<BlockKey>();
|
||||
Queue<KeyValuePair<BlockKey, int>> sinkQueue = new Queue<KeyValuePair<BlockKey, int>>();
|
||||
Dictionary<BlockKey, int> marked = new Dictionary<BlockKey, int>();
|
||||
|
||||
searchQueue.Enqueue(source);
|
||||
marked.Add(source, sourceDist);
|
||||
|
||||
// Identify sinks
|
||||
while (searchQueue.Count > 0) {
|
||||
BlockKey branch = searchQueue.Dequeue();
|
||||
int distance = marked[branch] & ~(int)LiquidState.FALLING;
|
||||
bool falling = (marked[branch] & (int)LiquidState.FALLING) > 0;
|
||||
|
||||
// Ignore blocks out of range
|
||||
if (distance > nearestDrop) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore invalid blocks
|
||||
BlockCoord branchHigh = TranslateCoord(branch.x, branch.y, branch.z);
|
||||
if (branchHigh.chunk == null || branch.y == 0) {
|
||||
marked.Remove(branch);
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we're not the magical source block...
|
||||
if (distance > 0 && !falling) {
|
||||
// Ignore blocks that block fluid (and thus could not become a fluid)
|
||||
BlockInfo branchHighInfo = branchHigh.chunk.GetInfo(branchHigh.lx, branchHigh.ly, branchHigh.lz);
|
||||
if (branchHighInfo.BlocksFluid) {
|
||||
marked.Remove(branch);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ignore blocks that have a "fuller" fluid level
|
||||
if (branchHighInfo.ID == BlockType.STATIONARY_WATER) {
|
||||
int cmp = branchHigh.chunk.GetData(branchHigh.lx, branchHigh.ly, branchHigh.lz);
|
||||
if (cmp <= distance || (cmp & (int)LiquidState.FALLING) > 0) {
|
||||
marked.Remove(branch);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a hole, add as a sink, mark the sink distance.
|
||||
BlockCoord branchLow = TranslateCoord(branch.x, branch.y - 1, branch.z);
|
||||
BlockInfo branchLowInfo = branchLow.chunk.GetInfo(branchLow.lx, branchLow.ly, branchLow.lz);
|
||||
if (!branchLowInfo.BlocksFluid) {
|
||||
foundDrop = true;
|
||||
nearestDrop = falling ? 0 : distance;
|
||||
sinkQueue.Enqueue(new KeyValuePair<BlockKey, int>(branch, falling ? distance | (int)LiquidState.FALLING : distance));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Didn't find a hole, hit the bottom, reset distance
|
||||
if (falling) {
|
||||
distance = 0;
|
||||
}
|
||||
|
||||
// Expand to neighbors
|
||||
if (distance < nearestDrop) {
|
||||
BlockKey[] keys = {
|
||||
new BlockKey(branch.x - 1, branch.y, branch.z),
|
||||
new BlockKey(branch.x + 1, branch.y, branch.z),
|
||||
new BlockKey(branch.x, branch.y, branch.z - 1),
|
||||
new BlockKey(branch.x, branch.y, branch.z + 1),
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!marked.ContainsKey(keys[i])) {
|
||||
searchQueue.Enqueue(keys[i]);
|
||||
marked.Add(keys[i], distance + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find any drops, fill out the mark table
|
||||
if (!foundDrop) {
|
||||
foreach (KeyValuePair<BlockKey, int> mark in marked) {
|
||||
BlockKey target = mark.Key;
|
||||
|
||||
BlockCoord targetHigh = TranslateCoord(target.x, target.y, target.z);
|
||||
targetHigh.chunk.SetID(targetHigh.lx, targetHigh.ly, targetHigh.lz, BlockType.STATIONARY_WATER);
|
||||
targetHigh.chunk.SetData(targetHigh.lx, targetHigh.ly, targetHigh.lz, mark.Value);
|
||||
}
|
||||
|
||||
// Skip sinks and tracing
|
||||
continue;
|
||||
}
|
||||
|
||||
// Create new target for each sink
|
||||
Queue<KeyValuePair<BlockKey, int>> traceQueue = new Queue<KeyValuePair<BlockKey, int>>();
|
||||
while (sinkQueue.Count > 0) {
|
||||
KeyValuePair<BlockKey, int> tkey = sinkQueue.Dequeue();
|
||||
BlockKey target = tkey.Key;
|
||||
int distance = tkey.Value;
|
||||
|
||||
//int distance = marked[target];
|
||||
marked.Remove(target);
|
||||
|
||||
targets.Enqueue(new KeyValuePair<BlockKey, int>(new BlockKey(target.x, target.y - 1, target.z), distance | (int)LiquidState.FALLING));
|
||||
traceQueue.Enqueue(new KeyValuePair<BlockKey, int>(target, distance));
|
||||
|
||||
/*BlockCoord targetLow = TranslateCoord(target.x, target.y - 1, target.z);
|
||||
targetLow.chunk.SetID(targetLow.lx, targetLow.ly, targetHigh.lz, BlockType.STATIONARY_WATER);
|
||||
targetLow.chunk.SetData(targetLow.lx, targetLow.ly, targetHigh.lz, tkey.Value);*/
|
||||
}
|
||||
|
||||
// Trace back from sinks
|
||||
while (traceQueue.Count > 0) {
|
||||
KeyValuePair<BlockKey, int> tkey = traceQueue.Dequeue();
|
||||
|
||||
BlockKey target = tkey.Key;
|
||||
int distance = tkey.Value & ~(int)LiquidState.FALLING;
|
||||
bool falling = (tkey.Value & (int)LiquidState.FALLING) > 0;
|
||||
|
||||
//if (distance == 0) {
|
||||
// distance |= (int)LiquidState.FALLING;
|
||||
//}
|
||||
|
||||
BlockCoord targetHigh = TranslateCoord(target.x, target.y, target.z);
|
||||
targetHigh.chunk.SetID(targetHigh.lx, targetHigh.ly, targetHigh.lz, BlockType.STATIONARY_WATER);
|
||||
targetHigh.chunk.SetData(targetHigh.lx, targetHigh.ly, targetHigh.lz, tkey.Value);
|
||||
|
||||
if (falling) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockKey[] keys = {
|
||||
new BlockKey(target.x - 1, target.y, target.z),
|
||||
new BlockKey(target.x + 1, target.y, target.z),
|
||||
new BlockKey(target.x, target.y, target.z - 1),
|
||||
new BlockKey(target.x, target.y, target.z + 1),
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int nval;
|
||||
if (!marked.TryGetValue(keys[i], out nval)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int ndist = nval & ~(int)LiquidState.FALLING;
|
||||
bool nfall = (nval & (int)LiquidState.FALLING) > 0;
|
||||
|
||||
marked.Remove(keys[i]);
|
||||
if (ndist < distance || nfall) {
|
||||
traceQueue.Enqueue(new KeyValuePair<BlockKey, int>(keys[i], nval));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private BlockCoord TranslateCoord (int x, int y, int z)
|
||||
{
|
||||
IBoundedDataBlockCollection chunk = GetChunk(x, z);
|
||||
|
||||
int lx = ((x % _xdim) + _xdim) % _xdim;
|
||||
int lz = ((z % _zdim) + _zdim) % _zdim;
|
||||
|
||||
return new BlockCoord(chunk, lx, y, lz);
|
||||
}
|
||||
|
||||
private IBoundedDataBlockCollection GetChunk (int x, int z)
|
||||
{
|
||||
int cx = x / _xdim + (x >> 31);
|
||||
int cz = z / _zdim + (z >> 31);
|
||||
|
||||
ChunkKey key = new ChunkKey(cx, cz);
|
||||
|
||||
IBoundedDataBlockCollection chunk;
|
||||
if (!_chunks.TryGetValue(key, out chunk)) {
|
||||
chunk = OnResolveNeighbor(cx, 0, cz);
|
||||
_chunks[key] = chunk;
|
||||
}
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
private IBoundedDataBlockCollection OnResolveNeighbor (int relX, int relY, int relZ)
|
||||
{
|
||||
if (ResolveNeighbor != null) {
|
||||
IBoundedDataBlockCollection n = ResolveNeighbor(relX, relY, relZ);
|
||||
|
||||
if (n == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (n.XDim != _xdim ||
|
||||
n.YDim != _ydim ||
|
||||
n.ZDim != _zdim) {
|
||||
throw new InvalidOperationException("Subscriber returned incompatible IDataBlockCollection");
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -178,6 +178,7 @@ namespace Substrate
|
|||
private int _opacity = MAX_OPACITY;
|
||||
private int _luminance = MIN_LUMINANCE;
|
||||
private bool _transmitLight = false;
|
||||
private bool _blocksFluid = true;
|
||||
|
||||
private BlockState _state = BlockState.SOLID;
|
||||
|
||||
|
@ -221,6 +222,11 @@ namespace Substrate
|
|||
get { return _opacity > MIN_OPACITY || !_transmitLight; }
|
||||
}
|
||||
|
||||
public bool BlocksFluid
|
||||
{
|
||||
get { return _blocksFluid; }
|
||||
}
|
||||
|
||||
public BlockState State
|
||||
{
|
||||
get { return _state; }
|
||||
|
@ -276,6 +282,20 @@ namespace Substrate
|
|||
public BlockInfo SetState (BlockState state)
|
||||
{
|
||||
_state = state;
|
||||
|
||||
if (_state == BlockState.SOLID) {
|
||||
_blocksFluid = true;
|
||||
}
|
||||
else {
|
||||
_blocksFluid = false;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockInfo SetBlocksFluid (bool blocks)
|
||||
{
|
||||
_blocksFluid = blocks;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -500,6 +520,12 @@ namespace Substrate
|
|||
Farmland.SetLightTransmission(false);
|
||||
CobbleStairs.SetLightTransmission(false);
|
||||
|
||||
// Override default fluid blocking rules
|
||||
|
||||
SignPost.SetBlocksFluid(true);
|
||||
WallSign.SetBlocksFluid(true);
|
||||
Cactus.SetBlocksFluid(false);
|
||||
|
||||
// Set Tile Entity Data
|
||||
|
||||
Dispenser.SetTileEntity("Trap");
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Substrate
|
|||
BlockKey primary = new BlockKey(lx, ly, lz);
|
||||
_update.Enqueue(primary);
|
||||
|
||||
BlockInfo info = _blockset.GetInfo(lx, ly, lz);
|
||||
//BlockInfo info = _blockset.GetInfo(lx, ly, lz);
|
||||
|
||||
//if (info.Luminance > BlockInfo.MIN_LUMINANCE || info.TransmitsLight) {
|
||||
if (ly > 0) {
|
||||
|
|
|
@ -6,8 +6,8 @@ namespace Substrate
|
|||
{
|
||||
public struct ChunkKey : IEquatable<ChunkKey>
|
||||
{
|
||||
readonly int cx;
|
||||
readonly int cz;
|
||||
public readonly int cx;
|
||||
public readonly int cz;
|
||||
|
||||
public ChunkKey (int _cx, int _cz)
|
||||
{
|
||||
|
|
|
@ -150,14 +150,12 @@ namespace Substrate
|
|||
|
||||
public class EntityEnumerator : IEnumerator<Entity>
|
||||
{
|
||||
private TagList _entities;
|
||||
private IEnumerator<TagValue> _enum;
|
||||
|
||||
private Entity _cur;
|
||||
|
||||
public EntityEnumerator (TagList entities)
|
||||
{
|
||||
_entities = entities;
|
||||
_enum = entities.GetEnumerator();
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,6 @@ namespace Substrate.NBT
|
|||
|
||||
private static void SerializeScaler (TagValue tag, StringBuilder str)
|
||||
{
|
||||
TagType type = tag.GetTagType();
|
||||
switch (tag.GetTagType()) {
|
||||
case TagType.TAG_STRING:
|
||||
str.Append("\"" + tag.ToTagString().Data + "\"");
|
||||
|
|
|
@ -279,7 +279,7 @@ namespace Substrate.NBT
|
|||
{
|
||||
TagType type = (TagType)_stream.ReadByte();
|
||||
if (type == TagType.TAG_COMPOUND) {
|
||||
string name = ReadString().ToTagString().Data;
|
||||
ReadString(); // name
|
||||
return ReadValue(type) as TagCompound;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,6 +187,9 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="Source\Experimental\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Makefile" />
|
||||
</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.
|
||||
|
|
Loading…
Reference in a new issue