fix(core): Fixes map issues at New Haven (#509)
- [X] Fixes map issues at New Haven by turning off static diffs - [X] Some code cleanup and reformatting of tile matrix, tile matrix patch, and tile data - [X] Adds NetState.Flush for generating spawners so it doesn't feel like the server is frozen - [X] Reverts NativeReader changes from a while back. - [X] Adds more string reading for BufferReader. Notes: BufferReader is still `little endian` compared to `SpanReader` which is `big endian` (for packets). To that end, the RunUO deserialization `ReadString()` was made obsolete since it is ambiguous, and contains extra fields other than simply reading a string. Furthermore, we shouldn't be using UTF8 (for now) since it is slow.
This commit is contained in:
parent
d070efeab3
commit
ea5d09a7d7
21 changed files with 724 additions and 482 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,6 +4,7 @@
|
|||
/Distribution/Assemblies
|
||||
/Distribution/Configuration/modernuo.json
|
||||
/Distribution/Configuration/email-settings.json
|
||||
/Distribution/Configuration/throttles.json
|
||||
/Distribution/Logs
|
||||
/Distribution/Backups
|
||||
/Distribution/Saves
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Server.Tests", "Projects\Se
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UOContent.Tests", "Projects\UOContent.Tests\UOContent.Tests.csproj", "{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Projects\Benchmarks\Benchmarks.csproj", "{38B7E4A1-FDDD-486C-98EE-BA7B13712528}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "Projects\Benchmarks\Benchmarks.csproj", "{38B7E4A1-FDDD-486C-98EE-BA7B13712528}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -43,6 +43,8 @@ Global
|
|||
{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Debug|x64.Build.0 = Debug|x64
|
||||
{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|x64.ActiveCfg = Release|x64
|
||||
{3C4797F9-603E-44EF-8E8C-9275CC9EA74B}.Release|x64.Build.0 = Release|x64
|
||||
{38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Analyze|x64.ActiveCfg = Release|x64
|
||||
{38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Analyze|x64.Build.0 = Release|x64
|
||||
{38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Debug|x64.Build.0 = Debug|x64
|
||||
{38B7E4A1-FDDD-486C-98EE-BA7B13712528}.Release|x64.ActiveCfg = Release|x64
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ namespace Server.Tests.Network
|
|||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
|
|
@ -55,6 +56,7 @@ namespace Server.Tests.Network
|
|||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
|
|
@ -90,6 +92,7 @@ namespace Server.Tests.Network
|
|||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,17 @@ namespace Server.Tests.Network
|
|||
{
|
||||
public class MapPatchesTests : IClassFixture<ServerFixture>
|
||||
{
|
||||
[Fact]
|
||||
public void TestMapPatches()
|
||||
[Theory]
|
||||
[InlineData(ProtocolChanges.Version500a, ClientFlags.Malas | ClientFlags.Trammel | ClientFlags.Felucca)]
|
||||
[InlineData(ProtocolChanges.Version7090, ClientFlags.TerMur | ClientFlags.Trammel | ClientFlags.Felucca)]
|
||||
public void TestMapPatches(ProtocolChanges protocolChanges, ClientFlags flags)
|
||||
{
|
||||
var expected = new MapPatches().Compile();
|
||||
|
||||
var ns = PacketTestUtilities.CreateTestNetState();
|
||||
ns.ProtocolChanges = protocolChanges;
|
||||
ns.Flags = flags;
|
||||
|
||||
var expected = ns.ProtocolChanges >= ProtocolChanges.Version6000 ? Span<byte>.Empty : new MapPatches().Compile();
|
||||
|
||||
ns.SendMapPatches();
|
||||
|
||||
var result = ns.SendPipe.Reader.TryRead();
|
||||
|
|
|
|||
|
|
@ -139,6 +139,30 @@ namespace System.Buffers
|
|||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public long ReadInt64()
|
||||
{
|
||||
if (!BinaryPrimitives.TryReadInt64BigEndian(_buffer.Slice(Position), out var value))
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
|
||||
Position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
if (!BinaryPrimitives.TryReadUInt64BigEndian(_buffer.Slice(Position), out var value))
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
|
||||
Position += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -249,9 +249,7 @@ namespace Server
|
|||
FeatureFlags supportedFeatures,
|
||||
CharacterListFlags charListFlags,
|
||||
HousingFlags customHousingFlag
|
||||
)
|
||||
: this(id, name, supportedFeatures, charListFlags, customHousingFlag) =>
|
||||
ClientFlags = clientFlags;
|
||||
) : this(id, name, supportedFeatures, charListFlags, customHousingFlag) => ClientFlags = clientFlags;
|
||||
|
||||
public ExpansionInfo(
|
||||
int id,
|
||||
|
|
@ -260,9 +258,7 @@ namespace Server
|
|||
FeatureFlags supportedFeatures,
|
||||
CharacterListFlags charListFlags,
|
||||
HousingFlags customHousingFlag
|
||||
)
|
||||
: this(id, name, supportedFeatures, charListFlags, customHousingFlag) =>
|
||||
RequiredClient = requiredClient;
|
||||
) : this(id, name, supportedFeatures, charListFlags, customHousingFlag) => RequiredClient = requiredClient;
|
||||
|
||||
private ExpansionInfo(
|
||||
int id,
|
||||
|
|
|
|||
|
|
@ -459,6 +459,7 @@ namespace Server
|
|||
AssemblyHandler.Invoke("Configure");
|
||||
|
||||
TileMatrixLoader.LoadTileMatrix();
|
||||
|
||||
RegionLoader.LoadRegions();
|
||||
World.Load();
|
||||
|
||||
|
|
|
|||
|
|
@ -2814,10 +2814,7 @@ namespace Server
|
|||
{
|
||||
ns.SendMapChange(Map);
|
||||
|
||||
if (!Core.SE && ns.ProtocolChanges < ProtocolChanges.Version6000)
|
||||
{
|
||||
ns.SendMapPatches();
|
||||
}
|
||||
ns.SendMapPatches();
|
||||
|
||||
ns.SendSeasonChange((byte)GetSeason(), true);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
|
|
@ -8,61 +9,121 @@ namespace Server
|
|||
{
|
||||
private static readonly INativeReader m_NativeReader;
|
||||
|
||||
static NativeReader()
|
||||
{
|
||||
if (Core.Unix)
|
||||
{
|
||||
m_NativeReader = new NativeReaderUnix();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NativeReader = new NativeReaderWin32();
|
||||
}
|
||||
}
|
||||
static NativeReader() => m_NativeReader = Core.Unix ? (INativeReader)new NativeReaderUnix() : new NativeReaderWin32();
|
||||
|
||||
public static unsafe void Read(IntPtr p, void* buffer, int length)
|
||||
{
|
||||
m_NativeReader.Read(p, buffer, length);
|
||||
}
|
||||
public static unsafe int Read(FileStream source, void* buffer, int length) =>
|
||||
m_NativeReader.Read(source, buffer, length);
|
||||
|
||||
public static unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) =>
|
||||
m_NativeReader.Read(source, buffer, bufferIndex, length);
|
||||
|
||||
public static unsafe int Read(FileStream source, int sourceIndex, void* buffer, int length) =>
|
||||
m_NativeReader.Read(source, sourceIndex, buffer, length);
|
||||
|
||||
public static unsafe int Read(FileStream source, int sourceIndex, void* buffer, int bufferIndex, int length) =>
|
||||
m_NativeReader.Read(source, sourceIndex, buffer, bufferIndex, length);
|
||||
}
|
||||
|
||||
public interface INativeReader
|
||||
{
|
||||
unsafe void Read(IntPtr p, void* buffer, int length);
|
||||
unsafe int Read(FileStream source, void* buffer, int length);
|
||||
unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length);
|
||||
unsafe int Read(FileStream source, int sourceIndex, void* buffer, int length);
|
||||
unsafe int Read(FileStream source, int sourceIndex, void* buffer, int bufferIndex, int length);
|
||||
}
|
||||
|
||||
public sealed class NativeReaderWin32 : INativeReader
|
||||
{
|
||||
public unsafe void Read(IntPtr p, void* buffer, int length)
|
||||
internal class UnsafeNativeMethods
|
||||
{
|
||||
uint lpNumberOfBytesRead = 0;
|
||||
UnsafeNativeMethods.ReadFile(p, buffer, (uint)length, ref lpNumberOfBytesRead, null);
|
||||
[DllImport("kernel32")]
|
||||
internal static extern unsafe bool ReadFile(IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToRead, ref uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped);
|
||||
}
|
||||
|
||||
internal static class UnsafeNativeMethods
|
||||
{
|
||||
/*[DllImport("kernel32")]
|
||||
internal unsafe static extern int _lread(IntPtr hFile, void* lpBuffer, int wBytes);*/
|
||||
public unsafe int Read(FileStream source, void* buffer, int length) => InternalRead(source, buffer, 0, length);
|
||||
|
||||
[DllImport("kernel32")]
|
||||
internal static extern unsafe bool ReadFile(
|
||||
IntPtr hFile, void* lpBuffer, uint nNumberOfBytesToRead,
|
||||
ref uint lpNumberOfBytesRead, NativeOverlapped* lpOverlapped
|
||||
);
|
||||
public unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) => InternalRead(source, buffer, bufferIndex, length);
|
||||
|
||||
public unsafe int Read(FileStream source, int sourceIndex, void* buffer, int length)
|
||||
{
|
||||
if (source.Seek(sourceIndex, SeekOrigin.Begin) == sourceIndex)
|
||||
{
|
||||
return InternalRead(source, buffer, 0, length);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public unsafe int Read(FileStream source, int sourceIndex, void* buffer, int bufferIndex, int length)
|
||||
{
|
||||
if (source.Seek(sourceIndex, SeekOrigin.Begin) == sourceIndex)
|
||||
{
|
||||
return InternalRead(source, buffer, bufferIndex, length);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal unsafe int InternalRead(FileStream source, void* buffer, int bufferIndex, int length)
|
||||
{
|
||||
var byteCount = 0U;
|
||||
|
||||
if (!UnsafeNativeMethods.ReadFile(source.SafeFileHandle!.DangerousGetHandle(), (byte*)buffer + bufferIndex, (uint)length, ref byteCount, null))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (byteCount > 0)
|
||||
{
|
||||
source.Seek(byteCount, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
return (int)byteCount;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NativeReaderUnix : INativeReader
|
||||
{
|
||||
public unsafe void Read(IntPtr p, void* buffer, int length)
|
||||
{
|
||||
_ = UnsafeNativeMethods.read(p, buffer, length);
|
||||
}
|
||||
|
||||
internal static class UnsafeNativeMethods
|
||||
internal class UnsafeNativeMethods
|
||||
{
|
||||
[DllImport("libc")]
|
||||
internal static extern unsafe int read(IntPtr p, void* buffer, int length);
|
||||
internal static extern unsafe int read(IntPtr ptr, void* buffer, int length);
|
||||
}
|
||||
|
||||
public unsafe int Read(FileStream source, void* buffer, int length) => InternalRead(source, buffer, 0, length);
|
||||
|
||||
public unsafe int Read(FileStream source, void* buffer, int bufferIndex, int length) => InternalRead(source, buffer, bufferIndex, length);
|
||||
|
||||
public unsafe int Read(FileStream source, int sourceIndex, void* buffer, int length)
|
||||
{
|
||||
if (source.Seek(sourceIndex, SeekOrigin.Begin) == sourceIndex)
|
||||
{
|
||||
return InternalRead(source, buffer, 0, length);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public unsafe int Read(FileStream source, int sourceIndex, void* buffer, int bufferIndex, int length)
|
||||
{
|
||||
if (source.Seek(sourceIndex, SeekOrigin.Begin) == sourceIndex)
|
||||
{
|
||||
return InternalRead(source, buffer, bufferIndex, length);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
internal unsafe int InternalRead(FileStream source, void* buffer, int bufferIndex, int length)
|
||||
{
|
||||
var byteCount = UnsafeNativeMethods.read(source.Handle, (byte*)buffer + bufferIndex, length);
|
||||
|
||||
if (byteCount > 0)
|
||||
{
|
||||
source.Seek(byteCount, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
return byteCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ namespace Server.Network
|
|||
public ProtocolChanges ProtocolChanges { get; set; }
|
||||
public ClientFlags Flags { get; set; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasFlag(ClientFlags flag) => (Flags & flag) != 0;
|
||||
|
||||
public ClientVersion Version
|
||||
{
|
||||
get => _version;
|
||||
|
|
|
|||
|
|
@ -306,10 +306,7 @@ namespace Server.Network
|
|||
|
||||
state.SendMapChange(m.Map);
|
||||
|
||||
if (!Core.SE && state.ProtocolChanges < ProtocolChanges.Version6000)
|
||||
{
|
||||
state.SendMapPatches();
|
||||
}
|
||||
state.SendMapPatches();
|
||||
|
||||
state.SendSeasonChange((byte)m.GetSeason(), true);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using Server.Diagnostics;
|
||||
using Server.Targeting;
|
||||
|
||||
|
|
@ -33,7 +34,8 @@ namespace Server.Network
|
|||
Serial serial = reader.ReadUInt32();
|
||||
int x = reader.ReadInt16();
|
||||
int y = reader.ReadInt16();
|
||||
int z = reader.ReadInt16();
|
||||
reader.ReadByte();
|
||||
int z = reader.ReadSByte();
|
||||
int graphic = reader.ReadUInt16();
|
||||
|
||||
if (targetID == unchecked((int)0xDEADBEEF))
|
||||
|
|
|
|||
|
|
@ -22,28 +22,55 @@ namespace Server.Network
|
|||
{
|
||||
public static void SendMapPatches(this NetState ns)
|
||||
{
|
||||
if (ns == null)
|
||||
if (ns == null || ns.ProtocolChanges >= ProtocolChanges.Version6000)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[41]);
|
||||
int count;
|
||||
|
||||
if (ns.HasFlag(ClientFlags.TerMur))
|
||||
{
|
||||
count = 6;
|
||||
}
|
||||
else if (ns.HasFlag(ClientFlags.Tokuno))
|
||||
{
|
||||
count = 5;
|
||||
}
|
||||
else if (ns.HasFlag(ClientFlags.Malas))
|
||||
{
|
||||
count = 4;
|
||||
}
|
||||
else if (ns.HasFlag(ClientFlags.Ilshenar))
|
||||
{
|
||||
count = 3;
|
||||
}
|
||||
else if (ns.HasFlag(ClientFlags.Trammel))
|
||||
{
|
||||
count = 2;
|
||||
}
|
||||
else if (ns.HasFlag(ClientFlags.Felucca))
|
||||
{
|
||||
count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var writer = new SpanWriter(stackalloc byte[9 + count * 8]);
|
||||
writer.Write((byte)0xBF); // Packet ID
|
||||
writer.Write((ushort)41); // Length
|
||||
writer.Write((ushort)0x18); // Subpacket
|
||||
writer.Write(4); // Map count?
|
||||
writer.Write(count);
|
||||
|
||||
writer.Write(Map.Felucca.Tiles.Patch.StaticBlocks);
|
||||
writer.Write(Map.Felucca.Tiles.Patch.LandBlocks);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var map = Map.Maps[i];
|
||||
|
||||
writer.Write(Map.Trammel.Tiles.Patch.StaticBlocks);
|
||||
writer.Write(Map.Trammel.Tiles.Patch.LandBlocks);
|
||||
|
||||
writer.Write(Map.Ilshenar.Tiles.Patch.StaticBlocks);
|
||||
writer.Write(Map.Ilshenar.Tiles.Patch.LandBlocks);
|
||||
|
||||
writer.Write(Map.Malas.Tiles.Patch.StaticBlocks);
|
||||
writer.Write(Map.Malas.Tiles.Patch.LandBlocks);
|
||||
writer.Write(map.Tiles.Patch.StaticBlocks);
|
||||
writer.Write(map.Tiles.Patch.LandBlocks);
|
||||
}
|
||||
|
||||
ns.Send(writer.Span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@ using System.Collections.Generic;
|
|||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Server.Guilds;
|
||||
using Server.Network;
|
||||
using Server.Text;
|
||||
|
||||
namespace Server
|
||||
|
|
@ -48,6 +50,7 @@ namespace Server
|
|||
_position = 0;
|
||||
}
|
||||
|
||||
[Obsolete("RunUO backward compatible method that should be replaced.")]
|
||||
public string ReadString()
|
||||
{
|
||||
if (!ReadBool())
|
||||
|
|
@ -56,16 +59,86 @@ namespace Server
|
|||
}
|
||||
|
||||
var length = ReadEncodedInt();
|
||||
if (length <= 0)
|
||||
return length <= 0 ? "" : ReadString(_encoding, false, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadString(Encoding encoding, bool safeString = false, int fixedLength = -1)
|
||||
{
|
||||
int sizeT = TextEncoding.GetByteLengthForEncoding(encoding);
|
||||
|
||||
bool isFixedLength = fixedLength > -1;
|
||||
|
||||
var remaining = _buffer.Length - _position;
|
||||
int size;
|
||||
|
||||
if (isFixedLength)
|
||||
{
|
||||
return "";
|
||||
size = fixedLength * sizeT;
|
||||
if (size > remaining)
|
||||
{
|
||||
throw new OutOfMemoryException();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size = remaining - (remaining & (sizeT - 1));
|
||||
}
|
||||
|
||||
var s = _encoding.GetString(_buffer.AsSpan(Position, length));
|
||||
_position += length;
|
||||
return s;
|
||||
var buffer = _buffer.AsSpan(Position, size);
|
||||
|
||||
int index = buffer.IndexOfTerminator(sizeT);
|
||||
|
||||
var span = buffer.SliceToLength(index < 0 ? size : index);
|
||||
_position += isFixedLength || index < 0 ? size : index + sizeT;
|
||||
return TextEncoding.GetString(span, encoding, safeString);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadLittleUniSafe(int fixedLength) => ReadString(TextEncoding.UnicodeLE, true, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadLittleUniSafe() => ReadString(TextEncoding.UnicodeLE, true);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadLittleUni(int fixedLength) => ReadString(TextEncoding.UnicodeLE, false, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadLittleUni() => ReadString(TextEncoding.UnicodeLE);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadBigUniSafe(int fixedLength) => ReadString(TextEncoding.Unicode, true, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadBigUniSafe() => ReadString(TextEncoding.Unicode, true);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadBigUni(int fixedLength) => ReadString(TextEncoding.Unicode, false, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadBigUni() => ReadString(TextEncoding.Unicode);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadUTF8Safe(int fixedLength) => ReadString(TextEncoding.UTF8, true, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadUTF8Safe() => ReadString(TextEncoding.UTF8, true);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadUTF8() => ReadString(TextEncoding.UTF8);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadAsciiSafe(int fixedLength) => ReadString(Encoding.ASCII, true, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadAsciiSafe() => ReadString(Encoding.ASCII, true);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadAscii(int fixedLength) => ReadString(Encoding.ASCII, false, fixedLength);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public string ReadAscii() => ReadString(Encoding.ASCII);
|
||||
|
||||
public DateTime ReadDateTime() => new(ReadLong(), DateTimeKind.Utc);
|
||||
|
||||
public TimeSpan ReadTimeSpan() => new(ReadLong());
|
||||
|
|
|
|||
|
|
@ -243,6 +243,15 @@ namespace Server
|
|||
return list;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfTerminator(this Span<byte> buffer, int sizeT) =>
|
||||
sizeT switch
|
||||
{
|
||||
2 => MemoryMarshal.Cast<byte, char>(buffer).IndexOf((char)0) * 2,
|
||||
4 => MemoryMarshal.Cast<byte, uint>(buffer).IndexOf((uint)0) * 4,
|
||||
_ => buffer.IndexOf((byte)0)
|
||||
};
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int IndexOfTerminator(this ReadOnlySpan<byte> buffer, int sizeT) =>
|
||||
sizeT switch
|
||||
|
|
|
|||
|
|
@ -1,139 +1,183 @@
|
|||
/*************************************************************************
|
||||
* ModernUO *
|
||||
* Copyright (C) 2019-2021 - ModernUO Development Team *
|
||||
* Email: hi@modernuo.com *
|
||||
* File: TileData.cs *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation, either version 3 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
|
||||
*************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Server.Network;
|
||||
|
||||
namespace Server
|
||||
{
|
||||
public struct LandData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public TileFlag Flags { get; set; }
|
||||
|
||||
public LandData(string name, TileFlag flags)
|
||||
{
|
||||
Name = name;
|
||||
Flags = flags;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public TileFlag Flags { get; set; }
|
||||
}
|
||||
|
||||
[PropertyObject]
|
||||
public struct ItemData
|
||||
{
|
||||
private byte m_Weight;
|
||||
private byte m_Quality;
|
||||
private byte m_Quantity;
|
||||
private byte m_Value;
|
||||
private byte m_Height;
|
||||
private byte _weight;
|
||||
private byte _quality;
|
||||
private ushort _animation;
|
||||
private byte _quantity;
|
||||
private byte _value;
|
||||
private byte _height;
|
||||
|
||||
public ItemData(string name, TileFlag flags, int weight, int quality, int quantity, int value, int height)
|
||||
public ItemData(string name, TileFlag flags, int weight, int quality, int animation, int quantity, int value, int height)
|
||||
{
|
||||
Name = name;
|
||||
Flags = flags;
|
||||
m_Weight = (byte)weight;
|
||||
m_Quality = (byte)quality;
|
||||
m_Quantity = (byte)quantity;
|
||||
m_Value = (byte)value;
|
||||
m_Height = (byte)height;
|
||||
_weight = (byte)weight;
|
||||
_quality = (byte)quality;
|
||||
_animation = (ushort)animation;
|
||||
_quantity = (byte)quantity;
|
||||
_value = (byte)value;
|
||||
_height = (byte)height;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public string Name { get; set; }
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public TileFlag Flags { get; set; }
|
||||
|
||||
public bool Bridge
|
||||
{
|
||||
get => (Flags & TileFlag.Bridge) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= TileFlag.Bridge;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~TileFlag.Bridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Impassable
|
||||
{
|
||||
get => (Flags & TileFlag.Impassable) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= TileFlag.Impassable;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~TileFlag.Impassable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Surface
|
||||
{
|
||||
get => (Flags & TileFlag.Surface) != 0;
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= TileFlag.Surface;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~TileFlag.Surface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Weight
|
||||
{
|
||||
get => m_Weight;
|
||||
set => m_Weight = (byte)value;
|
||||
get => _weight;
|
||||
set => _weight = (byte)value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Quality
|
||||
{
|
||||
get => m_Quality;
|
||||
set => m_Quality = (byte)value;
|
||||
get => _quality;
|
||||
set => _quality = (byte)value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Animation
|
||||
{
|
||||
get => _animation;
|
||||
set => _animation = (ushort)value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Quantity
|
||||
{
|
||||
get => m_Quantity;
|
||||
set => m_Quantity = (byte)value;
|
||||
get => _quantity;
|
||||
set => _quantity = (byte)value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Value
|
||||
{
|
||||
get => m_Value;
|
||||
set => m_Value = (byte)value;
|
||||
get => _value;
|
||||
set => _value = (byte)value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int Height
|
||||
{
|
||||
get => m_Height;
|
||||
set => m_Height = (byte)value;
|
||||
get => _height;
|
||||
set => _height = (byte)value;
|
||||
}
|
||||
|
||||
public int CalcHeight
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((Flags & TileFlag.Bridge) != 0)
|
||||
{
|
||||
return m_Height / 2;
|
||||
}
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public int CalcHeight => (Flags & TileFlag.Bridge) != 0 ? _height / 2 : _height;
|
||||
|
||||
return m_Height;
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Bridge
|
||||
{
|
||||
get => this[TileFlag.Bridge];
|
||||
set => this[TileFlag.Bridge] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Wall
|
||||
{
|
||||
get => this[TileFlag.Wall];
|
||||
set => this[TileFlag.Wall] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Window
|
||||
{
|
||||
get => this[TileFlag.Window];
|
||||
set => this[TileFlag.Window] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Impassable
|
||||
{
|
||||
get => this[TileFlag.Impassable];
|
||||
set => this[TileFlag.Impassable] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Surface
|
||||
{
|
||||
get => this[TileFlag.Surface];
|
||||
set => this[TileFlag.Surface] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool Roof
|
||||
{
|
||||
get => this[TileFlag.Roof];
|
||||
set => this[TileFlag.Roof] = value;
|
||||
}
|
||||
|
||||
[CommandProperty(AccessLevel.GameMaster, AccessLevel.Administrator)]
|
||||
public bool LightSource
|
||||
{
|
||||
get => this[TileFlag.LightSource];
|
||||
set => this[TileFlag.LightSource] = value;
|
||||
}
|
||||
|
||||
public bool this[TileFlag flag]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => (Flags & flag) != 0;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
set
|
||||
{
|
||||
if (value)
|
||||
{
|
||||
Flags |= flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
Flags &= ~flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum TileFlag : long
|
||||
public enum TileFlag : ulong
|
||||
{
|
||||
None = 0x00000000,
|
||||
Background = 0x00000001,
|
||||
|
|
@ -167,78 +211,125 @@ namespace Server
|
|||
Roof = 0x10000000,
|
||||
Door = 0x20000000,
|
||||
StairBack = 0x40000000,
|
||||
StairRight = 0x80000000
|
||||
StairRight = 0x80000000,
|
||||
|
||||
HS33 = 0x0000000100000000,
|
||||
HS34 = 0x0000000200000000,
|
||||
HS35 = 0x0000000400000000,
|
||||
HS36 = 0x0000000800000000,
|
||||
HS37 = 0x0000001000000000,
|
||||
HS38 = 0x0000002000000000,
|
||||
HS39 = 0x0000004000000000,
|
||||
HS40 = 0x0000008000000000,
|
||||
HS41 = 0x0000010000000000,
|
||||
HS42 = 0x0000020000000000,
|
||||
HS43 = 0x0000040000000000,
|
||||
HS44 = 0x0000080000000000,
|
||||
HS45 = 0x0000100000000000,
|
||||
HS46 = 0x0000200000000000,
|
||||
HS47 = 0x0000400000000000,
|
||||
HS48 = 0x0000800000000000,
|
||||
HS49 = 0x0001000000000000,
|
||||
HS50 = 0x0002000000000000,
|
||||
HS51 = 0x0004000000000000,
|
||||
HS52 = 0x0008000000000000,
|
||||
HS53 = 0x0010000000000000,
|
||||
HS54 = 0x0020000000000000,
|
||||
HS55 = 0x0040000000000000,
|
||||
HS56 = 0x0080000000000000,
|
||||
HS57 = 0x0100000000000000,
|
||||
HS58 = 0x0200000000000000,
|
||||
HS59 = 0x0400000000000000,
|
||||
HS60 = 0x0800000000000000,
|
||||
HS61 = 0x1000000000000000,
|
||||
HS62 = 0x2000000000000000,
|
||||
HS63 = 0x4000000000000000,
|
||||
HS64 = 0x8000000000000000
|
||||
}
|
||||
|
||||
public static class TileData
|
||||
{
|
||||
private static readonly byte[] m_StringBuffer = new byte[20];
|
||||
public static LandData[] LandTable { get; } = new LandData[0x4000];
|
||||
public static ItemData[] ItemTable { get; } = new ItemData[0x10000];
|
||||
public static int MaxLandValue { get; private set; }
|
||||
public static int MaxItemValue { get; private set; }
|
||||
|
||||
static TileData()
|
||||
{
|
||||
ItemTable = new ItemData[0x10000];
|
||||
LandTable = new LandData[0x4000];
|
||||
|
||||
if (Core.IsRunningFromXUnit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
private static void Load()
|
||||
{
|
||||
var filePath = Core.FindDataFile("tiledata.mul");
|
||||
|
||||
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
var bin = new BinaryReader(fs);
|
||||
var buffer = GC.AllocateUninitializedArray<byte>((int)fs.Length);
|
||||
fs.Read(buffer);
|
||||
var bin = new BufferReader(buffer);
|
||||
|
||||
var is7090 = fs.Length == 3188736;
|
||||
var is7000 = fs.Length == 1644544;
|
||||
bool is64BitFlags;
|
||||
const int landLength = 0x4000;
|
||||
int itemLength;
|
||||
|
||||
for (var i = 0; i < 0x4000; i++)
|
||||
if (fs.Length >= 3188736) // 7.0.9.0
|
||||
{
|
||||
// header
|
||||
if (is7090)
|
||||
{
|
||||
if (i == 1 || i > 0 && (i & 0x1F) == 0)
|
||||
{
|
||||
bin.ReadInt32();
|
||||
}
|
||||
}
|
||||
else if ((i & 0x1F) == 0)
|
||||
{
|
||||
bin.ReadInt32();
|
||||
}
|
||||
|
||||
var flags = (TileFlag)(is7090 ? bin.ReadInt64() : bin.ReadInt32());
|
||||
bin.ReadInt16(); // skip 2 bytes -- textureID
|
||||
|
||||
LandTable[i] = new LandData(ReadNameString(bin), flags);
|
||||
is64BitFlags = true;
|
||||
itemLength = 0x10000;
|
||||
}
|
||||
else if (fs.Length >= 1644544) // 7.0.0.0
|
||||
{
|
||||
is64BitFlags = false;
|
||||
itemLength = 0x8000;
|
||||
}
|
||||
else
|
||||
{
|
||||
is64BitFlags = false;
|
||||
itemLength = 0x4000;
|
||||
}
|
||||
|
||||
var length = is7090 ? 0x10000 :
|
||||
is7000 ? 0x8000 : 0x4000;
|
||||
for (var i = 0; i < landLength; i++)
|
||||
{
|
||||
if (is64BitFlags ? i == 1 || i > 0 && (i & 0x1F) == 0 : (i & 0x1F) == 0)
|
||||
{
|
||||
bin.ReadInt(); // header
|
||||
}
|
||||
|
||||
for (var i = 0; i < length; i++)
|
||||
var flags = (TileFlag)(is64BitFlags ? bin.ReadULong() : bin.ReadUInt());
|
||||
bin.ReadShort(); // skip 2 bytes -- textureID
|
||||
|
||||
LandTable[i] = new LandData(bin.ReadAscii(20), flags);
|
||||
}
|
||||
|
||||
for (var i = 0; i < itemLength; i++)
|
||||
{
|
||||
if ((i & 0x1F) == 0)
|
||||
{
|
||||
bin.ReadInt32(); // header
|
||||
bin.ReadInt(); // header
|
||||
}
|
||||
|
||||
var flags = (TileFlag)(is7090 ? bin.ReadInt64() : bin.ReadInt32());
|
||||
var flags = (TileFlag)(is64BitFlags ? bin.ReadULong() : bin.ReadUInt());
|
||||
int weight = bin.ReadByte();
|
||||
int quality = bin.ReadByte();
|
||||
bin.ReadInt16();
|
||||
int animation = bin.ReadUShort();
|
||||
bin.ReadByte();
|
||||
int quantity = bin.ReadByte();
|
||||
bin.ReadInt32();
|
||||
bin.ReadInt();
|
||||
bin.ReadByte();
|
||||
int value = bin.ReadByte();
|
||||
int height = bin.ReadByte();
|
||||
|
||||
ItemTable[i] = new ItemData(
|
||||
ReadNameString(bin),
|
||||
bin.ReadAscii(20),
|
||||
flags,
|
||||
weight,
|
||||
quality,
|
||||
animation,
|
||||
quantity,
|
||||
value,
|
||||
height
|
||||
|
|
@ -248,32 +339,5 @@ namespace Server
|
|||
MaxLandValue = LandTable.Length - 1;
|
||||
MaxItemValue = ItemTable.Length - 1;
|
||||
}
|
||||
|
||||
public static LandData[] LandTable { get; }
|
||||
|
||||
public static ItemData[] ItemTable { get; }
|
||||
|
||||
public static int MaxLandValue { get; }
|
||||
|
||||
public static int MaxItemValue { get; }
|
||||
|
||||
private static string ReadNameString(BinaryReader bin)
|
||||
{
|
||||
bin.Read(m_StringBuffer, 0, 20);
|
||||
|
||||
var count = 0;
|
||||
|
||||
while (count < 20)
|
||||
{
|
||||
if (m_StringBuffer[count] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return Encoding.ASCII.GetString(new ReadOnlySpan<byte>(m_StringBuffer, 0, count));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,81 +8,73 @@ namespace Server
|
|||
{
|
||||
public class TileMatrix
|
||||
{
|
||||
private static readonly List<TileMatrix> m_Instances = new();
|
||||
private static readonly List<TileMatrix> _instances = new();
|
||||
|
||||
private readonly int m_FileIndex;
|
||||
private readonly List<TileMatrix> m_FileShare = new();
|
||||
private readonly StaticTile[][][][][] _staticTiles;
|
||||
private readonly LandTile[][][] _landTiles;
|
||||
private readonly LandTile[] _invalidLandBlock;
|
||||
private readonly StaticTile[][][] _emptyStaticBlock;
|
||||
private readonly UOPIndex _mapIndex;
|
||||
private readonly int _fileIndex;
|
||||
private readonly Map _map;
|
||||
private readonly int[][] _staticPatches;
|
||||
private readonly int[][] _landPatches;
|
||||
private readonly List<TileMatrix> _fileShare = new();
|
||||
|
||||
private readonly BinaryReader m_IndexReader;
|
||||
|
||||
private readonly LandTile[] m_InvalidLandBlock;
|
||||
private readonly int[][] m_LandPatches;
|
||||
private readonly LandTile[][][] m_LandTiles;
|
||||
|
||||
private readonly UOPIndex m_MapIndex;
|
||||
|
||||
private readonly FileStream m_MapStream;
|
||||
|
||||
private readonly Map m_Owner;
|
||||
|
||||
private readonly int[][] m_StaticPatches;
|
||||
private readonly StaticTile[][][][][] m_StaticTiles;
|
||||
|
||||
private readonly TileList m_TilesList = new();
|
||||
|
||||
private TileList[][] m_Lists;
|
||||
private DateTime m_NextLandWarning;
|
||||
|
||||
private DateTime m_NextStaticWarning;
|
||||
|
||||
private StaticTile[] m_TileBuffer = new StaticTile[128];
|
||||
public TileMatrixPatch Patch { get; }
|
||||
public int BlockWidth { get; }
|
||||
public int BlockHeight { get; }
|
||||
public FileStream MapStream { get; }
|
||||
public FileStream IndexStream { get; }
|
||||
public FileStream DataStream { get; }
|
||||
public BinaryReader IndexReader { get; }
|
||||
|
||||
public TileMatrix(Map owner, int fileIndex, int mapID, int width, int height)
|
||||
{
|
||||
lock (m_Instances)
|
||||
lock (_instances)
|
||||
{
|
||||
for (var i = 0; i < m_Instances.Count; ++i)
|
||||
for (var i = 0; i < _instances.Count; ++i)
|
||||
{
|
||||
var tm = m_Instances[i];
|
||||
var tm = _instances[i];
|
||||
|
||||
if (tm.m_FileIndex == fileIndex)
|
||||
if (tm._fileIndex == fileIndex)
|
||||
{
|
||||
lock (m_FileShare)
|
||||
lock (_fileShare)
|
||||
{
|
||||
lock (tm.m_FileShare)
|
||||
lock (tm._fileShare)
|
||||
{
|
||||
tm.m_FileShare.Add(this);
|
||||
m_FileShare.Add(tm);
|
||||
tm._fileShare.Add(this);
|
||||
_fileShare.Add(tm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Instances.Add(this);
|
||||
_instances.Add(this);
|
||||
}
|
||||
|
||||
m_FileIndex = fileIndex;
|
||||
_fileIndex = fileIndex;
|
||||
BlockWidth = width >> 3;
|
||||
BlockHeight = height >> 3;
|
||||
|
||||
m_Owner = owner;
|
||||
_map = owner;
|
||||
|
||||
if (fileIndex != 0x7F)
|
||||
{
|
||||
var mapPath = Core.FindDataFile($"map{fileIndex}LegacyMUL.uop", false);
|
||||
var mapPath = Core.FindDataFile($"map{fileIndex}.mul", false);
|
||||
|
||||
if (mapPath != null)
|
||||
{
|
||||
m_MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
m_MapIndex = new UOPIndex(m_MapStream);
|
||||
MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
}
|
||||
else
|
||||
{
|
||||
mapPath = Core.FindDataFile($"map{fileIndex}.mul", false, true);
|
||||
mapPath = Core.FindDataFile($"map{fileIndex}LegacyMUL.uop", false, true);
|
||||
|
||||
if (mapPath != null)
|
||||
{
|
||||
m_MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
MapStream = new FileStream(mapPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
_mapIndex = new UOPIndex(MapStream);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +83,7 @@ namespace Server
|
|||
if (indexPath != null)
|
||||
{
|
||||
IndexStream = new FileStream(indexPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
m_IndexReader = new BinaryReader(IndexStream);
|
||||
IndexReader = new BinaryReader(IndexStream);
|
||||
}
|
||||
|
||||
var staticsPath = Core.FindDataFile($"statics{fileIndex}.mul", false, true);
|
||||
|
|
@ -102,39 +94,29 @@ namespace Server
|
|||
}
|
||||
}
|
||||
|
||||
EmptyStaticBlock = new StaticTile[8][][];
|
||||
_emptyStaticBlock = new StaticTile[8][][];
|
||||
|
||||
for (var i = 0; i < 8; ++i)
|
||||
{
|
||||
EmptyStaticBlock[i] = new StaticTile[8][];
|
||||
_emptyStaticBlock[i] = new StaticTile[8][];
|
||||
|
||||
for (var j = 0; j < 8; ++j)
|
||||
{
|
||||
EmptyStaticBlock[i][j] = Array.Empty<StaticTile>();
|
||||
_emptyStaticBlock[i][j] = new StaticTile[0];
|
||||
}
|
||||
}
|
||||
|
||||
m_InvalidLandBlock = new LandTile[196];
|
||||
_invalidLandBlock = new LandTile[196];
|
||||
|
||||
m_LandTiles = new LandTile[BlockWidth][][];
|
||||
m_StaticTiles = new StaticTile[BlockWidth][][][][];
|
||||
m_StaticPatches = new int[BlockWidth][];
|
||||
m_LandPatches = new int[BlockWidth][];
|
||||
_landTiles = new LandTile[BlockWidth][][];
|
||||
_staticTiles = new StaticTile[BlockWidth][][][][];
|
||||
_staticPatches = new int[BlockWidth][];
|
||||
_landPatches = new int[BlockWidth][];
|
||||
|
||||
Patch = new TileMatrixPatch(this, mapID);
|
||||
}
|
||||
|
||||
public FileStream IndexStream { get; }
|
||||
|
||||
public FileStream DataStream { get; }
|
||||
|
||||
public TileMatrixPatch Patch { get; }
|
||||
|
||||
public int BlockWidth { get; }
|
||||
|
||||
public int BlockHeight { get; }
|
||||
|
||||
public StaticTile[][][] EmptyStaticBlock { get; }
|
||||
public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock;
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public void SetStaticBlock(int x, int y, StaticTile[][][] value)
|
||||
|
|
@ -144,11 +126,11 @@ namespace Server
|
|||
return;
|
||||
}
|
||||
|
||||
m_StaticTiles[x] ??= new StaticTile[BlockHeight][][][];
|
||||
m_StaticTiles[x][y] = value;
|
||||
_staticTiles[x] ??= new StaticTile[BlockHeight][][][];
|
||||
_staticTiles[x][y] = value;
|
||||
|
||||
m_StaticPatches[x] ??= new int[(BlockHeight + 31) >> 5];
|
||||
m_StaticPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
||||
_staticPatches[x] ??= new int[(BlockHeight + 31) >> 5];
|
||||
_staticPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
|
|
@ -156,89 +138,102 @@ namespace Server
|
|||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || DataStream == null || IndexStream == null)
|
||||
{
|
||||
return EmptyStaticBlock;
|
||||
return _emptyStaticBlock;
|
||||
}
|
||||
|
||||
m_StaticTiles[x] ??= new StaticTile[BlockHeight][][][];
|
||||
_staticTiles[x] ??= new StaticTile[BlockHeight][][][];
|
||||
|
||||
var tiles = m_StaticTiles[x][y];
|
||||
var tiles = _staticTiles[x][y];
|
||||
|
||||
if (tiles != null)
|
||||
if (tiles == null)
|
||||
{
|
||||
return tiles;
|
||||
}
|
||||
|
||||
lock (m_FileShare)
|
||||
{
|
||||
for (var i = 0; tiles == null && i < m_FileShare.Count; ++i)
|
||||
lock (_fileShare)
|
||||
{
|
||||
var shared = m_FileShare[i];
|
||||
|
||||
lock (shared)
|
||||
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
|
||||
{
|
||||
if (x < shared.BlockWidth && y < shared.BlockHeight)
|
||||
var shared = _fileShare[i];
|
||||
|
||||
lock (shared)
|
||||
{
|
||||
var theirTiles = shared.m_StaticTiles[x];
|
||||
|
||||
if (theirTiles != null)
|
||||
if (x < shared.BlockWidth && y < shared.BlockHeight)
|
||||
{
|
||||
tiles = theirTiles[y];
|
||||
}
|
||||
var theirTiles = shared._staticTiles[x];
|
||||
|
||||
if (tiles != null)
|
||||
{
|
||||
var theirBits = shared.m_StaticPatches[x];
|
||||
|
||||
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
|
||||
if (theirTiles != null)
|
||||
{
|
||||
tiles = null;
|
||||
tiles = theirTiles[y];
|
||||
}
|
||||
|
||||
if (tiles != null)
|
||||
{
|
||||
var theirBits = shared._staticPatches[x];
|
||||
|
||||
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
|
||||
{
|
||||
tiles = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tiles ??= ReadStaticBlock(x, y);
|
||||
|
||||
_staticTiles[x][y] = tiles;
|
||||
}
|
||||
|
||||
return m_StaticTiles[x][y] = tiles ?? ReadStaticBlock(x, y);
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public StaticTile[] GetStaticTiles(int x, int y) => GetStaticBlock(x >> 3, y >> 3)[x & 0x7][y & 0x7];
|
||||
public StaticTile[] GetStaticTiles(int x, int y)
|
||||
{
|
||||
var tiles = GetStaticBlock(x >> 3, y >> 3);
|
||||
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
private readonly TileList m_TilesList = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public StaticTile[] GetStaticTiles(int x, int y, bool multis)
|
||||
{
|
||||
var tiles = GetStaticBlock(x >> 3, y >> 3);
|
||||
|
||||
if (!multis)
|
||||
if (multis)
|
||||
{
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
var eable = _map.GetMultiTilesAt(x, y);
|
||||
|
||||
if (eable == Map.NullEnumerable<StaticTile[]>.Instance)
|
||||
{
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
var any = false;
|
||||
|
||||
foreach (var multiTiles in eable)
|
||||
{
|
||||
if (!any)
|
||||
{
|
||||
any = true;
|
||||
}
|
||||
|
||||
m_TilesList.AddRange(multiTiles);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
if (!any)
|
||||
{
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
m_TilesList.AddRange(tiles[x & 0x7][y & 0x7]);
|
||||
|
||||
return m_TilesList.ToArray();
|
||||
}
|
||||
|
||||
var eable = m_Owner.GetMultiTilesAt(x, y);
|
||||
|
||||
if (eable == Map.NullEnumerable<StaticTile[]>.Instance)
|
||||
{
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
var any = false;
|
||||
|
||||
foreach (StaticTile[] multiTiles in eable)
|
||||
{
|
||||
any = true;
|
||||
m_TilesList.AddRange(multiTiles);
|
||||
}
|
||||
|
||||
eable.Free();
|
||||
|
||||
if (!any)
|
||||
{
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
m_TilesList.AddRange(tiles[x & 0x7][y & 0x7]);
|
||||
|
||||
return m_TilesList.ToArray();
|
||||
return tiles[x & 0x7][y & 0x7];
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
|
|
@ -249,41 +244,41 @@ namespace Server
|
|||
return;
|
||||
}
|
||||
|
||||
m_LandTiles[x] ??= new LandTile[BlockHeight][];
|
||||
m_LandTiles[x][y] = value;
|
||||
_landTiles[x] ??= new LandTile[BlockHeight][];
|
||||
_landTiles[x][y] = value;
|
||||
|
||||
m_LandPatches[x] ??= new int[(BlockHeight + 31) >> 5];
|
||||
m_LandPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
||||
_landPatches[x] ??= new int[(BlockHeight + 31) >> 5];
|
||||
_landPatches[x][y >> 5] |= 1 << (y & 0x1F);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
public LandTile[] GetLandBlock(int x, int y)
|
||||
{
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || m_MapStream == null)
|
||||
if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || MapStream == null)
|
||||
{
|
||||
return m_InvalidLandBlock;
|
||||
return _invalidLandBlock;
|
||||
}
|
||||
|
||||
m_LandTiles[x] ??= new LandTile[BlockHeight][];
|
||||
_landTiles[x] ??= new LandTile[BlockHeight][];
|
||||
|
||||
var tiles = m_LandTiles[x][y];
|
||||
var tiles = _landTiles[x][y];
|
||||
|
||||
if (tiles != null)
|
||||
{
|
||||
return tiles;
|
||||
}
|
||||
|
||||
lock (m_FileShare)
|
||||
lock (_fileShare)
|
||||
{
|
||||
for (var i = 0; tiles == null && i < m_FileShare.Count; ++i)
|
||||
for (var i = 0; tiles == null && i < _fileShare.Count; ++i)
|
||||
{
|
||||
var shared = m_FileShare[i];
|
||||
var shared = _fileShare[i];
|
||||
|
||||
lock (shared)
|
||||
{
|
||||
if (x < shared.BlockWidth && y < shared.BlockHeight)
|
||||
{
|
||||
var theirTiles = shared.m_LandTiles[x];
|
||||
var theirTiles = shared._landTiles[x];
|
||||
|
||||
if (theirTiles != null)
|
||||
{
|
||||
|
|
@ -292,7 +287,7 @@ namespace Server
|
|||
|
||||
if (tiles != null)
|
||||
{
|
||||
var theirBits = shared.m_LandPatches[x];
|
||||
var theirBits = shared._landPatches[x];
|
||||
|
||||
if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0)
|
||||
{
|
||||
|
|
@ -304,24 +299,37 @@ namespace Server
|
|||
}
|
||||
}
|
||||
|
||||
return m_LandTiles[x][y] = tiles ?? ReadLandBlock(x, y);
|
||||
tiles ??= ReadLandBlock(x, y);
|
||||
|
||||
_landTiles[x][y] = tiles;
|
||||
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public LandTile GetLandTile(int x, int y) => GetLandBlock(x >> 3, y >> 3)[((y & 0x7) << 3) + (x & 0x7)];
|
||||
public LandTile GetLandTile(int x, int y)
|
||||
{
|
||||
var tiles = GetLandBlock(x >> 3, y >> 3);
|
||||
|
||||
return tiles[((y & 0x7) << 3) + (x & 0x7)];
|
||||
}
|
||||
|
||||
private TileList[][] m_Lists;
|
||||
|
||||
private StaticTile[] m_TileBuffer = new StaticTile[128];
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
private unsafe StaticTile[][][] ReadStaticBlock(int x, int y)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_IndexReader.BaseStream.Seek((x * BlockHeight + y) * 12, SeekOrigin.Begin);
|
||||
IndexReader.BaseStream.Seek((x * BlockHeight + y) * 12, SeekOrigin.Begin);
|
||||
|
||||
var lookup = m_IndexReader.ReadInt32();
|
||||
var length = m_IndexReader.ReadInt32();
|
||||
var lookup = IndexReader.ReadInt32();
|
||||
var length = IndexReader.ReadInt32();
|
||||
|
||||
if (lookup < 0 || length <= 0)
|
||||
{
|
||||
return EmptyStaticBlock;
|
||||
return _emptyStaticBlock;
|
||||
}
|
||||
|
||||
var count = length / 7;
|
||||
|
|
@ -333,16 +341,11 @@ namespace Server
|
|||
m_TileBuffer = new StaticTile[count];
|
||||
}
|
||||
|
||||
var staTiles = m_TileBuffer; // new StaticTile[tileCount];
|
||||
var staTiles = m_TileBuffer; //new StaticTile[tileCount];
|
||||
|
||||
fixed (StaticTile* pTiles = staTiles)
|
||||
{
|
||||
var ptr = DataStream.SafeFileHandle?.DangerousGetHandle();
|
||||
if (ptr == null)
|
||||
{
|
||||
throw new Exception($"Cannot open {DataStream.Name}");
|
||||
}
|
||||
NativeReader.Read(ptr.Value, pTiles, length);
|
||||
NativeReader.Read(DataStream, pTiles, length);
|
||||
|
||||
if (m_Lists == null)
|
||||
{
|
||||
|
|
@ -366,7 +369,8 @@ namespace Server
|
|||
while (pCur < pEnd)
|
||||
{
|
||||
lists[pCur->m_X & 0x7][pCur->m_Y & 0x7].Add(pCur->m_ID, pCur->m_Z);
|
||||
pCur += 1;
|
||||
|
||||
pCur = pCur + 1;
|
||||
}
|
||||
|
||||
var tiles = new StaticTile[8][][];
|
||||
|
|
@ -384,24 +388,29 @@ namespace Server
|
|||
return tiles;
|
||||
}
|
||||
}
|
||||
catch (EndOfStreamException)
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
if (DateTime.UtcNow >= m_NextStaticWarning)
|
||||
{
|
||||
Console.WriteLine("Warning: Static EOS for {0} ({1}, {2})", m_Owner, x, y);
|
||||
Console.WriteLine("Warning: Static EOS for {0} ({1}, {2})", _map, x, y);
|
||||
m_NextStaticWarning = DateTime.UtcNow + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
|
||||
return EmptyStaticBlock;
|
||||
return _emptyStaticBlock;
|
||||
}
|
||||
}
|
||||
|
||||
private DateTime m_NextStaticWarning;
|
||||
private DateTime m_NextLandWarning;
|
||||
|
||||
public void Force()
|
||||
{
|
||||
if ((AssemblyHandler.Assemblies?.Length ?? 0) == 0)
|
||||
if (AssemblyHandler.Assemblies?.Length > 0)
|
||||
{
|
||||
throw new Exception();
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("No assemblies were loaded, therefore we cannot load TileMatrix.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
|
|
@ -411,45 +420,40 @@ namespace Server
|
|||
{
|
||||
var offset = (x * BlockHeight + y) * 196 + 4;
|
||||
|
||||
if (m_MapIndex != null)
|
||||
if (_mapIndex != null)
|
||||
{
|
||||
offset = m_MapIndex.Lookup(offset);
|
||||
offset = _mapIndex.Lookup(offset);
|
||||
}
|
||||
|
||||
m_MapStream.Seek(offset, SeekOrigin.Begin);
|
||||
MapStream.Seek(offset, SeekOrigin.Begin);
|
||||
|
||||
var tiles = new LandTile[64];
|
||||
|
||||
fixed (LandTile* pTiles = tiles)
|
||||
{
|
||||
var ptr = m_MapStream.SafeFileHandle?.DangerousGetHandle();
|
||||
if (ptr == null)
|
||||
{
|
||||
throw new Exception($"Cannot open {m_MapStream.Name}");
|
||||
}
|
||||
NativeReader.Read(ptr.Value, pTiles, 192);
|
||||
NativeReader.Read(MapStream, pTiles, 192);
|
||||
}
|
||||
|
||||
return tiles;
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (DateTime.UtcNow >= m_NextLandWarning)
|
||||
{
|
||||
Console.WriteLine("Warning: Land EOS for {0} ({1}, {2})", m_Owner, x, y);
|
||||
Console.WriteLine("Warning: Land EOS for {0} ({1}, {2})", _map, x, y);
|
||||
m_NextLandWarning = DateTime.UtcNow + TimeSpan.FromMinutes(1.0);
|
||||
}
|
||||
|
||||
return m_InvalidLandBlock;
|
||||
return _invalidLandBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_MapIndex?.Close();
|
||||
m_MapStream?.Close();
|
||||
_mapIndex?.Close();
|
||||
MapStream?.Close();
|
||||
DataStream?.Close();
|
||||
m_IndexReader?.Close();
|
||||
IndexReader?.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -461,11 +465,7 @@ namespace Server
|
|||
|
||||
public int ID => m_ID;
|
||||
|
||||
public int Z
|
||||
{
|
||||
get => m_Z;
|
||||
set => m_Z = (sbyte)value;
|
||||
}
|
||||
public int Z { get => m_Z; set => m_Z = (sbyte)value; }
|
||||
|
||||
public int Height => 0;
|
||||
|
||||
|
|
@ -495,29 +495,13 @@ namespace Server
|
|||
|
||||
public int ID => m_ID;
|
||||
|
||||
public int X
|
||||
{
|
||||
get => m_X;
|
||||
set => m_X = (byte)value;
|
||||
}
|
||||
public int X { get => m_X; set => m_X = (byte)value; }
|
||||
|
||||
public int Y
|
||||
{
|
||||
get => m_Y;
|
||||
set => m_Y = (byte)value;
|
||||
}
|
||||
public int Y { get => m_Y; set => m_Y = (byte)value; }
|
||||
|
||||
public int Z
|
||||
{
|
||||
get => m_Z;
|
||||
set => m_Z = (sbyte)value;
|
||||
}
|
||||
public int Z { get => m_Z; set => m_Z = (sbyte)value; }
|
||||
|
||||
public int Hue
|
||||
{
|
||||
get => m_Hue;
|
||||
set => m_Hue = (short)value;
|
||||
}
|
||||
public int Hue { get => m_Hue; set => m_Hue = (short)value; }
|
||||
|
||||
public int Height => TileData.ItemTable[m_ID & TileData.MaxItemValue].Height;
|
||||
|
||||
|
|
@ -558,10 +542,37 @@ namespace Server
|
|||
|
||||
public class UOPIndex
|
||||
{
|
||||
private readonly UOPEntry[] m_Entries;
|
||||
private readonly int m_Length;
|
||||
private class UOPEntry : IComparable<UOPEntry>
|
||||
{
|
||||
public int m_Offset;
|
||||
public readonly int m_Length;
|
||||
public int m_Order;
|
||||
|
||||
public UOPEntry(int offset, int length)
|
||||
{
|
||||
m_Offset = offset;
|
||||
m_Length = length;
|
||||
m_Order = 0;
|
||||
}
|
||||
|
||||
public int CompareTo(UOPEntry other)
|
||||
{
|
||||
return m_Order.CompareTo(other.m_Order);
|
||||
}
|
||||
}
|
||||
|
||||
private class OffsetComparer : IComparer<UOPEntry>
|
||||
{
|
||||
public static readonly IComparer<UOPEntry> Instance = new OffsetComparer();
|
||||
|
||||
public int Compare(UOPEntry x, UOPEntry y) => x!.m_Offset.CompareTo(y!.m_Offset);
|
||||
}
|
||||
|
||||
private readonly BinaryReader m_Reader;
|
||||
private readonly int m_Length;
|
||||
private readonly UOPEntry[] m_Entries;
|
||||
|
||||
public int Version { get; }
|
||||
|
||||
public UOPIndex(FileStream stream)
|
||||
{
|
||||
|
|
@ -603,7 +614,8 @@ namespace Server
|
|||
|
||||
stream.Seek(18, SeekOrigin.Current);
|
||||
}
|
||||
} while (nextTable != 0 && nextTable < m_Length);
|
||||
}
|
||||
while (nextTable != 0 && nextTable < m_Length);
|
||||
|
||||
entries.Sort(OffsetComparer.Instance);
|
||||
|
||||
|
|
@ -622,8 +634,6 @@ namespace Server
|
|||
m_Entries = entries.ToArray();
|
||||
}
|
||||
|
||||
public int Version { get; }
|
||||
|
||||
public int Lookup(int offset)
|
||||
{
|
||||
var total = 0;
|
||||
|
|
@ -647,30 +657,5 @@ namespace Server
|
|||
{
|
||||
m_Reader.Close();
|
||||
}
|
||||
|
||||
private class UOPEntry : IComparable<UOPEntry>
|
||||
{
|
||||
public readonly int m_Length;
|
||||
public int m_Offset;
|
||||
public int m_Order;
|
||||
|
||||
public UOPEntry(int offset, int length)
|
||||
{
|
||||
m_Offset = offset;
|
||||
m_Length = length;
|
||||
m_Order = 0;
|
||||
}
|
||||
|
||||
public int CompareTo(UOPEntry other) => m_Order.CompareTo(other.m_Order);
|
||||
}
|
||||
|
||||
private class OffsetComparer : IComparer<UOPEntry>
|
||||
{
|
||||
public static readonly IComparer<UOPEntry> Instance = new OffsetComparer();
|
||||
|
||||
public int Compare(UOPEntry x, UOPEntry y) =>
|
||||
x == null ? y == null ? 0 : 1 :
|
||||
y == null ? -1 : x.m_Offset.CompareTo(y.m_Offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
|
|
@ -6,45 +5,47 @@ namespace Server
|
|||
{
|
||||
public class TileMatrixPatch
|
||||
{
|
||||
private StaticTile[] m_TileBuffer = new StaticTile[128];
|
||||
private StaticTile[] _tileBuffer = new StaticTile[128];
|
||||
|
||||
public static bool PatchLandEnabled { get; private set; }
|
||||
public static bool PatchStaticsEnabled { get; private set; }
|
||||
|
||||
public int LandBlocks { get; }
|
||||
public int StaticBlocks { get; }
|
||||
|
||||
public TileMatrixPatch(TileMatrix matrix, int index)
|
||||
{
|
||||
if (!Enabled)
|
||||
if (PatchLandEnabled)
|
||||
{
|
||||
return;
|
||||
var mapDataPath = Core.FindDataFile($"mapdif{index}.mul", false);
|
||||
var mapIndexPath = Core.FindDataFile($"mapdifl{index}.mul", false);
|
||||
|
||||
if (mapDataPath != null && mapIndexPath != null)
|
||||
{
|
||||
LandBlocks = PatchLand(matrix, mapDataPath, mapIndexPath);
|
||||
}
|
||||
}
|
||||
|
||||
var mapDataPath = Core.FindDataFile($"mapdif{index}.mul", false);
|
||||
var mapIndexPath = Core.FindDataFile($"mapdifl{index}.mul", false);
|
||||
|
||||
if (File.Exists(mapDataPath) && File.Exists(mapIndexPath))
|
||||
if (PatchStaticsEnabled)
|
||||
{
|
||||
LandBlocks = PatchLand(matrix, mapDataPath, mapIndexPath);
|
||||
}
|
||||
var staDataPath = Core.FindDataFile($"stadif{index}.mul", false);
|
||||
var staIndexPath = Core.FindDataFile($"stadifl{index}.mul", false);
|
||||
var staLookupPath = Core.FindDataFile($"stadifi{index}.mul", false);
|
||||
|
||||
var staDataPath = Core.FindDataFile($"stadif{index}.mul", false);
|
||||
var staIndexPath = Core.FindDataFile($"stadifl{index}.mul", false);
|
||||
var staLookupPath = Core.FindDataFile($"stadifi{index}.mul", false);
|
||||
|
||||
if (File.Exists(staDataPath) && File.Exists(staIndexPath) && File.Exists(staLookupPath))
|
||||
{
|
||||
StaticBlocks = PatchStatics(matrix, staDataPath, staIndexPath, staLookupPath);
|
||||
if (staDataPath != null && staIndexPath != null && staLookupPath != null)
|
||||
{
|
||||
StaticBlocks = PatchStatics(matrix, staDataPath, staIndexPath, staLookupPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Enabled { get; set; }
|
||||
|
||||
public static void Configure()
|
||||
{
|
||||
// Using this requires the old mapDif files to be present. Only needed to support Clients < 6.0.0.0
|
||||
Enabled = ServerConfiguration.GetOrUpdateSetting("maps.enableTileMatrixPatches", !Core.SE);
|
||||
PatchLandEnabled = ServerConfiguration.GetOrUpdateSetting("maps.enableMapDiffPatches", false);
|
||||
PatchStaticsEnabled = ServerConfiguration.GetOrUpdateSetting("maps.enableStaticsDiffPatches", false);
|
||||
}
|
||||
|
||||
public int LandBlocks { get; }
|
||||
|
||||
public int StaticBlocks { get; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
private unsafe int PatchLand(TileMatrix matrix, string dataPath, string indexPath)
|
||||
{
|
||||
|
|
@ -66,13 +67,7 @@ namespace Server
|
|||
|
||||
fixed (LandTile* pTiles = tiles)
|
||||
{
|
||||
var ptr = fsData.SafeFileHandle?.DangerousGetHandle();
|
||||
if (ptr == null)
|
||||
{
|
||||
throw new Exception($"Cannot open {fsData.Name}");
|
||||
}
|
||||
|
||||
NativeReader.Read(ptr.Value, pTiles, 192);
|
||||
NativeReader.Read(fsData, pTiles, 192);
|
||||
}
|
||||
|
||||
matrix.SetLandBlock(x, y, tiles);
|
||||
|
|
@ -126,22 +121,16 @@ namespace Server
|
|||
|
||||
var tileCount = length / 7;
|
||||
|
||||
if (m_TileBuffer.Length < tileCount)
|
||||
if (_tileBuffer.Length < tileCount)
|
||||
{
|
||||
m_TileBuffer = new StaticTile[tileCount];
|
||||
_tileBuffer = new StaticTile[tileCount];
|
||||
}
|
||||
|
||||
var staTiles = m_TileBuffer;
|
||||
var staTiles = _tileBuffer;
|
||||
|
||||
fixed (StaticTile* pTiles = staTiles)
|
||||
{
|
||||
var ptr = fsData.SafeFileHandle?.DangerousGetHandle();
|
||||
if (ptr == null)
|
||||
{
|
||||
throw new Exception($"Cannot open {fsData.Name}");
|
||||
}
|
||||
|
||||
NativeReader.Read(ptr.Value, pTiles, length);
|
||||
NativeReader.Read(fsData, pTiles, length);
|
||||
|
||||
StaticTile* pCur = pTiles, pEnd = pTiles + tileCount;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Text.Json;
|
||||
using Server.Json;
|
||||
using Server.Network;
|
||||
using Server.Utilities;
|
||||
|
||||
namespace Server.Engines.Spawners
|
||||
|
|
@ -43,6 +44,8 @@ namespace Server.Engines.Spawners
|
|||
var file = files[i];
|
||||
from.SendMessage("GenerateSpawners: Generating spawners for {0}...", file.Name);
|
||||
|
||||
NetState.FlushAll();
|
||||
|
||||
try
|
||||
{
|
||||
var spawners = JsonConfig.Deserialize<List<DynamicJson>>(file.FullName);
|
||||
|
|
|
|||
|
|
@ -259,7 +259,7 @@ namespace Server.Spells
|
|||
return false;
|
||||
}
|
||||
|
||||
public static bool CanRevealCaster(Mobile m) => m is BaseCreature c && !c.Controlled;
|
||||
public static bool CanRevealCaster(Mobile m) => m is BaseCreature { Controlled: false };
|
||||
|
||||
public static void GetSurfaceTop(ref IPoint3D p)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{
|
||||
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
|
||||
"version": "0.8.1"
|
||||
"version": "0.8.2"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue