Skip to content

Commit

Permalink
merge feat/protobuf
Browse files Browse the repository at this point in the history
  • Loading branch information
d-roak committed Aug 8, 2024
2 parents 65bd867 + 185b790 commit 0452fb9
Show file tree
Hide file tree
Showing 19 changed files with 859 additions and 326 deletions.
10 changes: 10 additions & 0 deletions buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: v2
plugins:
- local: ./node_modules/ts-proto/protoc-gen-ts_proto
strategy: directory
out: ./packages
opt:
- esModuleInterop=true
- fileSuffix=_pb
inputs:
- directory: ./packages
5 changes: 2 additions & 3 deletions examples/canvas/src/objects/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TopologyObject } from "@topology-foundation/object";
import { IPixel, Pixel } from "./pixel";

export interface ICanvas extends TopologyObject {
export interface ICanvas {
width: number;
height: number;
canvas: IPixel[][];
Expand All @@ -20,13 +20,12 @@ export interface ICanvas extends TopologyObject {
merge(peerCanvas: Canvas): void;
}

export class Canvas extends TopologyObject implements ICanvas {
export class Canvas implements TopologyObject, ICanvas {
width: number;
height: number;
canvas: IPixel[][];

constructor(peerId: string, width: number, height: number) {
super(peerId);
this.width = width;
this.height = height;
this.canvas = Array.from(new Array(width), () =>
Expand Down
8 changes: 3 additions & 5 deletions examples/canvas/src/objects/pixel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { GCounter } from "@topology-foundation/crdt";
import { TopologyObject } from "@topology-foundation/object";

export interface IPixel extends TopologyObject {
export interface IPixel {
red: GCounter;
green: GCounter;
blue: GCounter;
Expand All @@ -11,13 +10,12 @@ export interface IPixel extends TopologyObject {
merge(peerPixel: IPixel): void;
}

export class Pixel extends TopologyObject implements IPixel {
export class Pixel implements IPixel {
red: GCounter;
green: GCounter;
blue: GCounter;

constructor(peerId: string) {
super(peerId);
constructor() {
this.red = new GCounter({});
this.green = new GCounter({});
this.blue = new GCounter({});
Expand Down
211 changes: 106 additions & 105 deletions examples/chat/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TopologyNode } from "@topology-foundation/node";
import * as topology from "@topology-foundation/node";
import { Chat, IChat } from "./objects/chat";
import { handleChatMessages } from "./handlers";
import { GSet } from "@topology-foundation/crdt";
Expand All @@ -11,119 +12,119 @@ let discoveryPeers: string[] = [];
let objectPeers: string[] = [];

const render = () => {
const element_peerId = <HTMLDivElement>document.getElementById("peerId");
element_peerId.innerHTML = node.networkNode.peerId;

const element_peers = <HTMLDivElement>document.getElementById("peers");
element_peers.innerHTML = "[" + peers.join(", ") + "]";

const element_discoveryPeers = <HTMLDivElement>document.getElementById("discoveryPeers");
element_discoveryPeers.innerHTML = "[" + discoveryPeers.join(", ") + "]";

const element_objectPeers = <HTMLDivElement>document.getElementById("objectPeers");
element_objectPeers.innerHTML = "[" + objectPeers.join(", ") + "]";

if(!chatCRO) return;
const chat = chatCRO.getMessages();
const element_chat = <HTMLDivElement>document.getElementById("chat");
element_chat.innerHTML = "";

if(chat.set().size == 0){
const div = document.createElement("div");
div.innerHTML = "No messages yet";
div.style.padding = "10px";
element_chat.appendChild(div);
return;
}
Array.from(chat.set()).sort().forEach((message: string) => {
const div = document.createElement("div");
div.innerHTML = message;
div.style.padding = "10px";
element_chat.appendChild(div);
});
const element_peerId = <HTMLDivElement>document.getElementById("peerId");
element_peerId.innerHTML = node.networkNode.peerId;

const element_peers = <HTMLDivElement>document.getElementById("peers");
element_peers.innerHTML = "[" + peers.join(", ") + "]";

const element_discoveryPeers = <HTMLDivElement>document.getElementById("discoveryPeers");
element_discoveryPeers.innerHTML = "[" + discoveryPeers.join(", ") + "]";

const element_objectPeers = <HTMLDivElement>document.getElementById("objectPeers");
element_objectPeers.innerHTML = "[" + objectPeers.join(", ") + "]";

if (!chatCRO) return;
const chat = chatCRO.getMessages();
const element_chat = <HTMLDivElement>document.getElementById("chat");
element_chat.innerHTML = "";

if (chat.set().size == 0) {
const div = document.createElement("div");
div.innerHTML = "No messages yet";
div.style.padding = "10px";
element_chat.appendChild(div);
return;
}
Array.from(chat.set()).sort().forEach((message: string) => {
const div = document.createElement("div");
div.innerHTML = message;
div.style.padding = "10px";
element_chat.appendChild(div);
});

}

async function sendMessage(message: string) {
let timestamp: string = Date.now().toString();
if(!chatCRO) {
console.error("Chat CRO not initialized");
alert("Please create or join a chat room first");
return;
}
console.log("Sending message: ", `(${timestamp}, ${message}, ${node.networkNode.peerId})`);
chatCRO.addMessage(timestamp, message, node.networkNode.peerId);

node.updateObject(chatCRO, `addMessage(${timestamp}, ${message}, ${node.networkNode.peerId})`);
render();
let timestamp: string = Date.now().toString();
if (!chatCRO) {
console.error("Chat CRO not initialized");
alert("Please create or join a chat room first");
return;
}
console.log("Sending message: ", `(${timestamp}, ${message}, ${node.networkNode.peerId})`);
chatCRO.addMessage(timestamp, message, node.networkNode.peerId);

// topology.updateObject(node, chatCRO, `addMessage(${timestamp}, ${message}, ${node.networkNode.peerId})`);
render();
}

async function main() {
await node.start();
await node.start();
render();

node.addCustomGroupMessageHandler(chatCRO.cro.id, (e) => {
handleChatMessages(chatCRO, e);
peers = node.networkNode.getAllPeers();
discoveryPeers = node.networkNode.getGroupPeers("topology::discovery");
if (chatCRO) objectPeers = node.networkNode.getGroupPeers(chatCRO.cro.id);
render();
});

node.addCustomGroupMessageHandler((e) => {
handleChatMessages(chatCRO, e);
peers = node.networkNode.getAllPeers();
discoveryPeers = node.networkNode.getGroupPeers("topology::discovery");
if(chatCRO) objectPeers = node.networkNode.getGroupPeers(chatCRO.getObjectId());
render();
});

let button_create = <HTMLButtonElement>document.getElementById("createRoom");
button_create.addEventListener("click", () => {
chatCRO = new Chat(node.networkNode.peerId);
node.createObject(chatCRO);
(<HTMLButtonElement>document.getElementById("chatId")).innerHTML = chatCRO.getObjectId();
render();
});

let button_connect = <HTMLButtonElement>document.getElementById("joinRoom");
button_connect.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("roomInput");
let objectId = input.value;
if(!objectId){
alert("Please enter a room id");
return;
}
await node.subscribeObject(objectId, true);
});

let button_fetch = <HTMLButtonElement>document.getElementById("fetchMessages");
button_fetch.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("roomInput");
let objectId = input.value;
try {

let object: any = node.getObject(objectId);
console.log("Object received: ", object);

let arr: string[] = Array.from(object["chat"]["_set"]);
object["chat"]["_set"] = new Set<string>(arr);
object["chat"] = Object.assign(new GSet<string>(new Set<string>()), object["chat"]);
chatCRO = Object.assign(new Chat(node.networkNode.peerId), object);

(<HTMLButtonElement>document.getElementById("chatId")).innerHTML = chatCRO.getObjectId();
render();
} catch (e) {
console.error("Error while connecting to the CRO ", objectId, e);
}
});

let button_send = <HTMLButtonElement>document.getElementById("sendMessage");
button_send.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("messageInput");
let message: string = input.value;
input.value = "";
if(!message){
console.error("Tried sending an empty message");
alert("Please enter a message");
return;
}
await sendMessage(message);
const element_chat = <HTMLDivElement>document.getElementById("chat");
element_chat.scrollTop = element_chat.scrollHeight;
});
let button_create = <HTMLButtonElement>document.getElementById("createRoom");
button_create.addEventListener("click", () => {
chatCRO = new Chat(node.networkNode.peerId);
topology.createObject(node, chatCRO);
(<HTMLButtonElement>document.getElementById("chatId")).innerHTML = chatCRO.cro.id;
render();
});

let button_connect = <HTMLButtonElement>document.getElementById("joinRoom");
button_connect.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("roomInput");
let objectId = input.value;
if (!objectId) {
alert("Please enter a room id");
return;
}
await topology.subscribeObject(node, objectId, true);
});

let button_fetch = <HTMLButtonElement>document.getElementById("fetchMessages");
button_fetch.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("roomInput");
let objectId = input.value;
try {

let object: any = node.objectStore.get(objectId);
console.log("Object received: ", object);

let arr: string[] = Array.from(object["chat"]["_set"]);
object["chat"]["_set"] = new Set<string>(arr);
object["chat"] = Object.assign(new GSet<string>(new Set<string>()), object["chat"]);
chatCRO = Object.assign(new Chat(node.networkNode.peerId), object);

(<HTMLButtonElement>document.getElementById("chatId")).innerHTML = chatCRO.cro.id;
render();
} catch (e) {
console.error("Error while connecting to the CRO ", objectId, e);
}
});

let button_send = <HTMLButtonElement>document.getElementById("sendMessage");
button_send.addEventListener("click", async () => {
let input: HTMLInputElement = <HTMLInputElement>document.getElementById("messageInput");
let message: string = input.value;
input.value = "";
if (!message) {
console.error("Tried sending an empty message");
alert("Please enter a message");
return;
}
await sendMessage(message);
const element_chat = <HTMLDivElement>document.getElementById("chat");
element_chat.scrollTop = element_chat.scrollHeight;
});
}

main();
main();
49 changes: 26 additions & 23 deletions examples/chat/src/objects/chat.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
import { TopologyObject } from "@topology-foundation/object";
import { newTopologyObject, TopologyObject } from "@topology-foundation/object";
import { GSet } from "@topology-foundation/crdt";

export interface IChat extends TopologyObject {
chat: GSet<string>;
addMessage(timestamp: string, message: string, node_id: string): void;
getMessages(): GSet<string>;
merge(other: Chat): void;
export interface IChat {
cro: TopologyObject;
chat: GSet<string>;
addMessage(timestamp: string, message: string, node_id: string): void;
getMessages(): GSet<string>;
merge(other: Chat): void;
}

export class Chat extends TopologyObject implements IChat {
// store messages as strings in the format (timestamp, message, peerId)
chat: GSet<string>;
export class Chat implements IChat {
// TODO: Change this to build a TopologyObject with the
// wasm compilation inside and just use the topology object
cro: TopologyObject;
// store messages as strings in the format (timestamp, message, peerId)
chat: GSet<string>;

constructor(peerId: string) {
super(peerId);
this.chat = new GSet<string>(new Set<string>());
}
constructor(peerId: string) {
this.cro = newTopologyObject(peerId);
this.chat = new GSet<string>(new Set<string>());
}

addMessage(timestamp: string, message: string, node_id: string): void {
this.chat.add(`(${timestamp}, ${message}, ${node_id})`);
}
addMessage(timestamp: string, message: string, node_id: string): void {
this.chat.add(`(${timestamp}, ${message}, ${node_id})`);
}

getMessages(): GSet<string> {
return this.chat;
}

merge(other: Chat): void {
this.chat.merge(other.chat);
}
getMessages(): GSet<string> {
return this.chat;
}

merge(other: Chat): void {
this.chat.merge(other.chat);
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"scripts": {
"docs": "typedoc",
"proto-gen": "buf generate",
"release": "release-it",
"test": "vitest"
},
Expand Down
5 changes: 3 additions & 2 deletions packages/network/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { TopologyNetworkNodeConfig, TopologyNetworkNode } from "./node.js";
export { stringToStream, streamToString } from "./stream.js";
export * from "./node.js";
export * from "./stream.js";
export * from "./proto/messages_pb.js";
Loading

0 comments on commit 0452fb9

Please sign in to comment.