Custom C++ Engine
Integrate Compuon into any C++17 codebase. Link the static library from the SDK package, include the headers, and connect the game client to the integrity server over WebSocket. The package’s INTEGRATION_TIPS.md is the full reference; this page is the short version.
CMake Integration
# COMPUON_SDK = the unzipped package; the library is at its root
set(COMPUON_SDK ${CMAKE_SOURCE_DIR}/third_party/compuon)
add_library(compuon STATIC IMPORTED)
set_target_properties(compuon PROPERTIES
IMPORTED_LOCATION ${COMPUON_SDK}/libcompuon.a # compuon.lib on Windows
INTERFACE_INCLUDE_DIRECTORIES ${COMPUON_SDK}/include
)
target_link_libraries(YourGame PRIVATE compuon)
# Windows: match the package's /MD runtime (see Build Requirements)
set_property(TARGET YourGame PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreadedDLL")include/ covers both compuon/ and protocol/, so <compuon/compuon.h> and <protocol/sync_handler.h> both resolve.
Initialization
#include <compuon/compuon.h>
#include <protocol/sync_handler.h>
bool running = true;
void update() { running = false; }
void render() {}
int main() {
// The two seeds are placeholders. The integrity server issues the real
// per-session seeds at AUTH_OK and SyncHandler applies them with
// compuon::api::rekey_session. The third argument is what matters:
// false = server-verified profile (no locally minted operands at boot).
compuon::api::init(0x1ULL, 0x2ULL, /*bootstrap_operands=*/false);
// Open your WebSocket, authenticate, and create Compuon<T> values only
// after AUTH_OK -- see "Connecting to the integrity server" below.
while (running) {
// No per-frame SDK call is required. Your socket code pumps the
// handler (on_message / tick / pop_outgoing).
update();
render();
}
// Terminal: every Compuon handle dangles after this.
compuon::api::shutdown();
}Call api::init() once, before any Compuon<T> is constructed. To re-key a session that is already initialized call api::rekey_session(); SyncHandler does this for you on AUTH_OK. Never call api::init() a second time: it re-reads its third argument (defaulting back to true) and silently re-arms locally minted operands in a session that asked for the server-verified profile.
The default package build is ungated: the local operand mint is still compiled in, so a build that passes bootstrap_operands = true (or omits the argument) keeps working offline, on operands it minted itself. If you want that code path absent from the shipped binary, ask us for a server-gated build (hello@compuon.dev); a gated build has no offline play, and TOOLCHAIN.txt records which kind you received.
Connecting to the integrity server
You own the socket; the SDK owns the protocol. SyncHandler (include/protocol/sync_handler.h) never touches the network: it parses the text frames you hand it and queues the text frames you send.
- +Endpoint:
wss://<host>/sync. The hosted demo server iscloud.compuon.dev; a self-hosted server (thecloudcontainer) listens onws://<host>:3000/sync. - +API key: a
ck_…key issued by whoever operates the server (npx tsx src/cli.ts create --project <name>on the server host; it is printed once). For the hosted server that is us: email hello@compuon.dev with your project name. This is notthe key you sign in to the build dashboard with — the dashboard requests builds, theck_key authenticates game clients.
Order of calls (the order is load-bearing):
#include <compuon/compuon.h>
#include <protocol/sync_handler.h>
#include <memory>
void on_verdict(int action, void* userdata); // action: NONE=0 WARN=1 KICK=2 ESCALATE=3
// 1. compuon::api::init(placeholder, placeholder, /*bootstrap_operands=*/false);
// 2. Open your WebSocket to wss://<host>/sync (your code).
// 3. One SyncHandler per connection. Opt in to the server-issued
// per-session seed BEFORE send_auth.
compuon::SyncHandler handler;
handler.set_request_session_seed(true);
handler.set_verdict_callback(on_verdict, nullptr);
handler.send_auth(player_id, project_id, api_key); // queues AUTH; api_key is the ck_ key
// 4. Pump from your socket code. Every inbound text frame -> on_message;
// every frame -> tick; then drain the outgoing queue onto the socket.
handler.on_message(frame_bytes, frame_len);
handler.tick();
auto out = std::make_unique<compuon::SyncHandler::OutMessage>(); // 32 KB: keep it off the stack
while (handler.pop_outgoing(*out)) // or pop_outgoing_into(buf, n)
ws_send_text(out->data, out->len);
// 5. Create protected values only after AUTH_OK. By then the handler has
// already called compuon::api::rekey_session with the server's seeds; a
// value created earlier is keyed to the placeholder seed and mismatches
// on its first spot-check.
if (handler.is_authenticated()) {
compuon::Compuon<int> hp(100);
handler.send_var_register(hp.ct_id(), compuon::Health(100, "hp"));
// ... on destroy: handler.send_var_unregister(hp.ct_id());
}What arrives on the wire, so the callbacks make sense:
- +
AUTH_OKcarries the per-session seeds (only if you opted in); the handler re-keys viarekey_session. - +
BUNDLE_DELIVERcarries server-issued material the SDK needs before it can verify; the handler stores it and answersBUNDLE_ACK, rejecting a bundle whose epoch does not advance. - +
VAR_REGISTERwith aVarProfile(Health(max, name),Currency(name),Counter(name)frommessages.h) tells the server how the value may legitimately move. With aname, the server binds the variable to its own manifest and ignores client-declared bounds — prefer that. - +
SYNC_REQperiodically picks a few of the registered ids and gives the handler a short deadline to answerSYNC_RESP. A missing, late, or empty answer scores as a timeout. - +
KEY_ROTATE: the handler callsapi::rotateand answersROTATE_ACK; your values are re-keyed in place. You do not callrotate()yourself. - +
VERDICTfires your callback with an action code. The codes are tags, not ranks: compare withverdict_severity(action), neveraction >= X.
Two mechanics to know: SyncHandler has no disconnect hook and stays authenticated once it has been, so construct a fresh one per connection rather than reusing it across reconnects; and tick()is currently a no-op kept for future timeouts — call it anyway.
When the server is unreachable:without a live session there is nothing verifying the values, and the SDK does not fail the game on its own — nor does it reconnect or re-verify once a connection drops. Verification resumes only when you open a new connection with a fresh handler. That policy is yours to set: put any reconnect, grace-period, or “no session → treat as untrusted” logic in the transport and game layers you own. The SDK hands you the verdict; the enforcement is yours.
Using Compuon<T>
#include <compuon/compuon.h>
using namespace compuon;
// Construct after AUTH_OK when you talk to the integrity server.
struct Player {
Compuon<int> hp{100};
Compuon<int> gold{0};
Compuon<float> speed{5.0f};
Compuon<float> x{0.0f}, y{0.0f};
void take_damage(int dmg) {
hp -= Compuon<int>(dmg);
if (hp <= 0) die(); // reads the cache; builds no temporary
}
void move(float dx, float dy) {
// dx and dy change per call, so each += builds a temporary
// value (see Operation Tiers)
x += Compuon<float>(dx * speed.val());
y += Compuon<float>(dy * speed.val());
}
void add_gold(int amount) {
gold += Compuon<int>(amount);
}
void haste() {
speed *= 2; // integer scalar: Tier 0
// speed *= 1.5f; // does not compile: the float-scalar overload is deleted.
// Fractional factor: speed *= Compuon<float>(1.5f) (Tier 1), or keep the
// quantity in tenths as Compuon<int> and scale by an integer.
}
void die() {}
};Supported Types
| Type | Use Case |
|---|---|
| Compuon<int> | Health, ammo, score, gold |
| Compuon<float> | Speed, position. Scalar *= takes an integer only: m *= 1.5f is a compile error (deleted overload). A fractional multiplier is Compuon<float> * Compuon<float> (Tier 1), or integer math on a Compuon<int> kept in tenths. |
| Compuon<int64_t> | Large counters, timestamps |
Build Requirements
- +C++17 or later
- +Windows: MSVC, Release,
/MD(MultiThreadedDLL) runtime, and the same MSVC toolset major version recorded inTOOLCHAIN.txt. The packagecompuon.libis built with/GL, so it only links under the compiler that produced it; a mismatch fails at link (C1047/LNK1257), and a/MTor Debug-CRT configuration fails withLNK2038. - +Linux: GCC or Clang with libstdc++;
libcompuon.ais plain GCC object code (no LTO). Match the GCC major inTOOLCHAIN.txtso the libstdc++ ABI agrees. - +No external dependencies. The public headers pull in only
<cstdint>,<cmath>,<type_traits>,<utility>,<cstddef>and<memory>. - +Link the static library at the package root. For another toolset or a Debug-CRT variant, email hello@compuon.dev.
Package layout:
compuon.lib // static library, at the root (libcompuon.a in the linux-x64 package)
include/
compuon/
compuon.h
platform.h
types.h
protocol/
sync_handler.h // SyncHandler: your link to the integrity server
messages.h // VarProfile, VerdictAction, verdict_severity()
INTEGRATION_TIPS.md // read this first
README.md
TOOLCHAIN.txt // compiler, CMake, commit, seed, gated or not