ModernUO/Projects/Scripts/Commands/Statics.cs
2019-08-02 18:16:11 -07:00

586 lines
22 KiB
C#

using System.Collections.Generic;
using System.IO;
using Server.Commands;
using Server.Gumps;
using Server.Items;
namespace Server
{
public class Statics
{
private const string BaseFreezeWarning = "{0} " +
"Those items <u>will be removed from the world</u> and placed into the server data files. " +
"Other players <u>will not see the changes</u> unless you distribute your data files to them.<br><br>" +
"This operation may not complete unless the server and client are using different data files. " +
"If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " +
"Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.<br><br>" +
"The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " +
"It is strongly recommended that you make backup of the data files mentioned above. " +
"Do you wish to proceed?";
private const string BaseUnfreezeWarning = "{0} " +
"Those items <u>will be removed from the static files</u> and exchanged with unmovable dynamic items. " +
"Other players <u>will not see the changes</u> unless you distribute your data files to them.<br><br>" +
"This operation may not complete unless the server and client are using different data files. " +
"If you receive a message stating 'output data files could not be opened,' then you are probably sharing data files. " +
"Create a new directory for the world data files (statics*.mul and staidx*.mul) and add that to Scritps/Misc/DataPath.cs.<br><br>" +
"The change will be in effect immediately on the server, however, you must restart your client and update it's data files for the changes to become visible. " +
"It is strongly recommended that you make backup of the data files mentioned above. " +
"Do you wish to proceed?";
private static Point3D NullP3D = new Point3D(int.MinValue, int.MinValue, int.MinValue);
private static byte[] m_Buffer;
private static StaticTile[] m_TileBuffer = new StaticTile[128];
public static void Initialize()
{
CommandSystem.Register("Freeze", AccessLevel.Administrator, Freeze_OnCommand);
CommandSystem.Register("FreezeMap", AccessLevel.Administrator, FreezeMap_OnCommand);
CommandSystem.Register("FreezeWorld", AccessLevel.Administrator, FreezeWorld_OnCommand);
CommandSystem.Register("Unfreeze", AccessLevel.Administrator, Unfreeze_OnCommand);
CommandSystem.Register("UnfreezeMap", AccessLevel.Administrator, UnfreezeMap_OnCommand);
CommandSystem.Register("UnfreezeWorld", AccessLevel.Administrator, UnfreezeWorld_OnCommand);
}
public delegate void FreezeCallback( Mobile from, bool okay, StateInfo si );
[Usage("Freeze")]
[Description("Makes a targeted area of dynamic items static.")]
public static void Freeze_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
BoundingBoxPicker.Begin(from, (map, start, end) => FreezeBox_Callback(from, map, start, end));
}
[Usage("FreezeMap")]
[Description("Makes every dynamic item in your map static.")]
public static void FreezeMap_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
Map map = from.Map;
if (map != null && map != Map.Internal)
SendWarning(from, "You are about to freeze <u>all items in {0}</u>.", BaseFreezeWarning, map, NullP3D,
NullP3D, FreezeWarning_Callback);
}
[Usage("FreezeWorld")]
[Description("Makes every dynamic item on all maps static.")]
public static void FreezeWorld_OnCommand(CommandEventArgs e)
{
SendWarning(e.Mobile, "You are about to freeze <u>every item on every map</u>.", BaseFreezeWarning, null,
NullP3D, NullP3D, FreezeWarning_Callback);
}
public static void SendWarning(Mobile m, string header, string baseWarning, Map map, Point3D start, Point3D end,
FreezeCallback callback)
{
m.SendGump(new WarningGump(1060635, 30720, string.Format(baseWarning, string.Format(header, map)), 0xFFC000, 420,
400, okay => callback(m, okay, new StateInfo(map, start, end))));
}
private static void FreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end)
{
SendWarning(from, "You are about to freeze a section of items.", BaseFreezeWarning, map, start, end,
FreezeWarning_Callback);
}
private static void FreezeWarning_Callback(Mobile from, bool okay, StateInfo si)
{
if (!okay)
return;
Freeze(from, si.m_Map, si.m_Start, si.m_End);
}
public static void Freeze(Mobile from, Map targetMap, Point3D start3d, Point3D end3d)
{
Dictionary<Map, Dictionary<Point2D, DeltaState>> mapTable = new Dictionary<Map, Dictionary<Point2D, DeltaState>>();
if (start3d == NullP3D && end3d == NullP3D)
{
if (targetMap == null)
CommandLogging.WriteLine(from, "{0} {1} invoking freeze for every item in every map", from.AccessLevel,
CommandLogging.Format(from));
else
CommandLogging.WriteLine(from, "{0} {1} invoking freeze for every item in {0}", from.AccessLevel,
CommandLogging.Format(from), targetMap);
foreach (Item item in World.Items.Values)
{
if (targetMap != null && item.Map != targetMap)
continue;
if (item.Parent != null)
continue;
if (item is Static || item is BaseFloor || item is BaseWall)
{
Map itemMap = item.Map;
if (itemMap == null || itemMap == Map.Internal)
continue;
if (!mapTable.TryGetValue(itemMap, out Dictionary<Point2D, DeltaState> table))
mapTable[itemMap] = table = new Dictionary<Point2D, DeltaState>();
Point2D p = new Point2D(item.X >> 3, item.Y >> 3);
if (!table.TryGetValue(p, out DeltaState state))
table[p] = state = new DeltaState(p);
state.m_List.Add(item);
}
}
}
else if (targetMap != null)
{
Point2D start = targetMap.Bound(new Point2D(start3d)), end = targetMap.Bound(new Point2D(end3d));
CommandLogging.WriteLine(from, "{0} {1} invoking freeze from {2} to {3} in {4}", from.AccessLevel,
CommandLogging.Format(from), start, end, targetMap);
IPooledEnumerable<Item> eable =
targetMap.GetItemsInBounds(new Rectangle2D(start.X, start.Y, end.X - start.X + 1, end.Y - start.Y + 1));
foreach (Item item in eable)
if (item is Static || item is BaseFloor || item is BaseWall)
{
Map itemMap = item.Map;
if (itemMap == null || itemMap == Map.Internal)
continue;
if (!mapTable.TryGetValue(itemMap, out Dictionary<Point2D, DeltaState> table))
mapTable[itemMap] = table = new Dictionary<Point2D, DeltaState>();
Point2D p = new Point2D(item.X >> 3, item.Y >> 3);
if (!table.TryGetValue(p, out DeltaState state))
table[p] = state = new DeltaState(p);
state.m_List.Add(item);
}
eable.Free();
}
if (mapTable.Count == 0)
{
from.SendGump(new NoticeGump(1060637, 30720,
"No freezable items were found. Only the following item types are frozen:<br> - Static<br> - BaseFloor<br> - BaseWall",
0xFFC000, 320, 240));
return;
}
bool badDataFile = false;
int totalFrozen = 0;
foreach (KeyValuePair<Map, Dictionary<Point2D, DeltaState>> de in mapTable)
{
Map map = de.Key;
Dictionary<Point2D, DeltaState> table = de.Value;
TileMatrix matrix = map.Tiles;
using (FileStream idxStream = OpenWrite(matrix.IndexStream))
{
using (FileStream mulStream = OpenWrite(matrix.DataStream))
{
if (idxStream == null || mulStream == null)
{
badDataFile = true;
continue;
}
BinaryReader idxReader = new BinaryReader(idxStream);
BinaryWriter idxWriter = new BinaryWriter(idxStream);
BinaryWriter mulWriter = new BinaryWriter(mulStream);
foreach (DeltaState state in table.Values)
{
StaticTile[] oldTiles = ReadStaticBlock(idxReader, mulStream, state.m_X, state.m_Y,
matrix.BlockWidth, matrix.BlockHeight, out int oldTileCount);
if (oldTileCount < 0)
continue;
int newTileCount = 0;
StaticTile[] newTiles = new StaticTile[state.m_List.Count];
for (int i = 0; i < state.m_List.Count; ++i)
{
Item item = state.m_List[i];
int xOffset = item.X - state.m_X * 8;
int yOffset = item.Y - state.m_Y * 8;
if (xOffset < 0 || xOffset >= 8 || yOffset < 0 || yOffset >= 8)
continue;
StaticTile newTile = new StaticTile((ushort)item.ItemID, (byte)xOffset, (byte)yOffset,
(sbyte)item.Z, (short)item.Hue);
newTiles[newTileCount++] = newTile;
item.Delete();
++totalFrozen;
}
int mulPos = -1;
int length = -1;
int extra = 0;
if (oldTileCount + newTileCount > 0)
{
mulWriter.Seek(0, SeekOrigin.End);
mulPos = (int)mulWriter.BaseStream.Position;
length = (oldTileCount + newTileCount) * 7;
extra = 1;
for (int i = 0; i < oldTileCount; ++i)
{
StaticTile toWrite = oldTiles[i];
mulWriter.Write((ushort)toWrite.ID);
mulWriter.Write((byte)toWrite.X);
mulWriter.Write((byte)toWrite.Y);
mulWriter.Write((sbyte)toWrite.Z);
mulWriter.Write((short)toWrite.Hue);
}
for (int i = 0; i < newTileCount; ++i)
{
StaticTile toWrite = newTiles[i];
mulWriter.Write((ushort)toWrite.ID);
mulWriter.Write((byte)toWrite.X);
mulWriter.Write((byte)toWrite.Y);
mulWriter.Write((sbyte)toWrite.Z);
mulWriter.Write((short)toWrite.Hue);
}
mulWriter.Flush();
}
int idxPos = (state.m_X * matrix.BlockHeight + state.m_Y) * 12;
idxWriter.Seek(idxPos, SeekOrigin.Begin);
idxWriter.Write(mulPos);
idxWriter.Write(length);
idxWriter.Write(extra);
idxWriter.Flush();
matrix.SetStaticBlock(state.m_X, state.m_Y, null);
}
}
}
}
if (totalFrozen == 0 && badDataFile)
from.SendGump(new NoticeGump(1060637, 30720,
"Output data files could not be opened and the freeze operation has been aborted.<br><br>This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.",
0xFFC000, 320, 240));
else
from.SendGump(new NoticeGump(1060637, 30720,
$"Freeze operation completed successfully.<br><br>{totalFrozen} item{(totalFrozen != 1 ? "s were" : " was")} frozen.<br><br>You must restart your client and update it's data files to see the changes.",
0xFFC000, 320, 240));
}
[Usage("Unfreeze")]
[Description("Makes a targeted area of static items dynamic.")]
public static void Unfreeze_OnCommand(CommandEventArgs e)
{
Mobile from = e.Mobile;
BoundingBoxPicker.Begin(from, (map, start, end) => UnfreezeBox_Callback(from, map, start, end));
}
[Usage("UnfreezeMap")]
[Description("Makes every static item in your map dynamic.")]
public static void UnfreezeMap_OnCommand(CommandEventArgs e)
{
Map map = e.Mobile.Map;
if (map != null && map != Map.Internal)
SendWarning(e.Mobile, "You are about to unfreeze <u>all items in {0}</u>.", BaseUnfreezeWarning, map,
NullP3D, NullP3D, UnfreezeWarning_Callback);
}
[Usage("UnfreezeWorld")]
[Description("Makes every static item on all maps dynamic.")]
public static void UnfreezeWorld_OnCommand(CommandEventArgs e)
{
SendWarning(e.Mobile, "You are about to unfreeze <u>every item on every map</u>.", BaseUnfreezeWarning, null,
NullP3D, NullP3D, UnfreezeWarning_Callback);
}
private static void UnfreezeBox_Callback(Mobile from, Map map, Point3D start, Point3D end)
{
SendWarning(from, "You are about to unfreeze a section of items.", BaseUnfreezeWarning, map, start, end,
UnfreezeWarning_Callback);
}
private static void UnfreezeWarning_Callback(Mobile from, bool okay, StateInfo si)
{
if (!okay)
return;
Unfreeze(from, si.m_Map, si.m_Start, si.m_End);
}
private static void DoUnfreeze(Map map, Point2D start, Point2D end, ref bool badDataFile, ref int totalUnfrozen)
{
start = map.Bound(start);
end = map.Bound(end);
int xStartBlock = start.X >> 3;
int yStartBlock = start.Y >> 3;
int xEndBlock = end.X >> 3;
int yEndBlock = end.Y >> 3;
int xTileStart = start.X, yTileStart = start.Y;
int xTileWidth = end.X - start.X + 1, yTileHeight = end.Y - start.Y + 1;
TileMatrix matrix = map.Tiles;
using (FileStream idxStream = OpenWrite(matrix.IndexStream))
{
using (FileStream mulStream = OpenWrite(matrix.DataStream))
{
if (idxStream == null || mulStream == null)
{
badDataFile = true;
return;
}
BinaryReader idxReader = new BinaryReader(idxStream);
BinaryWriter idxWriter = new BinaryWriter(idxStream);
BinaryWriter mulWriter = new BinaryWriter(mulStream);
for (int x = xStartBlock; x <= xEndBlock; ++x)
for (int y = yStartBlock; y <= yEndBlock; ++y)
{
StaticTile[] oldTiles = ReadStaticBlock(idxReader, mulStream, x, y, matrix.BlockWidth,
matrix.BlockHeight, out int oldTileCount);
if (oldTileCount < 0)
continue;
int newTileCount = 0;
StaticTile[] newTiles = new StaticTile[oldTileCount];
int baseX = (x << 3) - xTileStart, baseY = (y << 3) - yTileStart;
for (int i = 0; i < oldTileCount; ++i)
{
StaticTile oldTile = oldTiles[i];
int px = baseX + oldTile.X;
int py = baseY + oldTile.Y;
if (px < 0 || px >= xTileWidth || py < 0 || py >= yTileHeight)
{
newTiles[newTileCount++] = oldTile;
}
else
{
++totalUnfrozen;
Item item = new Static(oldTile.ID);
item.Hue = oldTile.Hue;
item.MoveToWorld(new Point3D(px + xTileStart, py + yTileStart, oldTile.Z), map);
}
}
int mulPos = -1;
int length = -1;
int extra = 0;
if (newTileCount > 0)
{
mulWriter.Seek(0, SeekOrigin.End);
mulPos = (int)mulWriter.BaseStream.Position;
length = newTileCount * 7;
extra = 1;
for (int i = 0; i < newTileCount; ++i)
{
StaticTile toWrite = newTiles[i];
mulWriter.Write((ushort)toWrite.ID);
mulWriter.Write((byte)toWrite.X);
mulWriter.Write((byte)toWrite.Y);
mulWriter.Write((sbyte)toWrite.Z);
mulWriter.Write((short)toWrite.Hue);
}
mulWriter.Flush();
}
int idxPos = (x * matrix.BlockHeight + y) * 12;
idxWriter.Seek(idxPos, SeekOrigin.Begin);
idxWriter.Write(mulPos);
idxWriter.Write(length);
idxWriter.Write(extra);
idxWriter.Flush();
matrix.SetStaticBlock(x, y, null);
}
}
}
}
public static void DoUnfreeze(Map map, ref bool badDataFile, ref int totalUnfrozen)
{
DoUnfreeze(map, Point2D.Zero, new Point2D(map.Width - 1, map.Height - 1), ref badDataFile, ref totalUnfrozen);
}
public static void Unfreeze(Mobile from, Map map, Point3D start, Point3D end)
{
int totalUnfrozen = 0;
bool badDataFile = false;
if (map == null)
{
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze for every item in every map", from.AccessLevel,
CommandLogging.Format(from));
DoUnfreeze(Map.Felucca, ref badDataFile, ref totalUnfrozen);
DoUnfreeze(Map.Trammel, ref badDataFile, ref totalUnfrozen);
DoUnfreeze(Map.Ilshenar, ref badDataFile, ref totalUnfrozen);
DoUnfreeze(Map.Malas, ref badDataFile, ref totalUnfrozen);
DoUnfreeze(Map.Tokuno, ref badDataFile, ref totalUnfrozen);
}
else if (start == NullP3D && end == NullP3D)
{
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze for every item in {2}", from.AccessLevel,
CommandLogging.Format(from), map);
DoUnfreeze(map, ref badDataFile, ref totalUnfrozen);
}
else
{
CommandLogging.WriteLine(from, "{0} {1} invoking unfreeze from {2} to {3} in {4}", from.AccessLevel,
CommandLogging.Format(from), new Point2D(start), new Point2D(end), map);
DoUnfreeze(map, new Point2D(start), new Point2D(end), ref badDataFile, ref totalUnfrozen);
}
if (totalUnfrozen == 0 && badDataFile)
from.SendGump(new NoticeGump(1060637, 30720,
"Output data files could not be opened and the unfreeze operation has been aborted.<br><br>This probably means your server and client are using the same data files. Instructions on how to resolve this can be found in the first warning window.",
0xFFC000, 320, 240));
else
from.SendGump(new NoticeGump(1060637, 30720,
$"Unfreeze operation completed successfully.<br><br>{totalUnfrozen} item{(totalUnfrozen != 1 ? "s were" : " was")} unfrozen.<br><br>You must restart your client and update it's data files to see the changes.",
0xFFC000, 320, 240));
}
private static FileStream OpenWrite(FileStream orig)
{
if (orig == null)
return null;
try
{
return new FileStream(orig.Name, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
}
catch
{
return null;
}
}
private static StaticTile[] ReadStaticBlock(BinaryReader idxReader, FileStream mulStream, int x, int y, int width,
int height, out int count)
{
try
{
if (x < 0 || x >= width || y < 0 || y >= height)
{
count = -1;
return m_TileBuffer;
}
idxReader.BaseStream.Seek((x * height + y) * 12, SeekOrigin.Begin);
int lookup = idxReader.ReadInt32();
int length = idxReader.ReadInt32();
if (lookup < 0 || length <= 0)
{
count = 0;
}
else
{
count = length / 7;
mulStream.Seek(lookup, SeekOrigin.Begin);
if (m_TileBuffer.Length < count)
m_TileBuffer = new StaticTile[count];
StaticTile[] staTiles = m_TileBuffer;
if (m_Buffer == null || length > m_Buffer.Length)
m_Buffer = new byte[length];
mulStream.Read(m_Buffer, 0, length);
int index = 0;
for (int i = 0; i < count; ++i)
staTiles[i].Set((ushort)(m_Buffer[index++] | (m_Buffer[index++] << 8)),
m_Buffer[index++], m_Buffer[index++], (sbyte)m_Buffer[index++],
(short)(m_Buffer[index++] | (m_Buffer[index++] << 8)));
}
}
catch
{
count = -1;
}
return m_TileBuffer;
}
private class DeltaState
{
public List<Item> m_List;
public int m_X, m_Y;
public DeltaState(Point2D p)
{
m_X = p.X;
m_Y = p.Y;
m_List = new List<Item>();
}
}
public class StateInfo
{
public Map m_Map;
public Point3D m_Start, m_End;
public StateInfo(Map map, Point3D start, Point3D end)
{
m_Map = map;
m_Start = start;
m_End = end;
}
}
}
}