Communicating Between SAPP and Chimera
While researching some existing SAPP and Chimera scripts, I noticed they were communicating with each other without using memory patches or any custom networking. After a bit of digging, I discovered they were using rprint() as a lightweight messaging system between the server and client.
It's a surprisingly simple technique, but I couldn't find much documentation explaining how it works. After putting together a minimal proof of concept, I was able to reproduce the same behaviour.
How it Works
The communication flow looks like this:
SAPP
|
| rprint(PlayerIndex, "|nhas_chimera?")
v
Halo
|
| RCON message
v
Chimera Lua
|
| execute_script("rcon password am_chimera")
v
SAPP EVENT_COMMAND
The server sends an RCON message to a specific player using rprint(). Chimera receives the message through the rcon message callback, performs whatever action you want, and can send information back to the server by executing an RCON command with execute_script().
This gives you a simple two-way communication channel between SAPP and Chimera.
Server (SAPP)
First, create a player variable that tracks whether a client is running Chimera.
add_var("has_chimera", 4) When a player joins, send them a message asking if Chimera is installed.
function OnJoin(PlayerIndex)
set_var(PlayerIndex, "$has_chimera", 0)
rprint(PlayerIndex, "|nhas_chimera?")
end When the client responds, handle it in EVENT_COMMAND.
function OnCommand(PlayerIndex, Command, Environment)
if Environment == 1 then
if Command == "am_chimera" then
set_var(PlayerIndex, "$has_chimera", 1)
say(PlayerIndex, "Chimera detected!")
return false
end
end
return true
end The important part here is checking Environment == 1. This tells SAPP the command originated from RCON rather than chat or the server console.
Client (Chimera)
Register the rcon message callback.
set_callback("rcon message", "OnRconMessage") Then listen for the incoming message.
function OnRconMessage(msg)
if msg == "|nhas_chimera?" then
execute_script("rcon password am_chimera")
return false
end
return true
end When Chimera receives the message, it simply executes an RCON command back to the server.
Why |n?
You might have noticed the message starts with |n.
rprint(PlayerIndex, "|nhas_chimera?") |n inserts a newline into the message. Halo only displays the first line of an incoming RCON message, so placing the actual command on the second line hides it from the player's console while still allowing Chimera to receive the full string.
Without it, players would briefly see every protocol message printed to their console.
Building Your Own Protocol
Once the communication channel is established, you can send whatever messages you need.
For example, the server could send:
rprint(PlayerIndex, "play_sound~flag_taken")
rprint(PlayerIndex, "set_colour~255~0~0")
rprint(PlayerIndex, "spawn_effect~explosion") On the client, split the message into arguments and use the first value as the command.
function OnRconMessage(msg)
local args = {}
for value in string.gmatch(msg, "([^~]+)") do
table.insert(args, value)
end
if args[1] == "play_sound" then
-- Play a sound
elseif args[1] == "set_colour" then
-- Update colours
elseif args[1] == "spawn_effect" then
-- Spawn a local effect
end
return true
end Using ~ as a delimiter makes it easy to pass multiple arguments in a single message. This is exactly how some larger SAPP/Chimera projects communicate between the server and client.
A Couple of Things I Found
There were two things that caught me out while testing this.
The first is that the callback is registered as "rcon message" (with a space), even though Chimera refers to it internally as rcon_message. If you're reading the source code, it's easy to assume the Lua callback uses the underscore version.
The second is that rprint() isn't just useful for printing text. Combined with Chimera's rcon message callback, it becomes a simple messaging layer that can be used for feature detection, synchronising client-side effects, or passing custom commands between the server and client.
It's a neat technique that's been hiding in plain sight.