mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-23 04:32:50 -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);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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"; } }
|
||||
|
@ -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" />
|
||||
|
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>
|
||||
/// <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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user