Add cancellable event for when BlockDB is being flushed to disc

This commit is contained in:
UnknownShadow200 2024-02-18 11:39:03 +11:00
parent ffe4707394
commit 3a7390df30
5 changed files with 81 additions and 30 deletions

View File

@ -17,6 +17,7 @@
*/ */
using System; using System;
using System.IO; using System.IO;
using MCGalaxy.Events.BlockDBEvents;
using MCGalaxy.Util; using MCGalaxy.Util;
using MCGalaxy.Maths; using MCGalaxy.Maths;
@ -56,8 +57,10 @@ namespace MCGalaxy.DB
} }
void ReadDimensions() { void ReadDimensions() {
if (!File.Exists(FilePath)) return; string path = FilePath;
using (Stream s = OpenRead()) if (!File.Exists(path)) return;
using (Stream s = OpenRead(path))
BlockDBFile.ReadHeader(s, out Dims); BlockDBFile.ReadHeader(s, out Dims);
} }
@ -66,8 +69,13 @@ namespace MCGalaxy.DB
public void FlushCache() { public void FlushCache() {
if (Cache.Head == null) return; if (Cache.Head == null) return;
BlockDBFile format = ValidateBackingFile(); string path = FilePath;
using (Stream s = OpenWrite()) { bool cancel = false;
OnBlockDBSaveEvent.Call(this, ref path, ref cancel);
if (cancel) return;
BlockDBFile format = ValidateBackingFile(path);
using (Stream s = OpenWrite(path)) {
// This truncates the lower 4 bits off - so e.g. if a power off occurred // 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 // and 21 bytes were in the file, this sets the position to byte 16
s.Position = s.Length & ~0x0F; s.Position = s.Length & ~0x0F;
@ -90,10 +98,11 @@ namespace MCGalaxy.DB
/// <summary> Outputs all block changes which affect the given coordinates. </summary> /// <summary> Outputs all block changes which affect the given coordinates. </summary>
/// <remarks> You must lock using Locker.AccquireRead() **before** entering this method. </remarks> /// <remarks> You must lock using Locker.AccquireRead() **before** entering this method. </remarks>
public void FindChangesAt(ushort x, ushort y, ushort z, Action<BlockDBEntry> output) { public void FindChangesAt(ushort x, ushort y, ushort z, Action<BlockDBEntry> output) {
if (!File.Exists(FilePath)) { FindInMemoryAt(x, y, z, output); return; } string path = FilePath;
if (!File.Exists(path)) { FindInMemoryAt(x, y, z, output); return; }
Vec3U16 dims; Vec3U16 dims;
using (Stream s = OpenRead()) { using (Stream s = OpenRead(path)) {
BlockDBFile format = BlockDBFile.ReadHeader(s, out dims); BlockDBFile format = BlockDBFile.ReadHeader(s, out dims);
if (x >= dims.X || y >= dims.Y || z >= dims.Z) return; if (x >= dims.X || y >= dims.Y || z >= dims.Z) return;
@ -110,7 +119,8 @@ namespace MCGalaxy.DB
BlockDBCacheEntry[] entries = node.Entries; BlockDBCacheEntry[] entries = node.Entries;
int count = node.Count; int count = node.Count;
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++)
{
if (entries[i].Index != index) continue; if (entries[i].Index != index) continue;
BlockDBEntry entry = node.Unpack(entries[i]); BlockDBEntry entry = node.Unpack(entries[i]);
output(entry); output(entry);
@ -125,13 +135,14 @@ namespace MCGalaxy.DB
public bool FindChangesBy(int[] ids, DateTime start, DateTime end, public bool FindChangesBy(int[] ids, DateTime start, DateTime end,
out Vec3U16 dims, Action<BlockDBEntry> output) { out Vec3U16 dims, Action<BlockDBEntry> output) {
int startDelta = ClampDelta(start.Subtract(Epoch)); int startDelta = ClampDelta(start.Subtract(Epoch));
int endDelta = ClampDelta(end.Subtract(Epoch)); int endDelta = ClampDelta(end.Subtract(Epoch));
dims = Dims; dims = Dims;
if (FindInMemoryBy(ids, startDelta, endDelta, output)) return true; if (FindInMemoryBy(ids, startDelta, endDelta, output)) return true;
string path = FilePath;
if (!File.Exists(FilePath)) return false; if (!File.Exists(path)) return false;
using (Stream s = OpenRead()) { using (Stream s = OpenRead(path)) {
BlockDBFile format = BlockDBFile.ReadHeader(s, out dims); BlockDBFile format = BlockDBFile.ReadHeader(s, out dims);
return format.FindChangesBy(s, ids, startDelta, endDelta, output); return format.FindChangesBy(s, ids, startDelta, endDelta, output);
} }
@ -143,12 +154,14 @@ namespace MCGalaxy.DB
int count = node.Count; int count = node.Count;
BlockDBCacheEntry[] entries = node.Entries; BlockDBCacheEntry[] entries = node.Entries;
for (int i = count - 1; i >= 0; i--) { for (int i = count - 1; i >= 0; i--)
{
BlockDBEntry entry = node.Unpack(entries[i]); BlockDBEntry entry = node.Unpack(entries[i]);
if (entry.TimeDelta < startDelta) return true; if (entry.TimeDelta < startDelta) return true;
if (entry.TimeDelta > endDelta) continue; if (entry.TimeDelta > endDelta) continue;
for (int j = 0; j < ids.Length; j++) { for (int j = 0; j < ids.Length; j++)
{
if (entry.PlayerID != ids[j]) continue; if (entry.PlayerID != ids[j]) continue;
output(entry); break; output(entry); break;
} }
@ -168,41 +181,43 @@ namespace MCGalaxy.DB
/// <summary> Deletes the backing file on disc if it exists. </summary> /// <summary> Deletes the backing file on disc if it exists. </summary>
public void DeleteBackingFile() { public void DeleteBackingFile() {
string path = FilePath;
using (IDisposable writeLock = Locker.AccquireWrite()) { using (IDisposable writeLock = Locker.AccquireWrite()) {
if (!File.Exists(FilePath)) return; if (!File.Exists(path)) return;
File.Delete(FilePath); File.Delete(path);
} }
} }
/// <summary> Checks if the backing file exists on disc, and if not, creates it. /// <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> /// Also recreates the backing file if dimensions on disc are less than those in memory. </summary>
BlockDBFile ValidateBackingFile() { BlockDBFile ValidateBackingFile(string path) {
BlockDBFile format = BlockDBFile.V1;
Vec3U16 fileDims; Vec3U16 fileDims;
BlockDBFile format = BlockDBFile.V1; if (!File.Exists(path)) {
if (!File.Exists(FilePath)) { using (Stream s = OpenWrite(path)) {
using (Stream s = OpenWrite()) {
fileDims = Dims; fileDims = Dims;
BlockDBFile.WriteHeader(s, fileDims); BlockDBFile.WriteHeader(s, fileDims);
} }
} else { } else {
using (Stream s = OpenRead()) { using (Stream s = OpenRead(path)) {
format = BlockDBFile.ReadHeader(s, out fileDims); format = BlockDBFile.ReadHeader(s, out fileDims);
} }
if (fileDims.X < Dims.X || fileDims.Y < Dims.Y || fileDims.Z < Dims.Z) { if (fileDims.X < Dims.X || fileDims.Y < Dims.Y || fileDims.Z < Dims.Z) {
BlockDBFile.ResizeBackingFile(this); BlockDBFile.ResizeBackingFile(this, path);
} }
} }
return format; return format;
} }
FileStream OpenWrite() { static FileStream OpenWrite(string path) {
return new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
} }
FileStream OpenRead() { static FileStream OpenRead(string path) {
return new FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite); return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite);
} }
} }
} }

View File

@ -93,12 +93,11 @@ namespace MCGalaxy.DB
File.Move(srcPath, dstPath); File.Move(srcPath, dstPath);
} }
public static void ResizeBackingFile(BlockDB db) { public static void ResizeBackingFile(BlockDB db, string path) {
Logger.Log(LogType.BackgroundActivity, "Resizing BlockDB for " + db.MapName); Logger.Log(LogType.BackgroundActivity, "Resizing BlockDB for " + db.MapName);
string filePath = FilePath(db.MapName);
string tempPath = TempPath(db.MapName); string tempPath = TempPath(db.MapName);
using (Stream src = File.OpenRead(filePath), dst = File.Create(tempPath)) { using (Stream src = File.OpenRead(path), dst = File.Create(tempPath)) {
Vec3U16 dims; Vec3U16 dims;
ReadHeader(src, out dims); ReadHeader(src, out dims);
WriteHeader(dst, db.Dims); WriteHeader(dst, db.Dims);
@ -123,8 +122,8 @@ namespace MCGalaxy.DB
} }
} }
File.Delete(filePath); File.Delete(path);
File.Move(tempPath, filePath); File.Move(tempPath, path);
} }

View File

@ -0,0 +1,36 @@
/*
Copyright 2011 MCForge
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
https://opensource.org/license/ecl-2-0/
https://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 MCGalaxy.DB;
namespace MCGalaxy.Events.BlockDBEvents
{
public delegate void OnBlockDBSave(BlockDB db, ref string path, ref bool cancel);
/// <summary> Called whenever a BlockDB is being flushed from memory to disc </summary>
public sealed class OnBlockDBSaveEvent : IEvent<OnBlockDBSave>
{
public static void Call(BlockDB db, ref string path, ref bool cancel) {
IEvent<OnBlockDBSave>[] items = handlers.Items;
for (int i = 0; i < items.Length; i++)
{
try { items[i].method(db, ref path, ref cancel); }
catch (Exception ex) { LogHandlerException(ex, items[i]); }
}
}
}
}

View File

@ -432,6 +432,7 @@
<Compile Include="Entity\ModelInfo.cs" /> <Compile Include="Entity\ModelInfo.cs" />
<Compile Include="Entity\Structs.cs" /> <Compile Include="Entity\Structs.cs" />
<Compile Include="Entity\TabList.cs" /> <Compile Include="Entity\TabList.cs" />
<Compile Include="Events\BlockDBEvents.cs" />
<Compile Include="Events\PlayerDBEvents.cs" /> <Compile Include="Events\PlayerDBEvents.cs" />
<Compile Include="Events\EconomyEvents.cs" /> <Compile Include="Events\EconomyEvents.cs" />
<Compile Include="Events\EntityEvents.cs" /> <Compile Include="Events\EntityEvents.cs" />