Make BlockDBFile extensible

This commit is contained in:
UnknownShadow200 2017-07-22 22:46:07 +10:00
parent 1ebdbd6e1f
commit fb4b0ab88e
5 changed files with 179 additions and 121 deletions

View File

@ -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 {
/// <summary> 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. </summary>
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;
}

View File

@ -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<BlockDBEntry> 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<BlockDBEntry> 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<BlockDBEntry> 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);
}
}
}

View File

@ -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<BlockDBEntry> 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;
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, FastList<BlockDBEntry> entries);
public abstract void WriteEntries(Stream s, BlockDBCache cache);
/// <summary> Iterates from the very oldest to newest entry in the BlockDB. </summary>
public static void FindChangesAt(Stream s, int index, Action<BlockDBEntry> 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<BlockDBEntry> output);
/// <summary> Iterates from the very newest to oldest entry in the BlockDB. </summary>
/// <returns> whether an entry before start time was reached. </returns>
public static bool FindChangesBy(Stream s, int[] ids, int start, int end,
Action<BlockDBEntry> 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<BlockDBEntry> output);
/// <summary> Deletes the backing file on disc if it exists. </summary>

View File

@ -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;
}

View File

@ -407,6 +407,7 @@
<Compile Include="Database\BlockDB\BlockDBFile.cs" />
<Compile Include="Database\BlockDB\BlockDBEntry.cs" />
<Compile Include="Database\BlockDB\BlockDBChange.cs" />
<Compile Include="Database\BlockDB\BlockDBFile.V1.cs" />
<Compile Include="Database\BlockDB\DBUpgrader.cs" />
<Compile Include="Database\BlockDB\BlockDBTableDumper.cs" />
<Compile Include="Database\BlockDB\NameConverter.cs" />