mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-23 04:32:50 -04:00
Make BlockDBFile extensible
This commit is contained in:
parent
1ebdbd6e1f
commit
fb4b0ab88e
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
158
MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs
Normal file
158
MCGalaxy/Database/BlockDB/BlockDBFile.V1.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user