[Major WIP] More untested cleanup for undo stuff.

This commit is contained in:
UnknownShadow200 2016-08-20 17:05:29 +10:00
parent 25fadd9987
commit f0ea6ba7b1
11 changed files with 328 additions and 250 deletions

View File

@ -60,7 +60,7 @@ namespace MCGalaxy.Commands {
done = HighlightBlocks(p, start, cache);
}
}
if (!done) UndoFormat.HighlightPlayer(p, name.ToLower(), start, ref found);
if (!done) UndoFormat.DoHighlight(p, name.ToLower(), start, ref found);
if (found) {
Player.Message(p, "Now highlighting &b" + seconds + " %Sseconds for " + Server.FindColor(name) + name);
@ -71,9 +71,8 @@ namespace MCGalaxy.Commands {
}
static bool HighlightBlocks(Player p, DateTime start, UndoCache cache) {
UndoEntriesArgs args = new UndoEntriesArgs(p, start);
UndoFormatOnline format = new UndoFormatOnline();
format.Cache = cache;
UndoFormatArgs args = new UndoFormatArgs(p, start);
UndoFormat format = new UndoFormatOnline(cache);
UndoFormat.DoHighlight(null, format, args);
return args.Stop;
}

View File

@ -60,11 +60,9 @@ namespace MCGalaxy.Commands {
op.who = who;
DrawOp.DoDrawOp(op, null, p, marks);
Level saveLevel = op.saveLevel;
Player.SendChatFrom(who, who.ColoredName +
"%S's actions for the past &b" + args.seconds + " seconds were undone.", false);
Server.s.Log(who.name + "'s actions for the past " + args.seconds + " seconds were undone.");
if (saveLevel != null) saveLevel.Save(true);
}
void UndoOfflinePlayer(Player p, string whoName, UndoArgs args, Vec3S32[] marks) {
@ -78,7 +76,7 @@ namespace MCGalaxy.Commands {
op.whoName = whoName;
DrawOp.DoDrawOp(op, null, p, marks);
if (op.foundUser) {
if (op.found) {
Chat.MessageAll("{0}{1}%S's actions for the past &b{2} %Sseconds were undone.",
group.color, whoName, args.seconds);
Server.s.Log(whoName + "'s actions for the past " + args.seconds + " seconds were undone.");

View File

@ -118,14 +118,12 @@ namespace MCGalaxy.Commands.Building {
op.who = who;
DrawOp.DoDrawOp(op, null, p, new Vec3S32[] { Vec3U16.MaxVal, Vec3U16.MaxVal } );
Level saveLevel = op.saveLevel;
if (p == who) {
Player.Message(p, "Undid your actions for the past &b" + seconds + " %Sseconds.");
} else {
Player.SendChatFrom(who, who.ColoredName + "%S's actions for the past &b" + seconds + " seconds were undone.", false);
}
Server.s.Log(who.name + "'s actions for the past " + seconds + " seconds were undone.");
if (saveLevel != null) saveLevel.Save(true);
}
void UndoOfflinePlayer(Player p, long seconds, string whoName) {
@ -136,7 +134,7 @@ namespace MCGalaxy.Commands.Building {
op.whoName = whoName;
DrawOp.DoDrawOp(op, null, p, new Vec3S32[] { Vec3U16.MaxVal, Vec3U16.MaxVal } );
if (op.foundUser) {
if (op.found) {
Chat.MessageAll("{0}{1}%S's actions for the past &b{2} %Sseconds were undone.",
Server.FindColor(whoName), whoName, seconds);
Server.s.Log(whoName + "'s actions for the past " + seconds + " seconds were undone.");

View File

@ -27,7 +27,7 @@ namespace MCGalaxy.Drawing.Ops {
public override string Name { get { return "UndoSelf"; } }
}
public class UndoOnlineDrawOp : DrawOp {
public class UndoOnlineDrawOp : DrawOp {
public override string Name { get { return "UndoOnline"; } }
/// <summary> Point in time that the /undo should go backwards up to. </summary>
@ -37,67 +37,36 @@ namespace MCGalaxy.Drawing.Ops {
public DateTime End = DateTime.MaxValue;
internal Player who;
internal Level saveLevel = null;
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
UndoCache cache = p.UndoBuffer;
using (IDisposable locker = cache.ClearLock.AccquireReadLock()) {
UndoBlocks(p, ref saveLevel);
}
if (UndoBlocks(p)) yield break;
}
bool found = false;
string target = who.name.ToLower();
bool foundUser = false;
Vec3S32[] bounds = { Min, Max };
UndoFormat.UndoPlayer(p, who.name.ToLower(), bounds, Start, ref foundUser);
if (Min.X != ushort.MaxValue)
UndoFormat.DoUndoArea(p, target, Start, Min, Max, ref found);
else
UndoFormat.DoUndo(p, target, Start, End, ref found);
yield break;
}
void UndoBlocks(Player p, ref Level saveLvl) {
UndoCache cache = who.UndoBuffer;
UndoCacheNode node = cache.Tail;
if (node == null) return;
bool UndoBlocks(Player p) {
UndoFormatArgs args = new UndoFormatArgs(p, Start);
UndoFormat format = new UndoFormatOnline(p.UndoBuffer);
Vec3U16 min = (Vec3U16)Min, max = (Vec3U16)Max;
bool undoArea = Min.X != ushort.MaxValue;
Player.UndoPos Pos = default(Player.UndoPos);
int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
while (node != null) {
Level lvl = LevelInfo.FindExact(node.MapName);
if (lvl == null) { node = node.Prev; continue; }
bool super = p == null || p.ircNick != null;
if (!super && !p.level.name.CaselessEq(lvl.name)) { node = node.Prev; continue; }
Pos.mapName = lvl.name;
saveLvl = lvl;
List<UndoCacheItem> items = node.Items;
BufferedBlockSender buffer = new BufferedBlockSender(lvl);
if (!undoArea) {
min = new Vec3U16(0, 0, 0);
max = new Vec3U16((ushort)(lvl.Width - 1), (ushort)(lvl.Height - 1), (ushort)(lvl.Length - 1));
}
for (int i = items.Count - 1; i >= 0; i--) {
UndoCacheItem item = items[i];
node.Unpack(item.Index, out Pos.x, out Pos.y, out Pos.z);
if (Pos.x < min.X || Pos.y < min.Y || Pos.z < min.Z ||
Pos.x > max.X || Pos.y > max.Y || Pos.z > max.Z) continue;
DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond);
if (time > End) continue;
if (time < Start) { buffer.CheckIfSend(true); return; }
item.GetNewBlock(out Pos.newtype, out Pos.newExtType);
item.GetBlock(out Pos.type, out Pos.extType);
UndoFormat.UndoBlock(p, lvl, Pos, buffer);
}
buffer.CheckIfSend(true);
node = node.Prev;
}
if (Min.X != ushort.MaxValue)
UndoFormat.DoUndoArea(null, Min, Max, format, args);
else
UndoFormat.DoUndo(null, End, format, args);
return args.Stop;
}
}
public class UndoOfflineDrawOp : DrawOp {
public override string Name { get { return "UndoOffline"; } }
@ -106,17 +75,20 @@ namespace MCGalaxy.Drawing.Ops {
public DateTime Start = DateTime.MinValue;
internal string whoName;
internal bool foundUser = false;
internal bool found = false;
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
Vec3S32[] bounds = { Min, Max };
UndoFormat.UndoPlayer(p, whoName.ToLower(), bounds, Start, ref foundUser);
string target = whoName.ToLower();
if (Min.X != ushort.MaxValue)
UndoFormat.DoUndoArea(p, target, Start, Min, Max, ref found);
else
UndoFormat.DoUndo(p, target, Start, DateTime.MaxValue, ref found);
yield break;
}
}
public class UndoPhysicsDrawOp : DrawOp {
public override string Name { get { return "UndoPhysics"; } }

View File

@ -541,6 +541,7 @@
<Compile Include="Player\PlayerInfo.cs" />
<Compile Include="Player\TabList.cs" />
<Compile Include="Player\Undo\UndoCache.cs" />
<Compile Include="Player\Undo\UndoFormat.Helpers.cs" />
<Compile Include="Player\Undo\UndoFormatCBin.cs" />
<Compile Include="Player\Undo\UndoFormatOnline.cs" />
<Compile Include="Player\Warp.cs" />

View File

@ -0,0 +1,198 @@
/*
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.Collections.Generic;
using System.IO;
namespace MCGalaxy.Undo {
/// <summary> Retrieves and saves undo data in a particular format. </summary>
/// <remarks> Note most formats only support retrieving undo data. </remarks>
public abstract partial class UndoFormat {
public static void DoUndo(Player p, string target,
DateTime start, DateTime end, ref bool found) {
List<string> files = GetUndoFiles(target);
UndoFormatArgs args = new UndoFormatArgs(p, start);
foreach (string file in files) {
found = true;
using (Stream s = File.OpenRead(file)) {
DoUndo(s, end, GetFormat(file), args);
if (args.Stop) break;
}
}
}
public static void DoUndo(Stream s, DateTime end,
UndoFormat format, UndoFormatArgs args) {
Level lvl = args.Player == null ? null : args.Player.level;
BufferedBlockSender buffer = new BufferedBlockSender(lvl);
string lastMap = null;
foreach (UndoFormatEntry P in format.GetEntries(s, args)) {
if (P.LevelName != lastMap) {
lvl = LevelInfo.FindExact(P.LevelName);
buffer.CheckIfSend(true);
buffer.level = lvl;
}
if (lvl == null || P.Time > end) continue;
UndoBlock(args.Player, lvl, P, buffer);
}
buffer.CheckIfSend(true);
}
public static void DoUndoArea(Player p, string target,
DateTime start, Vec3S32 min, Vec3S32 max, ref bool found) {
List<string> files = GetUndoFiles(target);
UndoFormatArgs args = new UndoFormatArgs(p, start);
foreach (string file in files) {
found = true;
using (Stream s = File.OpenRead(file)) {
DoUndoArea(s, min, max, GetFormat(file), args);
if (args.Stop) break;
}
}
}
public static void DoUndoArea(Stream s, Vec3S32 min, Vec3S32 max,
UndoFormat format, UndoFormatArgs args) {
Level lvl = args.Player == null ? null : args.Player.level;
BufferedBlockSender buffer = new BufferedBlockSender(lvl);
string lastMap = null;
foreach (UndoFormatEntry P in format.GetEntries(s, args)) {
if (P.LevelName != lastMap) {
lvl = LevelInfo.FindExact(P.LevelName);
buffer.CheckIfSend(true);
buffer.level = lvl;
}
if (lvl == null) continue;
if (P.X < min.X || P.Y < min.Y || P.Z < min.Z) continue;
if (P.X > max.X || P.Y > max.Y || P.Z > max.Z) continue;
UndoBlock(args.Player, lvl, P, buffer);
}
buffer.CheckIfSend(true);
}
public static void DoHighlight(Player p, string target,
DateTime start, ref bool found) {
List<string> files = GetUndoFiles(target);
UndoFormatArgs args = new UndoFormatArgs(p, start);
foreach (string file in files) {
found = true;
using (Stream s = File.OpenRead(file)) {
DoHighlight(s, GetFormat(file), args);
if (args.Stop) break;
}
}
}
public static void DoHighlight(Stream s, UndoFormat format, UndoFormatArgs args) {
BufferedBlockSender buffer = new BufferedBlockSender(args.Player);
Level lvl = args.Player.level;
foreach (UndoFormatEntry P in format.GetEntries(s, args)) {
byte block = P.Block, newBlock = P.NewBlock;
byte highlight = (newBlock == Block.air
|| Block.Convert(block) == Block.water || block == Block.waterstill
|| Block.Convert(block) == Block.lava || block == Block.lavastill)
? Block.red : Block.green;
buffer.Add(lvl.PosToInt(P.X, P.Y, P.Z), highlight, 0);
buffer.CheckIfSend(false);
}
buffer.CheckIfSend(true);
}
public static void UpgradePlayerUndoFiles(string name) {
UpgradeFiles(undoDir, name);
UpgradeFiles(prevUndoDir, name);
}
static void UpgradeFiles(string dir, string name) {
string path = Path.Combine(dir, name);
if (!Directory.Exists(path)) return;
string[] files = Directory.GetFiles(path);
List<Player.UndoPos> buffer = new List<Player.UndoPos>();
UndoFormatArgs args = new UndoFormatArgs(null, DateTime.MinValue);
for (int i = 0; i < files.Length; i++) {
path = files[i];
if (!path.EndsWith(BinFormat.Ext) && !path.EndsWith(TxtFormat.Ext)) continue;
IEnumerable<UndoFormatEntry> data = null;
Player.UndoPos pos;
using (FileStream s = File.OpenRead(path)) {
data = path.EndsWith(BinFormat.Ext)
? BinFormat.GetEntries(s, args) : TxtFormat.GetEntries(s, args);
foreach (UndoFormatEntry P in data) {
pos.x = P.X; pos.y = P.Y; pos.z = P.Z;
pos.type = P.Block; pos.extType = P.ExtBlock;
pos.newtype = P.NewBlock; pos.newExtType = P.NewExtBlock;
pos.timeDelta = (int)P.Time.Subtract(Server.StartTimeLocal).TotalSeconds;
pos.mapName = P.LevelName;
buffer.Add(pos);
}
buffer.Reverse();
string newPath = Path.ChangeExtension(path, NewFormat.Ext);
NewFormat.Save(buffer, newPath);
}
File.Delete(path);
}
}
static void UndoBlock(Player pl, Level lvl, UndoFormatEntry P,
BufferedBlockSender buffer) {
byte lvlBlock = lvl.GetTile(P.X, P.Y, P.Z);
if (lvlBlock == P.NewBlock || Block.Convert(lvlBlock) == Block.water
|| Block.Convert(lvlBlock) == Block.lava || lvlBlock == Block.grass) {
lvl.changed = true;
if (pl != null) {
if (lvl.DoBlockchange(pl, P.X, P.Y, P.Z, P.Block, P.ExtBlock, true)) {
buffer.Add(lvl.PosToInt(P.X, P.Y, P.Z), P.Block, P.ExtBlock);
buffer.CheckIfSend(false);
}
} else {
bool diff = Block.Convert(lvlBlock) != Block.Convert(P.Block);
if (!diff && lvlBlock == Block.custom_block)
diff = lvl.GetExtTile(P.X, P.Y, P.Z) != P.ExtBlock;
if (diff) {
buffer.Add(lvl.PosToInt(P.X, P.Y, P.Z), P.Block, P.ExtBlock);
buffer.CheckIfSend(false);
}
lvl.SetTile(P.X, P.Y, P.Z, P.Block);
if (P.ExtBlock == Block.custom_block)
lvl.SetExtTile(P.X, P.Y, P.Z, P.ExtBlock);
}
}
}
}
}

View File

@ -23,7 +23,7 @@ namespace MCGalaxy.Undo {
/// <summary> Retrieves and saves undo data in a particular format. </summary>
/// <remarks> Note most formats only support retrieving undo data. </remarks>
public abstract class UndoFormat {
public abstract partial class UndoFormat {
protected const string undoDir = "extra/undo", prevUndoDir = "extra/undoPrevious";
public static UndoFormat TxtFormat = new UndoFormatText();
@ -34,10 +34,12 @@ namespace MCGalaxy.Undo {
protected abstract void Save(UndoCache buffer, string path);
protected abstract IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args);
protected abstract IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args);
protected abstract string Ext { get; }
/// <summary> Saves the undo data for the given player to disc. </summary>
/// <remarks> Clears the player's in-memory undo buffer on success. </remarks>
public static void SaveUndo(Player p) {
if (p == null || p.UndoBuffer.Count < 1) return;
@ -64,46 +66,32 @@ namespace MCGalaxy.Undo {
lock (cache.AddLock)
cache.Clear();
}
}
}
public static void UndoPlayer(Player p, string target, Vec3S32[] marks, DateTime start, ref bool FoundUser) {
FilterEntries(p, undoDir, target, marks, start, false, ref FoundUser);
FilterEntries(p, prevUndoDir, target, marks, start, false, ref FoundUser);
}
public static void HighlightPlayer(Player p, string target, DateTime start, ref bool FoundUser) {
FilterEntries(p, undoDir, target, null, start, true, ref FoundUser);
FilterEntries(p, prevUndoDir, target, null, start, true, ref FoundUser);
}
static void FilterEntries(Player p, string dir, string name, Vec3S32[] marks,
DateTime start, bool highlight, ref bool FoundUser) {
string[] files = GetFiles(dir, name);
if (files == null || files.Length == 0) return;
UndoEntriesArgs args = new UndoEntriesArgs(p, start);
/// <summary> Gets a list of all undo file names for the given player. </summary>
/// <remarks> This list is sorted, such that the first element is the
/// most recent undo file, and the last element is the oldest.</remarks>
public static List<string> GetUndoFiles(string name) {
List<string> entries = new List<string>();
string[] cur = GetFiles(undoDir, name);
string[] prev = GetFiles(prevUndoDir, name);
for (int i = files.Length - 1; i >= 0; i--) {
string path = files[i];
if (path == null) continue;
UndoFormat format = GetFormat(path);
if (format == null) continue;
using (Stream s = File.OpenRead(path)) {
if (highlight) {
DoHighlight(s, format, args);
} else {
DoUndo(s, format, args);
}
if (args.Stop) break;
}
// Start from the last entry, because when undoing we want to start
// with the most recent file first
for (int i = cur.Length - 1; i >= 0; i--) {
if (cur[i] == null) continue;
entries.Add(cur[i]);
}
FoundUser = true;
for (int i = prev.Length - 1; i >= 0; i--) {
if (prev[i] == null) continue;
entries.Add(prev[i]);
}
return entries;
}
static string[] GetFiles(string dir, string name) {
string path = Path.Combine(dir, name);
if (!Directory.Exists(path)) return null;
if (!Directory.Exists(path)) return new string[0];
string[] files = Directory.GetFiles(path);
Array.Sort<string>(files, CompareFiles);
@ -111,6 +99,8 @@ namespace MCGalaxy.Undo {
name = Path.GetFileName(files[i]);
if (name.Length == 0 || name[0] < '0' || name[0] > '9')
files[i] = null;
if (files[i] != null && GetFormat(name) == null)
files[i] = null;
}
return files;
}
@ -132,121 +122,43 @@ namespace MCGalaxy.Undo {
return aNum.CompareTo(bNum);
}
public static void DoHighlight(Stream s, UndoFormat format, UndoEntriesArgs args) {
BufferedBlockSender buffer = new BufferedBlockSender(args.Player);
Level lvl = args.Player.level;
foreach (Player.UndoPos P in format.GetEntries(s, args)) {
byte type = P.type, newType = P.newtype;
byte block = (newType == Block.air
|| Block.Convert(type) == Block.water || type == Block.waterstill
|| Block.Convert(type) == Block.lava || type == Block.lavastill)
? Block.red : Block.green;
buffer.Add(lvl.PosToInt(P.x, P.y, P.z), block, 0);
buffer.CheckIfSend(false);
}
buffer.CheckIfSend(true);
}
public static void DoUndo(Stream s, UndoFormat format, UndoEntriesArgs args) {
Level lvl = args.Player == null ? null : args.Player.level;
BufferedBlockSender buffer = new BufferedBlockSender(lvl);
string lastMap = null;
foreach (Player.UndoPos P in format.GetEntries(s, args)) {
if (P.mapName != lastMap) {
lvl = LevelInfo.FindExact(P.mapName);
buffer.CheckIfSend(true);
buffer.level = lvl;
}
if (lvl == null) continue;
UndoBlock(args.Player, lvl, P, buffer);
}
buffer.CheckIfSend(true);
}
protected internal static void UndoBlock(Player pl, Level lvl, Player.UndoPos P,
BufferedBlockSender buffer) {
byte lvlTile = lvl.GetTile(P.x, P.y, P.z);
if (lvlTile == P.newtype || Block.Convert(lvlTile) == Block.water
|| Block.Convert(lvlTile) == Block.lava || lvlTile == Block.grass) {
byte newExtType = P.newExtType;
P.newtype = P.type; P.newExtType = P.extType;
P.extType = newExtType; P.type = lvlTile;
if (pl != null) {
if (lvl.DoBlockchange(pl, P.x, P.y, P.z, P.newtype, P.newExtType, true)) {
buffer.Add(lvl.PosToInt(P.x, P.y, P.z), P.newtype, P.newExtType);
buffer.CheckIfSend(false);
}
} else {
bool diffBlock = Block.Convert(lvlTile) != Block.Convert(P.newtype);
if (!diffBlock && lvlTile == Block.custom_block)
diffBlock = lvl.GetExtTile(P.x, P.y, P.z) != P.newExtType;
if (diffBlock) {
buffer.Add(lvl.PosToInt(P.x, P.y, P.z), P.newtype, P.newExtType);
buffer.CheckIfSend(false);
}
lvl.SetTile(P.x, P.y, P.z, P.newtype);
if (P.newtype == Block.custom_block)
lvl.SetExtTile(P.x, P.y, P.z, P.newExtType);
}
}
}
/// <summary> Creates the default base directories used to store undo data on disc. </summary>
public static void CreateDefaultDirectories() {
if (!Directory.Exists(undoDir))
Directory.CreateDirectory(undoDir);
if (!Directory.Exists(prevUndoDir))
Directory.CreateDirectory(prevUndoDir);
}
public static void UpgradePlayerUndoFiles(string name) {
UpgradeFiles(undoDir, name);
UpgradeFiles(prevUndoDir, name);
}
static void UpgradeFiles(string dir, string name) {
string path = Path.Combine(dir, name);
if (!Directory.Exists(path)) return;
string[] files = Directory.GetFiles(path);
List<Player.UndoPos> buffer = new List<Player.UndoPos>();
UndoEntriesArgs args = new UndoEntriesArgs(null, DateTime.MinValue);
for (int i = 0; i < files.Length; i++) {
path = files[i];
if (!path.EndsWith(BinFormat.Ext) && !path.EndsWith(TxtFormat.Ext)) continue;
IEnumerable<Player.UndoPos> data = null;
using (FileStream s = File.OpenRead(path)) {
data = path.EndsWith(BinFormat.Ext)
? BinFormat.GetEntries(s, args) : TxtFormat.GetEntries(s, args);
foreach (Player.UndoPos pos in data)
buffer.Add(pos);
buffer.Reverse();
string newPath = Path.ChangeExtension(path, NewFormat.Ext);
NewFormat.Save(buffer, newPath);
}
File.Delete(path);
}
}
}
public class UndoEntriesArgs {
public Player Player;
public byte[] Temp;
/// <summary> Arguments provided to an UndoFormat for retrieving undo data. </summary>
public class UndoFormatArgs {
/// <summary> Player associated with this undo, can be console or IRC. </summary>
internal readonly Player Player;
/// <summary> Small work buffer, used to avoid memory allocations. </summary>
internal byte[] Temp;
/// <summary> Whether the format has finished retrieving undo data,
/// due to finding an entry before the start range. </summary>
public bool Stop;
public DateTime StartRange;
public UndoEntriesArgs(Player p, DateTime start) {
/// <summary> First instance in time that undo data should be retrieved back to. </summary>
internal readonly DateTime Start;
public UndoFormatArgs(Player p, DateTime start) {
Player = p;
StartRange = start;
Start = start;
}
}
public struct UndoFormatEntry {
public string LevelName;
public DateTime Time;
public ushort X, Y, Z;
public byte Block, ExtBlock;
public byte NewBlock, NewExtBlock;
}
}

View File

@ -35,11 +35,11 @@ namespace MCGalaxy.Undo {
throw new NotSupportedException("Non-optimised binary undo files have been deprecated");
}
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
List<ChunkHeader> list = new List<ChunkHeader>();
Player.UndoPos pos;
UndoFormatEntry pos;
bool super = args.Player == null || args.Player.ircNick != null;
DateTime start = args.StartRange;
DateTime start = args.Start;
ReadHeaders(list, s);
for (int i = list.Count - 1; i >= 0; i--) {
@ -48,7 +48,7 @@ namespace MCGalaxy.Undo {
bool inRange = chunk.BaseTime.AddTicks(65536 * TimeSpan.TicksPerSecond) >= start;
if (!inRange) { args.Stop = true; yield break; }
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
pos.mapName = chunk.LevelName;
pos.LevelName = chunk.LevelName;
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
if (args.Temp == null)
@ -58,16 +58,15 @@ namespace MCGalaxy.Undo {
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
int offset = j * entrySize;
DateTime time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond);
if (time < start) { args.Stop = true; yield break; }
pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
pos.Time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond);
if (pos.Time < start) { args.Stop = true; yield break; }
pos.x = U16(temp, offset + 2);
pos.y = U16(temp, offset + 4);
pos.z = U16(temp, offset + 6);
pos.X = U16(temp, offset + 2);
pos.Y = U16(temp, offset + 4);
pos.Z = U16(temp, offset + 6);
pos.type = temp[offset + 8]; pos.extType = temp[offset + 9];
pos.newtype = temp[offset + 10]; pos.newExtType = temp[offset + 11];
pos.Block = temp[offset + 8]; pos.ExtBlock = temp[offset + 9];
pos.NewBlock = temp[offset + 10]; pos.NewExtBlock = temp[offset + 11];
yield return pos;
}
}

View File

@ -98,12 +98,12 @@ namespace MCGalaxy.Undo {
}
}
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
List<ChunkHeader> list = new List<ChunkHeader>();
Player.UndoPos pos;
UndoFormatEntry pos;
UndoCacheItem item = default(UndoCacheItem);
bool super = args.Player == null || args.Player.ircNick != null;
DateTime start = args.StartRange;
DateTime start = args.Start;
ReadHeaders(list, s);
for (int i = list.Count - 1; i >= 0; i--) {
@ -112,7 +112,7 @@ namespace MCGalaxy.Undo {
bool inRange = chunk.BaseTime.AddTicks((65536 >> 2) * TimeSpan.TicksPerSecond) >= start;
if (!inRange) { args.Stop = true; yield break; }
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
pos.mapName = chunk.LevelName;
pos.LevelName = chunk.LevelName;
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
if (args.Temp == null)
@ -123,19 +123,18 @@ namespace MCGalaxy.Undo {
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
int offset = j * entrySize;
item.Flags = U16(temp, offset + 0);
DateTime time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
if (time < start) { args.Stop = true; yield break; }
pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
pos.Time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
if (pos.Time < start) { args.Stop = true; yield break; }
int index = I32(temp, offset + 2);
pos.x = (ushort)(index % chunk.Width);
pos.y = (ushort)((index / chunk.Width) / chunk.Length);
pos.z = (ushort)((index / chunk.Width) % chunk.Length);
pos.X = (ushort)(index % chunk.Width);
pos.Y = (ushort)((index / chunk.Width) / chunk.Length);
pos.Z = (ushort)((index / chunk.Width) % chunk.Length);
item.Type = temp[offset + 6];
item.NewType = temp[offset + 7];
item.GetBlock(out pos.type, out pos.extType);
item.GetNewBlock(out pos.newtype, out pos.newExtType);
item.GetBlock(out pos.Block, out pos.ExtBlock);
item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock);
yield return pos;
}
}

View File

@ -24,7 +24,10 @@ namespace MCGalaxy.Undo {
public sealed class UndoFormatOnline : UndoFormat {
protected override string Ext { get { return null; } }
public UndoCache Cache;
readonly UndoCache cache;
public UndoFormatOnline(UndoCache cache) {
this.cache = cache;
}
protected override void Save(List<Player.UndoPos> buffer, string path) {
throw new NotSupportedException("UndoFileOnline is read only.");
@ -34,29 +37,29 @@ namespace MCGalaxy.Undo {
throw new NotSupportedException("UndoFileOnline is read only.");
}
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
UndoCacheNode node = Cache.Tail;
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
UndoCacheNode node = cache.Tail;
if (node == null) yield break;
Player.UndoPos pos;
UndoFormatEntry pos;
bool super = args.Player == null || args.Player.ircNick != null;
DateTime start = args.StartRange;
DateTime start = args.Start;
while (node != null) {
Level lvl = LevelInfo.FindExact(node.MapName);
if (!super && !args.Player.level.name.CaselessEq(node.MapName)) continue;
List<UndoCacheItem> items = node.Items;
pos.mapName = node.MapName;
pos.LevelName = node.MapName;
for (int i = items.Count - 1; i >= 0; i--) {
UndoCacheItem item = items[i];
DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond);
if (time < start) { args.Stop = true; yield break; }
pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
pos.Time = time;
node.Unpack(item.Index, out pos.x, out pos.y, out pos.z);
item.GetBlock(out pos.type, out pos.extType);
item.GetNewBlock(out pos.newtype, out pos.newExtType);
node.Unpack(item.Index, out pos.X, out pos.Y, out pos.Z);
item.GetBlock(out pos.Block, out pos.ExtBlock);
item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock);
yield return pos;
}
node = node.Prev;

View File

@ -34,32 +34,31 @@ namespace MCGalaxy.Undo {
throw new NotSupportedException("Text undo files have been deprecated");
}
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
Player.UndoPos pos;
pos.newExtType = 0; pos.extType = 0;
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
UndoFormatEntry pos;
pos.NewExtBlock = 0; pos.ExtBlock = 0;
string[] lines = new StreamReader(s).ReadToEnd().Split(' ');
Player p = args.Player;
bool super = p == null || p.ircNick != null;
DateTime start = args.StartRange;
DateTime start = args.Start;
// because we have space to end of each entry, need to subtract one otherwise we'll start at a "".
for (int i = (lines.Length - 1) / 7; i >= 0; i--) {
// line format: mapName x y z date oldblock newblock
string timeRaw = lines[(i * 7) - 3].Replace('&', ' ');
DateTime time = DateTime.Parse(timeRaw, CultureInfo.InvariantCulture);
if (time < start) { args.Stop = true; yield break; }
pos.timeDelta = (int)time.Subtract(Server.StartTimeLocal).TotalSeconds;
pos.Time = DateTime.Parse(timeRaw, CultureInfo.InvariantCulture);
if (pos.Time < start) { args.Stop = true; yield break; }
string map = lines[(i * 7) - 7];
if (!super && !p.level.name.CaselessEq(map)) continue;
pos.mapName = map;
pos.LevelName = map;
pos.x = Convert.ToUInt16(lines[(i * 7) - 6]);
pos.y = Convert.ToUInt16(lines[(i * 7) - 5]);
pos.z = Convert.ToUInt16(lines[(i * 7) - 4]);
pos.X = Convert.ToUInt16(lines[(i * 7) - 6]);
pos.Y = Convert.ToUInt16(lines[(i * 7) - 5]);
pos.Z = Convert.ToUInt16(lines[(i * 7) - 4]);
pos.newtype = Convert.ToByte(lines[(i * 7) - 1]);
pos.type = Convert.ToByte(lines[(i * 7) - 2]);
pos.NewBlock = Convert.ToByte(lines[(i * 7) - 1]);
pos.Block = Convert.ToByte(lines[(i * 7) - 2]);
yield return pos;
}
}