mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-24 05:03:34 -04:00
Reduce memory usage of CopyState by around ~43%, fixes #306.
This commit is contained in:
parent
02e694649c
commit
f5061dec9d
@ -110,22 +110,19 @@ namespace MCGalaxy.Commands.Building {
|
||||
for (ushort zz = minZ; zz <= maxZ; ++zz)
|
||||
for (ushort xx = minX; xx <= maxX; ++xx)
|
||||
{
|
||||
byte b = p.level.GetTile(xx, yy, zz), extB = 0;
|
||||
if (!p.group.CanModify[b]) { index++; continue; }
|
||||
if (b == Block.custom_block)
|
||||
extB = p.level.GetExtTile(xx, yy, zz);
|
||||
block = p.level.GetExtBlock(xx, yy, zz);
|
||||
if (!p.group.CanModify[block.BlockID]) { index++; continue; } // TODO: will need to fix this when extblock permissions
|
||||
|
||||
if (b != Block.air || cState.PasteAir)
|
||||
if (block.BlockID != Block.air || cState.PasteAir)
|
||||
cState.UsedBlocks++;
|
||||
cState.Blocks[index] = b;
|
||||
cState.ExtBlocks[index] = extB;
|
||||
cState.Set(block, index);
|
||||
index++;
|
||||
}
|
||||
|
||||
if (cState.UsedBlocks > p.group.maxBlocks) {
|
||||
Player.Message(p, "You tried to copy {0} blocks. You cannot copy more than {1} blocks.",
|
||||
cState.UsedBlocks, p.group.maxBlocks);
|
||||
cState.Blocks = null; cState.ExtBlocks = null; cState = null;
|
||||
cState.Clear(); cState = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -189,7 +186,7 @@ namespace MCGalaxy.Commands.Building {
|
||||
using (FileStream fs = File.OpenRead(path))
|
||||
using (GZipStream gs = new GZipStream(fs, CompressionMode.Decompress))
|
||||
{
|
||||
CopyState state = new CopyState(0, 0, 0, 0, 0, 0, null, null);
|
||||
CopyState state = new CopyState(0, 0, 0, 0, 0, 0);
|
||||
if (path.CaselessEnds(".cpb")) {
|
||||
state.LoadFrom(gs);
|
||||
} else {
|
||||
|
@ -22,7 +22,6 @@ using MCGalaxy.Drawing.Ops;
|
||||
namespace MCGalaxy.Drawing.Brushes {
|
||||
public sealed class SimplePasteBrush : Brush {
|
||||
readonly CopyState state;
|
||||
int index;
|
||||
|
||||
public SimplePasteBrush(CopyState state) { this.state = state; }
|
||||
|
||||
@ -41,18 +40,12 @@ namespace MCGalaxy.Drawing.Brushes {
|
||||
int z = (op.Coords.Z - op.Min.Z) % state.Length;
|
||||
if (z < 0) z += state.Length;
|
||||
|
||||
index = state.GetIndex(x, y, z);
|
||||
ExtBlock block;
|
||||
block.BlockID = state.Blocks[index];
|
||||
block.ExtID = state.ExtBlocks[index];
|
||||
return block;
|
||||
return state.Get(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PasteBrush : Brush {
|
||||
readonly CopyState state;
|
||||
int index;
|
||||
|
||||
public ExtBlock[] Include, Exclude;
|
||||
|
||||
public PasteBrush(CopyState state) { this.state = state; }
|
||||
@ -72,11 +65,7 @@ namespace MCGalaxy.Drawing.Brushes {
|
||||
int z = (op.Coords.Z - op.Min.Z) % state.Length;
|
||||
if (z < 0) z += state.Length;
|
||||
|
||||
index = state.GetIndex(x, y, z);
|
||||
ExtBlock block;
|
||||
block.BlockID = state.Blocks[index];
|
||||
block.ExtID = state.ExtBlocks[index];
|
||||
|
||||
ExtBlock block = state.Get(x, y, z);
|
||||
if (Exclude != null) {
|
||||
for (int i = 0; i < Exclude.Length; i++) {
|
||||
if (block == Exclude[i]) return ExtBlock.Invalid;
|
||||
|
@ -23,7 +23,7 @@ namespace MCGalaxy.Drawing {
|
||||
|
||||
public sealed class CopyState {
|
||||
|
||||
public byte[] Blocks, ExtBlocks;
|
||||
byte[] raw, isExt;
|
||||
public int X, Y, Z;
|
||||
public int OriginX, OriginY, OriginZ;
|
||||
public int Width, Height, Length;
|
||||
@ -31,34 +31,32 @@ namespace MCGalaxy.Drawing {
|
||||
public int UsedBlocks;
|
||||
public Vec3S32 Offset;
|
||||
|
||||
const int identifier = 0x434F5059; // 'COPY'
|
||||
const int identifierC = 0x434F5043; // 'COPC' (Copy compressed)
|
||||
internal int OppositeOriginX { get { return OriginX == X ? X + Width - 1 : X; } }
|
||||
internal int OppositeOriginY { get { return OriginY == Y ? Y + Height - 1 : Y; } }
|
||||
internal int OppositeOriginZ { get { return OriginZ == Z ? Z + Length - 1 : Z; } }
|
||||
|
||||
public int Volume {
|
||||
get { return Width * Height * Length; }
|
||||
}
|
||||
|
||||
public CopyState(int x, int y, int z, int width, int height, int length,
|
||||
byte[] blocks, byte[] extBlocks) {
|
||||
X = x; Y = y; Z = z;
|
||||
Width = width; Height = height; Length = length;
|
||||
Blocks = blocks;
|
||||
ExtBlocks = extBlocks;
|
||||
UsedBlocks = Volume;
|
||||
public CopyState(int x, int y, int z, int width, int height, int length) {
|
||||
Init(x, y, z, width, height, length);
|
||||
}
|
||||
|
||||
public CopyState(int x, int y, int z, int width, int height, int length)
|
||||
: this(x, y, z, width, height, length, null, null) {
|
||||
Blocks = new byte[width * height * length];
|
||||
ExtBlocks = new byte[width * height * length];
|
||||
void Init(int x, int y, int z, int width, int height, int length) {
|
||||
X = x; Y = y; Z = z;
|
||||
Width = width; Height = height; Length = length;
|
||||
raw = new byte[Volume];
|
||||
isExt = new byte[(Volume + 7) / 8]; // ceiling divide by 8, as 8 bits fit into 1 byte
|
||||
UsedBlocks = Volume;
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
Blocks = null;
|
||||
ExtBlocks = null;
|
||||
raw = null;
|
||||
isExt = null;
|
||||
}
|
||||
|
||||
|
||||
public void GetCoords(int index, out ushort x, out ushort y, out ushort z) {
|
||||
y = (ushort)(index / Width / Length);
|
||||
index -= y * Width * Length;
|
||||
@ -71,47 +69,74 @@ namespace MCGalaxy.Drawing {
|
||||
return (y * Length + z) * Width + x;
|
||||
}
|
||||
|
||||
public void Set(byte type, byte extType, int x, int y, int z) {
|
||||
Blocks[(y * Length + z) * Width + x] = type;
|
||||
ExtBlocks[(y * Length + z) * Width + x] = extType;
|
||||
public ExtBlock Get(int index) {
|
||||
return ExtBlock.FromRaw(raw[index],
|
||||
(isExt[index >> 3] & (1 << (index & 0x07))) != 0);
|
||||
}
|
||||
|
||||
public ExtBlock Get(int x, int y, int z) {
|
||||
int index = (y * Length + z) * Width + x;
|
||||
return ExtBlock.FromRaw(raw[index],
|
||||
(isExt[index >> 3] & (1 << (index & 0x07))) != 0);
|
||||
}
|
||||
|
||||
public void Set(ExtBlock block, int index) {
|
||||
isExt[index >> 3] &= (byte)~(1 << (index & 0x07));
|
||||
|
||||
if (block.BlockID == Block.custom_block) {
|
||||
raw[index] = block.ExtID;
|
||||
isExt[index >> 3] |= (byte)(1 << (index & 0x07));
|
||||
} else {
|
||||
raw[index] = block.BlockID;
|
||||
}
|
||||
}
|
||||
|
||||
public void Set(ExtBlock block, int x, int y, int z) {
|
||||
int index = (y * Length + z) * Width + x;
|
||||
isExt[index >> 3] &= (byte)~(1 << (index & 0x07));
|
||||
|
||||
if (block.BlockID == Block.custom_block) {
|
||||
raw[index] = block.ExtID;
|
||||
isExt[index >> 3] |= (byte)(1 << (index & 0x07));
|
||||
} else {
|
||||
raw[index] = block.BlockID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const int identifier1 = 0x434F5059; // version 1, 'COPY' (copy)
|
||||
const int identifier2 = 0x434F5043; // 'COPC' (copy compressed)
|
||||
const int identifier3 = 0x434F504F; // 'COPO' (copy optimised)
|
||||
|
||||
/// <summary> Saves this copy state to the given stream. </summary>
|
||||
public void SaveTo(Stream stream) {
|
||||
BinaryWriter w = new BinaryWriter(stream);
|
||||
w.Write(identifierC);
|
||||
w.Write(identifier3);
|
||||
w.Write(X); w.Write(Y); w.Write(Z);
|
||||
w.Write(Width); w.Write(Height); w.Write(Length);
|
||||
|
||||
byte[] blocks = Blocks.GZip();
|
||||
w.Write(blocks.Length);
|
||||
w.Write(blocks);
|
||||
blocks = ExtBlocks.GZip();
|
||||
w.Write(blocks.Length);
|
||||
w.Write(blocks);
|
||||
byte[] data = raw.GZip();
|
||||
w.Write(data.Length);
|
||||
w.Write(data);
|
||||
data = isExt.GZip();
|
||||
w.Write(data.Length);
|
||||
w.Write(data);
|
||||
|
||||
w.Write(OriginX); w.Write(OriginY); w.Write(OriginZ);
|
||||
w.Write((byte)0x0f); // 0ffset
|
||||
w.Write(Offset.X); w.Write(Offset.Y); w.Write(Offset.Z);
|
||||
}
|
||||
|
||||
/// <summary> Loads this copy state from the given stream. </summary>
|
||||
public void LoadFrom(Stream stream) {
|
||||
BinaryReader r = new BinaryReader(stream);
|
||||
int header = r.ReadInt32();
|
||||
if (!(header == identifier || header == identifierC))
|
||||
int identifier = r.ReadInt32();
|
||||
if (!(identifier == identifier1 || identifier == identifier2 || identifier == identifier3))
|
||||
throw new InvalidDataException("invalid identifier");
|
||||
|
||||
X = r.ReadInt32(); Y = r.ReadInt32(); Z = r.ReadInt32();
|
||||
Width = r.ReadInt32(); Height = r.ReadInt32(); Length = r.ReadInt32();
|
||||
if (header == identifier) {
|
||||
Blocks = r.ReadBytes(Width * Height * Length);
|
||||
ExtBlocks = r.ReadBytes(Width * Height * Length);
|
||||
} else {
|
||||
int uncompressedLen = Width * Height * Length;
|
||||
int blocksLen = r.ReadInt32();
|
||||
Blocks = r.ReadBytes(blocksLen).Decompress(uncompressedLen);
|
||||
blocksLen = r.ReadInt32();
|
||||
ExtBlocks = r.ReadBytes(blocksLen).Decompress(uncompressedLen);
|
||||
}
|
||||
LoadBlocks(r, identifier);
|
||||
|
||||
UsedBlocks = Volume;
|
||||
OriginX = r.ReadInt32(); OriginY = r.ReadInt32(); OriginZ = r.ReadInt32();
|
||||
@ -119,24 +144,64 @@ namespace MCGalaxy.Drawing {
|
||||
Offset.X = r.ReadInt32(); Offset.Y = r.ReadInt32(); Offset.Z = r.ReadInt32();
|
||||
}
|
||||
|
||||
void LoadBlocks(BinaryReader r, int identifier) {
|
||||
byte[] extBlocks;
|
||||
int dataLen;
|
||||
switch (identifier) {
|
||||
case identifier1:
|
||||
raw = r.ReadBytes(Volume);
|
||||
extBlocks = r.ReadBytes(Volume);
|
||||
UnpackExtBlocks(extBlocks);
|
||||
break;
|
||||
|
||||
case identifier2:
|
||||
dataLen = r.ReadInt32();
|
||||
raw = r.ReadBytes(dataLen).Decompress(Volume);
|
||||
dataLen = r.ReadInt32();
|
||||
extBlocks = r.ReadBytes(dataLen).Decompress(Volume);
|
||||
UnpackExtBlocks(extBlocks);
|
||||
break;
|
||||
|
||||
case identifier3:
|
||||
dataLen = r.ReadInt32();
|
||||
raw = r.ReadBytes(dataLen).Decompress(Volume);
|
||||
dataLen = r.ReadInt32();
|
||||
isExt = r.ReadBytes(dataLen).Decompress((Volume + 7) / 8);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Loads this copy state from the given stream, using the very old format. </summary>
|
||||
public void LoadFromOld(Stream stream, Stream underlying) {
|
||||
byte[] raw = new byte[underlying.Length];
|
||||
underlying.Read(raw, 0, (int)underlying.Length);
|
||||
raw = raw.Decompress();
|
||||
if (raw.Length == 0) return;
|
||||
|
||||
CalculateBoundsOld(raw);
|
||||
ExtBlock block = default(ExtBlock);
|
||||
CalculateBounds(raw);
|
||||
for (int i = 0; i < raw.Length; i += 7) {
|
||||
ushort x = BitConverter.ToUInt16(raw, i + 0);
|
||||
ushort y = BitConverter.ToUInt16(raw, i + 2);
|
||||
ushort z = BitConverter.ToUInt16(raw, i + 4);
|
||||
Set(raw[i + 6], 0, x - X, y - Y, z - Z);
|
||||
|
||||
block.BlockID = raw[i + 6];
|
||||
Set(block, x - X, y - Y, z - Z);
|
||||
}
|
||||
UsedBlocks = Volume;
|
||||
OriginX = X; OriginY = Y; OriginZ = Z;
|
||||
}
|
||||
|
||||
void CalculateBoundsOld(byte[] raw) {
|
||||
void UnpackExtBlocks(byte[] extBlocks) {
|
||||
isExt = new byte[(Volume + 7) / 8];
|
||||
for (int i = 0; i < raw.Length; i++) {
|
||||
if (raw[i] != Block.custom_block) continue;
|
||||
raw[i] = extBlocks[i];
|
||||
isExt[i >> 3] |= (byte)(1 << (i & 0x07));
|
||||
}
|
||||
}
|
||||
|
||||
void CalculateBounds(byte[] raw) {
|
||||
int minX = int.MaxValue, minY = int.MaxValue, minZ = int.MaxValue;
|
||||
int maxX = int.MinValue, maxY = int.MinValue, maxZ = int.MinValue;
|
||||
for (int i = 0; i < raw.Length; i += 7) {
|
||||
@ -147,18 +212,12 @@ namespace MCGalaxy.Drawing {
|
||||
minX = Math.Min(x, minX); maxX = Math.Max(x, maxX);
|
||||
minY = Math.Min(y, minY); maxY = Math.Max(y, maxY);
|
||||
minZ = Math.Min(z, minZ); maxZ = Math.Max(z, maxZ);
|
||||
|
||||
}
|
||||
X = minX; Y = minY; Z = minZ;
|
||||
Width = maxX - minX + 1;
|
||||
Height = maxY - minY + 1;
|
||||
Length = maxZ - minZ + 1;
|
||||
Blocks = new byte[Width * Height * Length];
|
||||
ExtBlocks = new byte[Width * Height * Length];
|
||||
}
|
||||
|
||||
internal int OppositeOriginX { get { return OriginX == X ? X + Width - 1 : X; } }
|
||||
internal int OppositeOriginY { get { return OriginY == Y ? Y + Height - 1 : Y; } }
|
||||
internal int OppositeOriginZ { get { return OriginZ == Z ? Z + Length - 1 : Z; } }
|
||||
Init(minX, minY, minZ,
|
||||
(maxX - minX) + 1,
|
||||
(maxY - minY) + 1,
|
||||
(maxZ - minZ) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,11 +55,13 @@ namespace MCGalaxy.Drawing {
|
||||
}
|
||||
|
||||
static CopyState Rotate(CopyState state, CopyState flipped, int[] m) {
|
||||
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
|
||||
for (int i = 0; i < blocks.Length; i++) {
|
||||
int volume = state.Volume;
|
||||
for (int i = 0; i < volume; i++) {
|
||||
ushort x, y, z;
|
||||
state.GetCoords(i, out x, out y, out z);
|
||||
flipped.Set(blocks[i], extBlocks[i],
|
||||
ExtBlock block = state.Get(i);
|
||||
|
||||
flipped.Set(block,
|
||||
Rotate(m[0], x, y, z, state),
|
||||
Rotate(m[1], x, y, z, state),
|
||||
Rotate(m[2], x, y, z, state));
|
||||
@ -81,12 +83,12 @@ namespace MCGalaxy.Drawing {
|
||||
const int posX = 0x100, negX = 0x200, posY = 0x010, negY = 0x020, posZ = 0x001, negZ = 0x002;
|
||||
static int Rotate(int row, int x, int y, int z, CopyState state) {
|
||||
switch (row) {
|
||||
case posX: return x;
|
||||
case negX: return (state.Width - 1 - x);
|
||||
case posY: return y;
|
||||
case negY: return (state.Height - 1 - y);
|
||||
case posZ: return z;
|
||||
case negZ: return (state.Length - 1 - z);
|
||||
case posX: return x;
|
||||
case negX: return (state.Width - 1 - x);
|
||||
case posY: return y;
|
||||
case negY: return (state.Height - 1 - y);
|
||||
case posZ: return z;
|
||||
case negZ: return (state.Length - 1 - z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -102,7 +104,6 @@ namespace MCGalaxy.Drawing {
|
||||
|
||||
public static void MirrorX(CopyState state) {
|
||||
int midZ = state.Length / 2, maxZ = state.Length - 1;
|
||||
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
|
||||
state.OriginZ = state.OppositeOriginZ;
|
||||
state.Offset.Z = -state.Offset.Z;
|
||||
|
||||
@ -112,7 +113,8 @@ namespace MCGalaxy.Drawing {
|
||||
int start = state.GetIndex(0, y, z);
|
||||
int end = state.GetIndex(0, y, endZ);
|
||||
for (int x = 0; x < state.Width; x++) {
|
||||
Swap(blocks, extBlocks, start, end);
|
||||
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
|
||||
state.Set(blockB, start); state.Set(blockA, end);
|
||||
start++; end++;
|
||||
}
|
||||
}
|
||||
@ -121,7 +123,6 @@ namespace MCGalaxy.Drawing {
|
||||
|
||||
public static void MirrorY(CopyState state) {
|
||||
int midY = state.Height / 2, maxY = state.Height - 1;
|
||||
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
|
||||
state.OriginY = state.OppositeOriginY;
|
||||
state.Offset.Y = -state.Offset.Y;
|
||||
|
||||
@ -131,7 +132,8 @@ namespace MCGalaxy.Drawing {
|
||||
int end = state.GetIndex(0, endY, 0);
|
||||
for (int z = 0; z < state.Length; z++) {
|
||||
for (int x = 0; x < state.Width; x++) {
|
||||
Swap(blocks, extBlocks, start, end);
|
||||
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
|
||||
state.Set(blockB, start); state.Set(blockA, end);
|
||||
start++; end++;
|
||||
}
|
||||
}
|
||||
@ -140,7 +142,6 @@ namespace MCGalaxy.Drawing {
|
||||
|
||||
public static void MirrorZ(CopyState state) {
|
||||
int midX = state.Width / 2, maxX = state.Width - 1;
|
||||
byte[] blocks = state.Blocks, extBlocks = state.ExtBlocks;
|
||||
state.OriginX = state.OppositeOriginX;
|
||||
state.Offset.X = -state.Offset.X;
|
||||
|
||||
@ -150,15 +151,12 @@ namespace MCGalaxy.Drawing {
|
||||
int endX = maxX - x;
|
||||
int start = state.GetIndex(x, y, z);
|
||||
int end = state.GetIndex(endX, y, z);
|
||||
Swap(blocks, extBlocks, start, end);
|
||||
|
||||
ExtBlock blockA = state.Get(start), blockB = state.Get(end);
|
||||
state.Set(blockB, start); state.Set(blockA, end);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Swap(byte[] blocks, byte[] extBlocks, int a, int b) {
|
||||
byte tmp = blocks[a]; blocks[a] = blocks[b]; blocks[b] = tmp;
|
||||
tmp = extBlocks[a]; extBlocks[a] = extBlocks[b]; extBlocks[b] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user