About
QuickNet is a high performance networking library meant as an alternative to default RemoteEvents and RemoteFunctions. I made this library for personal use in my projects, but I’ve decided to open source it in case anyone else finds it helpful.
Why Make QuickNet?
While there’s already many networking libraries out there, I wanted to make something that requires minimal setup without compromising the performance. Most of the best performing networking libraries are IDL (Interface Description Language) compilers, which require the user to download stuff like plugins, files, etc, run commands to generate code files for each network event, and so on. But if you’re lazy like me, you don’t want to do any of that tedious stuff. You want something that just works.
Why Use QuickNet?
QuickNet is super easy to pick up if you’ve never used a networking library before. If you’re already using RemoteEvents in your game, you can drop in QuickNet without changing any of your main code. This is possible because the API for QuickNet is the same as that of normal RemoteEvents and RemoteFunctions, meaning there is very little learning needed to start using QuickNet.
Performance
QuickNet’s ease of use does not come at the cost of performance. When compared to default RemoteEvents, QuickNet can reduce the network traffic by as much as 1,000x, while running at up to 10x higher FPS. Also, QuickNet’s performance is very competitive with other popular networking libraries, such as Packet, ByteNet, and Blink.
Performance Explanation
How is this possible? Under the hood, QuickNet serializes data into a buffer before sending it across the network, then on the receiving end the buffer is decoded back into raw data. When sending a buffer across the network, the Roblox engine will use much less CPU than when sending normal data. Therefore, the trick to get a network library faster than default Remotes is to make the buffer serialization cost less than the default engine serialization cost. QuickNet uses a ton of clever optimization techniques to minimize the CPU usage when encoding and decoding data. See source code for more details.
Features
- Optimized for CPU performance
- Fast path: extra performance boost for most common data types
- Supports 59 different data types + more composite types
- Supports dynamic types and nested structures
- Data serialization and buffer based networking
- Batched calls with action routing
- Protection against invalid data/“DDoS”
- Custom rate limiting on network events
- Type-safe network events
- Supports call-response events (RemoteFunctions) with custom timeouts
- Supports unreliable events
- Familiar API (same as default Remotes)
Benchmarks
Benchmarks are conducted by firing the network event 1000 times per frame with the same data for 10 seconds, at the end of which the average FPS and Kbps are recorded. Each data contains 500-2000 individual elements, depending on the test. The result shown for each entry is a 3-run average. All benchmarks are conducted in studio. See the benchmarks source code for more details.
Entities Test (100 dictionaries each with 5 entries per fire)
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteEvent | 3.91 | 1x | 1853031.17 |
| Jolt | 3.62 | 0.96x | 95.11 |
| Packet | 10.26 | 2.62x | 41.33 |
| ByteNet | 14.66 | 3.75x | 38.66 |
| Blink | 23.39 | 5.98x | 39.33 |
| NetRay Compile | 23.97 | 6.13x | 39.52 |
| QuickNet | 34.86 | 8.92x | 40.78 |
Booleans Test (1000 booleans per fire)
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteEvent | 7.36 | 1x | 523520.05 |
| Jolt | 3.39 | 0.46x | 124.53 |
| Packet | 11.19 | 1.52x | 9.83 |
| ByteNet | 12.98 | 1.76x | 9.25 |
| Blink | 46.56 | 6.65x | 8.07 |
| NetRay Compile | 77.97 | 10.59x | 3.10 |
| QuickNet | 141.23 | 19.19x | 2.89 |
Strings Test (500 strings per fire)
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteEvent | 10.82 | 1x | 814948.14 |
| Jolt | 5.19 | 0.48x | 56.22 |
| Packet | 10.53 | 0.97x | 21.38 |
| ByteNet | 0.21 | 0.02x | 39.83 |
| Blink | 22.20 | 2.05x | 21.62 |
| NetRay Compile | 15.98 | 1.47x | 19.39 |
| QuickNet | 44.15 | 4.08x | 18.63 |
Numbers Test (2000 numbers per fire)
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteEvent | 5.45 | 1x | 2115972.12 |
| Jolt | 2.01 | 0.39x | 332.85 |
| Packet | 6.20 | 1.14x | 129.53 |
| ByteNet | 6.84 | 1.26x | 120.27 |
| Blink | 23.11 | 4.24x | 122.20 |
| NetRay Compile | 24.79 | 4.54x | 123.86 |
| QuickNet | 60.23 | 11.05x | 125.42 |
Dictionary Test (1 dictionary with 500 key-value pairs per fire)
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteEvent | 6.12 | 1x | 1923782.58 |
| Jolt | 4.58 | 0.75x | 128.16 |
| Packet | 4.72 | 0.77x | 145.78 |
| ByteNet | 0.10 | 0.02x | 246.81 |
| Blink | 12.14 | 1.98x | 127.51 |
| NetRay Compile | 8.78 | 1.43x | 145.04 |
| QuickNet | 22.52 | 3.68x | 142.24 |
RemoteFunction Test
This test is conducted by sending 500 numbers 1000 times per frame with a response of 500 booleans for every send. Each fire is wrapped in a task.spawn to ensure calls aren’t blocked. The metrics are recorded at the end of a 5 minute period. This test can show us the performance degradation over a long period of heavy load.
| Networking Tool | FPS | FPS Scale | Kbps |
|---|---|---|---|
| RemoteFunction | 3.28 | 1x | 1435503.71 |
| Jolt | 3.24 | 0.99x | 307.97 |
| Packet | Crash | ||
| ByteNet | N/A | ||
| Blink | Crash | ||
| QuickNet | 58.29 | 17.77x | 159.12 |
Usage
Register network events on both client and server, or in a shared module:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local QuickNet = require(ReplicatedStorage.QuickNet)
local data = QuickNet.Data
local event1 = QuickNet:register("SomeEventName", data.NumberU8, data.String)
local function1 = QuickNet:register("SomeFunctionName", data.Color3, data.Vector3F32):Response(data.Boolean)
--how to define events with table types
--arrays
local staticArray = QuickNet:register("SomeEvent1", {data.NumberF32, data.String, data.CFrameF32})
local uniformArray = QuickNet:register("SomeEvent2", {data.Color3})
local dynamicArray = QuickNet:register("SomeEvent3", {data.Any})
--dictionaries
local staticDict = QuickNet:register("SomeEvent4", {health = data.NumberU8, damage = data.NumberI32, name = data.String})
local uniformDict = QuickNet:register("SomeEvent5", {[data.String] = data.NumberI32})
local dynamicDict = QuickNet:register("SomeEvent6", {[data.Any] = data.Any})
Client:
event1:FireServer(123, "whatever")
event1.OnClientEvent:Connect(function(num, str)
print(num)
print(str)
end)
local color = Color3.new(1, 1, 1)
local vector3 = Vector3.new(1, 2, 3)
local result = function1:InvokeServer(color, vector3)
print(result)
Server:
event1.OnServerEvent:Connect(function(player, num, str)
print(num)
print(str)
end)
function1.OnServerInvoke = function(player, color, vector3)
print(color)
print(vector3)
return true
end
event1:FireAllClients(456, "somerandomstring")
Arguments must match the types defined in schema. See documentation for more details.
Note
QuickNet is currently in beta and has only been tested in controlled environments, meaning bugs can arise in edge cases. If you encounter a bug please report it below and I will try to patch it ASAP. On a more general note, any feedback is welcome, such as optimizations I missed, features I should add, etc.
Current version: v0.3.2