Exemple #1
0
        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);
        }
Exemple #4
0
        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
        }