Introduction

KravasNet

KravasNet is a lightweight abstraction layer built on top of ByteNet (opens in a new tab), designed to simplify networking inside the Kravas framework for Roblox Studio.

ByteNet already gives you fast, typed, buffer-packed networking. KravasNet adds an organizational layer on top of it: Namespaces and Channels, so packet definitions read like a small, chainable API surface instead of a pile of raw ByteNet.definePacket() calls scattered across scripts.

⚠️

Version note. As of KravasNet v0.5, every packet is registered through ByteNet.defineNamespace() under the hood, matching the requirements of ByteNet 0.4.x. This is a breaking internal change from earlier KravasNet releases (which called ByteNet.definePacket() standalone) — the public API you write against is unchanged, but the timing of when a namespace becomes "live" on the network is new. See Building Namespaces and Best Practices before shipping.

Why KravasNet?

Problem with raw ByteNetHow KravasNet helps
Every packet needs its own defineNamespace(name, function() ... end) boilerplateOne KravasNet.createNamespace() call groups everything
Server methods take (data, player), client differs by contextUnified Channel API: sendTo(player, data) on both read naturally
No place to validate or sanitize incoming dataBuilt-in Middleware pipeline
Easy to forget a packet must be declared before the namespace is usedExplicit :build() step with a clear error if you add too late

Core Concepts

KravasNet is built around three ideas:

  • Namespace — a named container that groups related channels together (e.g. "Combat", "Player", "Queue"). Maps 1:1 to one ByteNet.defineNamespace() call.
  • Channel — a single typed packet definition (one ByteNet packet), with its own schema, reliability mode, and middleware chain.
  • Build — the moment a namespace's pending channels are handed to ByteNet as a single batch. This happens automatically on first use, or explicitly via :build() (recommended — see Best Practices).
local CombatNet = KravasNet.createNamespace("Combat")
	:add("Damage", { amount = KravasNet.float32, target = KravasNet.inst })
	:add("Position", { pos = KravasNet.vec3 }, { reliability = "unreliable" })
 
CombatNet:build() -- registers everything with ByteNet right now
 
-- Server
CombatNet.Damage:sendTo(player, { amount = 50, target = someCharacter })
 
-- Client
CombatNet.Position:on(function(data)
	print(data.pos)
end)

Notice KravasNet.float32, KravasNet.vec3, KravasNet.int8, etc. are used without parentheses — they are pre-built type descriptors, not factory functions. Only KravasNet.optional(), KravasNet.array(), KravasNet.struct(), and KravasNet.map() are actual functions you call. See Data Types for the full breakdown.

Continue to the Quick Start guide, or jump to Best Practices if you're migrating from an older KravasNet version.