Skip to content
Closed
Show file tree
Hide file tree
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 Feb 4, 2025
e07ccb6
feat: Social service v2 (#242)
kevinszuchet Feb 17, 2025
0870a3e
chore: merge main into experimental (#249)
pravusjif Feb 28, 2025
195803d
feat: Social service blocked users (#247)
kevinszuchet Mar 17, 2025
1f4cb5e
feat: virtual camera fov parameter (#250)
pravusjif Mar 18, 2025
0daf6ca
Feat/merge main (#256)
gonpombo8 Mar 24, 2025
3bf30a3
feat: Social settings (#253)
LautaroPetaccio Mar 28, 2025
deb9aa6
feat: Include is friend when asking for privacy settings (#258)
LautaroPetaccio Apr 22, 2025
071135f
Merge branch 'main' into experimental
AlejandroAlvarezMelucciDCL May 23, 2025
444eb93
Merge branch 'main' into experimental
AlejandroAlvarezMelucciDCL Jun 5, 2025
321145f
feat: Add optional from in chat (#270)
kevinszuchet Jun 10, 2025
9a42a0f
feat: z index and opacity for uielements (#274)
AlejandroAlvarezMelucciDCL Jun 10, 2025
ce7a4eb
feat: Voice chat protocol (#265)
LautaroPetaccio Jun 24, 2025
4d07dcd
feat: communities connectivity updates (#276)
aleortega Jun 30, 2025
db32c31
chore: bring main changes into experimental (#280)
pravusjif Jul 4, 2025
7b0267f
Chore: update experimental with main (#282)
AlejandroAlvarezMelucciDCL Jul 4, 2025
bca3a64
fix: emotes interpolation (#279)
lorux0 Jul 10, 2025
346fe2d
feat: experimental branch - control day night cycle (#278)
AlejandroAlvarezMelucciDCL Jul 15, 2025
bac58dd
Merge pull request #291 from decentraland/chore/sync
pravusjif Jul 21, 2025
5630df7
feat: improved light proto (#288)
daniele-dcl Jul 28, 2025
501dda6
feat: add communities voice chat proto objects (#283)
juanmahidalgo Jul 28, 2025
b0f1f6b
Merge branch 'main' into experimental
lorux0 Jul 28, 2025
25e60c0
feat: Enhance social service errors (#294)
kevinszuchet Jul 28, 2025
554f0f2
feat: add positions array to the CommunityVoiceChatUpdate (#297)
juanmahidalgo Aug 1, 2025
e03db90
feat: add end community call (#298)
juanmahidalgo Aug 4, 2025
16c6dee
feat: add action to reject request to speak (#299)
juanmahidalgo Aug 5, 2025
b786031
Merge branch 'experimental' into chore/sync
pravusjif Aug 7, 2025
862b555
Merge pull request #300 from decentraland/chore/sync
pravusjif Aug 7, 2025
d8676dd
fix experimental validate check (#302)
gonpombo8 Aug 7, 2025
e9319d0
emote timestamp as not-breaking change (#303)
popuz Aug 12, 2025
7dae801
feat: add is_raising_hand to RequestToSpeakInCommunityVoiceChatPayloa…
juanmahidalgo Aug 18, 2025
cb75aeb
Merge pull request #310 from decentraland/chore/sync
pravusjif Sep 29, 2025
905b963
feat: add mute speaker chat (#311)
juanmahidalgo Sep 30, 2025
3f84b58
Merge pull request #315 from decentraland/chore/sync
pravusjif Oct 8, 2025
e745352
Merge pull request #319 from decentraland/chore/sync
pravusjif Oct 29, 2025
b26a1a0
feat: locomotion settings (#320)
daniele-dcl Nov 24, 2025
6e930db
Merge pull request #325 from decentraland/chore/sync
pravusjif Nov 25, 2025
b4ca911
feat: add modifier input action (#316)
lorenzo-ranciaffi Nov 25, 2025
64a5330
feat: audio analysis (#328)
NickKhalow Dec 5, 2025
1d2c132
Added messages and fields to support Social Emotes (#309)
QThund Dec 6, 2025
b2ab885
feat: head sync (#326)
daniele-dcl Dec 17, 2025
91726de
Merge pull request #333 from decentraland/chore/sync
pravusjif Dec 22, 2025
9334182
Merge pull request #336 from decentraland/chore/sync
pravusjif Jan 6, 2026
d8a2d5b
fix: remove social service v3 (#346)
lorux0 Jan 28, 2026
7427639
feat: pointer max player distance (#344)
lorux0 Jan 28, 2026
6c9e07f
Merge pull request #347 from decentraland/chore/sync
pravusjif Jan 28, 2026
bb94532
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif Jan 30, 2026
3b5ca01
Merge pull request #351 from decentraland/chore/sync
pravusjif Jan 30, 2026
1c3d55b
feat: non pointer interaction (#353)
Maurizio-dcl Feb 27, 2026
203bb4a
feat: tween MoveRotateScale mode (#358)
pravusjif Mar 2, 2026
276e5b7
feat: double jump and glide (#340)
daniele-dcl Mar 2, 2026
50f6aeb
Chore: sync Experimental with Main (#365)
popuz Mar 2, 2026
f85988b
Merge pull request #354 from decentraland/chore/sync
pravusjif Mar 3, 2026
4f857fe
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif Mar 4, 2026
22c32c9
Merge pull request #367 from decentraland/chore/sync
pravusjif Mar 4, 2026
bd95935
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif Mar 20, 2026
4bea499
Merge pull request #378 from decentraland/chore/sync
pravusjif Mar 20, 2026
3882f15
Merge pull request #381 from decentraland/chore/sync
pravusjif Mar 23, 2026
814ceb0
Merge pull request #383 from decentraland/chore/sync
pravusjif Apr 3, 2026
3a0cc03
feat: avatar masks (#373)
Maurizio-dcl Apr 9, 2026
6915bc8
feat: add Reaction and ChatReaction comms messages for emoji reaction…
biotech77 Apr 16, 2026
25eb9ff
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif Apr 16, 2026
f092adf
corrected git wrong auto-merging
pravusjif Apr 16, 2026
6a96fbb
Merge pull request #387 from decentraland/chore/sync
pravusjif Apr 16, 2026
4851fd6
Merge pull request #388 from decentraland/chore/sync
pravusjif Apr 16, 2026
f823838
Merge branch 'experimental' into chore/sync
pravusjif May 13, 2026
1efee00
Merge pull request #391 from decentraland/chore/sync
pravusjif May 13, 2026
e36f074
Merge pull request #409 from decentraland/chore/sync
pravusjif Jun 8, 2026
424957b
fix: update oddish-action for npm Trusted Publishing (#416)
decentraland-bot Jun 8, 2026
a470b13
fix: setup-node 24, fix repository.url, and bump actions to v6 (#417)
decentraland-bot Jun 8, 2026
d63dc0c
Merge branch 'experimental' into chore/sync
pravusjif Jun 9, 2026
4c3067e
Merge pull request #419 from decentraland/chore/sync
pravusjif Jun 9, 2026
7ccf6fa
Merge branch 'experimental' into chore/sync
pravusjif Jun 9, 2026
c056d32
Merge pull request #422 from decentraland/chore/sync
pravusjif Jun 9, 2026
3c70e8a
Feat: Pulse with quantization (#360)
mikhail-dcl Jun 11, 2026
f0df3e0
chore: port protoc-gen-bitwise to Node (drop Python dependency) (#423)
mikhail-dcl Jun 15, 2026
8fde168
Merge pull request #425 from decentraland/chore/sync
pravusjif Jun 24, 2026
c2f6832
feat: add AMT_HIDE_NAMETAGS modifier (#411)
gonpombo8 Jun 25, 2026
c09a897
Feat: Pulse with quantization (#360)
mikhail-dcl Jun 11, 2026
58b8a92
chore: port protoc-gen-bitwise to Node (drop Python dependency) (#423)
mikhail-dcl Jun 15, 2026
74c5908
feat: power-law quantized float option; apply to Pulse velocity delta…
robtfm Jul 3, 2026
9faec2f
fix: additional quantization improvements (#435)
mikhail-dcl Jul 6, 2026
6771b13
Merge branch 'feat/pulse-prd' into experimental
mikhail-dcl Jul 6, 2026
f355d6c
docs: describe protoc-gen-bitwise as it actually works; drop dead Bit…
mikhail-dcl Jul 7, 2026
818f24d
fix: normalize CRLF when comparing gen:test golden fixtures
mikhail-dcl Jul 7, 2026
814d095
Merge branch 'experimental' of https://github.com/decentraland/protoc…
pravusjif Jul 8, 2026
1e80e43
Merge pull request #428 from decentraland/chore/sync
pravusjif Jul 8, 2026
5965969
fix: add realm in pulse PlayerJoined message
lorenzo-ranciaffi Jul 9, 2026
1c58939
added realm to teleport performed message
lorenzo-ranciaffi Jul 10, 2026
45edead
Merge branch 'experimental' into feat/pulse-prd
mikhail-dcl Jul 13, 2026
a442c71
feat: Pulse pure scenes listeners (#437)
mikhail-dcl Jul 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions .github/workflows/build-and-publish.yml

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want this?

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ jobs:
dcl_protocol_s3_bucket_key: ${{ steps.publish_dcl_protocol.outputs.s3-bucket-key }}
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Use Node.js 24
uses: actions/setup-node@v6
with:
Expand All @@ -36,7 +38,7 @@ jobs:
uses: decentraland/oddish-action@0074f2d535c43b5c83f136525304a6277166d77f # @master
id: publish_dcl_protocol
with:
registry-url: 'https://registry.npmjs.org'
registry-url: "https://registry.npmjs.org"
access: public
## use action runId instead of current date to generate snapshot numbers
deterministic-snapshot: true
Expand All @@ -46,7 +48,7 @@ jobs:

## publish every package to s3
s3-bucket: ${{ secrets.SDK_TEAM_S3_BUCKET }}
s3-bucket-key-prefix: '@dcl/protocol/branch/${{ steps.myref.outputs.branch }}'
s3-bucket-key-prefix: "@dcl/protocol/branch/${{ steps.myref.outputs.branch }}"
s3-bucket-region: ${{ secrets.SDK_TEAM_AWS_REGION }}
## inform gitlab after publishing to proceed with CDN propagation
gitlab-token: ${{ secrets.GITLAB_TOKEN }}
Expand All @@ -70,7 +72,7 @@ jobs:
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
comment-author: "github-actions[bot]"
body-includes: Test this pull request

- name: Get the current branch name
Expand All @@ -90,9 +92,13 @@ jobs:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body: |
# Test this pull request
- The `@dcl/protocol` package can be tested in scenes by running
# Test this pull request on NPM or Yarn
- The `@dcl/protocol` package can be tested in scenes by running the following NPM command:
```bash
npm install "${{ steps.url-generator.outputs.body }}"
```
- The `@dcl/protocol` package can be tested in scenes by running the following YARN command:
```bash
yarn add "${{ steps.url-generator.outputs.body }}"
```
edit-mode: replace
15 changes: 12 additions & 3 deletions .github/workflows/validate.yml

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we want this?

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
on:
push:
branches-ignore:
pull_request:
types: [opened, synchronize, reopened]
branches:
- "main"
- "experimental"

name: validate-compatibility
jobs:
Expand All @@ -13,10 +15,17 @@ jobs:
- name: install
run: make install
- name: buf breaking
run: make buf-breaking
env:
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
run: |
echo "base_ref=${{ github.event.pull_request.base.ref }}"
echo "BASE_BRANCH=$BASE_BRANCH"
make buf-breaking
- name: buf lint
run: make buf-lint
- name: build and compile test
run: make test
- name: run the check script
env:
BASE_BRANCH: ${{ github.event.pull_request.base.ref }}
run: ./scripts/check-proto-compabitility.sh
164 changes: 164 additions & 0 deletions CLAUDE.md
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)
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ buf-build: node_modules/.bin/buf
./node_modules/.bin/buf build proto/

buf-breaking: node_modules/.bin/buf
./node_modules/.bin/buf breaking proto/ --against 'https://github.com/decentraland/protocol.git#subdir=proto'
./node_modules/.bin/buf breaking proto/ --against 'https://github.com/decentraland/protocol.git#branch=$(or $(BASE_BRANCH),main),subdir=proto'

test: buf-lint
bash scripts/test.sh
Expand Down
Loading
Loading