I’ve recently been working on a basic roblox module composed of server-side events and got a working client-server networking model. In addition to client-server, it also handles client-client (because the server is actually a client cough).
To clarify, this is NOT talking about the Roblox Server/Roblox Client model. All of this code runs on the server. I’m not sure what to call this module, so for now it’s just “TCP/IP Module”.
Theoretically, it could be used in simulators or house building games where players could communicate via working PCs (a lot of work to implement, but could be a cool idea). I kind of just made this without an actual reason to use it, I just like having it.
Some code examples:
Example client code
local TCPIP = require(TCPIP)
local DeviceIF = TCPIP:CreateDevice(script.Parent, { IP = "10.0.0.2" })
local success = DeviceIF:Connect(path.to.network.switch.part)
if not success then return end
-- Send some data
print("[Client 1]: Connected to switch")
print("[Client 1]: Sending 'hi' to 10.0.0.1 ...")
local success, response = DeviceIF:Send("10.0.0.1", 80, "hi")
if success then
print("[Client 1] Response from 10.0.0.1:", response)
else
print("[Client 1 | Error] Response from 10.0.0.1:", response)
end
Example server code
local TCPIP = require(TCPIP)
local DeviceIF = TCPIP:CreateDevice(script.Parent, { IP = "10.0.0.1" })
local success = DeviceIF:Connect(path.to.network.switch.part)
if not success then return end
-- Start listening
print("[Server]: Connected to switch")
DeviceIF:Listen(80, function(ip, data) -- Listening on the HTTP port because why not?
print("[Server]:", ip, "sent", data)
if data == "hi" then
return true, "hello " .. ip
elseif data == "bye" then
return true, "goodbye " .. ip
else
return false, "lol wut"
end
end)
Example switch code
local TCPIP = require(TCPIP)
local SwitchIF = TCPIP:CreateSwitch(script.Parent)
-- Not required, but you can listen to the following:
SwitchIF.DeviceAdded:Connect(function(device)
print(device)
wait()
print("All devices:", SwitchIF.Devices)
end)
SwitchIF.PacketSent:Connect(function(dest, port, data)
print(dest, port, data)
-- Currently does not send you "from", coming soon ig
end)
TCP/IP Module
--[[
TCP/IP Suite written in Luau for Roblox
No, it's not the *entire* suite, nor does it come close to realistic
Took a damn lot of work though :P
- [redacted]
]]
-- Initialization
local function fetchDevice(devs, ip, timeout)
local d = devs[ip]
if d then return d end
wait(timeout)
return devs[ip]
end
-- Module
local mod = {}
function mod:CreateSwitch(p)
-- Setup events
local DA = Instance.new("BindableEvent", p)
local SP = Instance.new("BindableFunction", p)
DA.Name = "DeviceAdd"
SP.Name = "SendPacket"
-- Create return object
local internalSend = Instance.new("BindableEvent")
local self_ = {
DeviceAdded = DA.Event,
PacketSent = internalSend.Event,
Devices = {}
}
DA.Event:Connect(function(d) self_.Devices[d.IP] = d.Host end)
SP.OnInvoke = function(from, dest, port, data)
internalSend:Fire(dest, port, data) -- Just for any extra hooks the switch (may) have
local dev = fetchDevice(self_.Devices, dest, 1)
if dev == nil then return false, "device not found" end
-- Send data to device
local recv = dev:WaitForChild("Receive", 1)
if recv == nil then return false, "device not initialized" end
return recv:Invoke(from, port, data)
end
return self_
end
function mod:CreateDevice(p, v)
-- Configure IP
local IP = Instance.new("StringValue", p)
IP.Name = "IP"
if v.IP then IP.Value = v.IP end
-- Create return object
local self_ = {
SetIP = function(ip) IP.Value = ip end,
GetIP = function() return IP.Value end,
ConnectedSendEvt = nil,
FirewallMap = {}
}
function self_:Listen(port, cb) self_.FirewallMap[port] = cb end
function self_:Connect(dev)
local DA = dev:WaitForChild("DeviceAdd", 1)
if DA == nil then return error("Cannot connect to a non-switch device!") end
DA:Fire({ IP = IP.Value, Host = p })
self_.ConnectedSendEvt = dev:WaitForChild("SendPacket")
return true
end
function self_:Send(dest, port, data)
if self_.ConnectedSendEvt == nil then return false, "not connected" end
return self_.ConnectedSendEvt:Invoke(IP.Value, dest, port, data)
end
-- Handle receiving data
local RC = Instance.new("BindableFunction", p)
RC.Name = "Receive"
RC.OnInvoke = function(ip, port, data)
local cb = self_.FirewallMap[port]
if not cb then return false, "port not open" end
return cb(ip, data)
end
return self_
end
return mod
API Reference
TCPIP:CreateSwitch(part)
Initializes a network switch with the required events and functions.
Attributes:
- Switch.DeviceAdded: BindableEvent
callback(device: Table{ IP = "0.0.0.0", Host = part })
- Switch.PacketSent: BindableEvent
callback(dest: String, port: Integer, data: Any)
- Switch.Devices: Table
{ IP: PART, ... }
TCPIP:CreateDevice(part, values)
Initializes a networked device with default values provided
Available values:
- IP (string): the IP to use; can be set later manually with Device:SetIP(ip)
Attributes:
- Device:SetIP(ip: String) -> nil
- Device:GetIP() -> String
- Device.FirewallMap: Table
{ PORT: Function(ip, data), ... }
- (private) Device.ConnectedSendEvt
PLEASE NOTE: In order for any connectivity at all, clients have to be connected to a network switch. Is it realistic? No, but this is still the very first “version” of this module that I’ve worked on.
Feel free to give it a shot:
TCP IP Testing.rbxl (37.7 KB)
Gladly open to suggestions.