{"id":"elentium/voidsentry","name":"voidsentry","scope":"elentium","platform":"roblox","description":"low-level buffer serializer with high performance & flexibility","version":"0.0.8","latest":"0.0.8","versions":["0.0.5","0.0.6","0.0.7","0.0.8"],"license":"Apache-2.0","licenseRating":"safe","licenseCaveats":["Modified files must carry a notice of changes. If the package ships a NOTICE file, its attributions must be preserved."],"licenseVerified":true,"dependencies":{},"integrity":"41d79344e8f080b4fb799943d35d410b550b15cdbb602ca0c9c78f1d88845a0d","likes":0,"downloads":0,"install":"forest install elentium/voidsentry","url":"https://forest.dev/p/roblox/elentium/voidsentry","files":"https://api.forest.dev/ai/package/roblox/elentium/voidsentry/files","readme":"<p align=\"center\">\n  <img src=\"https://img.shields.io/badge/VoidSentry-buffer_serialization-7c3aed?style=for-the-badge&labelColor=5b21b6\" alt=\"VoidSentry\" />\n</p>\n\n<p align=\"center\">\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/License-Apache%202.0-blue?style=flat-square\" alt=\"License\" /></a>\n  <a href=\"https://wally.run/package/elentium/voidsentry\"><img src=\"https://img.shields.io/badge/📦_Wally-elentium%2Fvoidsentry-00b4ab?style=flat-square\" alt=\"Wally\" /></a>\n  <a href=\"https://elentium.github.io/VoidSentry/\"><img src=\"https://img.shields.io/badge/📖_Docs-GitHub_Pages-8b5cf6?style=flat-square\" alt=\"Documentation\" /></a>\n</p>\n\n<p align=\"center\">\n  <strong>A high-performance buffer serialization library for Roblox</strong>\n</p>\n\n---\n\nVoidSentry is a powerful, low-level buffer serializer designed for efficient data transmission in Roblox games. It provides both static (schema-based) and dynamic (schemaless) serialization with support for a wide range of data types.\n\n## Features\n\n- **Two Serialization Modes**\n  - **Static Serializer**: Schema-based serialization for maximum performance\n  - **Dynamic Serializer**: Flexible schemaless serialization with type inference\n\n- **Rich Type Support**: 30+ built-in types including primitives, Roblox types, and complex data structures\n\n- **Optional Compression**: Built-in Zstd compression support for reduced bandwidth\n\n- **Type Safety**: Full strict-mode Luau type annotations for better IDE support\n\n- **High Performance**: Native optimizations and efficient buffer operations\n\n- **Zero Dependencies**: Standalone library with no external requirements\n\n## Installation\n\n### Using Wally\n\nAdd VoidSentry to your `wally.toml`:\n\n```toml\n[dependencies]\nVoidSentry = \"elentium/voidsentry@0.0.8\"\n```\n\nThen run:\n\n```bash\nwally install\n```\n\n### Manual Installation\n\n1. Download the latest release\n2. Place the `VoidSentry` folder in your `ReplicatedStorage.Packages`\n3. Require it in your scripts\n\n## Quick Start\n\n### Static Serializer (Recommended for Performance)\n\nThe static serializer requires you to define a schema upfront, but offers the best performance:\n\n```luau\n--!strict\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal VoidSentry = require(ReplicatedStorage.Packages.VoidSentry)\n\nlocal Types = VoidSentry.Types\n\n-- Create a serializer with a fixed schema\nlocal Serializer = VoidSentry.Static.new(\n    nil, -- No compression\n    Types.int32,\n    Types.string,\n    Types.struct({\n        Hello = Types.string,\n        World = Types.int32,\n    })\n)\n\n-- Serialize data\nlocal b = Serializer:serialize(\n    nil, -- No offset\n    42,\n    \"Hello, world!\",\n    {\n        Hello = \"hi\",\n        World = 999,\n    }\n)\n\nprint(\"Buffer size:\", buffer.len(b)) -- 27 bytes\n\n-- Deserialize data\nlocal int, str, struct = Serializer:deserialize(nil, b)\nprint(int, str, struct) -- 42, \"Hello, world!\", {Hello = \"hi\", World = 999}\n```\n\n### Dynamic Serializer (Flexible)\n\nThe dynamic serializer automatically infers types, offering more flexibility at the cost of performance:\n\n```luau\n--!strict\nlocal ReplicatedStorage = game:GetService(\"ReplicatedStorage\")\nlocal VoidSentry = require(ReplicatedStorage.Packages.VoidSentry)\n\nlocal Dynamic = VoidSentry.Dynamic\n\n-- No schema required!\nlocal buffer = Dynamic.serialize(\n    nil, -- No compression\n    nil, -- No offset\n    42,\n    \"Dynamic serialization\",\n    Vector3.new(10, 20, 30),\n    true\n)\n\n-- Deserialize automatically\nlocal int, str, vec, bool = Dynamic.deserialize(nil, nil, buffer)\nprint(int, str, vec, bool)\n```\n\n## API Reference\n\n### Static Serializer\n\n#### `VoidSentry.Static.new(compressionLevel: number?, ...TypeNode): StaticObject`\n\nCreates a new static serializer with a fixed schema.\n\n**Parameters:**\n\n- `compressionLevel` (optional): Zstd compression level (-7 to 22, or `nil` for no compression)\n- `...TypeNode`: Variable number of type nodes defining the schema\n\n**Returns:** A `StaticObject` with `serialize` and `deserialize` methods\n\n#### `StaticObject:serialize(offset: number?, ...values): buffer`\n\nSerializes data according to the schema.\n\n**Parameters:**\n\n- `offset` (optional): Starting byte offset in the buffer (reserves `offset` bytes at the beginning for custom metadata)\n- `...values`: Values matching the schema types\n\n**Returns:** A buffer containing the serialized data\n\n#### `StaticObject:deserialize(offset: number?, buffer: buffer): ...values`\n\nDeserializes data from a buffer.\n\n**Parameters:**\n\n- `offset` (optional): Starting byte offset to read from (skips `offset` bytes at the beginning, e.g., custom metadata)\n- `buffer`: The buffer to deserialize\n\n**Returns:** One or multiple values matching the schema\n\n### Dynamic Serializer\n\n#### `VoidSentry.Dynamic.serialize(compressionLevel: number?, offset: number?, ...values): buffer`\n\nSerializes data with automatic type inference.\n\n**Parameters:**\n\n- `compressionLevel` (optional): Zstd compression level (-7 to 22)\n- `offset` (optional): Starting byte offset (reserves `offset` bytes at the beginning for custom metadata)\n- `...values`: Any serializable values\n\n**Returns:** A buffer with type information and data\n\n#### `VoidSentry.Dynamic.deserialize(compressionLevel: number?, offset: number?, buffer: buffer): ...values`\n\nDeserializes data with embedded type information.\n\n**Parameters:**\n\n- `compressionLevel` (optional): Must match serialization compression level\n- `offset` (optional): Starting byte offset (skips `offset` bytes at the beginning, e.g., custom metadata)\n- `buffer`: The buffer to deserialize\n\n**Returns:** All values that were serialized\n\n## Available Types\n\n### Numeric Types\n\n| Type | Description | Size | Range |\n|------|-------------|------|-------|\n| `Types.int8` | Signed 8-bit integer | 1 byte | -128 to 127 |\n| `Types.uInt8` | Unsigned 8-bit integer | 1 byte | 0 to 255 |\n| `Types.int16` | Signed 16-bit integer | 2 bytes | -32,768 to 32,767 |\n| `Types.uInt16` | Unsigned 16-bit integer | 2 bytes | 0 to 65,535 |\n| `Types.int32` | Signed 32-bit integer | 4 bytes | -2³¹ to 2³¹-1 |\n| `Types.uInt32` | Unsigned 32-bit integer | 4 bytes | 0 to 2³²-1 |\n| `Types.float32` | 32-bit floating point | 4 bytes | IEEE 754 single precision |\n| `Types.float64` | 64-bit floating point | 8 bytes | IEEE 754 double precision |\n| `Types.float24` | 24-bit floating point | 3 bytes | Reduced precision (custom format) |\n\n### String Types\n\n| Type | Description | Max Size |\n|------|-------------|----------|\n| `Types.string` | Standard string | 65,535 + 2 bytes (16-bit length prefix) |\n| `Types.stringTiny` | Compact string | 255 + 1 byte (8-bit length prefix) |\n| `Types.stringFixed` | Fixed length string | User-defined length |\n| `Types.stringNullTerminated` | Null-terminated string (C-style) | Unlimited |\n\n### Boolean & Special Types\n\n- `Types.bool` - Boolean value (1 byte)\n- `Types.void` - Empty table (0 bytes)\n- `Types.nothing` - Nothing value (0 bytes)\n- `Types.any` - Any type (dynamic, includes type information)\n\n### Roblox Types\n\n| Type | Description | Size |\n|------|-------------|------|\n| `Types.vector3` | Full precision Vector3 | 12 bytes |\n| `Types.vector3F24` | Reduced precision Vector3 | 9 bytes |\n| `Types.vector3Int16` | Integer Vector3 | 6 bytes |\n| `Types.vector2` | Full precision Vector2 | 8 bytes |\n| `Types.vector2F24` | Reduced precision Vector2 | 6 bytes |\n| `Types.vector2Int16` | Integer Vector2 | 4 bytes |\n| `Types.vector` | Full precision vector (Luau native vector type) | 12 bytes |\n| `Types.vectorF24` | Reduced precision vector | 9 bytes |\n| `Types.vectorInt16` | Integer vector | 6 bytes |\n| `Types.cframe` | Full precision CFrame | 48 bytes |\n| `Types.cframeQ` | Quaternion CFrame | 28 bytes |\n| `Types.color3` | RGB color | 3 bytes |\n| `Types.enum` | Enum value (requires Enum parameter) | 2 bytes |\n| `Types.instance` | Roblox Instance (requires ClassName) | Variable |\n\n### Collection Types\n\n#### `Types.array(elementType)`\n\nCreates a variable-size array type with 16-bit length prefix (max 65,535 elements).\n\n```luau\nlocal NumberArray = Types.array(Types.int32)\nlocal VectorArray = Types.array(Types.vector3)\n```\n\n#### `Types.arrayTiny(elementType)`\n\nCreates a compact array type with 8-bit length prefix (max 255 elements).\n\n```luau\nlocal SmallArray = Types.arrayTiny(Types.float32)\n```\n\n#### `Types.arrayFixed(elementType, length)`\n\nCreates a fixed-size array with no length prefix.\n\n```luau\nlocal FixedArray = Types.arrayFixed(Types.int32, 10) -- Exactly 10 elements\n```\n\n#### `Types.map(keyType, valueType)`\n\nCreates a map/dictionary type with 16-bit length prefix (max 65,535 entries).\n\n```luau\nlocal StringToIntMap = Types.map(Types.string, Types.int32)\nlocal IdToPlayerMap = Types.map(Types.int32, Types.string)\n```\n\n#### `Types.mapFixed(keyType, valueType, length)`\n\nCreates a fixed-size map with no length prefix.\n\n```luau\nlocal FixedMap = Types.mapFixed(Types.string, Types.bool, 10) -- Exactly 10 entries\n```\n\n#### `Types.struct(schema)`\n\nCreates a fixed structure with named fields.\n\n```luau\nlocal PlayerData = Types.struct({\n    Name = Types.string,\n    Level = Types.int32,\n    Position = Types.vector3,\n    Inventory = Types.array(Types.string)\n})\n```\n\n#### `Types.optional(type)`\n\nMakes a type optional (nullable).\n\n```luau\nlocal OptionalString = Types.optional(Types.string)\nlocal OptionalInt = Types.optional(Types.int32)\n```\n\n#### `Types.boolPacked`\n\nCreates an array of exactly 8 booleans, each taking 1 bit of memory (totals 1 byte).\n\n```luau\nlocal PackedBools = Types.boolPacked\n-- Serialize: {true, false, true, false, true, false, true, false}\n```\n\n#### `Types.bits._8(count)`, `Types.bits._16(count)`, `Types.bits._32(count)`\n\nCreates a fixed-size array of bit values (8-bit, 16-bit, or 32-bit unsigned integers).\n\n```luau\nlocal Bits8 = Types.bits._8(4)  -- Array of 4 bytes (8-bit values)\nlocal Bits16 = Types.bits._16(2) -- Array of 2 shorts (16-bit values)\nlocal Bits32 = Types.bits._32(1) -- Array of 1 int (32-bit values)\n```\n\n#### `Types.enum(EnumType)`\n\nSerializes an EnumItem value. Requires the Enum type as a parameter.\n\n```luau\nlocal MaterialEnum = Types.enum(Enum.Material)\nlocal HumanoidStateEnum = Types.enum(Enum.HumanoidStateType)\n\n-- Serialize\nlocal b = Serializer:serialize(nil, Enum.Material.Plastic)\n\n-- Deserialize\nlocal material = Serializer:deserialize(nil, b)\n```\n\n#### `Types.instance(ClassName)`\n\nSerializes a Roblox Instance by its properties. Requires pre-defined serialization schemas for each class.\n\n**Currently supported classes:**\n\n- `Part` - Serializes Name, CFrame, Color, Transparency, Material, CanCollide, CanTouch, CanQuery, CastShadow\n- `MeshPart` - Same properties as Part\n\n```luau\n-- Static serialization\nlocal PartSerializer = VoidSentry.Static.new(nil, Types.instance(\"Part\"))\n\nlocal part = Instance.new(\"Part\")\npart.Name = \"MyPart\"\npart.CFrame = CFrame.new(10, 5, 0)\npart.Color = Color3.new(1, 0, 0)\npart.Transparency = 0.5\n\nlocal b = PartSerializer:serialize(nil, part)\n\n-- Deserialize creates a new Instance with the serialized properties\nlocal deserializedPart = PartSerializer:deserialize(nil, b)\n```\n\n**Dynamic serialization** also supports Instance types:\n\n```luau\nlocal part = workspace.SomePart\nlocal b = VoidSentry.Dynamic.serialize(nil, nil, part)\nlocal deserializedPart = VoidSentry.Dynamic.deserialize(nil, nil, b)\n```\n\n> **Note:** The Instance type creates new instances on deserialization. It does not preserve parent-child relationships or references to other instances. For custom classes, you can extend the serialization data in `src/sections/types/instance/serialize_data.luau`.\n\n## Usage Examples\n\n### Example 1: Player Data Replication\n\n```luau\nlocal PlayerDataSerializer = VoidSentry.Static.new(\n    5, -- Compression level 5\n    Types.struct({\n        UserId = Types.int32,\n        Username = Types.string,\n        Position = Types.vector3,\n        Health = Types.float32,\n        Inventory = Types.array(Types.string),\n        Level = Types.int32,\n        Premium = Types.bool\n    })\n)\n\nlocal data = {\n    UserId = 123456,\n    Username = \"Player123\",\n    Position = Vector3.new(100, 50, 200),\n    Health = 75.5,\n    Inventory = {\"Sword\", \"Shield\", \"Potion\"},\n    Level = 42,\n    Premium = true\n}\n\nlocal b = PlayerDataSerializer:serialize(nil, data)\n-- Send buffer over RemoteEvent\n```\n\n### Example 2: Game State Snapshot\n\n```luau\nlocal GameStateSerializer = VoidSentry.Static.new(\n    nil, -- No compression for speed\n    Types.int32, -- Timestamp\n    Types.array(Types.struct({\n        PlayerId = Types.int32,\n        Position = Types.vector3F24, -- Reduced precision\n        Rotation = Types.cframeQ, -- Compact CFrame\n    })),\n    Types.map(Types.string, Types.int32) -- Entity counts\n)\n\nlocal timestamp = os.time()\nlocal players = {\n    {PlayerId = 1, Position = Vector3.new(0, 5, 0), Rotation = CFrame.new()},\n    {PlayerId = 2, Position = Vector3.new(10, 5, 10), Rotation = CFrame.new()},\n}\nlocal entityCounts = {\n    Zombies = 15,\n    Treasure = 3,\n}\n\nlocal b = GameStateSerializer:serialize(nil, timestamp, players, entityCounts)\n```\n\n### Example 3: Dynamic Configuration\n\n```luau\n-- When you don't know the data structure ahead of time\nlocal config = {\n    maxPlayers = 50,\n    mapName = \"Desert Arena\",\n    spawnPoint = Vector3.new(0, 10, 0),\n    enablePvP = true,\n    difficulty = 2.5\n}\n\nlocal b = VoidSentry.Dynamic.serialize(10, nil, config)\n\n-- Later, deserialize\nlocal loadedConfig = VoidSentry.Dynamic.deserialize(10, nil, b)\n```\n\n## Performance Tips\n\n1. **Use Static Serializer When Possible**: It's significantly faster than dynamic serialization\n2. **Choose Appropriate Types**: Use `Vector3F24` instead of `Vector3` if you don't need full precision\n3. **Compression Trade-offs**: Compression reduces bandwidth but increases CPU usage\n4. **Batch Serialization**: Serialize multiple values at once rather than separately\n5. **Reuse Serializers**: Create serializer objects once and reuse them\n6. **Use Tiny Variants**: `StringTiny` and `ArrayTiny` save bytes for small data\n7. **Select Appropriate Numeric Types**: Use `Int16` or `Int8` when values fit in smaller ranges\n\n## Advanced Features\n\n### Custom Offsets\n\nYou can specify a starting offset to write data at specific positions:\n\n```luau\n-- Write at offset 20\nlocal b = Serializer:serialize(20, myData)\nprint(buffer.len(b)) -- Buffer has 20 extra bytes at the beginning for custom use\n```\n\n### Compression Levels\n\nZstd compression levels (-7 to 22):\n\n- **-7 to 3**: Fast compression, lower ratio\n- **5 to 10**: Balanced (recommended)\n- **15 to 22**: Maximum compression, slower\n\n## Contributing\n\nContributions are welcome! Please feel free to submit issues or pull requests.\n\n## License\n\nThis project is licensed under the Apache 2.0 License. See the [LICENSE](LICENSE) file for details.\n\n## Author\n\n**IAMNOTULTRA3** (a.k.a elentium/elite)\n\n## Support\n\nFor questions, issues, or feature requests, please open an issue on the repository or contact the author.\n","readmeTruncated":false}