Are you tired of slow serialization? BufferConverter2 is a complete redesign of my original BufferConverter module—built for speed.
Unlike the previous version, BufferConverter2 uses a schema-based approach to dramatically improve serialization performance. Whether you’re handling networking, save data, or structured buffers, this module ensures your data is compact, efficient, and fast!
Why Use BufferConverter2?
Blazing-Fast Serialization – Schema-based for maximum efficiency.
Compact & Optimized Data – Reduce size and improve performance.
Supports Complex Data Types – Serialize CFrames, Vectors, Enums, Instances, and more!
Perfect for Networking – Send data faster with smaller payloads.
Lightweight & Easy to Use – Just define a schema and go!
How It Works
BufferConverter2 relies on SchemaData, which defines how data should be serialized. This allows for precise control over how your data is packed and unpacked.
Supported Schema Types:
Primitive & Structured Data
Strings with configurable length sizes
Numbers with custom precision (e.g., u32
, f32
)
Booleans
Structs (Nested objects with mixed data types)
Arrays (Key-value serialization)
Unions (one of two datatypes)
Roblox-Specific Types
CFrames (Highly optimized, only 20 bytes per CFrame! (minimum))
Vector2s & Vector3s
Enums
Instance References
Color3s
Custom Types
Vector4
Class OOP objects
Full SchemaData Reference
SchemaData
SchemaData are what this module uses to know how you want to serialize your data.
There are quite a few SchemaDatas, so let’s go over all of them!
(All SchemaData are stored within BufferConverter2.data
)
Primitive
-
SchemaData.empty
- Indicates that the value here is nil.
- Indicates that the value here is nil.
-
SchemaData.number(sizeType="f32": SizeType?)
- This function creates a new number SchemaData, with the number being stored as size type
sizeType
.
For example, if you passed “u32” as thesizeType
, any numbers you serialize with this schema can only be integers between 0 and 65k.
- This function creates a new number SchemaData, with the number being stored as size type
-
SchemaData.bool
- Self explanatory.
- Self explanatory.
-
SchemaData.enum(enumType: Enum, itemSizeType="u8": SizeType?)
- This function creates a new EnumItem SchemaData.
You must pass what enumType the EnumItem will belong to, and you can also specify what size type you want to store the EnumItem value as.
- This function creates a new EnumItem SchemaData.
-
SchemaData.string(lenSizeType = "u32": SizeType?)
- This function creates a new SchemaData with its length byte being of size
lenSizeType
.
For example, if you passed “u8” aslenSizeType
, the string you serialize using this SchemaData is 255 characters long max. If you passed “u16”, it would be 65k characters long max, etc.
The reason why it’s done this way (instead of say, using null terminators) is to utilizebuffer.writestring()
andbuffer.readstring()
, both of which are very fast.
- This function creates a new SchemaData with its length byte being of size
-
SchemaData.bitarray
- Reads and writes an array of 0s and 1s bitpacked into the bytes, making it so that each byte can store 8 of these bits (duh).
Very useful if you, for example, wanna represent something in binary more easily or you want to send alot of flags
- Reads and writes an array of 0s and 1s bitpacked into the bytes, making it so that each byte can store 8 of these bits (duh).
-
SchemaData.class(classMetatable: any, struct: SchemaData)
- Indicates that the value stored here uses a metatable from a specific class, and when read the metatable should be set to it.
Also requires a struct SchemaData for serializing the class members.
- Indicates that the value stored here uses a metatable from a specific class, and when read the metatable should be set to it.
Spatial
-
SchemaData.cframe(posSizeType="f32": SizeType?, rotSizeType="f16": SizeType?)
- This function creates a new CFrame SchemaData with the position components being stored with size type
posSizeType
, and the rotation (axis, angle) components being stored asrotSizeType
.
With the default values (f32 and f16) the CFrame only takes 20 bytes to store! (albeit with a little loss in precision.)
- This function creates a new CFrame SchemaData with the position components being stored with size type
-
SchemaData.vec3(componentSizeType="f32": SizeType?)
- This function creates a new
Vector3
SchemaData, and stores the XYZ components with size typecomponentSizeType
.
- This function creates a new
-
SchemaData.vec2(componentSizeType="f32": SizeType?)
- Same as SchemaData.vec3(), just for Vector2s.
- Same as SchemaData.vec3(), just for Vector2s.
-
SchemaData.vec4(componentSizeType="f32": SizeType?)
- Same as SchemaData.vec3(), just using a custom Vector4 data format.
- Same as SchemaData.vec3(), just using a custom Vector4 data format.
Composite
-
SchemaData.union(first: SchemaData, second: SchemaData)
- This SchemaData indicates that the value here can be of datatype first OR second.
Useful if you need stuff like options, also can be nested!
- This SchemaData indicates that the value here can be of datatype first OR second.
-
SchemaData.struct(struct: {[any]: SchemaData})
- This function creates a new struct SchemaData.
The way structs work is, basically every key in the struct has to be of 1 primitive datatype (for example, if you use numbers it should all be numbers, if you use strings it should all be strings), but the values can have differing datatypes.
See the Examples section if you’re still confused.
- This function creates a new struct SchemaData.
-
SchemaData.map(keySerde: SchemaData, valueSerde: SchemaData, lenSizeType="u32": SizeType?)
- This function creates a new array SchemaData, using the
keySerde
for all keys, and thevalueSerde
for all values.
This means that all of your keys must be the same datatype, same with values.
This is unlike structs, which can have differing value datatypes.
- This function creates a new array SchemaData, using the
-
SchemaData.mapnterm(keySerde: SchemaData, valueSerde: SchemaData)
- This is just
SchemaData.map()
but without a size limit, at the cost of not being able to use\0
characters, the number 0 and nil.
- This is just
-
SchemaData.array(valueSerde: SchemaData, lenSizeType="u16": SizeType?)
- This function creates a new array SchemaData, reading and writing all values in the array using the valueSerde.
Useful if you don’t want to store all of the keys (unlike in a map).
- This function creates a new array SchemaData, reading and writing all values in the array using the valueSerde.
-
SchemaData.arraynterm(valueSerde: SchemaData)
- Same as
SchemaData.array()
, just without a size limit and with the same limitations asSchemaData.mapnterm()
.
- Same as
Other
-
SchemaData.instref
- This SchemaData simply indicates that the value here should be a reference to an Instance (as a UniqueId.)
- This SchemaData simply indicates that the value here should be a reference to an Instance (as a UniqueId.)
-
SchemaData.color
- Indicates that the value stored here should be a Color3.
- Indicates that the value stored here should be a Color3.
Examples
Sending Camera Configurations Over RemoteEvent
local Converter = require(path_to_bufferconverter2)
local data = Converter.data
local schema = data.struct {
CFrame = data.cframe(),
CamType = data.enum(Enum.CameraType)
}
local buf = Converter.serialize(schema, {
CFrame = CFrame.identity,
CamType = Enum.CameraType.Scriptable
})
game.ReplicatedStorage.RemoteEvent:FireAllClients(buf)
Using Every SchemaData Type
local Converter = require(path_to_bufferconverter2)
local data = Converter.data
local schema = data.struct {
Hello = data.cframe("f32", "f16"),
Array = data.array(data.number("u8"), data.enum(Enum.CameraType, "u8")),
Skibidi = data.struct {
[1] = data.string("u16"),
[2] = data.instref
},
Hi = data.bool,
Poopy = data.vec3("f16"),
Vec2 = data.vec2("u8"),
}
local buf = Converter.serialize(schema, {
Hello = CFrame.new(10, 20, 3),
Array = {
Enum.CameraType.Track,
Enum.CameraType.Fixed
},
Skibidi = {
"Hello World!",
workspace
},
Hi = true,
Poopy = Vector3.new(10, 2, 10),
Vec2 = Vector2.new(12, 28, 3)
})
local result = Converter.deserialize(schema, buf)
print(result) -- Same table as before
Replicating Camera CFrame To The Server Every Frame
local Converter = require(path_to_bufferconverter2)
local data = Converter.data
local schema = data.cframe()
local camera = workspace.CurrentCamera
local remote = game:GetService("ReplicatedStorage").CameraReplicate
game:GetService("RunService").Heartbeat:Connect(function()
local buf = Converter.serialize(schema, camera.CFrame)
remote:FireServer(buf)
end)
Download & Try It Now!
BufferConverter2.rbxm (7.0 KB)
Questions? Feedback? Let me know in the replies!
Latest Module Update: