NetworkInspectorPlugin¶
The NetworkInspectorPlugin is a plugin for the Feather Debugger that lets you inspect network traffic in your LÖVE game. It logs outgoing and incoming packets with timestamps, sizes, and decoded payloads — similar to the browser DevTools Network tab.
Installation¶
Configuration¶
FeatherPluginManager.createPlugin(NetworkInspectorPlugin, "network-inspector", {
maxPackets = 1000, -- max stored packets (oldest trimmed)
maxPayloadPreview = 200, -- max chars shown per payload
hookSocket = false, -- true = auto-hook LuaSocket TCP globally
captureFeatherTraffic = false, -- true = include Feather's own WS traffic
})
Options¶
| Option | Type | Default | Description |
|---|---|---|---|
maxPackets |
number | 1000 |
Maximum packets stored (FIFO overflow). |
maxPayloadPreview |
number | 200 |
Max characters of payload shown in the table. |
hookSocket |
boolean | false |
Auto-hook LuaSocket TCP send/receive at metatable level. |
captureFeatherTraffic |
boolean | false |
Include Feather's own WebSocket traffic when auto-hooking. |
How It Works¶
Manual wrapping (recommended)¶
Wrap your send/receive functions to log packets under a named endpoint:
local net = DEBUGGER.pluginManager:getPlugin("network-inspector")
if net then
-- Wrap your send function
mySendFn = net.instance:wrapSend("game-server", mySendFn)
-- Wrap your receive function
myRecvFn = net.instance:wrapReceive("game-server", myRecvFn)
end
The wrapped functions are transparent — they call the original and log the packet.
Method wrapping on objects¶
For OOP-style network objects, wrap the methods directly:
if net then
local client = myNetworkClient
client.send = net.instance:wrapSendMethod("lobby", client.send)
client.receive = net.instance:wrapReceiveMethod("lobby", client.receive)
end
Use wrapSend and wrapReceive for plain functions. Use wrapSendMethod and wrapReceiveMethod for colon-style calls where the object is passed as the first argument.
HTTP requests (LuaSocket HTTP)¶
Wrap socket.http.request to log all HTTP traffic:
local http = require("socket.http")
local ltn12 = require("ltn12")
local net = DEBUGGER.pluginManager:getPlugin("network-inspector")
if net then
local originalRequest = http.request
http.request = function(url, body)
-- Simple form: http.request(url)
if type(url) == "string" then
local endpoint = url:match("https?://([^/]+)") or url
net.instance:_record("out", endpoint, body or ("GET " .. url), "ok")
local result, code, headers, status = originalRequest(url, body)
if code and code >= 200 and code < 400 then
net.instance:_record("in", endpoint, tostring(code) .. " " .. (status or ""), "ok")
else
net.instance:_record("in", endpoint, tostring(status), "error", "HTTP " .. tostring(code))
end
return result, code, headers, status
end
-- Table form: http.request{ url = ..., sink = ..., ... }
local reqTable = url
local endpoint = reqTable.url and reqTable.url:match("https?://([^/]+)") or "http"
local method = reqTable.method or (reqTable.source and "POST" or "GET")
net.instance:_record("out", endpoint, method .. " " .. (reqTable.url or ""), "ok")
local result, code, headers, status = originalRequest(reqTable, body)
if code and code >= 200 and code < 400 then
net.instance:_record("in", endpoint, tostring(code) .. " " .. (status or ""), "ok")
else
net.instance:_record("in", endpoint, tostring(status), "error", "HTTP " .. tostring(code))
end
return result, code, headers, status
end
end
Global LuaSocket hooking¶
Set hookSocket = true to automatically intercept all tcp:send() and tcp:receive() calls at the LuaSocket metatable level. This captures everything without manual wrapping, but is noisier.
By default the plugin filters out Feather's own WebSocket traffic so the table focuses on your game traffic. Set captureFeatherTraffic = true if you need to see those packets too.
Partial receive data returned with a timeout is recorded when LuaSocket provides it, while empty timeout polls are ignored to keep the table readable.
Programmatic recording¶
You can record packets directly from game code:
net.instance:_record("out", "game-server", '{"action":"move","x":10}', "ok")
net.instance:_record("in", "game-server", '{"ack":true}', "ok")
net.instance:_record("out", "game-server", nil, "error", "connection refused")
Payloads can be strings or tables. Tables are JSON-encoded for preview, and control characters in binary payloads are escaped as \xNN.
Actions¶
| Action | Description |
|---|---|
| Pause | Toggle pause/resume packet recording |
| Clear | Remove all recorded packets |
| Export | Save packets to a JSON file via save dialog |
Desktop UI¶
The table displays packets with:
- # — Packet ID
- Dir — Direction (
→ OUT/← IN) - Endpoint — Named source/destination
- Size — Payload size (B/KB/MB)
- Status —
✓for success,✗+ error message for failures - Time (s) — Game time when the packet was sent/received
- Payload — Truncated payload preview
Use the filter input to search by endpoint, payload content, or direction.