Merge branch 'UndoCleanup'

This commit is contained in:
UnknownShadow200 2016-02-25 22:45:13 +11:00
commit f8e83786c2
10 changed files with 268 additions and 86 deletions

View File

@ -16,6 +16,7 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using MCGalaxy.Util; using MCGalaxy.Util;
@ -52,7 +53,7 @@ namespace MCGalaxy.Commands {
Player who = PlayerInfo.Find(name); Player who = PlayerInfo.Find(name);
if (who != null) { if (who != null) {
FoundUser = true; FoundUser = true;
HighlightOnline(p, seconds, who); PerformHighlight(p, seconds, who.UndoBuffer);
} }
UndoFile.HighlightPlayer(p, name.ToLower(), seconds, ref FoundUser); UndoFile.HighlightPlayer(p, name.ToLower(), seconds, ref FoundUser);
@ -64,24 +65,31 @@ namespace MCGalaxy.Commands {
} }
} }
static void HighlightOnline(Player p, long seconds, Player who) { static void PerformHighlight(Player p, long seconds, UndoCache cache) {
for (int i = who.UndoBuffer.Count - 1; i >= 0; --i) { UndoCacheNode node = cache.Tail;
try { if (node == null) return;
Player.UndoPos undo = who.UndoBuffer[i];
Level foundLevel = LevelInfo.FindExact(undo.mapName);
if (foundLevel != p.level) continue;
byte b = foundLevel.GetTile(undo.x, undo.y, undo.z); while (node != null) {
DateTime time = Server.StartTime.AddSeconds(undo.timeDelta + seconds); Level lvl = LevelInfo.FindExact(node.MapName);
if (time < DateTime.UtcNow) break; if (lvl != p.level) continue;
List<UndoCacheItem> items = node.Items;
if (b == undo.newtype || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava) { for (int i = items.Count - 1; i >= 0; i--) {
UndoCacheItem item = items[i];
ushort x, y, z;
node.Unpack(item.Index, out x, out y, out z);
DateTime time = node.BaseTime.AddSeconds(item.TimeDelta + seconds);
if (time < DateTime.UtcNow) return;
byte b = lvl.GetTile(x, y, z);
if (b == item.NewType || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava) {
if (b == Block.air || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava) if (b == Block.air || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava)
p.SendBlockchange(undo.x, undo.y, undo.z, Block.red); p.SendBlockchange(x, y, z, Block.red);
else else
p.SendBlockchange(undo.x, undo.y, undo.z, Block.green); p.SendBlockchange(x, y, z, Block.green);
} }
} catch { } }
node = node.Prev;
} }
} }

View File

