Editorial illustration: Building event payloads without hand-written JSON

Building event payloads without hand-written JSON

The problem I needed to solve

Events cannot be compared reliably if numbers and field names change with device language. I separated field construction from sending and JSON encoding.

Implementation in this snapshot

AnalyticsFieldBuilder.Add trims and lowercases keys and caps long values. Numeric overloads use InvariantCulture, while booleans become true or false.

The design decision and its cost

AddList limits item count and skips empty strings. ToDictionary returns a copy, so consumers need not mutate the builder’s internal collection to build a payload.

Current boundaries

This builder stringifies values and is not a complete schema validator. Final transport types and the overall event size limit also matter; individual field limits are insufficient.

Review scenarios and contract meaning

A type distinction is hidden in this small builder: AnalyticsFieldBuilder’s numeric overload converts a value to text using invariant culture. That stabilizes textual formatting but does not produce a JSON number. PHP’s Number schema requires an actual int or float and rejects a numeric-looking string. Reporting likewise includes only actual numbers in sums and averages. A field intended as a numeric metric must therefore use a correctly typed Dictionary and SDK path. A duration_seconds label or correctly formatted text does not establish the final type.

Server validation adds other boundaries. data must be an Object, timestamps must include a timezone and received_at is reserved for the server. Duplicate JSON keys are rejected, and integers outside the supported exact range need strings to avoid silent rounding. Such strings suit identifiers rather than replacing report metrics. Required checks absence or null; domain meaning still needs contract design. When extra fields are prohibited, validation checks data roots rather than claiming strict validation of every Object subkey. Disciplined construction starts the work; agreement on types, size and meaning between Unity and PHP is the necessary next stage.

A report layout is portable, but importing it into another game does not create the required data. Its data paths must match actual payloads. An empty chart may indicate a wrong field rather than no players. For general Bunny Circus Analytics use, a template begins a contract that must match sender code without substituting sample events for real behavior.

What this stage taught me

For me, event discipline starts before a dashboard. Stable names and numbers let interface language change without changing data meaning, while avoiding unbounded information in a single event.

Explore Dalo

Project source and discussion: GitHub · Telegram

Latest devlogs

Editorial illustration: Presenting selection and captures in Pasur

Presenting selection and captures in Pasur

Pasur motion should explain which hand card was played and which table cards were captured.

Read article
Editorial illustration: Pasur bot decisions from capture options

Pasur bot decisions from capture options

My teammate’s Pasur bot chooses among legal options without inspecting hidden opposing cards.

Read article
Editorial illustration: Capturing table cards in Pasur

Capturing table cards in Pasur

In my teammate’s implementation, Pasur combines selecting a hand card with selecting table cards. Its rules must produce valid sets and prohibit ignoring an available capture.

Read article
Dalo