private static void WritePacked(ReadOnlySpan <byte> span, ref SpanWriter writer) { var length = span.Length; if (length == 0) { writer.Write(0); return; } var wantLength = Zlib.MaxPackSize(length); var packBuffer = _packBuffer; byte[] rentedBuffer = null; if (wantLength > packBuffer.Length) { packBuffer = rentedBuffer = ArrayPool <byte> .Shared.Rent(wantLength); } var packLength = wantLength; var error = Zlib.Pack(packBuffer, ref packLength, span, length, ZlibQuality.Default); if (error != ZlibError.Okay) { Utility.PushColor(ConsoleColor.Red); Core.WriteConsoleLine($"Gump compression failed {error}"); Utility.PopColor(); writer.Write(4); writer.Write(0); return; } writer.Write(4 + packLength); writer.Write(length); writer.Write(packBuffer.AsSpan(0, packLength)); if (rentedBuffer != null) { ArrayPool <byte> .Shared.Return(rentedBuffer); } }
public static void SendDisplayGump(this NetState ns, Gump gump, out int switches, out int entries) { switches = 0; entries = 0; if (ns == null) { return; } var packed = ns.Unpack; var layoutWriter = new SpanWriter(_layoutBuffer); if (!gump.Draggable) { layoutWriter.Write(Gump.NoMove); } if (!gump.Closable) { layoutWriter.Write(Gump.NoClose); } if (!gump.Disposable) { layoutWriter.Write(Gump.NoDispose); } if (!gump.Resizable) { layoutWriter.Write(Gump.NoResize); } var stringsList = new OrderedHashSet <string>(11); foreach (var entry in gump.Entries) { entry.AppendTo(ref layoutWriter, stringsList, ref entries, ref switches); } var stringsWriter = new SpanWriter(_stringsBuffer); foreach (var str in stringsList) { var s = str ?? ""; stringsWriter.Write((ushort)s.Length); stringsWriter.WriteBigUni(s); } int maxLength; if (packed) { var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); maxLength = 40 + worstLayoutLength + worstStringsLength; } else { maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; } var writer = new SpanWriter(stackalloc byte[maxLength]); writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID writer.Seek(2, SeekOrigin.Current); writer.Write(gump.Serial); writer.Write(gump.TypeID); writer.Write(gump.X); writer.Write(gump.Y); if (packed) { layoutWriter.Write((byte)0); // Layout text terminator WritePacked(layoutWriter.Span, ref writer); writer.Write(stringsList.Count); WritePacked(stringsWriter.Span, ref writer); } else { writer.Write((ushort)layoutWriter.BytesWritten); writer.Write(layoutWriter.Span); writer.Write((ushort)stringsList.Count); writer.Write(stringsWriter.Span); } writer.WritePacketLength(); ns.Send(writer.Span); }
public static byte[] CreateHouseDesignStateDetailed(uint serial, int revision, MultiComponentList components) { var xMin = components.Min.X; var yMin = components.Min.Y; var xMax = components.Max.X; var yMax = components.Max.Y; var width = xMax - xMin + 1; var height = yMax - yMin + 1; var tiles = components.List; var planeLength = width * height * 2; using var planeOffsetWriter = new SpanWriter(0x500, true); using var planesWriter = new SpanWriter(planeLength * planeCount); Span <bool> planesUsed = stackalloc bool[9]; int index; var totalPlaneOffsets = 0; var totalPlanes = 0; for (var i = 0; i < tiles.Length; ++i) { var mte = tiles[i]; var x = mte.OffsetX - xMin; var y = mte.OffsetY - yMin; int z = mte.OffsetZ; var floor = TileData.ItemTable[mte.ItemId & TileData.MaxItemValue].Height <= 0; int plane = z switch { 0 => 0, 7 => 1, 27 => 2, 47 => 3, 67 => 4, _ => - 1 }; if (plane > -1) { int size; if (plane == 0) { size = height; } else if (floor) { size = height - 2; x -= 1; y -= 1; } else { size = height - 1; plane += 4; } index = (x * size + y) * 2; if (x >= 0 && y >= 0 && y < size && index + 1 < maxPlaneLength) { var planeUsed = planesUsed[plane]; var planeWriterIndex = planeLength * plane; if (!planeUsed) { planesUsed[plane] = true; totalPlanes++; planesWriter.Seek(planeWriterIndex, SeekOrigin.Begin); planesWriter.Clear(planeLength); } planesWriter.Seek(planeWriterIndex + index, SeekOrigin.Begin); planesWriter.Write(mte.ItemId); continue; } } planeOffsetWriter.Write(mte.ItemId); planeOffsetWriter.Write((byte)mte.OffsetX); planeOffsetWriter.Write((byte)mte.OffsetY); planeOffsetWriter.Write((byte)mte.OffsetZ); totalPlaneOffsets++; } var maxPlanesLength = (Zlib.MaxPackSize(planeLength) + 4) * totalPlanes; var maxPlaneOffsetLength = totalPlaneOffsets == 0 ? 0 : (maxPackedPlaneOffsetBuffer + 4) * (totalPlaneOffsets / maxPerPlaneOffsetBuffer + 1); var buffer = GC.AllocateUninitializedArray <byte>(18 + maxPlanesLength + maxPlaneOffsetLength); var writer = new SpanWriter(buffer); writer.Write((byte)0xD8); // Packet ID writer.Seek(2, SeekOrigin.Current); // Length writer.Write((byte)0x03); // Compression Type writer.Write((byte)0x00); // Unknown writer.Write(serial); writer.Write(revision); writer.Write((short)tiles.Length); writer.Seek(3, SeekOrigin.Current); // Buffer Length, Plane Count var totalLength = 1; // includes plane count for (var i = 0; i < planeCount; i++) { if (!planesUsed[i]) { continue; } int size = i switch { 0 => planeLength, < 5 => (width - 1) * (height - 2) * 2, _ => width * (height - 1) * 2 }; var planeWriterIndex = planeLength * i; var source = planesWriter.RawBuffer.Slice(planeWriterIndex, size); writer.Write((byte)(0x20 | i)); WritePacked(source, ref writer, out int destLength); totalLength += 4 + destLength; } index = 0; while (totalPlaneOffsets > 0) { var count = Math.Min(maxPerPlaneOffsetBuffer, totalPlaneOffsets); totalPlaneOffsets -= count; var source = planeOffsetWriter.RawBuffer.Slice(index * 5, count * 5); writer.Write((byte)(9 + index++)); WritePacked(source, ref writer, out int destLength); totalLength += 4 + destLength; totalPlanes++; } writer.Seek(15, SeekOrigin.Begin); writer.Write((short)totalLength); writer.Write((byte)totalPlanes); writer.WritePacketLength(); // TODO: Avoid this somehow. Array.Resize(ref buffer, writer.BytesWritten); return(buffer); }
public static void CreateDisplayGump(Gump gump, out int switches, out int entries) { switches = 0; entries = 0; const bool packed = false; var layoutWriter = new SpanWriter(_layoutBuffer); if (!gump.Draggable) { layoutWriter.Write(Gump.NoMove); } if (!gump.Closable) { layoutWriter.Write(Gump.NoClose); } if (!gump.Disposable) { layoutWriter.Write(Gump.NoDispose); } if (!gump.Resizable) { layoutWriter.Write(Gump.NoResize); } var stringsList = new OrderedHashSet <string>(32); foreach (var entry in gump.Entries) { entry.AppendTo(ref layoutWriter, stringsList, ref entries, ref switches); } var stringsWriter = new SpanWriter(_stringsBuffer); foreach (var str in stringsList) { var s = str ?? ""; stringsWriter.Write((ushort)s.Length); stringsWriter.WriteBigUni(s); } int maxLength; if (packed) { var worstLayoutLength = Zlib.MaxPackSize(layoutWriter.BytesWritten); var worstStringsLength = Zlib.MaxPackSize(stringsWriter.BytesWritten); maxLength = 40 + worstLayoutLength + worstStringsLength; } else { maxLength = 23 + layoutWriter.BytesWritten + stringsWriter.BytesWritten; } var writer = new SpanWriter(maxLength); writer.Write((byte)(packed ? 0xDD : 0xB0)); // Packet ID writer.Seek(2, SeekOrigin.Current); writer.Write(gump.Serial); writer.Write(gump.TypeID); writer.Write(gump.X); writer.Write(gump.Y); if (packed) { layoutWriter.Write((byte)0); // Layout text terminator OutgoingGumpPackets.WritePacked(layoutWriter.Span, ref writer); writer.Write(stringsList.Count); OutgoingGumpPackets.WritePacked(stringsWriter.Span, ref writer); } else { writer.Write((ushort)layoutWriter.BytesWritten); writer.Write(layoutWriter.Span); writer.Write((ushort)stringsList.Count); writer.Write(stringsWriter.Span); } writer.WritePacketLength(); layoutWriter.Dispose(); // Just in case stringsWriter.Dispose(); // Just in case }