Skip to content

Overview

Bolt is a networking package designed to provide strong type safety for networking in Roblox experiences. It originally began as a simple code snippet, which is why it has no external dependencies and fully contained within a single file.

The goal is to simplify networking and get good type checking, decent message size and optional serialization / deserialization. The default serializer is built to handle most of the data developers commonly send, and custom serializers are generally only needed when further reducing message size or optimizing for very specialized cases.

Adding Bolt to a Project

To add the Bolt package to your project, add the following to your wally.toml file:

[dependencies]
SequenceAnimator = "dubit/bolt@^0"

Examples

Example Usage - ReliableEvent
return {
    Messages = Bolt.ReliableEvent("Messages") :: Bolt.ReliableEvent<string>,
}
Networking.Messages.OnServerEvent:Connect(function(player, message)
    print(`{player.DisplayName} says {message}`) --> Username says Hello world!
end)
Networking.Messages:FireServer("Hello world!")
Example Usage - RemoteFunction
return {
    PurchaseItem = Bolt.RemoteFunction("PurchaseItem") :: Bolt.RemoteFunction<(string), (boolean)>,
}
Networking.PurchaseItem.OnServerInvoke = function(player, item)
    if typeof(item) ~= "string" then
        return false
    end

    -- some logic

    return true
end
local success = Networking.PurchaseItem:InvokeServer("apple")
Example Usage - RemoteProperty
return {
    GoldBalance = Bolt.RemoteProperty("GoldBalance", 0) :: Bolt.RemoteProperty<number>,
}
local function playerAdded(player: Player)
    Networking.GoldBalance:SetFor(player, 5)
end

Players.PlayerAdded:Connect(playerAdded)
for _, player in Players:GetPlayers() do
    task.spawn(playerAdded, player)
end
Networking.GoldBalance:Observe(function(newValue)
    print(newValue)
end)

--> 0
--> 5
Example Usage - Custom serialization

Let's say in this example we know that player won't ever have more than 255 bullets in his weapon, we can optimize the message generated by this remote event as the default serializer used internally by Bolt uses 64bit floating point numbers, which are 8 bytes in size, 255 fits within one byte so we can serialize the number into one byte using unsigned 8bit integer and then deserialize it when we receive the given message.

With this simple optimization we've decreased our message size by 87.5%!

return {
    PlayerAmmo = Bolt.ReliableEvent("PlayerAmmo", function(bufferWriter: BufferWriter, amount: number)
        bufferWriter:WriteU8()
    end, function(bufferReader: BufferReader)
        return bufferReader:ReadU8()
    end) :: Bolt.ReliableEvent<number>,
}

Example Usage - More complex custom serialization

Bolt by default doesn't have a way of serializing ColorSequences, but you can add that functionality using a custom seralization and deserialization!

Bolt.ReliableEvent("Color Sequence Event", function(writer, colorSequence: ColorSequence)
    writer:WriteU8(#colorSequence.Keypoints)
    for _, keypoint in colorSequence.Keypoints do
        writer:WriteU8(math.clamp(keypoint.Time * 255, 0, 255))
        writer:WriteColor3(keypoint.Value)
    end
end, function(reader)
    local keypointsCount = reader:ReadU8()
    local keypoints = table.create(keypointsCount)
    for i = 1, keypointsCount do
        local keypointTime = reader:ReadU8() / 255
        local keypointsColor = reader:ReadColor3()
        table.insert(keypoints, ColorSequenceKeypoint.new(keypointTime, keypointsColor))
    end
    return ColorSequence.new(keypoints)
end) :: Bolt.ReliableEvent<ColorSequence>