sync/state.js

import { HandlerType, SyncFlags } from "../../common/sync/constants.js";
import Receiver from "./receiver.js";
import MapReceiver from "./map-receiver.js";
import CollectionReceiver from "./collection-receiver.js";
import { decode } from "../../common/sync/protocol.js";

class State {
    /**
     * client state
     * @example
     * let state = new State();
     */
    constructor() {
        this.receivers = new Map();
        this.queued_changes = new Map();

        //init root receiver
        let root_receiver = new Receiver(0, this, this);
        this[SyncFlags.INSTANCE] = root_receiver;
        this.receivers.set(0, root_receiver);
    }

    addReceiver(id, type) {
        let target;
        let receiver;

        switch (type) {
            case HandlerType.MAP:
                target = new Map();
                receiver = new MapReceiver(id, target, this);
                break;
            case HandlerType.COLLECTION:
                target = [];
                receiver = new CollectionReceiver(id, target, this);
                break;
            default:
                target = {};
                receiver = new Receiver(id, target, this);
                break;
        }

        target[SyncFlags.INSTANCE] = receiver;
        this.receivers.set(id, receiver);

        return target;
    }

    deleteReceiver(id) {
        let r = this.receivers.get(id);

        r.children.forEach(child => {
            this.deleteReceiver(child[SyncFlags.INSTANCE].id);
        });

        this.receivers.delete(id);
    }

    update(changes) {
        if (!changes.length) return;

        if (!isNaN(changes[0])) {
            changes = decode(changes);
        }

        let updated_receivers = new Set();

        changes.forEach(c => {
            if (!this.receivers.has(c.ref)) {
                //we received a change from a ref we do not know yet, store it for later
                let queued = this.queued_changes.get(c.ref);
                if (!queued) {
                    queued = [];
                    this.queued_changes.set(c.ref, queued);
                }

                queued.push(c);

                if (queued.length > 500) {
                    console.warn("Queued over 500 changes for unknown ref id " + c.ref);
                }

                return;
            }

            updated_receivers.add(c.ref);
            this.receivers.get(c.ref).receive(c);
        });

        updated_receivers.forEach(ref => {
            if (!this.receivers.has(ref)) return;
            this.receivers.get(ref).flushStoredEvents();
        })
    }
}

export default State