Created Classic BlockDB draft (markdown)

UnknownShadow200 2016-02-26 21:06:51 +11:00
parent 7bbc648cc3
commit fbf95d2eac

157
Classic-BlockDB-draft.md Normal file

@ -0,0 +1,157 @@
```CSharp
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace ClassicBlockDB {
public unsafe sealed class BlockDB {
public ushort Width, Height, Length;
sbyte referenceOffset = 30;
public DateTime ReferenceTime;
int padding = 0;
const byte formatVersion = 1;
const int bufferSize = 64 * 1024;
public void WriteHeader(BinaryWriter w) {
w.Write((byte)'c'); w.Write((byte)'b'); w.Write((byte)'d'); w.Write((byte)'b');
w.Write(formatVersion);
w.Write(referenceOffset);
w.Write(Width); w.Write(Height); w.Write(Length);
w.Write(padding);
}
public void ReadHeader(BinaryReader r) {
if (r.ReadByte() != 'c' || r.ReadByte() != 'b' || r.ReadByte() != 'd' || r.ReadByte() != 'b')
throw new InvalidDataException("Expected 'cbdb' for the header.");
if (r.ReadByte() > formatVersion)
throw new NotSupportedException("Only version 1 of the format is currently supported.");
referenceOffset = r.ReadSByte();
Width = r.ReadUInt16(); Height = r.ReadUInt16(); Length = r.ReadUInt16();
padding = r.ReadInt32();
ReferenceTime = new DateTime(1970 + referenceOffset, 1, 1, 1, 1, 1, DateTimeKind.Utc);
}
public void WriteEntries(Stream s, BlockDBEntry[] entries) {
byte[] chunk = new byte[bufferSize];
for (int i = 0; i < entries.Length; i += bufferSize / BlockDBEntry.Size) {
int count = Math.Min(bufferSize / BlockDBEntry.Size, entries.Length - i);
for (int j = 0; j < count; j++) {
BlockDBEntry entry = entries[i + j];
int chunkIndex = j * BlockDBEntry.Size;
WriteInt32(chunk, chunkIndex, entry.PlayerID);
WriteInt32(chunk, chunkIndex + 4, entry.Index);
WriteInt32(chunk, chunkIndex + 8, entry.TimeDelta);
chunk[chunkIndex + 12] = entry.OldBlock;
chunk[chunkIndex + 13] = entry.NewBlock;
chunk[chunkIndex + 14] = entry.Context;
chunk[chunkIndex + 15] = entry.Flags;
}
s.Write(chunk, 0, count * BlockDBEntry.Size);
}
}
public BlockDBEntry[] ReadEntries(Stream s) {
int count = (int)((s.Length - s.Position) / BlockDBEntry.Size);
BlockDBEntry[] entries = new BlockDBEntry[count];
byte[] chunk = new byte[bufferSize];
for (int i = 0; i < entries.Length; i += bufferSize / BlockDBEntry.Size) {
int read = s.Read(chunk, 0, chunk.Length);
for (int j = 0; j < read; j += BlockDBEntry.Size) {
BlockDBEntry entry = default(BlockDBEntry);
entry.PlayerID = ReadInt32(chunk, j);
entry.Index = ReadInt32(chunk, j + 4);
entry.TimeDelta = ReadInt32(chunk, j + 8);
entry.OldBlock = chunk[j + 12];
entry.NewBlock = chunk[j + 13];
entry.Context = chunk[j + 14];
entry.Flags = chunk[j + 15];
entries[i + (j / BlockDBEntry.Size)] = entry;
}
}
return entries;
}
public void AdjustDimensions(Stream s, ushort width, ushort height, ushort length) {
// If the new BlockDB dimensions are smaller than old, there is no point recalculating the indices.
bool updateIndices = width > Width || height > Height || length > Length;
Swap(ref width, ref Width); Swap(ref height, ref Height); Swap(ref length, ref Length);
WriteHeader(new BinaryWriter(s));
if (!updateIndices) return;
byte[] chunk = new byte[bufferSize];
while (s.Position < s.Length) {
long pos = s.Position;
int read = s.Read(chunk, 0, chunk.Length);
for (int i = 0; i < read; i += BlockDBEntry.Size) {
int index = ReadInt32(chunk, i + 4);
int x = index % width;
int y = (index / width) / length;
int z = (index / width) % length;
index = x + Width * (z + y * Length);
WriteInt32(chunk, i + 4, index);
}
s.Seek(pos, SeekOrigin.Begin);
s.Write(chunk, 0, read);
}
}
static int ReadInt32(byte[] chunk, int i) {
return chunk[i] | chunk[i + 1] << 8 | chunk[i + 2] << 16 | chunk[i + 3] << 24;
}
static void WriteInt32(byte[] chunk, int i, int value) {
chunk[i] = (byte)value;
chunk[i + 1] = (byte)(value >> 8);
chunk[i + 2] = (byte)(value >> 16);
chunk[i + 3] = (byte)(value >> 24);
}
static void Swap(ref ushort a, ref ushort b) {
ushort c = a; a = b; b = c;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BlockDBEntry {
public int PlayerID; // numerical player id unique for each player
public int Index; // packed index according to (width, height, length)
public int TimeDelta; // seconds since the reference point
public byte OldBlock, NewBlock;
public byte Context, Flags;
// MCGalaxy specific flags:
// bit 0 set: old block is a physics block, not an ext tile.
// bit 1 set: new block is a physics block, not an ext tile.
// TODO: should we be using uint16 bitflags for context, with 4 bits leftover for the software?
public const int Size = 16;
}
public static class BlockDBContext {
public const byte Place = 0;
public const byte Delete = 1;
public const byte Drawn = 2;
public const byte Paint = 3;
public const byte Replace = 4;
public const byte Paste = 5;
public const byte Cut = 6;
public const byte Fill = 7;
public const byte Restore = 8;
public const byte PhysicsChange = 9;
public const byte Undo = 10;
public const byte Redo = 11;
public const byte UndoPlayer = 12;
// ProCraft specific contexts
// Portal = 127
// Door = 128
}
}
```