UNPKG

@dcl/ecs

Version:
164 lines (163 loc) 7.22 kB
import { ISchema } from '../schemas'; import { ByteBuffer } from '../serialization/ByteBuffer'; import { CrdtMessageBody, DeleteComponentMessageBody, PutComponentMessageBody } from '../serialization/crdt'; import { Entity } from './entity'; import { DeepReadonly, DeepReadonlySet } from './readonly'; /** * Component types are used to pick the wire protocol and the conflict resolution algorithm * @public */ export declare const enum ComponentType { LastWriteWinElementSet = 0, GrowOnlyValueSet = 1 } /** * A conflict resolution message is the response to an outdated or invalid state * in the CRDT. * @public */ export type ConflictResolutionMessage = PutComponentMessageBody | DeleteComponentMessageBody; /** * @public */ export interface BaseComponent<T> { readonly componentId: number; readonly componentName: string; readonly componentType: ComponentType; readonly schema: ISchema<T>; /** * This function receives a CRDT update and returns a touple with a "conflict * resoluton" message, in case of the sender being updated or null in case of noop/accepted * change. The second element of the touple is the modified/changed/deleted value. * @public */ updateFromCrdt(body: CrdtMessageBody): [null | ConflictResolutionMessage, T | undefined]; /** * This function returns an iterable with all the CRDT updates that need to be * broadcasted to other actors in the system. After returning, this function * clears the internal dirty state. Updates are produced only once. * @public */ getCrdtUpdates(): Iterable<CrdtMessageBody>; /** * This function writes the whole state of the component into a ByteBuffer * @public */ dumpCrdtStateToBuffer(buffer: ByteBuffer, filterEntity?: (entity: Entity) => boolean): void; /** * @public * Marks the entity as deleted and signals it cannot be used ever again. It must * clear the component internal state, produces a synchronization message to remove * the component from the entity. * @param entity - Entity ID that was deleted. */ entityDeleted(entity: Entity, markAsDirty: boolean): void; /** * Get if the entity has this component * @param entity - entity to test */ has(entity: Entity): boolean; /** * Get the readonly component of the entity (to mutate it, use getMutable instead), * throws an error if the entity doesn't have the component. * @param entity - Entity that will be used to get the component * @returns */ get(entity: Entity): any; /** * @public * Triggers the callback if the entity has changed on the last tick. * If the value is undefined, the component was deleted. */ onChange(entity: Entity, cb: (value: T | undefined) => void): void; } /** * @public */ export interface LastWriteWinElementSetComponentDefinition<T> extends BaseComponent<T> { readonly componentType: ComponentType.LastWriteWinElementSet; /** * Get the readonly component of the entity (to mutate it, use getMutable instead), * throws an error if the entity doesn't have the component. * @param entity - Entity that will be used to get the component * @returns */ get(entity: Entity): DeepReadonly<T>; /** * Get the readonly component of the entity (to mutate it, use getMutable instead), or null if the entity doesn't have the component. * @param entity - Entity that will be used to try to get the component */ getOrNull(entity: Entity): DeepReadonly<T> | null; /** * Add the current component to an entity, throw an error if the component already exists (use `createOrReplace` instead). * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity that will be used to create the component * @param val - The initial value */ create(entity: Entity, val?: T): T; /** * Add the current component to an entity or replace the content if the entity already has the component * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity that will be used to create or replace the component * @param val - The initial or new value */ createOrReplace(entity: Entity, val?: T): T; /** * @public * Delete the current component to an entity, return null if the entity doesn't have the current component. * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity to delete the component from */ deleteFrom(entity: Entity): T | null; /** * Get the mutable component of the entity, throw an error if the entity doesn't have the component. * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity to get the component from */ getMutable(entity: Entity): T; /** * Get the mutable component of the entity, return null if the entity doesn't have the component. * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity to get the component from */ getMutableOrNull(entity: Entity): T | null; /** * Get the mutable component of the entity. If the entity doesn't have the component, it's created. * - Internal comment: This method adds the &lt;entity,component&gt; to the list to be reviewed next frame * @param entity - Entity to get the component from * @param val - The initial value if it doesn't exist */ getOrCreateMutable(entity: Entity, initialValue?: T): T; } /** * @public */ export type ReadOnlyLastWriteWinElementSetComponentDefinition<T> = Omit<LastWriteWinElementSetComponentDefinition<T>, 'create' | 'createOrReplace' | 'deleteFrom' | 'getMutable' | 'getMutableOrNull' | 'getOrCreateMutable'>; /** * @public */ export interface GrowOnlyValueSetComponentDefinition<T> extends BaseComponent<T> { readonly componentType: ComponentType.GrowOnlyValueSet; /** * Appends an element to the set. * @param entity - Entity that will host the value * @param val - The final value. The Set will freeze the value, it won't be editable from * the script. */ addValue(entity: Entity, val: DeepReadonly<T>): DeepReadonlySet<T>; /** * Get the readonly component of the entity (to mutate it, use getMutable instead), * throws an error if the entity doesn't have the component. * @param entity - Entity that will be used to get the component * @returns */ get(entity: Entity): DeepReadonlySet<T>; } /** * @public */ export type ReadOnlyGrowOnlyValueSetComponentDefinition<T> = Omit<GrowOnlyValueSetComponentDefinition<T>, 'addValue'>; /** * @public */ export type ComponentDefinition<T> = LastWriteWinElementSetComponentDefinition<T> | GrowOnlyValueSetComponentDefinition<T> | ReadOnlyGrowOnlyValueSetComponentDefinition<T> | ReadOnlyLastWriteWinElementSetComponentDefinition<T>;