-
Notifications
You must be signed in to change notification settings - Fork 20
feat: Pulse with quantization #429
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
91 commits
Select commit
Hold shift + click to select a range
5fbeb3c
feat: dynamic lights (#234)
AlejandroAlvarezMelucciDCL e07ccb6
feat: Social service v2 (#242)
kevinszuchet 0870a3e
chore: merge main into experimental (#249)
pravusjif 195803d
feat: Social service blocked users (#247)
kevinszuchet 1f4cb5e
feat: virtual camera fov parameter (#250)
pravusjif 0daf6ca
Feat/merge main (#256)
gonpombo8 3bf30a3
feat: Social settings (#253)
LautaroPetaccio deb9aa6
feat: Include is friend when asking for privacy settings (#258)
LautaroPetaccio 071135f
Merge branch 'main' into experimental
AlejandroAlvarezMelucciDCL 444eb93
Merge branch 'main' into experimental
AlejandroAlvarezMelucciDCL 321145f
feat: Add optional from in chat (#270)
kevinszuchet 9a42a0f
feat: z index and opacity for uielements (#274)
AlejandroAlvarezMelucciDCL ce7a4eb
feat: Voice chat protocol (#265)
LautaroPetaccio 4d07dcd
feat: communities connectivity updates (#276)
aleortega db32c31
chore: bring main changes into experimental (#280)
pravusjif 7b0267f
Chore: update experimental with main (#282)
AlejandroAlvarezMelucciDCL bca3a64
fix: emotes interpolation (#279)
lorux0 346fe2d
feat: experimental branch - control day night cycle (#278)
AlejandroAlvarezMelucciDCL bac58dd
Merge pull request #291 from decentraland/chore/sync
pravusjif 5630df7
feat: improved light proto (#288)
daniele-dcl 501dda6
feat: add communities voice chat proto objects (#283)
juanmahidalgo b0f1f6b
Merge branch 'main' into experimental
lorux0 25e60c0
feat: Enhance social service errors (#294)
kevinszuchet 554f0f2
feat: add positions array to the CommunityVoiceChatUpdate (#297)
juanmahidalgo e03db90
feat: add end community call (#298)
juanmahidalgo 16c6dee
feat: add action to reject request to speak (#299)
juanmahidalgo b786031
Merge branch 'experimental' into chore/sync
pravusjif 862b555
Merge pull request #300 from decentraland/chore/sync
pravusjif d8676dd
fix experimental validate check (#302)
gonpombo8 e9319d0
emote timestamp as not-breaking change (#303)
popuz 7dae801
feat: add is_raising_hand to RequestToSpeakInCommunityVoiceChatPayloa…
juanmahidalgo cb75aeb
Merge pull request #310 from decentraland/chore/sync
pravusjif 905b963
feat: add mute speaker chat (#311)
juanmahidalgo 3f84b58
Merge pull request #315 from decentraland/chore/sync
pravusjif e745352
Merge pull request #319 from decentraland/chore/sync
pravusjif b26a1a0
feat: locomotion settings (#320)
daniele-dcl 6e930db
Merge pull request #325 from decentraland/chore/sync
pravusjif b4ca911
feat: add modifier input action (#316)
lorenzo-ranciaffi 64a5330
feat: audio analysis (#328)
NickKhalow 1d2c132
Added messages and fields to support Social Emotes (#309)
QThund b2ab885
feat: head sync (#326)
daniele-dcl 91726de
Merge pull request #333 from decentraland/chore/sync
pravusjif 9334182
Merge pull request #336 from decentraland/chore/sync
pravusjif d8a2d5b
fix: remove social service v3 (#346)
lorux0 7427639
feat: pointer max player distance (#344)
lorux0 6c9e07f
Merge pull request #347 from decentraland/chore/sync
pravusjif bb94532
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif 3b5ca01
Merge pull request #351 from decentraland/chore/sync
pravusjif 1c3d55b
feat: non pointer interaction (#353)
Maurizio-dcl 203bb4a
feat: tween MoveRotateScale mode (#358)
pravusjif 276e5b7
feat: double jump and glide (#340)
daniele-dcl 50f6aeb
Chore: sync Experimental with Main (#365)
popuz f85988b
Merge pull request #354 from decentraland/chore/sync
pravusjif 4f857fe
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif 22c32c9
Merge pull request #367 from decentraland/chore/sync
pravusjif bd95935
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif 4bea499
Merge pull request #378 from decentraland/chore/sync
pravusjif 3882f15
Merge pull request #381 from decentraland/chore/sync
pravusjif 814ceb0
Merge pull request #383 from decentraland/chore/sync
pravusjif 3a0cc03
feat: avatar masks (#373)
Maurizio-dcl 6915bc8
feat: add Reaction and ChatReaction comms messages for emoji reaction…
biotech77 25eb9ff
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif f092adf
corrected git wrong auto-merging
pravusjif 6a96fbb
Merge pull request #387 from decentraland/chore/sync
pravusjif 4851fd6
Merge pull request #388 from decentraland/chore/sync
pravusjif f823838
Merge branch 'experimental' into chore/sync
pravusjif 1efee00
Merge pull request #391 from decentraland/chore/sync
pravusjif e36f074
Merge pull request #409 from decentraland/chore/sync
pravusjif 424957b
fix: update oddish-action for npm Trusted Publishing (#416)
decentraland-bot a470b13
fix: setup-node 24, fix repository.url, and bump actions to v6 (#417)
decentraland-bot d63dc0c
Merge branch 'experimental' into chore/sync
pravusjif 4c3067e
Merge pull request #419 from decentraland/chore/sync
pravusjif 7ccf6fa
Merge branch 'experimental' into chore/sync
pravusjif c056d32
Merge pull request #422 from decentraland/chore/sync
pravusjif 3c70e8a
Feat: Pulse with quantization (#360)
mikhail-dcl f0df3e0
chore: port protoc-gen-bitwise to Node (drop Python dependency) (#423)
mikhail-dcl 8fde168
Merge pull request #425 from decentraland/chore/sync
pravusjif c2f6832
feat: add AMT_HIDE_NAMETAGS modifier (#411)
gonpombo8 c09a897
Feat: Pulse with quantization (#360)
mikhail-dcl 58b8a92
chore: port protoc-gen-bitwise to Node (drop Python dependency) (#423)
mikhail-dcl 74c5908
feat: power-law quantized float option; apply to Pulse velocity delta…
robtfm 9faec2f
fix: additional quantization improvements (#435)
mikhail-dcl 6771b13
Merge branch 'feat/pulse-prd' into experimental
mikhail-dcl f355d6c
docs: describe protoc-gen-bitwise as it actually works; drop dead Bit…
mikhail-dcl 818f24d
fix: normalize CRLF when comparing gen:test golden fixtures
mikhail-dcl 814d095
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif 1e80e43
Merge pull request #428 from decentraland/chore/sync
pravusjif 5965969
fix: add realm in pulse PlayerJoined message
lorenzo-ranciaffi 1c58939
added realm to teleport performed message
lorenzo-ranciaffi 45edead
Merge branch 'experimental' into feat/pulse-prd
mikhail-dcl a442c71
feat: Pulse pure scenes listeners (#437)
mikhail-dcl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we want this? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # CLAUDE.md — MMO Networking Stack | ||
|
|
||
| ## Project Overview | ||
|
|
||
| High-performance MMO-style multiplayer networking stack. Protocol is **open** (Unity C# client + others). Infrastructure on **AWS**. Goals: high concurrency, aggressive interest management, low-latency state sync. | ||
|
|
||
| | Layer | Choice | | ||
| |---|---| | ||
| | Transport | ENet (UDP, channel-based) | | ||
| | Client | Unity (C#) | | ||
| | Server | Custom server | | ||
| | Schema source of truth | `.proto` files | | ||
| | Serialization | Standard protobuf wire format + custom protoc plugin (quantized float accessors) | | ||
| | Auth | Decentraland ECDSA chain validation (local, on HANDSHAKE channel 0) | | ||
|
|
||
| --- | ||
|
|
||
| ## Serialization: Custom Protoc Plugin | ||
|
|
||
| ### What it does | ||
| Reads `.proto` files with custom field options and generates C# **partial classes** (`*.Bitwise.cs`) that add typed float accessors on top of quantized `uint32` fields, keeping the quantization math bit-for-bit identical across all client implementations. | ||
|
|
||
| The wire format is **standard protobuf** — a quantized value lives in a plain `uint32` field and travels as an ordinary varint. There is no custom bit stream; any protobuf-capable client can parse the messages without this plugin. Per annotated field the plugin emits: | ||
|
|
||
| - `float {Field}Quantized` — computed accessor (no backing cache): the getter decodes the stored `uint32`, the setter encodes a float back into it, via the static `Quantize` helpers | ||
| - `const float {Field}QuantizedStep` — the coarsest quantization step of the field, safe as an equality tolerance | ||
| - per message: `bool AreQuantizedFieldsInRange()` — pure-integer check that every stored code fits its declared bit width (`0 .. 2^bits-1`); reject malformed/hostile messages before storing or relaying | ||
|
|
||
| Only non-repeated `uint32` fields get accessors; `bit_packed` and unannotated fields pass through with no generated code. | ||
|
|
||
| ### Custom Field Options (`options.proto`) | ||
|
|
||
| ```protobuf | ||
| syntax = "proto3"; | ||
| import "google/protobuf/descriptor.proto"; | ||
|
|
||
| message QuantizedFloatOptions { | ||
| float min = 1; | ||
| float max = 2; | ||
| uint32 bits = 3; | ||
| } | ||
|
|
||
| // Signed power-law quantizer: an (bits-1)-bit magnitude (high bits) plus a sign | ||
| // (LSB), decoded as sign * max * u^pow. Exact zero; pow>1 concentrates resolution | ||
| // near zero; sign in the LSB keeps small magnitudes in one varint byte. | ||
| message QuantizedPowerFloatOptions { | ||
| float max = 1; | ||
| float pow = 2; | ||
| uint32 bits = 3; | ||
| } | ||
|
|
||
| message BitPackedOptions { | ||
| uint32 bits = 1; | ||
| } | ||
|
|
||
| extend google.protobuf.FieldOptions { | ||
| QuantizedFloatOptions quantized = 50001; | ||
| BitPackedOptions bit_packed = 50002; | ||
| QuantizedPowerFloatOptions quantized_power = 50003; | ||
| } | ||
| ``` | ||
|
|
||
| ### Usage example | ||
|
|
||
| Quantized fields are declared **`uint32`** (not `float`) — the float type exists only in the generated accessor: | ||
|
|
||
| ```protobuf | ||
| message PositionDelta { | ||
| uint32 dx = 1 [(quantized) = { min: -100.0, max: 100.0, bits: 16 }]; | ||
| uint32 dy = 2 [(quantized) = { min: -100.0, max: 100.0, bits: 16 }]; | ||
| uint32 dz = 3 [(quantized) = { min: -100.0, max: 100.0, bits: 16 }]; | ||
| uint32 entity_id = 4 [(bit_packed) = { bits: 20 }]; | ||
| uint32 sequence = 5 [(bit_packed) = { bits: 12 }]; | ||
| } | ||
| // Varint wire cost: dx/dy/dz/entity_id ≤ 4 B each (1 B tag + ≤ 3 B varint), | ||
| // sequence ≤ 3 B — worst-case 19 B, less when proto3 omits zero-valued fields. | ||
| ``` | ||
|
|
||
| `proto/decentraland/common/quantization_example.proto` is the fully worked reference: per-field wire costs for the linear, power-law, and bit-packed annotations. | ||
|
|
||
| ### Plugin structure | ||
|
|
||
| ``` | ||
| protoc-gen-bitwise/ | ||
| ├── plugin.js # stdin -> CodeGeneratorRequest, stdout -> CodeGeneratorResponse (Node) | ||
| ├── generator_csharp.js # emits the *.Bitwise.cs accessor partials for Unity | ||
| ├── options.js # parses the custom quantized / quantized_power / bit_packed field options | ||
| ├── wire.js # self-contained protobuf wire codec (zero runtime deps) | ||
| └── runtime/cs/ # C# runtime: Quantize.cs — consumers copy it next to the generated files | ||
| ``` | ||
|
|
||
| Plugin contract: a protoc plugin that reads a serialized `CodeGeneratorRequest` from stdin and writes a serialized `CodeGeneratorResponse` to stdout. It is a plain Node script — **no `npm install` required, only `node` on PATH**. protoc invokes it through a tiny wrapper that runs `node plugin.js` (`.cmd` on Windows, a shell script elsewhere, since protoc cannot exec a `.js` directly). | ||
|
|
||
| ```bash | ||
| protoc \ | ||
| --proto_path=proto \ | ||
| --bitwise_out=generated/ \ | ||
| --plugin=protoc-gen-bitwise=protoc-gen-bitwise/plugin.js \ | ||
| movement.proto position.proto | ||
| ``` | ||
|
|
||
| Parity is locked down by `npm run gen:test` (compares generator output against golden C# fixtures in `protoc-gen-bitwise/test/`). | ||
|
|
||
| --- | ||
|
|
||
| ## Quantize Runtime | ||
|
|
||
| Generated accessors call the static `Quantize` class (`protoc-gen-bitwise/runtime/cs/Quantize.cs`, namespace `Decentraland.Networking.Bitwise`) — the only C# runtime file; consumers copy it next to the generated `*.Bitwise.cs` partials. Quantization uses **`Round`** (not truncate) to minimize error; identical rounding on both sides makes encode -> decode a round-trip no-op. | ||
|
|
||
| ### Core math — linear (`Quantize.Encode` / `Quantize.Decode`) | ||
|
|
||
| ``` | ||
| encoded = Round(clamp01((value - min) / (max - min)) * (2^bits - 1)) | ||
| decoded = encoded / (2^bits - 1) * (max - min) + min | ||
| ``` | ||
|
|
||
| ### Core math — power-law (`Quantize.EncodePower` / `Quantize.DecodePower`) | ||
|
|
||
| For signed fields like velocity that need an exact zero and fine resolution near zero: | ||
|
|
||
| ``` | ||
| u = clamp01(|value| / max) ^ (1 / pow) | ||
| encoded = (Round(u * (2^(bits-1) - 1)) << 1) | sign // magnitude in high bits, sign in LSB | ||
| decoded = sign * max * ((encoded >> 1) / (2^(bits-1) - 1)) ^ pow | ||
| ``` | ||
|
|
||
| - Zero encodes exactly to code `0` (a zero magnitude never sets the sign bit), so proto3 omits a stopped field entirely | ||
| - `pow > 1` concentrates resolution near zero, coarse near `±max` | ||
| - Sign in the LSB makes the varint cost track magnitude, not direction — a small `|value|` of either sign stays in one varint byte | ||
|
|
||
| --- | ||
|
|
||
| ## Precision Reference | ||
|
|
||
| | Range | Bits | Step size | | ||
| |-------------|------|---------------| | ||
| | [-100, 100] | 16 | ~0.003 units | | ||
| | [-10, 10] | 12 | ~0.005 units | | ||
| | [-100, 100] | 12 | ~0.049 units | | ||
|
|
||
| Sub-centimeter precision is achievable at 12-16 bits for position deltas. | ||
|
|
||
| Wire cost is varint-based — 1 tag byte per present field (field numbers ≤ 15) plus: | ||
|
|
||
| | Code bits | Worst-case varint | Worst-case field total | | ||
| |-----------|-------------------|------------------------| | ||
| | ≤ 7 | 1 B | 2 B | | ||
| | ≤ 14 | 2 B | 3 B | | ||
| | ≤ 21 | 3 B | 4 B | | ||
|
|
||
| Proto3 omits fields equal to 0, so typical cost is lower than worst-case. | ||
|
|
||
| --- | ||
|
|
||
| ## Key Design Principles | ||
|
|
||
| - `.proto` files are the **single source of truth** for all message schemas | ||
| - The protoc plugin generates the **C# quantized accessors** from the schema — never hand-write quantization math; standard protobuf handles the wire encoding | ||
| - Encode -> decode is a **no-op** (round-trip safe) due to consistent use of `Round` | ||
| - Validate inbound quantized messages with `AreQuantizedFieldsInRange()` before storing or relaying — the server relays raw codes verbatim | ||
| - Prefer **client-driven resync** over proactive server corrections | ||
| - Push complexity to clients where appropriate; server maintains authority | ||
| - Channel 0: reliable messages (STATE_FULL snapshots, ACKs, resync requests, HANDSHAKE) | ||
| - Channel 1: unreliable sequenced (high-frequency position deltas, client input) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we want this?