diff --git a/MCGalaxy/Database/BlockDB/BlockDB.cs b/MCGalaxy/Database/BlockDB/BlockDB.cs
index ff1ca1a7d..303037856 100644
--- a/MCGalaxy/Database/BlockDB/BlockDB.cs
+++ b/MCGalaxy/Database/BlockDB/BlockDB.cs
@@ -36,12 +36,9 @@ namespace MCGalaxy.DB {
/// The path of this BlockDB's backing file on disc.
public string FilePath { get { return BlockDBFile.FilePath(MapName); } }
-
- /// Used to synchronise adding to Cache by multiple threads.
- internal readonly object CacheLock = new object();
/// In-memory list of recent BlockDB changes.
- public FastList Cache = new FastList();
+ public BlockDBCache Cache = new BlockDBCache();
/// Whether changes are actually added to the BlockDB.
public bool Used;
@@ -83,14 +80,12 @@ namespace MCGalaxy.DB {
entry.Flags |= BlockDBFlags.OldCustom;
entry.OldRaw = oldExt;
}
-
- lock (CacheLock)
- Cache.Add(entry);
+ Cache.Add(ref entry);
}
public void WriteEntries() {
using (IDisposable writeLock = locker.AccquireWriteLock()) {
- if (Cache.Count == 0) return;
+ if (Cache.Head == null) return;
ValidateBackingFile();
using (Stream s = File.OpenWrite(FilePath)) {
@@ -98,9 +93,7 @@ namespace MCGalaxy.DB {
// and 21 bytes were in the file, this sets the position to byte 16
s.Position = s.Length & ~0x0F;
BlockDBFile.WriteEntries(s, Cache);
-
- lock (CacheLock)
- Cache = new FastList();
+ Cache.Clear();
}
}
}
@@ -124,13 +117,17 @@ namespace MCGalaxy.DB {
}
void FindInMemoryAt(ushort x, ushort y, ushort z, Action output) {
- Vec3U16 dims = Dims;
- int count = Cache.Count, index = (y * dims.Z + z) * dims.X + x;
- BlockDBEntry[] items = Cache.Items;
-
- for (int i = 0; i < count; i++) {
- if (items[i].Index != index) continue;
- output(items[i]);
+ int index = (y * Dims.Z + z) * Dims.X + x;
+ BlockDBCacheNode node = Cache.Tail;
+ while (node != null) {
+ BlockDBEntry[] entries = node.Entries;
+ int count = node.Count;
+
+ for (int i = 0; i < count; i++) {
+ if (entries[i].Index != index) continue;
+ output(entries[i]);
+ }
+ lock (Cache.Locker) node = node.Next;
}
}
@@ -138,16 +135,29 @@ namespace MCGalaxy.DB {
/// whether an entry before start time was reached.
public bool FindChangesBy(int[] ids, DateTime start, DateTime end,
out Vec3U16 dims, Action output) {
- long startDelta = (long)start.Subtract(Epoch).TotalSeconds;
- long endDelta = (long)end.Subtract(Epoch).TotalSeconds;
+ int startDelta = ClampDelta(start.Subtract(Epoch));
+ int endDelta = ClampDelta(end.Subtract(Epoch));
using (IDisposable readLock = locker.AccquireReadLock()) {
dims = Dims;
- // Read entries from memory cache
- int count = Cache.Count;
- BlockDBEntry[] items = Cache.Items;
+ if (FindInMemoryBy(ids, startDelta, endDelta, output)) return true;
+
+ if (!File.Exists(FilePath)) return false;
+ using (Stream s = File.OpenRead(FilePath)) {
+ BlockDBFile.ReadHeader(s, out dims);
+ return BlockDBFile.FindChangesBy(s, ids, startDelta, endDelta, output);
+ }
+ }
+ }
+
+ bool FindInMemoryBy(int[] ids, int startDelta, int endDelta, Action output) {
+ BlockDBCacheNode node = Cache.Head;
+ while (node != null) {
+ int count = node.Count;
+ BlockDBEntry[] entries = node.Entries;
+
for (int i = count - 1; i >= 0; i--) {
- BlockDBEntry entry = items[i];
+ BlockDBEntry entry = entries[i];
if (entry.TimeDelta < startDelta) return true;
if (entry.TimeDelta > endDelta) continue;
@@ -156,14 +166,16 @@ namespace MCGalaxy.DB {
output(entry); break;
}
}
-
- // Read entries from disc cache
- if (!File.Exists(FilePath)) return false;
- using (Stream s = File.OpenRead(FilePath)) {
- BlockDBFile.ReadHeader(s, out dims);
- return BlockDBFile.FindChangesBy(s, ids, startDelta, endDelta, output);
- }
+ lock (Cache.Locker) node = node.Prev;
}
+ return false;
+ }
+
+ static int ClampDelta(TimeSpan delta) {
+ long secs = (long)delta.TotalSeconds;
+ if (secs < int.MinValue) return int.MinValue;
+ if (secs > int.MaxValue) return int.MaxValue;
+ return (int)secs;
}
diff --git a/MCGalaxy/Database/BlockDB/BlockDBCache.cs b/MCGalaxy/Database/BlockDB/BlockDBCache.cs
index 27ccf1f09..c1036cbfc 100644
--- a/MCGalaxy/Database/BlockDB/BlockDBCache.cs
+++ b/MCGalaxy/Database/BlockDB/BlockDBCache.cs
@@ -16,17 +16,56 @@
permissions and limitations under the Licenses.
*/
using System;
-using System.Data;
-using MCGalaxy.SQL;
namespace MCGalaxy.DB {
/// Optimised in-memory BlockDB cache.
public sealed class BlockDBCache {
- public BlockDBCacheNode Head, Tail;
+ public BlockDBCacheNode Tail, Head;
- int nextSize = 10000;
+ /// Used to synchronise adding to Cache by multiple threads.
+ public readonly object Locker = new object();
+
+ public void Add(ref BlockDBEntry entry) {
+ lock (Locker) {
+ if (Head == null || Head.Count == Head.Entries.Length)
+ AddNextNode();
+
+ Head.Entries[Head.Count] = entry; Head.Count++;
+ }
+ }
+
+ public void Clear() {
+ lock (Locker) {
+ if (Tail == null) return;
+
+ BlockDBCacheNode cur = Tail;
+ while (cur != null) {
+ // Unlink the nodes
+ cur.Prev = null;
+ BlockDBCacheNode next = cur.Next;
+ cur.Next = null;
+ cur = next;
+ }
+ Head = null; Tail = null;
+ }
+ }
+
+ void AddNextNode() {
+ BlockDBCacheNode newHead = new BlockDBCacheNode(nextSize);
+ newHead.Prev = Head;
+ if (Head != null) Head.Next = newHead;
+ Head = newHead;
+ if (Tail == null) Tail = Head;
+
+ // use smaller increases at first to minimise memory usage
+ if (nextSize == 50 * 1000) nextSize = 100 * 1000;
+ if (nextSize == 20 * 1000) nextSize = 50 * 1000;
+ if (nextSize == 10 * 1000) nextSize = 20 * 1000;
+ }
+
+ int nextSize = 10 * 1000;
}
// TODO: track start time so we can use int16 instead of int32 time delta
@@ -37,5 +76,9 @@ namespace MCGalaxy.DB {
public int Count;
public BlockDBEntry[] Entries;
+
+ public BlockDBCacheNode(int capacity) {
+ Entries = new BlockDBEntry[capacity];
+ }
}
}
diff --git a/MCGalaxy/Database/BlockDB/BlockDBFile.cs b/MCGalaxy/Database/BlockDB/BlockDBFile.cs
index 9beea397e..053eaadfd 100644
--- a/MCGalaxy/Database/BlockDB/BlockDBFile.cs
+++ b/MCGalaxy/Database/BlockDB/BlockDBFile.cs
@@ -61,11 +61,27 @@ namespace MCGalaxy.DB {
public static void WriteEntries(Stream s, FastList entries) {
byte[] bulk = new byte[BulkEntries * EntrySize];
+ WriteEntries(s, bulk, entries.Items, entries.Count);
+ }
+
+ public static void WriteEntries(Stream s, BlockDBCache cache) {
+ byte[] bulk = new byte[BulkEntries * EntrySize];
+ BlockDBCacheNode node = cache.Tail;
- for (int i = 0; i < entries.Count; i += BulkEntries) {
- int count = Math.Min(BulkEntries, entries.Count - i);
- for (int j = 0; j < count; j++) {
- BlockDBEntry entry = entries.Items[i + j];
+ while (node != null) {
+ WriteEntries(s, bulk, node.Entries, node.Count);
+ lock (cache.Locker)
+ node = node.Next;
+ }
+ }
+
+ static void WriteEntries(Stream s, byte[] bulk, BlockDBEntry[] entries, int count) {
+ if (count == 0) return;
+
+ for (int i = 0; i < count; i += BulkEntries) {
+ int bulkCount = Math.Min(BulkEntries, count - i);
+ for (int j = 0; j < bulkCount; j++) {
+ BlockDBEntry entry = entries[i + j];
WriteI32(entry.PlayerID, bulk, j * EntrySize);
WriteI32(entry.TimeDelta, bulk, j * EntrySize + 4);
WriteI32(entry.Index, bulk, j * EntrySize + 8);
@@ -73,7 +89,7 @@ namespace MCGalaxy.DB {
bulk[j * EntrySize + 13] = entry.NewRaw;
WriteU16(entry.Flags, bulk, j * EntrySize + 14);
}
- s.Write(bulk, 0, count * EntrySize);
+ s.Write(bulk, 0, bulkCount * EntrySize);
}
}
@@ -101,7 +117,7 @@ namespace MCGalaxy.DB {
/// Iterates from the very newest to oldest entry in the BlockDB.
/// whether an entry before start time was reached.
- public static bool FindChangesBy(Stream s, int[] ids, long start, long end,
+ public static bool FindChangesBy(Stream s, int[] ids, int start, int end,
Action output) {
byte[] bulk = new byte[BulkEntries * EntrySize];
fixed (byte* ptr = bulk) {
diff --git a/MCGalaxy/Levels/LevelDB.cs b/MCGalaxy/Levels/LevelDB.cs
index a209caabc..c2295e3f3 100644
--- a/MCGalaxy/Levels/LevelDB.cs
+++ b/MCGalaxy/Levels/LevelDB.cs
@@ -25,7 +25,7 @@ namespace MCGalaxy {
public static class LevelDB {
public unsafe static void SaveBlockDB(Level lvl) {
- if (lvl.BlockDB.Cache.Count == 0) return;
+ if (lvl.BlockDB.Cache.Head == null) return;
if (!lvl.UseBlockDB) { lvl.BlockDB.Cache.Clear(); return; }
lvl.BlockDB.WriteEntries();
diff --git a/MCGalaxy/MCGalaxy_.csproj b/MCGalaxy/MCGalaxy_.csproj
index b4567a154..03b233270 100644
--- a/MCGalaxy/MCGalaxy_.csproj
+++ b/MCGalaxy/MCGalaxy_.csproj
@@ -403,6 +403,7 @@
+