@ -16,6 +16,8 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Collections.Generic;
using MCGalaxy.Util;
namespace MCGalaxy.Commands { namespace MCGalaxy.Commands {
@ -30,20 +32,31 @@ namespace MCGalaxy.Commands {
public override void Use(Player p, string message) { public override void Use(Player p, string message) {
if (message != "") { Help(p); return; } if (message != "") { Help(p); return; }
PerformRedo(p, p.RedoBuffer);
for (int i = p.RedoBuffer.Count - 1; i >= 0; i--) { Player.SendMessage(p, "Redo performed.");
Player.UndoPos Pos = p.RedoBuffer[i];
Level lvl = LevelInfo.FindExact(Pos.mapName);
if (lvl == null)
continue;
byte type = lvl.GetTile(Pos.x, Pos.y, Pos.z), extType = 0;
if (type == Block.custom_block)
extType = lvl.GetExtTile(Pos.x, Pos.y, Pos.z);
lvl.Blockchange(p, Pos.x, Pos.y, Pos.z, Pos.type, Pos.extType);
} }
Player.SendMessage(p, "Redo performed."); static void PerformRedo(Player p, UndoCache cache) {
UndoCacheNode node = cache.Tail;
if (node == null) return;
while (node != null) {
Level lvl = LevelInfo.FindExact(node.MapName);
if (lvl == null) continue;
List<UndoCacheItem> items = node.Items;
for (int i = items.Count - 1; i >= 0; i--) {
UndoCacheItem item = items[i];
ushort x, y, z;
node.Unpack(item.Index, out x, out y, out z);
byte type = lvl.GetTile(x, y, z), extType = 0;
if (type == Block.custom_block)
extType = lvl.GetExtTile(x, y, z);
lvl.Blockchange(p, x, y, z, item.Type, item.ExtType);
}
node = node.Prev;
}
} }
public override void Help(Player p) { public override void Help(Player p) {

View File

@ -16,8 +16,7 @@
permissions and limitations under the Licenses. permissions and limitations under the Licenses.
*/ */
using System; using System;
using System.Globalization; using System.Collections.Generic;
using System.IO;
using MCGalaxy.Util; using MCGalaxy.Util;
namespace MCGalaxy.Commands namespace MCGalaxy.Commands
@ -89,12 +88,7 @@ namespace MCGalaxy.Commands
} }
Level saveLevel = null; Level saveLevel = null;
for (int i = who.UndoBuffer.Count - 1; i >= 0; --i) { PerformUndo(p, seconds, who.UndoBuffer, ref saveLevel);
try {
Player.UndoPos Pos = who.UndoBuffer[i];
if (!CheckBlockPlayer(p, seconds, Pos, ref saveLevel)) break;
} catch { }
}
bool foundUser = false; bool foundUser = false;
UndoFile.UndoPlayer(p, whoName.ToLower(), seconds, ref foundUser); UndoFile.UndoPlayer(p, whoName.ToLower(), seconds, ref foundUser);
@ -159,24 +153,41 @@ namespace MCGalaxy.Commands
p.level.Save(true); p.level.Save(true);
} }
bool CheckBlockPlayer(Player p, long seconds, Player.UndoPos undo, ref Level saveLevel) { static void PerformUndo(Player p, long seconds, UndoCache cache, ref Level saveLvl) {
Level lvl = LevelInfo.FindExact(undo.mapName); UndoCacheNode node = cache.Tail;
saveLevel = lvl; if (node == null) return;
byte b = lvl.GetTile(undo.x, undo.y, undo.z);
DateTime time = Server.StartTime.AddSeconds(undo.timeDelta + seconds);
if (time < DateTime.UtcNow) return false;
if (b == undo.newtype || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava) { while (node != null) {
undo.newtype = undo.type; undo.newExtType = undo.extType; Level lvl = LevelInfo.FindExact(node.MapName);
if (lvl == null) continue;
saveLvl = lvl;
List<UndoCacheItem> items = node.Items;
for (int i = items.Count - 1; i >= 0; i--) {
UndoCacheItem item = items[i];
ushort x, y, z;
node.Unpack(item.Index, out x, out y, out z);
DateTime time = node.BaseTime.AddSeconds(item.TimeDelta + seconds);
if (time < DateTime.UtcNow) return;
byte b = lvl.GetTile(x, y, z);
if (b == item.NewType || Block.Convert(b) == Block.water || Block.Convert(b) == Block.lava) {
Player.UndoPos uP = default(Player.UndoPos);
uP.newtype = item.Type; uP.newExtType = item.ExtType;
byte extType = 0; byte extType = 0;
if (b == Block.custom_block) if (b == Block.custom_block) extType = lvl.GetExtTile(x, y, z);
extType = lvl.GetExtTile(undo.x, undo.y, undo.z); lvl.Blockchange(p, x, y, z, item.Type, item.ExtType);
lvl.Blockchange(p, undo.x, undo.y, undo.z, undo.type, undo.extType); uP.type = b; uP.extType = extType;
undo.type = b; undo.extType = extType; uP.x = x; uP.y = y; uP.z = z;
if (p != null) p.RedoBuffer.Add(undo); uP.mapName = node.MapName;
time = node.BaseTime.AddSeconds(item.TimeDelta);
uP.timeDelta = (int)time.Subtract(Server.StartTime).TotalSeconds;
if (p != null) p.RedoBuffer.Add(lvl, uP);
}
}
node = node.Prev;
} }
return true;
} }
bool CheckBlockPhysics(Player p, long seconds, int i, Level.UndoPos undo) { bool CheckBlockPhysics(Player p, long seconds, int i, Level.UndoPos undo) {

View File

@ -160,7 +160,7 @@ namespace MCGalaxy {
Pos.type = oldType; Pos.extType = oldExtType; Pos.type = oldType; Pos.extType = oldExtType;
Pos.newtype = type; Pos.newExtType = extType; Pos.newtype = type; Pos.newExtType = extType;
Pos.timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; Pos.timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
p.UndoBuffer.Add(Pos); p.UndoBuffer.Add(this, Pos);
} }
bool CheckTNTWarsChange(Player p, ushort x, ushort y, ushort z, ref byte type) { bool CheckTNTWarsChange(Player p, ushort x, ushort y, ushort z, ref byte type) {
@ -328,7 +328,7 @@ namespace MCGalaxy {
Pos.type = b; Pos.extType = extB; Pos.type = b; Pos.extType = extB;
Pos.newtype = type; Pos.newExtType = extType; Pos.newtype = type; Pos.newExtType = extType;
Pos.timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds; Pos.timeDelta = (int)DateTime.UtcNow.Subtract(Server.StartTime).TotalSeconds;
p.UndoBuffer.Add(Pos); p.UndoBuffer.Add(this, Pos);
errorLocation = "Setting tile"; errorLocation = "Setting tile";
p.loginBlocks++; p.loginBlocks++;

View File

@ -443,6 +443,7 @@
<Compile Include="Player\Group\GroupProperties.cs" /> <Compile Include="Player\Group\GroupProperties.cs" />
<Compile Include="Player\Player.Handlers.cs" /> <Compile Include="Player\Player.Handlers.cs" />
<Compile Include="Player\PlayerInfo.cs" /> <Compile Include="Player\PlayerInfo.cs" />
<Compile Include="Player\Undo\UndoCache.cs" />
<Compile Include="Player\Waypoint.cs" /> <Compile Include="Player\Waypoint.cs" />
<Compile Include="Player\Player.Timers.cs" /> <Compile Include="Player\Player.Timers.cs" />
<Compile Include="Player\Undo\UndoFile.cs" /> <Compile Include="Player\Undo\UndoFile.cs" />

View File

@ -235,8 +235,8 @@ namespace MCGalaxy {
//Undo //Undo
public struct UndoPos { public ushort x, y, z; public byte type, extType, newtype, newExtType; public string mapName; public int timeDelta; } public struct UndoPos { public ushort x, y, z; public byte type, extType, newtype, newExtType; public string mapName; public int timeDelta; }
public List<UndoPos> UndoBuffer = new List<UndoPos>(); public UndoCache UndoBuffer = new UndoCache();
public List<UndoPos> RedoBuffer = new List<UndoPos>(); public UndoCache RedoBuffer = new UndoCache();
public bool showPortals = false; public bool showPortals = false;

111
Player/Undo/UndoCache.cs Normal file
View File

@ -0,0 +1,111 @@
/*
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.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace MCGalaxy.Util {
public sealed class UndoCache {
/// <summary> The oldest/first node in the cache. </summary>
public UndoCacheNode Head;
/// <summary> The newest/last node in the cache. </summary>
public UndoCacheNode Tail;
/// <summary> Total number of items in the cache. </summary>
public volatile int Count;
/// <summary> Appends an item to the cache. </summary>
public void Add(Level lvl, Player.UndoPos item) {
DateTime time = Server.StartTime.AddSeconds(item.timeDelta);
if (Head == null) {
Head = UndoCacheNode.Make(lvl, time);
Tail = Head;
}
if (lvl.name != Tail.MapName || lvl.Width != Tail.Width || lvl.Height != Tail.Height ||
lvl.Length != Tail.Length || Math.Abs((time - Tail.BaseTime).TotalSeconds) > 32767) {
UndoCacheNode node = UndoCacheNode.Make(lvl, time);
Tail.Next = node; node.Prev = Tail;
Tail = node;
}
short timeDiff = (short)(time - Tail.BaseTime).TotalSeconds;
Tail.Items.Add(UndoCacheItem.Make(Tail, timeDiff, ref item));
Count++;
}
/// <summary> Removes all items from the cache and resets the state to default. </summary>
public void Clear() {
Count = 0;
if( Head == null ) return;
UndoCacheNode node = Head;
while( node != null ) {
node.Items.Clear();
node = node.Next;
}
Head = null; Tail = null;
}
}
public sealed class UndoCacheNode {
public string MapName;
public int Width, Height, Length;
public DateTime BaseTime;
public UndoCacheNode Prev, Next;
public List<UndoCacheItem> Items = new List<UndoCacheItem>();
public static UndoCacheNode Make(Level lvl, DateTime time) {
UndoCacheNode node = new UndoCacheNode();
node.MapName = lvl.name;
node.Width = lvl.Width; node.Height = lvl.Height; node.Length = lvl.Length;
node.BaseTime = time;
return node;
}
public void Unpack(int index, out ushort x, out ushort y, out ushort z) {
x = (ushort)(index % Width);
y = (ushort)(index / (Width * Length));
z = (ushort)((index / Width) % Length);
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UndoCacheItem {
public int Index;
public byte Type, ExtType;
public byte NewType, NewExtType;
public short TimeDelta;
public static UndoCacheItem Make(UndoCacheNode node, short timeDelta, ref Player.UndoPos pos) {
UndoCacheItem item = default(UndoCacheItem);
item.Index = pos.x + node.Width * (pos.z + node.Length * pos.y);
item.Type = pos.type; item.ExtType = pos.extType;
item.NewType = pos.newtype; item.NewExtType = pos.newExtType;
item.TimeDelta = timeDelta;
return item;
}
}
}

View File

@ -30,6 +30,8 @@ namespace MCGalaxy.Util {
protected abstract void SaveUndoData(List<Player.UndoPos> buffer, string path); protected abstract void SaveUndoData(List<Player.UndoPos> buffer, string path);
protected abstract void SaveUndoData(UndoCache buffer, string path);
protected abstract void ReadUndoData(List<Player.UndoPos> buffer, string path); protected abstract void ReadUndoData(List<Player.UndoPos> buffer, string path);
protected abstract bool UndoEntry(Player p, string path, ref byte[] temp, long seconds); protected abstract bool UndoEntry(Player p, string path, ref byte[] temp, long seconds);
@ -39,7 +41,7 @@ namespace MCGalaxy.Util {
protected abstract string Extension { get; } protected abstract string Extension { get; }
public static void SaveUndo(Player p) { public static void SaveUndo(Player p) {
if( p == null || p.UndoBuffer == null || p.UndoBuffer.Count < 1) return; if( p == null || p.UndoBuffer.Count < 1) return;
CreateDefaultDirectories(); CreateDefaultDirectories();
if (Directory.GetDirectories(undoDir).Length >= Server.totalUndo) { if (Directory.GetDirectories(undoDir).Length >= Server.totalUndo) {
@ -54,7 +56,7 @@ namespace MCGalaxy.Util {
int numFiles = Directory.GetFiles(playerDir).Length; int numFiles = Directory.GetFiles(playerDir).Length;
string path = Path.Combine(playerDir, numFiles + NewFormat.Extension); string path = Path.Combine(playerDir, numFiles + NewFormat.Extension);
NewFormat.SaveUndoData(p.UndoBuffer.ToList(), path); NewFormat.SaveUndoData(p.UndoBuffer, path);
} }
public static void UndoPlayer(Player p, string targetName, long seconds, ref bool FoundUser) { public static void UndoPlayer(Player p, string targetName, long seconds, ref bool FoundUser) {

View File

@ -32,24 +32,57 @@ namespace MCGalaxy.Util {
using (FileStream fs = File.Create(path)) { using (FileStream fs = File.Create(path)) {
BinaryWriter w = new BinaryWriter(fs); BinaryWriter w = new BinaryWriter(fs);
long entriesPos = 0; long entriesPos = 0;
ChunkHeader lastChunk = default(ChunkHeader); ChunkHeader last = default(ChunkHeader);
foreach (Player.UndoPos uP in buffer) { foreach (Player.UndoPos uP in buffer) {
DateTime time = Server.StartTime.AddSeconds(uP.timeDelta); DateTime time = Server.StartTime.AddSeconds(uP.timeDelta);
int timeDiff = (int)(time - lastChunk.BaseTime).TotalSeconds; int timeDiff = (int)(time - last.BaseTime).TotalSeconds;
if (lastChunk.LevelName != uP.mapName || timeDiff > 65535 || lastChunk.Entries == ushort.MaxValue) { if (last.LevelName != uP.mapName || timeDiff > 65535 || last.Entries == ushort.MaxValue) {
WriteChunkEntries(w, lastChunk.Entries, entriesPos); WriteChunkEntries(w, last.Entries, entriesPos);
lastChunk = WriteEmptyChunk(w, uP, ref entriesPos); last = WriteEmptyChunk(w, uP.mapName, time, ref entriesPos);
} }
w.Write((ushort)timeDiff); w.Write((ushort)timeDiff);
w.Write(uP.x); w.Write(uP.y); w.Write(uP.z); w.Write(uP.x); w.Write(uP.y); w.Write(uP.z);
w.Write(uP.type); w.Write(uP.extType); w.Write(uP.type); w.Write(uP.extType);
w.Write(uP.newtype); w.Write(uP.newExtType); w.Write(uP.newtype); w.Write(uP.newExtType);
lastChunk.Entries++; last.Entries++;
}
if (last.Entries > 0)
WriteChunkEntries(w, last.Entries, entriesPos);
}
}
protected override void SaveUndoData(UndoCache buffer, string path) {
using (FileStream fs = File.Create(path)) {
BinaryWriter w = new BinaryWriter(fs);
long entriesPos = 0;
ChunkHeader last = default(ChunkHeader);
UndoCacheNode node = buffer.Tail;
while (node != null) {
List<UndoCacheItem> items = node.Items;
for (int i = 0; i < items.Count; i++) {
UndoCacheItem uP = items[i];
DateTime time = node.BaseTime.AddSeconds(uP.TimeDelta);
int timeDiff = (int)(time - last.BaseTime).TotalSeconds;
if (last.LevelName != node.MapName || timeDiff > 65535 || last.Entries == ushort.MaxValue) {
WriteChunkEntries(w, last.Entries, entriesPos);
last = WriteEmptyChunk(w, node.MapName, time, ref entriesPos);
}
ushort x, y, z;
node.Unpack(uP.Index, out x, out y, out z);
w.Write((ushort)timeDiff);
w.Write(x); w.Write(y); w.Write(z);
w.Write(uP.Type); w.Write(uP.ExtType);
w.Write(uP.NewType); w.Write(uP.NewExtType);
last.Entries++;
}
if (last.Entries > 0)
WriteChunkEntries(w, last.Entries, entriesPos);
node = node.Prev;
} }
if (lastChunk.Entries > 0)
WriteChunkEntries(w, lastChunk.Entries, entriesPos);
} }
} }
@ -117,7 +150,7 @@ namespace MCGalaxy.Util {
Pos.newtype = oldType; Pos.newExtType = oldExtType; Pos.newtype = oldType; Pos.newExtType = oldExtType;
Pos.extType = newExtType; Pos.timeDelta = timeDelta; Pos.extType = newExtType; Pos.timeDelta = timeDelta;
lvl.Blockchange(p, Pos.x, Pos.y, Pos.z, Pos.newtype, Pos.newExtType); lvl.Blockchange(p, Pos.x, Pos.y, Pos.z, Pos.newtype, Pos.newExtType);
if (p != null) p.RedoBuffer.Add(Pos); if (p != null) p.RedoBuffer.Add(lvl, Pos);
} }
} }
} }
@ -216,9 +249,8 @@ namespace MCGalaxy.Util {
w.BaseStream.Seek(curPos, SeekOrigin.Begin); w.BaseStream.Seek(curPos, SeekOrigin.Begin);
} }
static ChunkHeader WriteEmptyChunk(BinaryWriter w, Player.UndoPos uP, ref long entriesPos) { static ChunkHeader WriteEmptyChunk(BinaryWriter w, string mapName, DateTime time, ref long entriesPos) {
DateTime time = Server.StartTime.AddSeconds(uP.timeDelta); byte[] mapBytes = Encoding.UTF8.GetBytes(mapName);
byte[] mapBytes = Encoding.UTF8.GetBytes(uP.mapName);
w.Write((ushort)mapBytes.Length); w.Write((ushort)mapBytes.Length);
w.Write(mapBytes); w.Write(mapBytes);
w.Write(time.ToLocalTime().Ticks); w.Write(time.ToLocalTime().Ticks);
@ -226,7 +258,7 @@ namespace MCGalaxy.Util {
entriesPos = w.BaseStream.Position; entriesPos = w.BaseStream.Position;
w.Write((ushort)0); w.Write((ushort)0);
ChunkHeader header = default(ChunkHeader); ChunkHeader header = default(ChunkHeader);
header.LevelName = uP.mapName; header.BaseTime = time; header.LevelName = mapName; header.BaseTime = time;
return header; return header;
} }
} }

View File

@ -39,6 +39,10 @@ namespace MCGalaxy.Util {
} }
} }
protected override void SaveUndoData(UndoCache buffer, string path) {
throw new NotImplementedException("The .txt based undo files are deprecated.");
}
protected override void ReadUndoData(List<Player.UndoPos> buffer, string path) { protected override void ReadUndoData(List<Player.UndoPos> buffer, string path) {
Player.UndoPos Pos; Player.UndoPos Pos;
Pos.extType = 0; Pos.newExtType = 0; Pos.extType = 0; Pos.newExtType = 0;
@ -74,14 +78,14 @@ namespace MCGalaxy.Util {
try { try {
// line format: mapName x y z date oldblock newblock // line format: mapName x y z date oldblock newblock
if (!InTime(lines[(i * 7) - 3], seconds)) return false; if (!InTime(lines[(i * 7) - 3], seconds)) return false;
Level foundLevel = LevelInfo.FindExact(lines[(i * 7) - 7]); Level lvl = LevelInfo.FindExact(lines[(i * 7) - 7]);
if (foundLevel == null) continue; if (lvl == null) continue;
Pos.mapName = foundLevel.name; Pos.mapName = lvl.name;
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.type = foundLevel.GetTile(Pos.x, Pos.y, Pos.z); Pos.type = lvl.GetTile(Pos.x, Pos.y, Pos.z);
if (Pos.type == Convert.ToByte(lines[(i * 7) - 1]) || Block.Convert(Pos.type) == Block.water || if (Pos.type == Convert.ToByte(lines[(i * 7) - 1]) || Block.Convert(Pos.type) == Block.water ||
Block.Convert(Pos.type) == Block.lava || Pos.type == Block.grass) { Block.Convert(Pos.type) == Block.lava || Pos.type == Block.grass) {
@ -89,8 +93,8 @@ namespace MCGalaxy.Util {
Pos.newtype = Convert.ToByte(lines[(i * 7) - 2]); Pos.newtype = Convert.ToByte(lines[(i * 7) - 2]);
Pos.timeDelta = timeDelta; Pos.timeDelta = timeDelta;
foundLevel.Blockchange(p, Pos.x, Pos.y, Pos.z, Pos.newtype, 0); lvl.Blockchange(p, Pos.x, Pos.y, Pos.z, Pos.newtype, 0);
if (p != null) p.RedoBuffer.Add(Pos); if (p != null) p.RedoBuffer.Add(lvl, Pos);
} }
} catch { } catch {
} }