Skip to content
Windows only · Invite only. To get the SDK, DM @snailyt on Telegram.

Heartbeats and threading

Kind Examples Where it runs
State IsLoggedIn, HasTier, Subscriptions, Variable, Hwid Reads an in-memory snapshot. Never blocks; call it from anywhere
Async LoginAsync, FetchVariableAsync, DownloadFileAsync … Queued on the SDK’s one worker thread, in order. The callback goes through the dispatcher
Blocking Login, FetchVariable, Heartbeat … The network round trip on your thread. Never on a UI thread

Every function is thread-safe. There is one SDK instance per process.

Without a dispatcher, callbacks and events run on the SDK’s worker thread. Set one so they arrive where your UI lives:

// Win32: post to a window, handle WM_APP_EONAUTH with eonauth::win32::RunDispatched(lParam).
eonauth::SetDispatcher(eonauth::win32::WindowDispatcher(hwnd, WM_APP_EONAUTH));
// Anything else: your own function that runs `work` on the right thread.
eonauth::SetDispatcher([](std::function<void()> work) { PostToUiThread(std::move(work)); });

Event handlers (AddEventHandler) go through the dispatcher too. AddEventHandler returns an ID for RemoveEventHandler.

After sign-in, the worker thread sends a heartbeat at the interval set in App settings, with ±10% random jitter so clients started by the same update don’t arrive at once. Each heartbeat:

  • re-checks the user, key, ban, PC lock and blacklist on the server,
  • refreshes the subscriptions (raising SubscriptionsChanged if they changed),
  • keeps the session’s idle expiry from running out.

A ban or revoked key reaches a running app at its next heartbeat.

  1. A failed heartbeat raises ConnectionProblem. The user stays signed in, and IsConnectionHealthy() is false.
  2. The SDK retries after 15 seconds, doubling up to the heartbeat interval.
  3. The first heartbeat that gets through raises ConnectionRestored.
  4. If nothing gets through before the server-side session expires, it raises SessionLost with NetworkError, so the app locks.

Only a definitive answer from the server ends a session. A flaky connection is never mistaken for a ban.

Turn background heartbeats off with Config::autoHeartbeat = false and call Heartbeat() yourself.

  • Connect timeout 5 seconds, receive timeout 15 seconds.
  • Network errors and server errors get up to 3 attempts with backoff and jitter. Each retry is freshly encrypted and signed.
  • RateLimited is never retried blindly: Result::retryAfterSeconds says how long to wait.
  • Register, key login and redeem are never retried once the request may have reached the server. A blind retry could burn a key or create a second account. You get NetworkError with a message saying it may have gone through, so the user can check before trying again.

The server accepts requests within 30 seconds of its own clock. If the PC’s clock is more than 10 seconds off, the SDK corrects for it using the server’s time and retries once. If that fails, you get ClockSkew, and UserHint suggests turning on Set time automatically.

Call Shutdown() before the process exits, for example in WM_DESTROY. Queued async calls that haven’t started are dropped and their callbacks don’t run; callbacks already posted to a window that is being destroyed are dropped too.

For console tools and your own worker threads, every async call has a blocking twin:

#include "eonauth.h"
int main() {
if (!eonauth::Init(MakeConfig())) return 1;
eonauth::Result r = eonauth::LoginWithKey(key, username);
if (!r) { std::puts(eonauth::UserMessage(r).c_str()); return 2; }
std::string value;
if (eonauth::FetchVariable("var_0123456789abcdef0123456789abcdef", &value)) Use(value);
eonauth::Shutdown(true); // also signs out
}
eonauth::SetLogHandler([](eonauth::LogLevel level, const std::string& line) { MyLog(level, line); });

Lines are already scrubbed: the SDK never logs tokens, keys or passwords, and also redacts anything shaped like a license key or a long base64 or hex string. Pass nullptr to go back to stdout/stderr.