{"id":"minecoiii2/bitnet","name":"bitnet","scope":"minecoiii2","platform":"roblox","description":"Lightning-fast buffer-based networking library for Roblox","version":"3.0.1","latest":"3.0.1","versions":["3.0.0","3.0.1"],"license":"MIT","licenseRating":"safe","licenseCaveats":[],"licenseVerified":true,"dependencies":{},"integrity":"959d799fe25d2786e264c5ce4aad97b9d47c7f3b5db8dfe9c2efda0a50f9f1ed","likes":0,"downloads":0,"install":"forest install minecoiii2/bitnet","url":"https://forest.dev/p/roblox/minecoiii2/bitnet","files":"https://api.forest.dev/ai/package/roblox/minecoiii2/bitnet/files","readme":"# BitNet\n\nBuffer-based networking library for Roblox. Replaces RemoteEvents and RemoteFunctions with typed, batched events and funcs that all run over two shared remotes.\n\n- Every fire in a frame is packed into one buffer per player and sent once per Heartbeat\n- Schemas describe the payload, so data goes out as tightly packed bytes instead of Lua tables\n- Booleans cost one bit, Instances cost no buffer space\n- Reliable and unreliable events, two-way funcs, rate limiting and runtime typechecking\n\n## Installation\n\nBitNet comes with **Networking**, a template module where you define all of your events and funcs.\n\n**Manual:** download `BitNet.rbxm` and `Networking.rbxm` from [Releases](https://github.com/minecoiii2/bitnet/releases) and insert both into `ReplicatedStorage`.\n\n**Wally**\n```toml\n[dependencies]\nBitNet = \"minecoiii2/bitnet@3.0.0\"\n```\nThen copy [`Networking.luau`](Networking.luau) into your project and change its require to point at your packages folder, e.g. `ReplicatedStorage.Packages.BitNet`.\n\n## Quick start\n\nEvery event and func goes in the table that `Networking` returns. The server and client both require this one module, so everything registers in the same order on both sides.\n\n```lua\n-- ReplicatedStorage.Networking\nreturn {\n\tChat = RemoteEvent({\n\t\tArgs = args(string, u8),\n\t}),\n\tHit = RemoteEvent({\n\t\tArgs = struct({ target = Instance, damage = u16 }),\n\t\tReliable = false,\n\t\tRateLimit = BuildRatelimit(20, 1),\n\t}),\n\tGetPrice = RemoteFunction({\n\t\tArgs = string,\n\t\tReturns = u32,\n\t}),\n}\n```\n\nThe template sets up short names for everything at the top: `RemoteEvent` is `BitNet.event`, `RemoteFunction` is `BitNet.func`, `args` is `tuple`, and the types are named after their Roblox equivalents (`Vector3`, `CFrame`, `Instance`, ...). Two names differ from the table below: `any` is `ref`, and `auto` is BitNet's `any`.\n\n```lua\n-- Server\nconst Networking = require(ReplicatedStorage.Networking)\n\nNetworking.Chat.OnServerEvent:Connect(function(player, message, channel)\n\tNetworking.Chat:FireAllClients(message, channel)\nend)\n\nNetworking.GetPrice:SetCallback(function(player, itemId)\n\treturn 100\nend)\n```\n\n```lua\n-- Client\nconst Networking = require(ReplicatedStorage.Networking)\n\nNetworking.Chat.OnClientEvent:Connect(function(message, channel)\n\tprint(message)\nend)\n\nNetworking.Chat:FireServer(\"hello\", 1)\nconst price = Networking.GetPrice:InvokeServer(\"sword\")\n```\n\n## Types\n\n| Type | Bytes | Notes |\n| ---- | ----- | ----- |\n| `u8` `u16` `u24` `u32` | 1–4 | Unsigned integers |\n| `i8` `i16` `i24` `i32` | 1–4 | Signed integers |\n| `f24` `f32` `f64` | 3, 4, 8 | `number` is `f64`. `f24` is lossy (~3e-5 relative error) |\n| `bool` | 1 bit | Packed eight to a byte |\n| `string` `buffer` | 1–5 + length | |\n| `uuid` | 16 | Canonical 36-character lowercase form |\n| `vec2` `vec3` | 8, 12 | |\n| `vec2i16(scale?)` `vec3i16(scale?)` | 4, 6 | Fixed point, ±327 studs at the default scale of 100 |\n| `cframe` `cframelong` | 18, 24 | `cframe` uses 16-bit rotation angles |\n| `color3` | 3 | 8 bits per channel |\n| `brickcolor` `numberrange` `udim` `udim2` | 2, 8, 4, 8 | |\n| `numbersequence` `colorsequence` | varies | |\n| `instance` `ref` `unknown` | 0 | Sent next to the buffer as normal Roblox values |\n| `any` | 1 + value | Picks an encoding at runtime |\n| `nothing` | 0 | Always nil |\n\nThe narrow types (`u24`, `i24`, `f24`, `vec*i16`, `cframe`) save bytes but cost a bit of CPU. Use them when bandwidth matters.\n\n**Combinators**\n- `struct(format)`: table with fixed keys\n- `array(value)`: array of one type\n- `map(key, value)`: dictionary, up to 16,383 entries\n- `optional(value)`: value or nil\n- `tuple(...)`: multiple arguments\n- `enum(Enum.X)`: a Roblox EnumItem\n- `enumFromKeys(t)` / `enumFromValues(t)`: string from a known set, sent as an index\n- `compress(value, level?)`: Zstd-compresses the value when that makes it smaller\n\n## Events\n\n`bitnet.event(options)`\n\n| Option | Default | Description |\n| ------ | ------- | ----------- |\n| `Args` | required | Payload type |\n| `Reliable` | `true` | Use the reliable or unreliable channel |\n| `Typecheck` | Studio only | Validate payloads before sending |\n| `RateLimit` | none | `{ calls, per }` limit on fires from each client |\n\n**Methods:** `:FireServer(...)`, `:FireClient(player, ...)`, `:FireAllClients(...)`, `:FireClientsStreamed(position, range, ...)`, `:FireClientsStreamedWithExclusion(position, range, player, ...)`\n\n**Listening:** `.OnServerEvent` gives `(player, ...)` and `.OnClientEvent` gives `(...)`. `.OnReceived` is the same signal under another name.\n\nOn the client, fires that arrive before anything is connected are queued and delivered to the first listener.\n\n## Funcs\n\n`bitnet.func(options)`\n\n| Option | Default | Description |\n| ------ | ------- | ----------- |\n| `Args` | required | Request type |\n| `Returns` | required | Response type |\n| `Timeout` | `7` | Seconds before the invoke errors. `0` or `math.huge` waits forever |\n| `Typecheck` | Studio only | Validate payloads before sending |\n| `RateLimit` | none | `{ calls, per }` limit on invokes from each client |\n\n**Methods:** `:InvokeServer(...)`, `:InvokeClient(player, ...)`, `:SetCallback(fn)`, plus `:SetServerCallback(fn)` and `:SetClientCallback(fn)`, which error when called on the wrong side.\n\nFuncs work in both directions and are always reliable. Invoking a client has some safety rules built in:\n- `:InvokeClient` requires a finite `Timeout`\n- A player can have at most 32 pending invokes\n- Only the invoked player can answer, and pending invokes fail as soon as that player leaves\n\n## Signal\n\n`bitnet.signal()` creates a local signal that isn't networked. It has `:Fire`, `:Connect`, `:Once`, `:Wait`, `:DisconnectAll` and `:Destroy`. Connections have `:Disconnect`, `:Reconnect` and `:Destroy`.\n\n## Gotchas\n\n- **Register in the same order on both sides.** IDs are assigned in registration order, and a mismatch sends data to the wrong handler without any error. Define everything in `Networking` and require it before anything fires.\n- **`enumFromKeys` and `enumFromValues` need the same set on both sides.** Keep the source table in ReplicatedStorage, written out literally.\n- **`array` can't hold nil.** `array(optional(x))` gets cut off at the first nil.\n- **`udim`, `udim2` and `vec*i16` wrap around when out of range.** Only `Typecheck` catches it.\n- **Unreliable fires over 1,000 bytes are dropped** with a warning.\n- **`compress` costs about 25µs per fire to decompress.** Only use it on large, repetitive payloads.\n- **`any` picks default encodings**, so a CFrame inside `any` is sent as `cframe`. Name the type yourself when you need a specific one.\n- **`Typecheck` is on in Studio and off in live servers** unless you set it.\n\n## License\n\nMIT\n","readmeTruncated":false}