From fb4b0ab88e437de48a8fd965c3b830cfb0513881 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 22 Jul 2017 22:46:07 +1000 Subject: [PATCH] Make BlockDBFile extensible --- MCGalaxy/Database/BlockDB/BlockDB.cs | 18 +- MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs | 158 ++++++++++++++++++ MCGalaxy/Database/BlockDB/BlockDBFile.cs | 121 +------------- .../Database/BlockDB/BlockDBTableDumper.cs | 2 +- MCGalaxy/MCGalaxy_.csproj | 1 + 5 files changed, 179 insertions(+), 121 deletions(-) create mode 100644 MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs diff --git a/MCGalaxy/Database/BlockDB/BlockDB.cs b/MCGalaxy/Database/BlockDB/BlockDB.cs index 02d715a08..0e3c5e48e 100644 --- a/MCGalaxy/Database/BlockDB/BlockDB.cs +++ b/MCGalaxy/Database/BlockDB/BlockDB.cs @@ -65,12 +65,12 @@ namespace MCGalaxy.DB { public void WriteEntries() { if (Cache.Head == null) return; - ValidateBackingFile(); + BlockDBFile format = ValidateBackingFile(); using (Stream s = OpenWrite()) { // This truncates the lower 4 bits off - so e.g. if a power off occurred // and 21 bytes were in the file, this sets the position to byte 16 s.Position = s.Length & ~0x0F; - BlockDBFile.WriteEntries(s, Cache); + format.WriteEntries(s, Cache); Cache.Clear(); } } @@ -93,11 +93,11 @@ namespace MCGalaxy.DB { Vec3U16 dims; using (Stream s = OpenRead()) { - BlockDBFile.ReadHeader(s, out dims); + BlockDBFile format = BlockDBFile.ReadHeader(s, out dims); if (x >= dims.X || y >= dims.Y || z >= dims.Z) return; int index = (y * dims.Z + z) * dims.X + x; - BlockDBFile.FindChangesAt(s, index, output); + format.FindChangesAt(s, index, output); } FindInMemoryAt(x, y, z, output); } @@ -131,8 +131,8 @@ namespace MCGalaxy.DB { if (!File.Exists(FilePath)) return false; using (Stream s = OpenRead()) { - BlockDBFile.ReadHeader(s, out dims); - return BlockDBFile.FindChangesBy(s, ids, startDelta, endDelta, output); + BlockDBFile format = BlockDBFile.ReadHeader(s, out dims); + return format.FindChangesBy(s, ids, startDelta, endDelta, output); } } @@ -175,9 +175,10 @@ namespace MCGalaxy.DB { /// Checks if the backing file exists on disc, and if not, creates it. /// Also recreates the backing file if dimensions on disc are less than those in memory. - void ValidateBackingFile() { + BlockDBFile ValidateBackingFile() { Vec3U16 fileDims; + BlockDBFile format = BlockDBFile.V1; if (!File.Exists(FilePath)) { using (Stream s = OpenWrite()) { fileDims = Dims; @@ -185,12 +186,13 @@ namespace MCGalaxy.DB { } } else { using (Stream s = OpenRead()) { - BlockDBFile.ReadHeader(s, out fileDims); + format = BlockDBFile.ReadHeader(s, out fileDims); } if (fileDims.X < Dims.X || fileDims.Y < Dims.Y || fileDims.Z < Dims.Z) { BlockDBFile.ResizeBackingFile(this); } } + return format; } diff --git a/MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs b/MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs new file mode 100644 index 000000000..875b5188e --- /dev/null +++ b/MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs @@ -0,0 +1,158 @@ +/* + Copyright 2015 MCGalaxy + + Dual-licensed under the Educational Community License, Version 2.0 and + the GNU General Public License, Version 3 (the "Licenses"); you may + not use this file except in compliance with the Licenses. You may + obtain a copy of the Licenses at + + http://www.opensource.org/licenses/ecl2.php + http://www.gnu.org/licenses/gpl-3.0.html + + Unless required by applicable law or agreed to in writing, + software distributed under the Licenses are distributed on an "AS IS" + BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied. See the Licenses for the specific language governing + permissions and limitations under the Licenses. + */ +using System; +using System.IO; +using MCGalaxy.Util; +using MCGalaxy.Maths; + +namespace MCGalaxy.DB { + + public unsafe sealed class BlockDBFile_V1 : BlockDBFile { + + public override void WriteEntries(Stream s, FastList entries) { + byte[] bulk = new byte[BulkEntries * EntrySize]; + WriteEntries(s, bulk, entries.Items, entries.Count); + } + + public override void WriteEntries(Stream s, BlockDBCache cache) { + byte[] bulk = new byte[BulkEntries * EntrySize]; + BlockDBCacheNode node = cache.Tail; + + while (node != null) { + WriteEntries(s, bulk, node); + lock (cache.Locker) + node = node.Next; + } + } + + static void WriteEntries(Stream s, byte[] bulk, BlockDBEntry[] entries, int count) { + for (int i = 0; i < count; i += BulkEntries) { + int bulkCount = Math.Min(BulkEntries, count - i); + for (int j = 0; j < bulkCount; j++) { + WriteEntry(entries[i + j], bulk, j * EntrySize); + } + s.Write(bulk, 0, bulkCount * EntrySize); + } + } + + static void WriteEntries(Stream s, byte[] bulk, BlockDBCacheNode node) { + int count = node.Count; + for (int i = 0; i < count; i += BulkEntries) { + int bulkCount = Math.Min(BulkEntries, count - i); + for (int j = 0; j < bulkCount; j++) { + BlockDBEntry entry = node.Unpack(node.Entries[i + j]); + WriteEntry(entry, bulk, j * EntrySize); + } + s.Write(bulk, 0, bulkCount * EntrySize); + } + } + + // Inlined WriteI32/WriteU16 for better performance + static void WriteEntry(BlockDBEntry entry, byte[] bulk, int index) { + bulk[index + 0 ] = (byte)(entry.PlayerID); + bulk[index + 1 ] = (byte)(entry.PlayerID >> 8); + bulk[index + 2 ] = (byte)(entry.PlayerID >> 16); + bulk[index + 3 ] = (byte)(entry.PlayerID >> 24); + + bulk[index + 4 ] = (byte)(entry.TimeDelta); + bulk[index + 5 ] = (byte)(entry.TimeDelta >> 8); + bulk[index + 6 ] = (byte)(entry.TimeDelta >> 16); + bulk[index + 7 ] = (byte)(entry.TimeDelta >> 24); + + bulk[index + 8 ] = (byte)(entry.Index); + bulk[index + 9 ] = (byte)(entry.Index >> 8); + bulk[index + 10] = (byte)(entry.Index >> 16); + bulk[index + 11] = (byte)(entry.Index >> 24); + + bulk[index + 12] = entry.OldRaw; + bulk[index + 13] = entry.NewRaw; + bulk[index + 14] = (byte)(entry.Flags); + bulk[index + 15] = (byte)(entry.Flags >> 8); + } + + + public override void FindChangesAt(Stream s, int index, Action output) { + byte[] bulk = new byte[BulkEntries * EntrySize]; + fixed (byte* ptr = bulk) { + int dbEntries = (int)(s.Length / EntrySize) - HeaderEntries; + while (dbEntries > 0) { + int count = Math.Min(dbEntries, BulkEntries); + ReadFully(s, bulk, count * EntrySize); + BlockDBEntry* entryPtr = (BlockDBEntry*)ptr; + + for (int i = 0; i < count; i++) { + if (entryPtr->Index == index) { + output(*entryPtr); + } + entryPtr++; + } + dbEntries -= count; + } + } + } + + public override bool FindChangesBy(Stream s, int[] ids, int start, int end, Action output) { + byte[] bulk = new byte[BulkEntries * EntrySize]; + fixed (byte* ptr = bulk) { + int dbEntries = (int)(s.Length / EntrySize) - HeaderEntries; + + while (dbEntries > 0) { + int count = Math.Min(dbEntries, BulkEntries); + // find the correct position for the start of this bulk read + s.Position = (dbEntries - count + HeaderEntries) * (long)EntrySize; + + ReadFully(s, bulk, count * EntrySize); + BlockDBEntry* entryPtr = (BlockDBEntry*)ptr; + entryPtr += (count - 1); + + for (int i = count - 1; i >= 0; i--) { + if (entryPtr->TimeDelta < start) return true; + + if (entryPtr->TimeDelta <= end) { + for (int j = 0; j < ids.Length; j++) { + if (entryPtr->PlayerID != ids[j]) continue; + output(*entryPtr); break; + } + } + entryPtr--; + } + dbEntries -= count; + } + } + return false; + } + + static ushort ReadU16(byte[] array, int offset) { + return (ushort)(array[offset] | array[offset + 1] << 8); + } + + static void WriteU16(ushort value, byte[] array, int index) { + array[index++] = (byte)(value); + array[index++] = (byte)(value >> 8); + } + + static void ReadFully(Stream stream, byte[] dst, int count) { + int total = 0; + do { + int read = stream.Read(dst, total, count - total); + if (read == 0) throw new EndOfStreamException(); + total += read; + } while (total < count); + } + } +} \ No newline at end of file diff --git a/MCGalaxy/Database/BlockDB/BlockDBFile.cs b/MCGalaxy/Database/BlockDB/BlockDBFile.cs index c7b0fd48b..fcdfec414 100644 --- a/MCGalaxy/Database/BlockDB/BlockDBFile.cs +++ b/MCGalaxy/Database/BlockDB/BlockDBFile.cs @@ -22,13 +22,15 @@ using MCGalaxy.Maths; namespace MCGalaxy.DB { - public unsafe static class BlockDBFile { + public unsafe abstract class BlockDBFile { public const byte Version = 1; public const int EntrySize = 16; public const int HeaderEntries = 1; public const int BulkEntries = 256; + public static BlockDBFile V1 = new BlockDBFile_V1(); + public static string FilePath(string map) { return "blockdb/" + map + ".cbdb"; } public static string DumpPath(string map) { return "blockdb/" + map + ".dump"; } public static string TempPath(string map) { return "blockdb/" + map + ".temp"; } @@ -43,7 +45,7 @@ namespace MCGalaxy.DB { s.Write(header, 0, EntrySize); } - public static void ReadHeader(Stream s, out Vec3U16 dims) { + public static BlockDBFile ReadHeader(Stream s, out Vec3U16 dims) { dims = default(Vec3U16); byte[] header = new byte[EntrySize * HeaderEntries]; ReadFully(s, header, header.Length); @@ -57,125 +59,20 @@ namespace MCGalaxy.DB { dims.X = ReadU16(header, 10); dims.Y = ReadU16(header, 12); dims.Z = ReadU16(header, 14); + return V1; } - public static void WriteEntries(Stream s, FastList entries) { - byte[] bulk = new byte[BulkEntries * EntrySize]; - WriteEntries(s, bulk, entries.Items, entries.Count); - } + public abstract void WriteEntries(Stream s, FastList entries); - public static void WriteEntries(Stream s, BlockDBCache cache) { - byte[] bulk = new byte[BulkEntries * EntrySize]; - BlockDBCacheNode node = cache.Tail; - - while (node != null) { - WriteEntries(s, bulk, node); - lock (cache.Locker) - node = node.Next; - } - } - - static void WriteEntries(Stream s, byte[] bulk, BlockDBEntry[] entries, int count) { - for (int i = 0; i < count; i += BulkEntries) { - int bulkCount = Math.Min(BulkEntries, count - i); - for (int j = 0; j < bulkCount; j++) { - WriteEntry(entries[i + j], bulk, j * EntrySize); - } - s.Write(bulk, 0, bulkCount * EntrySize); - } - } - - static void WriteEntries(Stream s, byte[] bulk, BlockDBCacheNode node) { - int count = node.Count; - for (int i = 0; i < count; i += BulkEntries) { - int bulkCount = Math.Min(BulkEntries, count - i); - for (int j = 0; j < bulkCount; j++) { - BlockDBEntry entry = node.Unpack(node.Entries[i + j]); - WriteEntry(entry, bulk, j * EntrySize); - } - s.Write(bulk, 0, bulkCount * EntrySize); - } - } - - // Inlined WriteI32/WriteU16 for better performance - static void WriteEntry(BlockDBEntry entry, byte[] bulk, int index) { - bulk[index + 0 ] = (byte)(entry.PlayerID); - bulk[index + 1 ] = (byte)(entry.PlayerID >> 8); - bulk[index + 2 ] = (byte)(entry.PlayerID >> 16); - bulk[index + 3 ] = (byte)(entry.PlayerID >> 24); - - bulk[index + 4 ] = (byte)(entry.TimeDelta); - bulk[index + 5 ] = (byte)(entry.TimeDelta >> 8); - bulk[index + 6 ] = (byte)(entry.TimeDelta >> 16); - bulk[index + 7 ] = (byte)(entry.TimeDelta >> 24); - - bulk[index + 8 ] = (byte)(entry.Index); - bulk[index + 9 ] = (byte)(entry.Index >> 8); - bulk[index + 10] = (byte)(entry.Index >> 16); - bulk[index + 11] = (byte)(entry.Index >> 24); - - bulk[index + 12] = entry.OldRaw; - bulk[index + 13] = entry.NewRaw; - bulk[index + 14] = (byte)(entry.Flags); - bulk[index + 15] = (byte)(entry.Flags >> 8); - } - + public abstract void WriteEntries(Stream s, BlockDBCache cache); /// Iterates from the very oldest to newest entry in the BlockDB. - public static void FindChangesAt(Stream s, int index, Action output) { - byte[] bulk = new byte[BulkEntries * EntrySize]; - fixed (byte* ptr = bulk) { - int dbEntries = (int)(s.Length / EntrySize) - HeaderEntries; - while (dbEntries > 0) { - int count = Math.Min(dbEntries, BulkEntries); - ReadFully(s, bulk, count * EntrySize); - BlockDBEntry* entryPtr = (BlockDBEntry*)ptr; - - for (int i = 0; i < count; i++) { - if (entryPtr->Index == index) { - output(*entryPtr); - } - entryPtr++; - } - dbEntries -= count; - } - } - } + public abstract void FindChangesAt(Stream s, int index, Action output); /// 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, int start, int end, - Action output) { - byte[] bulk = new byte[BulkEntries * EntrySize]; - fixed (byte* ptr = bulk) { - int dbEntries = (int)(s.Length / EntrySize) - HeaderEntries; - - while (dbEntries > 0) { - int count = Math.Min(dbEntries, BulkEntries); - // find the correct position for the start of this bulk read - s.Position = (dbEntries - count + HeaderEntries) * (long)EntrySize; - - ReadFully(s, bulk, count * EntrySize); - BlockDBEntry* entryPtr = (BlockDBEntry*)ptr; - entryPtr += (count - 1); - - for (int i = count - 1; i >= 0; i--) { - if (entryPtr->TimeDelta < start) return true; - - if (entryPtr->TimeDelta <= end) { - for (int j = 0; j < ids.Length; j++) { - if (entryPtr->PlayerID != ids[j]) continue; - output(*entryPtr); break; - } - } - entryPtr--; - } - dbEntries -= count; - } - } - return false; - } + public abstract bool FindChangesBy(Stream s, int[] ids, int start, int end, Action output); /// Deletes the backing file on disc if it exists. diff --git a/MCGalaxy/Database/BlockDB/BlockDBTableDumper.cs b/MCGalaxy/Database/BlockDB/BlockDBTableDumper.cs index 75dd09e2e..dd02caa5a 100644 --- a/MCGalaxy/Database/BlockDB/BlockDBTableDumper.cs +++ b/MCGalaxy/Database/BlockDB/BlockDBTableDumper.cs @@ -93,7 +93,7 @@ namespace MCGalaxy.DB { if (buffer.Count == 0) return; if (!force && buffer.Count < 4096) return; - BlockDBFile.WriteEntries(stream, buffer); + BlockDBFile.V1.WriteEntries(stream, buffer); buffer.Count = 0; } diff --git a/MCGalaxy/MCGalaxy_.csproj b/MCGalaxy/MCGalaxy_.csproj index 71d9f28b7..f3e6d7028 100644 --- a/MCGalaxy/MCGalaxy_.csproj +++ b/MCGalaxy/MCGalaxy_.csproj @@ -407,6 +407,7 @@ +