Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
8a0180b
wip
gonpombo8 Jul 23, 2025
fb177a1
feat: auth server arch
gonpombo8 Jul 24, 2025
e76d738
update snapshots
gonpombo8 Jul 24, 2025
f94ac23
fix test
gonpombo8 Jul 24, 2025
c82cc82
go back to req_crt_statate cause unity takes more time to connect to …
gonpombo8 Jul 26, 2025
fde348b
add server side pre-validations when recieving crdt messages
gonpombo8 Aug 1, 2025
97ca00e
fix tests
gonpombo8 Aug 1, 2025
3b0f1f3
add tests
gonpombo8 Aug 2, 2025
d391769
add authorative crdt messages
gonpombo8 Aug 7, 2025
26b0c1e
add custom events with proper schema definitions
gonpombo8 Aug 22, 2025
a664a96
replace eventBuswith room
gonpombo8 Sep 2, 2025
a60ba86
fix tests
gonpombo8 Sep 2, 2025
963f1af
remove inspector stashed
gonpombo8 Sep 2, 2025
dc80129
fix linter
gonpombo8 Sep 2, 2025
76c7c26
fix misspelling authoritative
gonpombo8 Sep 2, 2025
ec8f675
remove eventBus references
gonpombo8 Sep 10, 2025
7bd2f86
create new messages types to avoid filter from unity
gonpombo8 Oct 19, 2025
aef3609
fix invalid messages
gonpombo8 Jan 8, 2026
031b285
export auth put component
gonpombo8 Jan 9, 2026
d44f44e
fix auth comp
gonpombo8 Jan 9, 2026
de8de5d
fix tests
gonpombo8 Jan 9, 2026
6678139
fix request state if not received
gonpombo8 Jan 9, 2026
9e156b4
feat: Add Server Side Storage utils (#1276)
cyaiox Feb 5, 2026
6b2952c
fix: Use multiplayerId instead of world
cyaiox Feb 6, 2026
1a1dce4
fix: Show properly dev multiplayer-server logs
cyaiox Feb 10, 2026
b598024
refactor: Start multiplayer-server using npx
cyaiox Feb 10, 2026
e21df84
fix: Only react when actually transitioning from ready to not ready
cyaiox Feb 10, 2026
3ef4e9e
replace defualt server
gonpombo8 Feb 11, 2026
85015a2
add auth-server tag
gonpombo8 Feb 13, 2026
7c5f263
feat: Enhance Storage SDK API (#1311)
cyaiox Feb 13, 2026
f333edc
add authoritativeMultiplayer by defualt
gonpombo8 Feb 17, 2026
23795c5
fix tests
gonpombo8 Feb 17, 2026
ffa88e5
fix logs in auth-server (#1320)
gonpombo8 Feb 17, 2026
aef26d6
add server-logs command to package.json
gonpombo8 Feb 19, 2026
6d4f676
feat: Add analytics to the storage command (#1325)
cyaiox Mar 2, 2026
360aecf
Simplify room-ready checks and remove unused createRoom (#1341)
gonpombo8 Mar 3, 2026
27bdacf
Fix empty CRDT state response causing infinite retries (#1342)
gonpombo8 Mar 3, 2026
2dc1e88
remove log
gonpombo8 Mar 10, 2026
e45c3c9
Regenerate CRDT snapshots after merge
gonpombo8 Mar 10, 2026
cad0d72
feat: support --world and --position for server logs
nicoecheza Apr 20, 2026
40084b0
update snapshots
nicoecheza May 4, 2026
2e5e613
sync: merge main into auth-server (#1392)
decentraland-bot May 7, 2026
543624c
update snapshots
nicoecheza May 19, 2026
24aee1d
fix: snapshots
nicoecheza Jun 18, 2026
be9f20d
fix(ecs): don't seed lastSentData from incoming CRDT updates
nicoecheza Jun 18, 2026
1a6c780
update snapshots
nicoecheza Jun 25, 2026
f68af6e
merged main + rebuilt snapshots
pravusjif Jul 1, 2026
697ce9e
Merge pull request #1435 from decentraland/chore/sync-main-to-authserver
pravusjif Jul 2, 2026
9aee22d
Merge branch 'main' of https://github.com/decentraland/js-sdk-toolcha…
pravusjif Jul 15, 2026
d270434
Merge pull request #1488 from decentraland/chore/sync-main-to-authserver
pravusjif Jul 16, 2026
5ffe873
feat: storage cache, unchanged-write skipping, memoized storage URL (…
pravusjif Jul 27, 2026
37a7755
Merge branch 'main' of https://github.com/decentraland/js-sdk-toolcha…
pravusjif Aug 12, 2026
adf872c
fixed merge problems
pravusjif Aug 12, 2026
26ad1a9
Merge branch 'main' of https://github.com/decentraland/js-sdk-toolcha…
pravusjif Aug 13, 2026
96e9a29
Merge pull request #1538 from decentraland/chore/sync-main-to-authserver
pravusjif Aug 13, 2026
d83690c
Merge branch 'main' of https://github.com/decentraland/js-sdk-toolcha…
pravusjif Aug 18, 2026
0b97733
Merge pull request #1548 from decentraland/chore/sync-main-to-authserver
pravusjif Aug 18, 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
37 changes: 37 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"extends": "@dcl/eslint-config/sdk",
"parserOptions": {
"project": [
"packages/@dcl/sdk/types/tsconfig.ecs7.json",
"packages/@dcl/ecs/tsconfig.json",
"packages/@dcl/sdk-commands/tsconfig.json",
"packages/@dcl/sdk/tsconfig.json",
"packages/@dcl/playground-assets/tsconfig.json",
"packages/@dcl/react-ecs/tsconfig.json",
"scripts/tsconfig.json",
"test/tsconfig.json",
"test/ecs/snippets/tsconfig.json"
]
},
"rules": {
"no-param-reassign": "warn",
"no-console": "error",
"@typescript-eslint/no-unused-vars": [
"warn",
{
"ignoreRestSiblings": true,
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_|ReactEcs",
"caughtErrorsIgnorePattern": "^_"
}
]
},
"overrides": [
{
"files": ["packages/@dcl/ecs/**/*.ts", "packages/@dcl/sdk/**/*.ts", "scripts/**/*.ts", "test/**/*.ts"],
"rules": {
"no-console": "off"
}
}
]
}
4 changes: 3 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ on:
branches:
- main
- experimental
- auth-server
pull_request:
release:
types:
- created

env:
BRANCH_TAG: ${{ github.ref_name == 'experimental' && 'experimental' || '' }}
BRANCH_TAG: ${{ github.ref_name == 'experimental' && 'experimental' || github.ref_name == 'auth-server' && 'auth-server' || '' }}

name: CI
jobs:
Expand Down Expand Up @@ -136,6 +137,7 @@ jobs:
AWS_ACCESS_KEY_ID: ${{ secrets.SDK_TEAM_AWS_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.SDK_TEAM_AWS_SECRET }}


- name: publish @dcl/ecs package
uses: decentraland/oddish-action@master
id: publish_ecs
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ deep-clean:
packages/@dcl/ecs/node_modules/ \
packages/@dcl/react-ecs/node_modules/ \
packages/@dcl/sdk/node_modules/ \
packages/@dcl/sdk-commands/node_modules \
packages/@dcl/sdk-commands/node_modules
make clean

update-snapshots: export UPDATE_SNAPSHOTS=true
Expand Down
10 changes: 8 additions & 2 deletions packages/@dcl/ecs/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
TouchScreenControlsComponentDefinitionExtended
} from './extended/TouchScreenControls'
import defineTagsComponent, { TagsComponentDefinitionExtended } from './manual/Tags'
import defineCreatedBy, { ICreatedByType } from './manual/CreatedBy'

export * from './generated/index.gen'

Expand Down Expand Up @@ -118,19 +119,24 @@ export const SyncComponents: (
/**
* @alpha
*/
/* @__PURE__ */
export const NetworkEntity: (
engine: Pick<IEngine, 'defineComponent'>
) => LastWriteWinElementSetComponentDefinition<INetowrkEntityType> = (engine) => defineNetworkEntity(engine)

/**
* @alpha
*/
/* @__PURE__ */
export const NetworkParent: (
engine: Pick<IEngine, 'defineComponent'>
) => LastWriteWinElementSetComponentDefinition<INetowrkParentType> = (engine) => defineNetworkParent(engine)

/**
* @public
*/
export const CreatedBy: (
engine: Pick<IEngine, 'defineComponent'>
) => LastWriteWinElementSetComponentDefinition<ICreatedByType> = (engine) => defineCreatedBy(engine)

export { MediaState }

export type { AudioAnalysisView }
17 changes: 17 additions & 0 deletions packages/@dcl/ecs/src/components/manual/CreatedBy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IEngine, LastWriteWinElementSetComponentDefinition } from '../../engine/types'
import { Schemas } from '../../schemas'

export interface ICreatedByType {
address: string
}

export type ICreatedBy = LastWriteWinElementSetComponentDefinition<ICreatedByType>

function defineCreatedBy(engine: Pick<IEngine, 'defineComponent'>) {
const CreatedBy = engine.defineComponent('core-schema::Created-By', {
address: Schemas.String
})
return CreatedBy
}

export default defineCreatedBy
6 changes: 3 additions & 3 deletions packages/@dcl/ecs/src/components/manual/Transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export interface TransformComponentExtended extends TransformComponent {
}

/**
* @internal
* @public
*/
export const COMPONENT_ID = 1

Expand All @@ -49,10 +49,10 @@ export type TransformType = {
parent?: Entity
}

/** @internal */
/** @public */
export const TRANSFORM_LENGTH = 44

/** @internal */
/** @public */
export const TransformSchema: ISchema<TransformType> = {
serialize(value: TransformType, builder: ByteBuffer): void {
const ptr = builder.incrementWriteOffset(TRANSFORM_LENGTH)
Expand Down
1 change: 1 addition & 0 deletions packages/@dcl/ecs/src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type { TagsComponentDefinitionExtended, TagsType } from './manual/Tags'
export type { ISyncComponents, ISyncComponentsType } from './manual/SyncComponents'
export type { INetowrkEntity, INetowrkEntityType } from './manual/NetworkEntity'
export type { INetowrkParent, INetowrkParentType } from './manual/NetworkParent'
export type { ICreatedBy, ICreatedByType } from './manual/CreatedBy'
export type { InputModifierHelper, InputModifierComponentDefinitionExtended } from './extended/InputModifier'
export type { LightSourceHelper, LightSourceComponentDefinitionExtended } from './extended/LightSource'
export type { TriggerAreaComponentDefinitionExtended } from './extended/TriggerArea'
Expand Down
102 changes: 101 additions & 1 deletion packages/@dcl/ecs/src/engine/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { ISchema } from '../schemas'
import { ByteBuffer } from '../serialization/ByteBuffer'
import { CrdtMessageBody, DeleteComponentMessageBody, PutComponentMessageBody } from '../serialization/crdt'
import {
CrdtMessageBody,
DeleteComponentMessageBody,
ProcessMessageResultType,
PutComponentMessageBody
} from '../serialization/crdt'
import { Entity } from './entity'
import { DeepReadonly, DeepReadonlySet } from './readonly'

Expand Down Expand Up @@ -38,6 +43,14 @@ export interface BaseComponent<T> {
* @public
*/
updateFromCrdt(body: CrdtMessageBody): [null | ConflictResolutionMessage, T | undefined]
/**
* @internal
*/
__forceUpdateFromCrdt(body: CrdtMessageBody): [null, T | undefined]
/**
* @internal
*/
__dry_run_updateFromCrdt(body: CrdtMessageBody): ProcessMessageResultType

/**
* This function returns an iterable with all the CRDT updates that need to be
Expand Down Expand Up @@ -104,8 +117,87 @@ export interface BaseComponent<T> {
* If the value is undefined, the component was deleted.
*/
onChange(entity: Entity, cb: (value: T | undefined) => void): void

/**
* @public
*
*/
validateBeforeChange(entity: Entity, cb: ValidateCallback<T>): void
validateBeforeChange(cb: ValidateCallback<T>): void
/**
* @internal
*/
__run_validateBeforeChange(entity: Entity, newValue: T | undefined, senderAddress: string, createdBy: string): boolean

/**
* Get the CRDT state for an entity (serialized data and timestamp)
* @param entity - Entity to get the CRDT state for
* @returns Object with serialized data and timestamp, or null if entity doesn't have the component
* @public
*/
getCrdtState(entity: Entity): { data: Uint8Array; timestamp: number } | null
}

/**
* Internal component interface that exposes all internal methods for SDK use
* This is not exposed to users, only for internal SDK operations
*/
export interface InternalBaseComponent<T> extends BaseComponent<T> {
// This interface inherits all methods from BaseComponent but makes the internal methods
// more accessible for SDK internal use. The main difference is that this interface
// is marked as @internal and can be used within the SDK package without exposing
// internal methods to end users.

// All the internal methods are already available through inheritance:
// - __forceUpdateFromCrdt
// - __dry_run_updateFromCrdt
// - __onChangeCallbacks
// - __run_validateBeforeChange
// - dirtyIterator
// - iterator

/**
* @public
* Force update component state regardless of timestamp - used for server authoritative messages
*/
__forceUpdateFromCrdt(body: CrdtMessageBody): [null, T | undefined]

/**
* @public
* Dry run update to check if a CRDT message would be accepted without actually applying it
*/
__dry_run_updateFromCrdt(body: CrdtMessageBody): ProcessMessageResultType

/**
* @public
* Get the iterator to every entity has the component
*/
iterator(): Iterable<[Entity, any]>

/**
* @public
*/
dirtyIterator(): Iterable<Entity>

/**
* @public
*/
__onChangeCallbacks(entity: Entity, value: T): void

/**
* @public
*/
__run_validateBeforeChange(entity: Entity, newValue: T | undefined, senderAddress: string, createdBy: string): boolean
}

export type ValidateCallback<T> = (value: {
entity: Entity // The entity being validated
currentValue: T | undefined // Current value of the component (undefined if component doesn't exist)
newValue: T | undefined // New value being set (undefined if component is being deleted)
senderAddress: string // Peer address/sender of the change
createdBy: string // Address of the user who created the change
}) => boolean

/**
* @public
*/
Expand Down Expand Up @@ -211,6 +303,14 @@ export interface GrowOnlyValueSetComponentDefinition<T> extends BaseComponent<T>
* @returns
*/
get(entity: Entity): DeepReadonlySet<T>

/**
* Get the CRDT state for an entity (serialized data and timestamp)
* @param entity - Entity to get the CRDT state for
* @returns Object with serialized data and timestamp, or null if entity doesn't have the component
* @internal
*/
getCrdtState(entity: Entity): { data: Uint8Array; timestamp: number } | null
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { ISchema } from '../schemas'
import { ReadWriteByteBuffer } from '../serialization/ByteBuffer'
import { AppendValueMessageBody, AppendValueOperation, CrdtMessageType } from '../serialization/crdt'
import { ComponentType, GrowOnlyValueSetComponentDefinition } from './component'
import {
AppendValueMessageBody,
AppendValueOperation,
CrdtMessageType,
ProcessMessageResultType
} from '../serialization/crdt'
import { ComponentType, GrowOnlyValueSetComponentDefinition, ValidateCallback } from './component'
import { __DEV__ } from '../runtime/invariant'
import { Entity } from './entity'
import { DeepReadonly, DeepReadonlySet } from './readonly'

const emptyReadonlySet = freezeSet(new Set())
const __GLOBAL_ENTITY = '__GLOBAL_ENTITY' as any as Entity

function frozenError() {
throw new Error('The set is frozen')
Expand Down Expand Up @@ -50,6 +56,7 @@ export function createValueSetComponentDefinitionFromSchema<T>(
const dirtyIterator = new Set<Entity>()
const queuedCommands: AppendValueMessageBody[] = []
const onChangeCallbacks = new Map<Entity, ((data: T | undefined) => void)[]>()
const validateCallbacks = new Map<Entity, ValidateCallback<T>>()

// only sort the array if the latest (N) element has a timestamp <= N-1
function shouldSort(row: InternalDatastructure) {
Expand Down Expand Up @@ -111,8 +118,11 @@ export function createValueSetComponentDefinitionFromSchema<T>(
has(entity: Entity): boolean {
return data.has(entity)
},
entityDeleted(entity: Entity): void {
entityDeleted(entity: Entity, markAsDirty: boolean): void {
data.delete(entity)
if (markAsDirty) {
// For grow-only sets, we don't need to mark as dirty since deletion doesn't generate CRDT messages
}
},
get(entity: Entity): DeepReadonlySet<T> {
const values = data.get(entity)
Expand Down Expand Up @@ -180,6 +190,47 @@ export function createValueSetComponentDefinitionFromSchema<T>(
for (const cb of cbs) {
cb(value)
}
},
__dry_run_updateFromCrdt(_body) {
return ProcessMessageResultType.StateUpdatedData
},
validateBeforeChange(entityOrCb: Entity | ValidateCallback<T>, cb?: ValidateCallback<T>): void {
if (arguments.length === 1) {
// Second overload: just callback (global validation)
validateCallbacks.set(__GLOBAL_ENTITY, entityOrCb as ValidateCallback<T>)
} else {
if (cb) {
validateCallbacks.set(entityOrCb as Entity, cb)
}
}
},
__run_validateBeforeChange(entity, newValue, senderAddress, createdBy): boolean {
const cb = entity && validateCallbacks.get(entity)
const globalCb = validateCallbacks.get(__GLOBAL_ENTITY)
const currentValue = [...this.get(entity).values()]

const value = { entity, currentValue: currentValue as T, newValue, senderAddress, createdBy }

const globalResult = globalCb?.(value) ?? true
const entityResult = (globalResult && cb?.(value)) ?? true

return globalResult && entityResult
},
getCrdtState(entity: Entity): { data: Uint8Array; timestamp: number } | null {
const row = data.get(entity)
if (!row || row.raw.length === 0) {
return null
}

// For GrowOnlySet, we need to return the complete CRDT messages for all values
// This is complex because GrowOnlySet uses APPEND messages, not a single PUT
// For now, return null to indicate this component type doesn't support simple corrections
return null
},
__forceUpdateFromCrdt(_msg) {
// GrowOnlySet doesn't support authoritative corrections in the same way as LWW
// since it uses APPEND_VALUE messages instead of PUT_COMPONENT messages
return [null, undefined]
}
}

Expand Down
Loading
Loading