ModernUO/Projects/Scripts/Commands/Statics.cs
Kamron Batman 179cb50557 Updates Packets & Randomizer (#43)
* Fixes a bug with the main loop. Removes unnecessary optimizations for RDRand.

* WIP - Rewriting packets.

* Cleanup and updates README

* Converts more packets

* Fixes header

* Converts Effects packets.

* Forgot the send command

* Adds the start of containers packets.

* Adds message packets with caching

* Extends the send methods

* Starts to add Acquire methods

* Converted the packets

* Cleanup

* Cleanup

* Adds more packets

* Adds more packets

* Fixes clearing arrays from pool. Adds Mobile Incoming

* Converts more packets.

* Moves more packets

* Moves more packets

* Test compression

* Merges

* Migrating to an idiomatic syntax that also supports compression, and proxying.

* Optimize the stack alloc. Changes attributes

* Converted container packets

* Visual Studio doesn't auto save files. I am still getting used to subpar IDEs.

* Adds static packet caching. Will profile later. Converts more packets

* Fixes packets and changes how compression is configured

* WIP - Adds basics for Gumps. Not finished though.

* Fix file

* Fixes formatting

* Changed SpanWriter to be more idiomatic.

* Fixes Span vs RawSpan and missing stackallocs

* Converts over some more gumps

* WIP - Deletes 32bit support.

* Drops RDRand32 support.

* Fixes gump compilation

* Converting gump components

* Removes old huffman compression function

* Revert signature for backwards compatibility

* WIP - Converting more gump components

* Creates ArraySet for the strings. Updates AppendTo to reference that.

* Converts the maining gump components

* Cleans up gump components

* Cleans up directives

* Cleans up ArraySet and moves it. Adds null-coalescing-assignment

* Cleanup, Target Packets, and C# 8 changes.

* Removed OPL Packet

* World packets

* Fixing packet uses

* Cleans up code. Fixes packet uses in various places.

* More cleanup for packets

* Code cleanup

* Converts more packet uses and cleans up more code

* More code cleanup

* Finishes fixing the packets in Item

* Updates secure trade packets

* Updates core and gets it to compile.

* Moved packets to scripts. Fixed account handler use of packets

* Code cleanup

* Updates chat packets

* Code cleanuo

* Adds party packets, but need to implement them.

* Party packets WIP

* Finishes party packets

* Rearrange movement namespaces and classes

* Finishes plant packets

* Code Formatting

* Fixes moving effects

* Code cleanup, eliminates equipinfo

* Adds more packets. Fixes bugs with various packets.

* Finishes mahjon packets

* Fixes mahjong packet

* Code cleanup

* Finishes mahjong packets

* Adds Map packets

* Add multifacet maps and charts

* Cleans up some packets with UTF8

* Optimizes packets

* Cleans up more packets. Moves the MessageHelper

* Removes assistant support. Removes extended protocol. Incorporates MapUO packets as normal packets.

* Updates protocol extensions packet receiver

* Fixes a few bugs. Fixes a few more packets.

* Code cleanup and fixing more packets

* Fixes packet effects

* Cleaned up more code

* Buff Icon cleanup

* Removed unused constructors

* Code Cleanup. Adds BoatHS Packets

* Moves house files. Updates house foundation packets.

* Deployment cleanup

* Fixes:

* Code cleanup

* More code cleanup

* More code cleanup

* Cleaned up BaseHouse

* Enforces styling

* Converts foreach to linq where possible.

* Dont need that

* Goals/Readme updates

* Removes 32bit support at the highest level. Turns on HRT by default.

* Code cleanup. Fixes extended features packet.

* Fixes various bugs

* Code cleanup

* Code cleanup

* Code cleanup. Fixes gump X/Y assignment.

* Code cleanup using |= operator

* More code cleanup

* Cleanup

* Fixes spacing issues. Thanks Visual Studio. You suck.

* Compiler error

* Fixes NPE from RunUO 2.7

* Renames ScriptCompiler to AssemblyHandler. Fixes packets. Updates README

* Fixes more packets. Stupid trailing nulls.

* Fixes various bugs.

* Fixes for gumps

* Fixes more gump stuff. Going to split it out later since it is getting insane

* Recoded the gump writing

* WIP

* *Added output path of scripts project to dev branch
*Activated debugging in code
2019-11-11 12:40:16 +01:00

574 lines
21 KiB
C#

using System.Collections.Generic;
using System.IO;
using Server.Gumps;
using Server.Items;
namespace Server.Commands
{
public static 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 (var (map, table) in mapTable)
{
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;
}
}
}
}