mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-24 05:03:34 -04:00
[Major WIP] More untested cleanup for undo stuff.
This commit is contained in:
parent
25fadd9987
commit
f0ea6ba7b1
@ -60,7 +60,7 @@ namespace MCGalaxy.Commands {
|
|||||||
done = HighlightBlocks(p, start, cache);
|
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) {
|
if (found) {
|
||||||
Player.Message(p, "Now highlighting &b" + seconds + " %Sseconds for " + Server.FindColor(name) + name);
|
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) {
|
static bool HighlightBlocks(Player p, DateTime start, UndoCache cache) {
|
||||||
UndoEntriesArgs args = new UndoEntriesArgs(p, start);
|
UndoFormatArgs args = new UndoFormatArgs(p, start);
|
||||||
UndoFormatOnline format = new UndoFormatOnline();
|
UndoFormat format = new UndoFormatOnline(cache);
|
||||||
format.Cache = cache;
|
|
||||||
UndoFormat.DoHighlight(null, format, args);
|
UndoFormat.DoHighlight(null, format, args);
|
||||||
return args.Stop;
|
return args.Stop;
|
||||||
}
|
}
|
||||||
|
@ -60,11 +60,9 @@ namespace MCGalaxy.Commands {
|
|||||||
op.who = who;
|
op.who = who;
|
||||||
DrawOp.DoDrawOp(op, null, p, marks);
|
DrawOp.DoDrawOp(op, null, p, marks);
|
||||||
|
|
||||||
Level saveLevel = op.saveLevel;
|
|
||||||
Player.SendChatFrom(who, who.ColoredName +
|
Player.SendChatFrom(who, who.ColoredName +
|
||||||
"%S's actions for the past &b" + args.seconds + " seconds were undone.", false);
|
"%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.");
|
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) {
|
void UndoOfflinePlayer(Player p, string whoName, UndoArgs args, Vec3S32[] marks) {
|
||||||
@ -78,7 +76,7 @@ namespace MCGalaxy.Commands {
|
|||||||
op.whoName = whoName;
|
op.whoName = whoName;
|
||||||
DrawOp.DoDrawOp(op, null, p, marks);
|
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.",
|
Chat.MessageAll("{0}{1}%S's actions for the past &b{2} %Sseconds were undone.",
|
||||||
group.color, whoName, args.seconds);
|
group.color, whoName, args.seconds);
|
||||||
Server.s.Log(whoName + "'s actions for the past " + args.seconds + " seconds were undone.");
|
Server.s.Log(whoName + "'s actions for the past " + args.seconds + " seconds were undone.");
|
||||||
|
@ -118,14 +118,12 @@ namespace MCGalaxy.Commands.Building {
|
|||||||
op.who = who;
|
op.who = who;
|
||||||
DrawOp.DoDrawOp(op, null, p, new Vec3S32[] { Vec3U16.MaxVal, Vec3U16.MaxVal } );
|
DrawOp.DoDrawOp(op, null, p, new Vec3S32[] { Vec3U16.MaxVal, Vec3U16.MaxVal } );
|
||||||
|
|
||||||
Level saveLevel = op.saveLevel;
|
|
||||||
if (p == who) {
|
if (p == who) {
|
||||||
Player.Message(p, "Undid your actions for the past &b" + seconds + " %Sseconds.");
|
Player.Message(p, "Undid your actions for the past &b" + seconds + " %Sseconds.");
|
||||||
} else {
|
} else {
|
||||||
Player.SendChatFrom(who, who.ColoredName + "%S's actions for the past &b" + seconds + " seconds were undone.", false);
|
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.");
|
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) {
|
void UndoOfflinePlayer(Player p, long seconds, string whoName) {
|
||||||
@ -136,7 +134,7 @@ namespace MCGalaxy.Commands.Building {
|
|||||||
op.whoName = whoName;
|
op.whoName = whoName;
|
||||||
DrawOp.DoDrawOp(op, null, p, new Vec3S32[] { Vec3U16.MaxVal, Vec3U16.MaxVal } );
|
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.",
|
Chat.MessageAll("{0}{1}%S's actions for the past &b{2} %Sseconds were undone.",
|
||||||
Server.FindColor(whoName), whoName, seconds);
|
Server.FindColor(whoName), whoName, seconds);
|
||||||
Server.s.Log(whoName + "'s actions for the past " + seconds + " seconds were undone.");
|
Server.s.Log(whoName + "'s actions for the past " + seconds + " seconds were undone.");
|
||||||
|
@ -37,64 +37,33 @@ namespace MCGalaxy.Drawing.Ops {
|
|||||||
public DateTime End = DateTime.MaxValue;
|
public DateTime End = DateTime.MaxValue;
|
||||||
|
|
||||||
internal Player who;
|
internal Player who;
|
||||||
internal Level saveLevel = null;
|
|
||||||
|
|
||||||
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
|
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
|
||||||
|
|
||||||
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
|
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
|
||||||
UndoCache cache = p.UndoBuffer;
|
UndoCache cache = p.UndoBuffer;
|
||||||
using (IDisposable locker = cache.ClearLock.AccquireReadLock()) {
|
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;
|
if (Min.X != ushort.MaxValue)
|
||||||
Vec3S32[] bounds = { Min, Max };
|
UndoFormat.DoUndoArea(p, target, Start, Min, Max, ref found);
|
||||||
UndoFormat.UndoPlayer(p, who.name.ToLower(), bounds, Start, ref foundUser);
|
else
|
||||||
|
UndoFormat.DoUndo(p, target, Start, End, ref found);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
void UndoBlocks(Player p, ref Level saveLvl) {
|
bool UndoBlocks(Player p) {
|
||||||
UndoCache cache = who.UndoBuffer;
|
UndoFormatArgs args = new UndoFormatArgs(p, Start);
|
||||||
UndoCacheNode node = cache.Tail;
|
UndoFormat format = new UndoFormatOnline(p.UndoBuffer);
|
||||||
if (node == null) return;
|
|
||||||
|
|
||||||
Vec3U16 min = (Vec3U16)Min, max = (Vec3U16)Max;
|
if (Min.X != ushort.MaxValue)
|
||||||
bool undoArea = Min.X != ushort.MaxValue;
|
UndoFormat.DoUndoArea(null, Min, Max, format, args);
|
||||||
Player.UndoPos Pos = default(Player.UndoPos);
|
else
|
||||||
int timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
|
UndoFormat.DoUndo(null, End, format, args);
|
||||||
|
return args.Stop;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,13 +75,16 @@ namespace MCGalaxy.Drawing.Ops {
|
|||||||
public DateTime Start = DateTime.MinValue;
|
public DateTime Start = DateTime.MinValue;
|
||||||
|
|
||||||
internal string whoName;
|
internal string whoName;
|
||||||
internal bool foundUser = false;
|
internal bool found = false;
|
||||||
|
|
||||||
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
|
public override long GetBlocksAffected(Level lvl, Vec3S32[] marks) { return -1; }
|
||||||
|
|
||||||
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
|
public override IEnumerable<DrawOpBlock> Perform(Vec3S32[] marks, Player p, Level lvl, Brush brush) {
|
||||||
Vec3S32[] bounds = { Min, Max };
|
string target = whoName.ToLower();
|
||||||
UndoFormat.UndoPlayer(p, whoName.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, DateTime.MaxValue, ref found);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,6 +541,7 @@
|
|||||||
<Compile Include="Player\PlayerInfo.cs" />
|
<Compile Include="Player\PlayerInfo.cs" />
|
||||||
<Compile Include="Player\TabList.cs" />
|
<Compile Include="Player\TabList.cs" />
|
||||||
<Compile Include="Player\Undo\UndoCache.cs" />
|
<Compile Include="Player\Undo\UndoCache.cs" />
|
||||||
|
<Compile Include="Player\Undo\UndoFormat.Helpers.cs" />
|
||||||
<Compile Include="Player\Undo\UndoFormatCBin.cs" />
|
<Compile Include="Player\Undo\UndoFormatCBin.cs" />
|
||||||
<Compile Include="Player\Undo\UndoFormatOnline.cs" />
|
<Compile Include="Player\Undo\UndoFormatOnline.cs" />
|
||||||
<Compile Include="Player\Warp.cs" />
|
<Compile Include="Player\Warp.cs" />
|
||||||
|
198
Player/Undo/UndoFormat.Helpers.cs
Normal file
198
Player/Undo/UndoFormat.Helpers.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ namespace MCGalaxy.Undo {
|
|||||||
|
|
||||||
/// <summary> Retrieves and saves undo data in a particular format. </summary>
|
/// <summary> Retrieves and saves undo data in a particular format. </summary>
|
||||||
/// <remarks> Note most formats only support retrieving undo data. </remarks>
|
/// <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";
|
protected const string undoDir = "extra/undo", prevUndoDir = "extra/undoPrevious";
|
||||||
public static UndoFormat TxtFormat = new UndoFormatText();
|
public static UndoFormat TxtFormat = new UndoFormatText();
|
||||||
@ -34,10 +34,12 @@ namespace MCGalaxy.Undo {
|
|||||||
|
|
||||||
protected abstract void Save(UndoCache buffer, string path);
|
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; }
|
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) {
|
public static void SaveUndo(Player p) {
|
||||||
if (p == null || p.UndoBuffer.Count < 1) return;
|
if (p == null || p.UndoBuffer.Count < 1) return;
|
||||||
|
|
||||||
@ -66,44 +68,30 @@ namespace MCGalaxy.Undo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UndoPlayer(Player p, string target, Vec3S32[] marks, DateTime start, ref bool FoundUser) {
|
/// <summary> Gets a list of all undo file names for the given player. </summary>
|
||||||
FilterEntries(p, undoDir, target, marks, start, false, ref FoundUser);
|
/// <remarks> This list is sorted, such that the first element is the
|
||||||
FilterEntries(p, prevUndoDir, target, marks, start, false, ref FoundUser);
|
/// 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);
|
||||||
|
|
||||||
public static void HighlightPlayer(Player p, string target, DateTime start, ref bool FoundUser) {
|
// Start from the last entry, because when undoing we want to start
|
||||||
FilterEntries(p, undoDir, target, null, start, true, ref FoundUser);
|
// with the most recent file first
|
||||||
FilterEntries(p, prevUndoDir, target, null, start, true, ref FoundUser);
|
for (int i = cur.Length - 1; i >= 0; i--) {
|
||||||
|
if (cur[i] == null) continue;
|
||||||
|
entries.Add(cur[i]);
|
||||||
}
|
}
|
||||||
|
for (int i = prev.Length - 1; i >= 0; i--) {
|
||||||
static void FilterEntries(Player p, string dir, string name, Vec3S32[] marks,
|
if (prev[i] == null) continue;
|
||||||
DateTime start, bool highlight, ref bool FoundUser) {
|
entries.Add(prev[i]);
|
||||||
string[] files = GetFiles(dir, name);
|
|
||||||
if (files == null || files.Length == 0) return;
|
|
||||||
UndoEntriesArgs args = new UndoEntriesArgs(p, start);
|
|
||||||
|
|
||||||
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;
|
return entries;
|
||||||
}
|
|
||||||
}
|
|
||||||
FoundUser = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static string[] GetFiles(string dir, string name) {
|
static string[] GetFiles(string dir, string name) {
|
||||||
string path = Path.Combine(dir, 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);
|
string[] files = Directory.GetFiles(path);
|
||||||
Array.Sort<string>(files, CompareFiles);
|
Array.Sort<string>(files, CompareFiles);
|
||||||
|
|
||||||
@ -111,6 +99,8 @@ namespace MCGalaxy.Undo {
|
|||||||
name = Path.GetFileName(files[i]);
|
name = Path.GetFileName(files[i]);
|
||||||
if (name.Length == 0 || name[0] < '0' || name[0] > '9')
|
if (name.Length == 0 || name[0] < '0' || name[0] > '9')
|
||||||
files[i] = null;
|
files[i] = null;
|
||||||
|
if (files[i] != null && GetFormat(name) == null)
|
||||||
|
files[i] = null;
|
||||||
}
|
}
|
||||||
return files;
|
return files;
|
||||||
}
|
}
|
||||||
@ -132,121 +122,43 @@ namespace MCGalaxy.Undo {
|
|||||||
return aNum.CompareTo(bNum);
|
return aNum.CompareTo(bNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void DoHighlight(Stream s, UndoFormat format, UndoEntriesArgs args) {
|
/// <summary> Creates the default base directories used to store undo data on disc. </summary>
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void CreateDefaultDirectories() {
|
public static void CreateDefaultDirectories() {
|
||||||
if (!Directory.Exists(undoDir))
|
if (!Directory.Exists(undoDir))
|
||||||
Directory.CreateDirectory(undoDir);
|
Directory.CreateDirectory(undoDir);
|
||||||
if (!Directory.Exists(prevUndoDir))
|
if (!Directory.Exists(prevUndoDir))
|
||||||
Directory.CreateDirectory(prevUndoDir);
|
Directory.CreateDirectory(prevUndoDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void UpgradePlayerUndoFiles(string name) {
|
|
||||||
UpgradeFiles(undoDir, name);
|
|
||||||
UpgradeFiles(prevUndoDir, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void UpgradeFiles(string dir, string name) {
|
/// <summary> Arguments provided to an UndoFormat for retrieving undo data. </summary>
|
||||||
string path = Path.Combine(dir, name);
|
public class UndoFormatArgs {
|
||||||
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++) {
|
/// <summary> Player associated with this undo, can be console or IRC. </summary>
|
||||||
path = files[i];
|
internal readonly Player Player;
|
||||||
if (!path.EndsWith(BinFormat.Ext) && !path.EndsWith(TxtFormat.Ext)) continue;
|
|
||||||
|
|
||||||
IEnumerable<Player.UndoPos> data = null;
|
/// <summary> Small work buffer, used to avoid memory allocations. </summary>
|
||||||
using (FileStream s = File.OpenRead(path)) {
|
internal byte[] Temp;
|
||||||
data = path.EndsWith(BinFormat.Ext)
|
|
||||||
? BinFormat.GetEntries(s, args) : TxtFormat.GetEntries(s, args);
|
|
||||||
|
|
||||||
foreach (Player.UndoPos pos in data)
|
/// <summary> Whether the format has finished retrieving undo data,
|
||||||
buffer.Add(pos);
|
/// due to finding an entry before the start range. </summary>
|
||||||
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;
|
|
||||||
public bool Stop;
|
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;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
@ -35,11 +35,11 @@ namespace MCGalaxy.Undo {
|
|||||||
throw new NotSupportedException("Non-optimised binary undo files have been deprecated");
|
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>();
|
List<ChunkHeader> list = new List<ChunkHeader>();
|
||||||
Player.UndoPos pos;
|
UndoFormatEntry pos;
|
||||||
bool super = args.Player == null || args.Player.ircNick != null;
|
bool super = args.Player == null || args.Player.ircNick != null;
|
||||||
DateTime start = args.StartRange;
|
DateTime start = args.Start;
|
||||||
|
|
||||||
ReadHeaders(list, s);
|
ReadHeaders(list, s);
|
||||||
for (int i = list.Count - 1; i >= 0; i--) {
|
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;
|
bool inRange = chunk.BaseTime.AddTicks(65536 * TimeSpan.TicksPerSecond) >= start;
|
||||||
if (!inRange) { args.Stop = true; yield break; }
|
if (!inRange) { args.Stop = true; yield break; }
|
||||||
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
|
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
|
||||||
pos.mapName = chunk.LevelName;
|
pos.LevelName = chunk.LevelName;
|
||||||
|
|
||||||
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
|
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
|
||||||
if (args.Temp == null)
|
if (args.Temp == null)
|
||||||
@ -58,16 +58,15 @@ namespace MCGalaxy.Undo {
|
|||||||
|
|
||||||
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
|
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
|
||||||
int offset = j * entrySize;
|
int offset = j * entrySize;
|
||||||
DateTime time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond);
|
pos.Time = chunk.BaseTime.AddTicks(U16(temp, offset + 0) * TimeSpan.TicksPerSecond);
|
||||||
if (time < start) { args.Stop = true; yield break; }
|
if (pos.Time < start) { args.Stop = true; yield break; }
|
||||||
pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
|
|
||||||
|
|
||||||
pos.x = U16(temp, offset + 2);
|
pos.X = U16(temp, offset + 2);
|
||||||
pos.y = U16(temp, offset + 4);
|
pos.Y = U16(temp, offset + 4);
|
||||||
pos.z = U16(temp, offset + 6);
|
pos.Z = U16(temp, offset + 6);
|
||||||
|
|
||||||
pos.type = temp[offset + 8]; pos.extType = temp[offset + 9];
|
pos.Block = temp[offset + 8]; pos.ExtBlock = temp[offset + 9];
|
||||||
pos.newtype = temp[offset + 10]; pos.newExtType = temp[offset + 11];
|
pos.NewBlock = temp[offset + 10]; pos.NewExtBlock = temp[offset + 11];
|
||||||
yield return pos;
|
yield return pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>();
|
List<ChunkHeader> list = new List<ChunkHeader>();
|
||||||
Player.UndoPos pos;
|
UndoFormatEntry pos;
|
||||||
UndoCacheItem item = default(UndoCacheItem);
|
UndoCacheItem item = default(UndoCacheItem);
|
||||||
bool super = args.Player == null || args.Player.ircNick != null;
|
bool super = args.Player == null || args.Player.ircNick != null;
|
||||||
DateTime start = args.StartRange;
|
DateTime start = args.Start;
|
||||||
|
|
||||||
ReadHeaders(list, s);
|
ReadHeaders(list, s);
|
||||||
for (int i = list.Count - 1; i >= 0; i--) {
|
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;
|
bool inRange = chunk.BaseTime.AddTicks((65536 >> 2) * TimeSpan.TicksPerSecond) >= start;
|
||||||
if (!inRange) { args.Stop = true; yield break; }
|
if (!inRange) { args.Stop = true; yield break; }
|
||||||
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
|
if (!super && !args.Player.level.name.CaselessEq(chunk.LevelName)) continue;
|
||||||
pos.mapName = chunk.LevelName;
|
pos.LevelName = chunk.LevelName;
|
||||||
|
|
||||||
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
|
s.Seek(chunk.DataPosition, SeekOrigin.Begin);
|
||||||
if (args.Temp == null)
|
if (args.Temp == null)
|
||||||
@ -123,19 +123,18 @@ namespace MCGalaxy.Undo {
|
|||||||
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
|
for (int j = chunk.Entries - 1; j >= 0; j-- ) {
|
||||||
int offset = j * entrySize;
|
int offset = j * entrySize;
|
||||||
item.Flags = U16(temp, offset + 0);
|
item.Flags = U16(temp, offset + 0);
|
||||||
DateTime time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
|
pos.Time = chunk.BaseTime.AddTicks((item.Flags & 0x3FFF) * TimeSpan.TicksPerSecond);
|
||||||
if (time < start) { args.Stop = true; yield break; }
|
if (pos.Time < start) { args.Stop = true; yield break; }
|
||||||
pos.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
|
|
||||||
|
|
||||||
int index = I32(temp, offset + 2);
|
int index = I32(temp, offset + 2);
|
||||||
pos.x = (ushort)(index % chunk.Width);
|
pos.X = (ushort)(index % chunk.Width);
|
||||||
pos.y = (ushort)((index / chunk.Width) / chunk.Length);
|
pos.Y = (ushort)((index / chunk.Width) / chunk.Length);
|
||||||
pos.z = (ushort)((index / chunk.Width) % chunk.Length);
|
pos.Z = (ushort)((index / chunk.Width) % chunk.Length);
|
||||||
|
|
||||||
item.Type = temp[offset + 6];
|
item.Type = temp[offset + 6];
|
||||||
item.NewType = temp[offset + 7];
|
item.NewType = temp[offset + 7];
|
||||||
item.GetBlock(out pos.type, out pos.extType);
|
item.GetBlock(out pos.Block, out pos.ExtBlock);
|
||||||
item.GetNewBlock(out pos.newtype, out pos.newExtType);
|
item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock);
|
||||||
yield return pos;
|
yield return pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,10 @@ namespace MCGalaxy.Undo {
|
|||||||
public sealed class UndoFormatOnline : UndoFormat {
|
public sealed class UndoFormatOnline : UndoFormat {
|
||||||
|
|
||||||
protected override string Ext { get { return null; } }
|
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) {
|
protected override void Save(List<Player.UndoPos> buffer, string path) {
|
||||||
throw new NotSupportedException("UndoFileOnline is read only.");
|
throw new NotSupportedException("UndoFileOnline is read only.");
|
||||||
@ -34,29 +37,29 @@ namespace MCGalaxy.Undo {
|
|||||||
throw new NotSupportedException("UndoFileOnline is read only.");
|
throw new NotSupportedException("UndoFileOnline is read only.");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
|
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
|
||||||
UndoCacheNode node = Cache.Tail;
|
UndoCacheNode node = cache.Tail;
|
||||||
if (node == null) yield break;
|
if (node == null) yield break;
|
||||||
|
|
||||||
Player.UndoPos pos;
|
UndoFormatEntry pos;
|
||||||
bool super = args.Player == null || args.Player.ircNick != null;
|
bool super = args.Player == null || args.Player.ircNick != null;
|
||||||
DateTime start = args.StartRange;
|
DateTime start = args.Start;
|
||||||
|
|
||||||
while (node != null) {
|
while (node != null) {
|
||||||
Level lvl = LevelInfo.FindExact(node.MapName);
|
Level lvl = LevelInfo.FindExact(node.MapName);
|
||||||
if (!super && !args.Player.level.name.CaselessEq(node.MapName)) continue;
|
if (!super && !args.Player.level.name.CaselessEq(node.MapName)) continue;
|
||||||
List<UndoCacheItem> items = node.Items;
|
List<UndoCacheItem> items = node.Items;
|
||||||
pos.mapName = node.MapName;
|
pos.LevelName = node.MapName;
|
||||||
|
|
||||||
for (int i = items.Count - 1; i >= 0; i--) {
|
for (int i = items.Count - 1; i >= 0; i--) {
|
||||||
UndoCacheItem item = items[i];
|
UndoCacheItem item = items[i];
|
||||||
DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond);
|
DateTime time = node.BaseTime.AddTicks(item.TimeDelta * TimeSpan.TicksPerSecond);
|
||||||
if (time < start) { args.Stop = true; yield break; }
|
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);
|
node.Unpack(item.Index, out pos.X, out pos.Y, out pos.Z);
|
||||||
item.GetBlock(out pos.type, out pos.extType);
|
item.GetBlock(out pos.Block, out pos.ExtBlock);
|
||||||
item.GetNewBlock(out pos.newtype, out pos.newExtType);
|
item.GetNewBlock(out pos.NewBlock, out pos.NewExtBlock);
|
||||||
yield return pos;
|
yield return pos;
|
||||||
}
|
}
|
||||||
node = node.Prev;
|
node = node.Prev;
|
||||||
|
@ -34,32 +34,31 @@ namespace MCGalaxy.Undo {
|
|||||||
throw new NotSupportedException("Text undo files have been deprecated");
|
throw new NotSupportedException("Text undo files have been deprecated");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<Player.UndoPos> GetEntries(Stream s, UndoEntriesArgs args) {
|
protected override IEnumerable<UndoFormatEntry> GetEntries(Stream s, UndoFormatArgs args) {
|
||||||
Player.UndoPos pos;
|
UndoFormatEntry pos;
|
||||||
pos.newExtType = 0; pos.extType = 0;
|
pos.NewExtBlock = 0; pos.ExtBlock = 0;
|
||||||
string[] lines = new StreamReader(s).ReadToEnd().Split(' ');
|
string[] lines = new StreamReader(s).ReadToEnd().Split(' ');
|
||||||
Player p = args.Player;
|
Player p = args.Player;
|
||||||
bool super = p == null || p.ircNick != null;
|
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 "".
|
// 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--) {
|
for (int i = (lines.Length - 1) / 7; i >= 0; i--) {
|
||||||
// line format: mapName x y z date oldblock newblock
|
// line format: mapName x y z date oldblock newblock
|
||||||
string timeRaw = lines[(i * 7) - 3].Replace('&', ' ');
|
string timeRaw = lines[(i * 7) - 3].Replace('&', ' ');
|
||||||
DateTime time = DateTime.Parse(timeRaw, CultureInfo.InvariantCulture);
|
pos.Time = DateTime.Parse(timeRaw, CultureInfo.InvariantCulture);
|
||||||
if (time < start) { args.Stop = true; yield break; }
|
if (pos.Time < start) { args.Stop = true; yield break; }
|
||||||
pos.timeDelta = (int)time.Subtract(Server.StartTimeLocal).TotalSeconds;
|
|
||||||
|
|
||||||
string map = lines[(i * 7) - 7];
|
string map = lines[(i * 7) - 7];
|
||||||
if (!super && !p.level.name.CaselessEq(map)) continue;
|
if (!super && !p.level.name.CaselessEq(map)) continue;
|
||||||
pos.mapName = map;
|
pos.LevelName = map;
|
||||||
|
|
||||||
pos.x = Convert.ToUInt16(lines[(i * 7) - 6]);
|
pos.X = Convert.ToUInt16(lines[(i * 7) - 6]);
|
||||||
pos.y = Convert.ToUInt16(lines[(i * 7) - 5]);
|
pos.Y = Convert.ToUInt16(lines[(i * 7) - 5]);
|
||||||
pos.z = Convert.ToUInt16(lines[(i * 7) - 4]);
|
pos.Z = Convert.ToUInt16(lines[(i * 7) - 4]);
|
||||||
|
|
||||||
pos.newtype = Convert.ToByte(lines[(i * 7) - 1]);
|
pos.NewBlock = Convert.ToByte(lines[(i * 7) - 1]);
|
||||||
pos.type = Convert.ToByte(lines[(i * 7) - 2]);
|
pos.Block = Convert.ToByte(lines[(i * 7) - 2]);
|
||||||
yield return pos;
|
yield return pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user