diff --git a/examples/canvas/src/index.ts b/examples/canvas/src/index.ts index 28b6a2a6..31346b82 100644 --- a/examples/canvas/src/index.ts +++ b/examples/canvas/src/index.ts @@ -1,12 +1,9 @@ -import { GCounter } from "@topology-foundation/crdt"; import { TopologyNode } from "@topology-foundation/node"; import type { TopologyObject } from "@topology-foundation/object"; -import { handleObjectOps } from "./handlers"; import { Canvas } from "./objects/canvas"; -import { Pixel } from "./objects/pixel"; const node = new TopologyNode(); -let topologyObject: TopologyObject; +let topologyObject: TopologyObject; let canvasCRO: Canvas; let peers: string[] = []; let discoveryPeers: string[] = []; @@ -25,34 +22,23 @@ const render = () => { document.getElementById("object_peers") ); object_element.innerHTML = `[${objectPeers.join(", ")}]`; + (document.getElementById("canvasId")).innerText = + topologyObject?.id; if (!canvasCRO) return; const canvas = canvasCRO.canvas; - const canvas_element = document.getElementById("canvas"); - canvas_element.innerHTML = ""; - canvas_element.style.display = "inline-grid"; - - canvas_element.style.gridTemplateColumns = Array(canvas.length) - .fill("1fr") - .join(" "); - for (let x = 0; x < canvas.length; x++) { for (let y = 0; y < canvas[x].length; y++) { - const pixel = document.createElement("div"); - pixel.id = `${x}-${y}`; - pixel.style.width = "25px"; - pixel.style.height = "25px"; + const pixel = document.getElementById(`${x}-${y}`); + if (!pixel) continue; pixel.style.backgroundColor = `rgb(${canvas[x][y].color()[0]}, ${canvas[x][y].color()[1]}, ${canvas[x][y].color()[2]})`; - pixel.style.cursor = "pointer"; - pixel.addEventListener("click", () => paint_pixel(pixel)); - canvas_element.appendChild(pixel); } } }; const random_int = (max: number) => Math.floor(Math.random() * max); -async function paint_pixel(pixel: HTMLDivElement) { +function paint_pixel(pixel: HTMLDivElement) { const [x, y] = pixel.id.split("-").map((v) => Number.parseInt(v, 10)); const painting: [number, number, number] = [ random_int(256), @@ -64,8 +50,39 @@ async function paint_pixel(pixel: HTMLDivElement) { pixel.style.backgroundColor = `rgb(${r}, ${g}, ${b})`; } +async function createConnectHandlers() { + node.addCustomGroupMessageHandler(topologyObject.id, (e) => { + if (topologyObject) + objectPeers = node.networkNode.getGroupPeers(topologyObject.id); + render(); + }); + + node.objectStore.subscribe(topologyObject.id, (_, _obj) => { + render(); + }); +} + async function init() { await node.start(); + render(); + + const canvas_element = document.getElementById("canvas"); + canvas_element.innerHTML = ""; + canvas_element.style.display = "inline-grid"; + + canvas_element.style.gridTemplateColumns = Array(5).fill("1fr").join(" "); + for (let x = 0; x < 5; x++) { + for (let y = 0; y < 10; y++) { + const pixel = document.createElement("div"); + pixel.id = `${x}-${y}`; + pixel.style.width = "25px"; + pixel.style.height = "25px"; + pixel.style.backgroundColor = "rgb(0, 0, 0)"; + pixel.style.cursor = "pointer"; + pixel.addEventListener("click", () => paint_pixel(pixel)); + canvas_element.appendChild(pixel); + } + } node.addCustomGroupMessageHandler("", (e) => { peers = node.networkNode.getAllPeers(); @@ -78,20 +95,7 @@ async function init() { topologyObject = await node.createObject(new Canvas(5, 10)); canvasCRO = topologyObject.cro as Canvas; - // message handler for the CRO - node.addCustomGroupMessageHandler(topologyObject.id, (e) => { - // on create/connect - if (topologyObject) - objectPeers = node.networkNode.getGroupPeers(topologyObject.id); - render(); - }); - - node.objectStore.subscribe(topologyObject.id, (_, obj) => { - handleObjectOps(canvasCRO, obj.vertices); - }); - - (document.getElementById("canvasId")).innerText = - topologyObject.id; + createConnectHandlers(); render(); }); @@ -100,24 +104,15 @@ async function init() { const croId = (document.getElementById("canvasIdInput")) .value; try { - topologyObject = await node.createObject(new Canvas(5, 10), croId); + topologyObject = await node.createObject( + new Canvas(5, 10), + croId, + undefined, + true, + ); canvasCRO = topologyObject.cro as Canvas; - // message handler for the CRO - node.addCustomGroupMessageHandler(topologyObject.id, (e) => { - // on create/connect - if (topologyObject) - objectPeers = node.networkNode.getGroupPeers(topologyObject.id); - (document.getElementById("canvasId")).innerText = - topologyObject.id; - render(); - }); - - node.objectStore.subscribe(topologyObject.id, (_, obj) => { - handleObjectOps(canvasCRO, obj.vertices); - render(); - }); - + createConnectHandlers(); render(); } catch (e) { console.error("Error while connecting with CRO", croId, e); diff --git a/examples/canvas/src/objects/canvas.ts b/examples/canvas/src/objects/canvas.ts index f6e1a87d..38de32f8 100644 --- a/examples/canvas/src/objects/canvas.ts +++ b/examples/canvas/src/objects/canvas.ts @@ -25,6 +25,23 @@ export class Canvas implements CRO { offset: [number, number], size: [number, number], rgb: [number, number, number], + ): void { + this._splash(nodeId, offset, size, rgb); + } + + paint( + nodeId: string, + offset: [number, number], + rgb: [number, number, number], + ): void { + this._paint(nodeId, offset, rgb); + } + + private _splash( + nodeId: string, + offset: [number, number], + size: [number, number], + rgb: [number, number, number], ): void { if (offset[0] < 0 || this.width < offset[0]) return; if (offset[1] < 0 || this.height < offset[1]) return; @@ -36,7 +53,7 @@ export class Canvas implements CRO { } } - paint( + private _paint( nodeId: string, offset: [number, number], rgb: [number, number, number], @@ -64,7 +81,18 @@ export class Canvas implements CRO { mergeCallback(operations: Operation[]): void { for (const op of operations) { if (!op.value) continue; - this.merge(op.value); + switch (op.type) { + case "splash": { + const [nodeId, offset, size, rgb] = op.value; + this._splash(nodeId, offset, size, rgb); + break; + } + case "paint": { + const [nodeId, offset, rgb] = op.value; + this._paint(nodeId, offset, rgb); + break; + } + } } } } diff --git a/packages/network/src/proto/messages.proto b/packages/network/src/proto/messages.proto index f26ec7df..a13aea46 100644 --- a/packages/network/src/proto/messages.proto +++ b/packages/network/src/proto/messages.proto @@ -6,7 +6,7 @@ import "google/protobuf/struct.proto"; message Vertex { message Operation { string type = 1; - repeated google.protobuf.Value value = 2; + google.protobuf.Value value = 2; } string hash = 1; string nodeId = 2; diff --git a/packages/network/src/proto/messages_pb.ts b/packages/network/src/proto/messages_pb.ts index bbbcff67..b82c69c3 100644 --- a/packages/network/src/proto/messages_pb.ts +++ b/packages/network/src/proto/messages_pb.ts @@ -20,7 +20,7 @@ export interface Vertex { export interface Vertex_Operation { type: string; - value: any[]; + value: any | undefined; } export interface Message { @@ -208,7 +208,7 @@ export const Vertex = { }; function createBaseVertex_Operation(): Vertex_Operation { - return { type: "", value: [] }; + return { type: "", value: undefined }; } export const Vertex_Operation = { @@ -216,8 +216,8 @@ export const Vertex_Operation = { if (message.type !== "") { writer.uint32(10).string(message.type); } - for (const v of message.value) { - Value.encode(Value.wrap(v!), writer.uint32(18).fork()).join(); + if (message.value !== undefined) { + Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).join(); } return writer; }, @@ -241,7 +241,7 @@ export const Vertex_Operation = { break; } - message.value.push(Value.unwrap(Value.decode(reader, reader.uint32()))); + message.value = Value.unwrap(Value.decode(reader, reader.uint32())); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -255,7 +255,7 @@ export const Vertex_Operation = { fromJSON(object: any): Vertex_Operation { return { type: isSet(object.type) ? globalThis.String(object.type) : "", - value: globalThis.Array.isArray(object?.value) ? [...object.value] : [], + value: isSet(object?.value) ? object.value : undefined, }; }, @@ -264,7 +264,7 @@ export const Vertex_Operation = { if (message.type !== "") { obj.type = message.type; } - if (message.value?.length) { + if (message.value !== undefined) { obj.value = message.value; } return obj; @@ -276,7 +276,7 @@ export const Vertex_Operation = { fromPartial, I>>(object: I): Vertex_Operation { const message = createBaseVertex_Operation(); message.type = object.type ?? ""; - message.value = object.value?.map((e) => e) || []; + message.value = object.value ?? undefined; return message; }, }; diff --git a/packages/object/src/index.ts b/packages/object/src/index.ts index 25f8d02b..5d5ff174 100644 --- a/packages/object/src/index.ts +++ b/packages/object/src/index.ts @@ -85,7 +85,7 @@ export class TopologyObject implements ITopologyObject { nodeId: vertex.nodeId, operation: { type: vertex.operation.type, - value: [vertex.operation.value], + value: vertex.operation.value, }, dependencies: vertex.dependencies, }); @@ -110,7 +110,7 @@ export class TopologyObject implements ITopologyObject { nodeId: vertex.nodeId, operation: { type: vertex.operation.type, - value: [vertex.operation.value], + value: vertex.operation.value, }, dependencies: vertex.dependencies, }; diff --git a/packages/object/src/proto/object.proto b/packages/object/src/proto/object.proto index d196dbf3..0e37776f 100644 --- a/packages/object/src/proto/object.proto +++ b/packages/object/src/proto/object.proto @@ -6,7 +6,7 @@ import "google/protobuf/struct.proto"; message Vertex { message Operation { string type = 1; - repeated google.protobuf.Value value = 2; + google.protobuf.Value value = 2; } string hash = 1; string nodeId = 2; diff --git a/packages/object/src/proto/object_pb.ts b/packages/object/src/proto/object_pb.ts index e234e793..abda1053 100644 --- a/packages/object/src/proto/object_pb.ts +++ b/packages/object/src/proto/object_pb.ts @@ -20,7 +20,7 @@ export interface Vertex { export interface Vertex_Operation { type: string; - value: any[]; + value: any | undefined; } export interface TopologyObjectBase { @@ -139,7 +139,7 @@ export const Vertex = { }; function createBaseVertex_Operation(): Vertex_Operation { - return { type: "", value: [] }; + return { type: "", value: undefined }; } export const Vertex_Operation = { @@ -147,8 +147,8 @@ export const Vertex_Operation = { if (message.type !== "") { writer.uint32(10).string(message.type); } - for (const v of message.value) { - Value.encode(Value.wrap(v!), writer.uint32(18).fork()).join(); + if (message.value !== undefined) { + Value.encode(Value.wrap(message.value), writer.uint32(18).fork()).join(); } return writer; }, @@ -172,7 +172,7 @@ export const Vertex_Operation = { break; } - message.value.push(Value.unwrap(Value.decode(reader, reader.uint32()))); + message.value = Value.unwrap(Value.decode(reader, reader.uint32())); continue; } if ((tag & 7) === 4 || tag === 0) { @@ -186,7 +186,7 @@ export const Vertex_Operation = { fromJSON(object: any): Vertex_Operation { return { type: isSet(object.type) ? globalThis.String(object.type) : "", - value: globalThis.Array.isArray(object?.value) ? [...object.value] : [], + value: isSet(object?.value) ? object.value : undefined, }; }, @@ -195,7 +195,7 @@ export const Vertex_Operation = { if (message.type !== "") { obj.type = message.type; } - if (message.value?.length) { + if (message.value !== undefined) { obj.value = message.value; } return obj; @@ -207,7 +207,7 @@ export const Vertex_Operation = { fromPartial, I>>(object: I): Vertex_Operation { const message = createBaseVertex_Operation(); message.type = object.type ?? ""; - message.value = object.value?.map((e) => e) || []; + message.value = object.value ?? undefined; return message; }, };