New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@mml-io/3d-web-user-networking

Package Overview
Dependencies
Maintainers
0
Versions
173
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mml-io/3d-web-user-networking - npm Package Compare versions

Comparing version 0.0.0-experimental-80967c9-20231110 to 0.0.0-experimental-81deb84-20240724

build/UserData.d.ts

3

build/index.d.ts
export * from "./UserNetworkingCodec";
export * from "./UserNetworkingServer";
export * from "./UserNetworkingClient";
export * from "./UserData";
export * from "./ReconnectingWebSocket";
export * from "./messages";
export * from "./UserNetworkingMessages";

@@ -30,9 +30,2 @@ // src/UserNetworkingCodec.ts

// src/messages.ts
var CONNECTED_MESSAGE_TYPE = "connected";
var DISCONNECTED_MESSAGE_TYPE = "disconnected";
var IDENTITY_MESSAGE_TYPE = "identity";
var PING_MESSAGE_TYPE = "ping";
var PONG_MESSAGE_TYPE = "pong";
// src/user-networking-settings.ts

@@ -43,27 +36,33 @@ var pingPongRate = 1500;

// src/UserNetworkingMessages.ts
var USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE = "disconnected";
var USER_NETWORKING_IDENTITY_MESSAGE_TYPE = "identity";
var USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE = "user_auth";
var USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE = "user_profile";
var USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE = "user_update";
var USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE = "error";
var USER_NETWORKING_PING_MESSAGE_TYPE = "ping";
var USER_NETWORKING_PONG_MESSAGE_TYPE = "pong";
var USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE = "CONNECTION_LIMIT_REACHED";
var USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE = "AUTHENTICATION_FAILED";
var USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE = "SERVER_SHUTDOWN";
var USER_NETWORKING_UNKNOWN_ERROR = "UNKNOWN_ERROR";
// src/UserNetworkingServer.ts
var WebSocketOpenStatus = 1;
var UserNetworkingServer = class {
constructor() {
this.clients = /* @__PURE__ */ new Map();
this.clientLastPong = /* @__PURE__ */ new Map();
setInterval(this.sendUpdates.bind(this), packetsUpdateRate);
setInterval(this.pingClients.bind(this), pingPongRate);
setInterval(this.heartBeat.bind(this), heartBeatRate);
constructor(options) {
this.options = options;
this.allClientsById = /* @__PURE__ */ new Map();
this.authenticatedClientsById = /* @__PURE__ */ new Map();
this.sendUpdatesIntervalTimer = setInterval(this.sendUpdates.bind(this), packetsUpdateRate);
this.pingClientsIntervalTimer = setInterval(this.pingClients.bind(this), pingPongRate);
this.heartbeatIntervalTimer = setInterval(this.heartBeat.bind(this), heartBeatRate);
}
heartBeat() {
const now = Date.now();
this.clientLastPong.forEach((clientLastPong, id) => {
if (now - clientLastPong > heartBeatRate) {
this.clients.delete(id);
this.clientLastPong.delete(id);
const disconnectMessage = JSON.stringify({
id,
type: DISCONNECTED_MESSAGE_TYPE
});
for (const { socket: otherSocket } of this.clients.values()) {
if (otherSocket.readyState === WebSocketOpenStatus) {
otherSocket.send(disconnectMessage);
}
}
this.allClientsById.forEach((client) => {
if (now - client.lastPong > heartBeatRate) {
client.socket.close();
this.handleDisconnectedClient(client);
}

@@ -73,3 +72,3 @@ });

pingClients() {
this.clients.forEach((client) => {
this.authenticatedClientsById.forEach((client) => {
if (client.socket.readyState === WebSocketOpenStatus) {

@@ -82,4 +81,5 @@ client.socket.send(JSON.stringify({ type: "ping" }));

let id = 1;
while (this.clients.has(id))
while (this.allClientsById.has(id)) {
id++;
}
return id;

@@ -89,22 +89,8 @@ }

const id = this.getId();
console.log(`Client ID: ${id} joined`);
const connectMessage = JSON.stringify({
console.log(`Client ID: ${id} joined, waiting for user-identification`);
const client = {
id,
type: CONNECTED_MESSAGE_TYPE
});
for (const { socket: otherSocket } of this.clients.values()) {
if (otherSocket.readyState === WebSocketOpenStatus) {
otherSocket.send(connectMessage);
}
}
const identityMessage = JSON.stringify({
id,
type: IDENTITY_MESSAGE_TYPE
});
socket.send(identityMessage);
for (const { update } of this.clients.values()) {
socket.send(UserNetworkingCodec.encodeUpdate(update));
}
this.clients.set(id, {
lastPong: Date.now(),
socket,
authenticatedUser: null,
update: {

@@ -116,3 +102,4 @@ id,

}
});
};
this.allClientsById.set(id, client);
socket.on("message", (message, _isBinary) => {

@@ -123,14 +110,83 @@ if (message instanceof Buffer) {

update.id = id;
if (this.clients.get(id) !== void 0) {
this.clients.get(id).update = update;
}
client.update = update;
} else {
let parsed;
try {
const data = JSON.parse(message);
if (data.type === "pong") {
this.clientLastPong.set(id, Date.now());
}
parsed = JSON.parse(message);
} catch (e) {
console.error("Error parsing JSON message", message, e);
return;
}
if (!client.authenticatedUser) {
if (parsed.type === USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE) {
this.handleUserAuth(client, parsed).then((authResult) => {
var _a, _b;
if (!authResult) {
const serverError = JSON.stringify({
type: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
errorType: USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
message: "Authentication failed"
});
socket.send(serverError);
socket.close();
} else {
if (this.options.connectionLimit !== void 0 && this.authenticatedClientsById.size >= this.options.connectionLimit) {
const serverError = JSON.stringify({
type: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
errorType: USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
message: "Connection limit reached"
});
socket.send(serverError);
socket.close();
return;
}
const userData = authResult;
const userProfileMessage = JSON.stringify({
id: client.id,
type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
username: userData.username,
characterDescription: userData.characterDescription
});
client.socket.send(userProfileMessage);
const identityMessage = JSON.stringify({
id: client.id,
type: USER_NETWORKING_IDENTITY_MESSAGE_TYPE
});
client.socket.send(identityMessage);
const userUpdateMessage = UserNetworkingCodec.encodeUpdate(client.update);
for (const [, otherClient] of this.authenticatedClientsById) {
if (otherClient.socket.readyState !== WebSocketOpenStatus || otherClient === client) {
continue;
}
client.socket.send(
JSON.stringify({
id: otherClient.update.id,
type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
username: (_a = otherClient.authenticatedUser) == null ? void 0 : _a.username,
characterDescription: (_b = otherClient.authenticatedUser) == null ? void 0 : _b.characterDescription
})
);
client.socket.send(UserNetworkingCodec.encodeUpdate(otherClient.update));
otherClient.socket.send(userProfileMessage);
otherClient.socket.send(userUpdateMessage);
}
this.authenticatedClientsById.set(id, client);
}
});
} else {
console.error(`Unhandled message pre-auth: ${JSON.stringify(parsed)}`);
socket.close();
}
} else {
switch (parsed.type) {
case USER_NETWORKING_PONG_MESSAGE_TYPE:
client.lastPong = Date.now();
break;
case USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE:
this.handleUserUpdate(id, parsed);
break;
default:
console.error(`Unhandled message: ${JSON.stringify(parsed)}`);
}
}
}

@@ -140,19 +196,90 @@ });

console.log("Client disconnected", id);
this.clients.delete(id);
this.handleDisconnectedClient(client);
});
}
handleDisconnectedClient(client) {
if (!this.allClientsById.has(client.id)) {
return;
}
this.allClientsById.delete(client.id);
if (client.authenticatedUser !== null) {
this.options.onClientDisconnect(client.id);
this.authenticatedClientsById.delete(client.id);
const disconnectMessage = JSON.stringify({
id,
type: DISCONNECTED_MESSAGE_TYPE
id: client.id,
type: USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE
});
for (const [clientId, { socket: otherSocket }] of this.clients) {
if (otherSocket.readyState === WebSocketOpenStatus) {
otherSocket.send(disconnectMessage);
for (const [, otherClient] of this.authenticatedClientsById) {
if (otherClient.socket.readyState === WebSocketOpenStatus) {
otherClient.socket.send(disconnectMessage);
}
}
}
}
async handleUserAuth(client, credentials) {
const userData = this.options.onClientConnect(
client.id,
credentials.sessionToken,
credentials.userIdentity
);
let resolvedUserData;
if (userData instanceof Promise) {
resolvedUserData = await userData;
} else {
resolvedUserData = userData;
}
if (resolvedUserData === null) {
console.error(`Client-id ${client.id} user_auth unauthorized and ignored`);
return false;
}
console.log("Client authenticated", client.id, resolvedUserData);
client.authenticatedUser = resolvedUserData;
return resolvedUserData;
}
updateUserCharacter(clientId, userData) {
this.internalUpdateUser(clientId, userData);
}
internalUpdateUser(clientId, userData) {
const client = this.authenticatedClientsById.get(clientId);
client.authenticatedUser = userData;
this.authenticatedClientsById.set(clientId, client);
const newUserData = JSON.stringify({
id: clientId,
type: USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
username: userData.username,
characterDescription: userData.characterDescription
});
for (const [otherClientId, otherClient] of this.authenticatedClientsById) {
if (otherClient.socket.readyState === WebSocketOpenStatus) {
otherClient.socket.send(newUserData);
}
}
}
async handleUserUpdate(clientId, message) {
const client = this.authenticatedClientsById.get(clientId);
if (!client) {
console.error(`Client-id ${clientId} user_update ignored, client not found`);
return;
}
const authorizedUserData = this.options.onClientUserIdentityUpdate(
clientId,
message.userIdentity
);
let resolvedAuthorizedUserData;
if (authorizedUserData instanceof Promise) {
resolvedAuthorizedUserData = await authorizedUserData;
} else {
resolvedAuthorizedUserData = authorizedUserData;
}
if (!resolvedAuthorizedUserData) {
console.warn(`Client-id ${clientId} user_update unauthorized and ignored`);
return;
}
this.internalUpdateUser(clientId, resolvedAuthorizedUserData);
}
sendUpdates() {
for (const [clientId, client] of this.clients) {
for (const [clientId, client] of this.authenticatedClientsById) {
const update = client.update;
const encodedUpdate = UserNetworkingCodec.encodeUpdate(update);
for (const [otherClientId, otherClient] of this.clients) {
for (const [otherClientId, otherClient] of this.authenticatedClientsById) {
if (otherClientId !== clientId && otherClient.socket.readyState === WebSocketOpenStatus) {

@@ -164,11 +291,23 @@ otherClient.socket.send(encodedUpdate);

}
dispose(clientCloseError) {
clearInterval(this.sendUpdatesIntervalTimer);
clearInterval(this.pingClientsIntervalTimer);
clearInterval(this.heartbeatIntervalTimer);
const stringifiedError = clientCloseError ? JSON.stringify(clientCloseError) : void 0;
for (const [, client] of this.authenticatedClientsById) {
if (stringifiedError) {
client.socket.send(stringifiedError);
}
client.socket.close();
}
}
};
// src/ReconnectingWebSocket.ts
var WebsocketStatus = /* @__PURE__ */ ((WebsocketStatus3) => {
WebsocketStatus3[WebsocketStatus3["Connecting"] = 0] = "Connecting";
WebsocketStatus3[WebsocketStatus3["Connected"] = 1] = "Connected";
WebsocketStatus3[WebsocketStatus3["Reconnecting"] = 2] = "Reconnecting";
WebsocketStatus3[WebsocketStatus3["Disconnected"] = 3] = "Disconnected";
return WebsocketStatus3;
var WebsocketStatus = /* @__PURE__ */ ((WebsocketStatus2) => {
WebsocketStatus2[WebsocketStatus2["Connecting"] = 0] = "Connecting";
WebsocketStatus2[WebsocketStatus2["Connected"] = 1] = "Connected";
WebsocketStatus2[WebsocketStatus2["Reconnecting"] = 2] = "Reconnecting";
WebsocketStatus2[WebsocketStatus2["Disconnected"] = 3] = "Disconnected";
return WebsocketStatus2;
})(WebsocketStatus || {});

@@ -286,3 +425,3 @@ var startingBackoffTimeMilliseconds = 100;

}
console.log("NetworkedDOMWebsocket close", e);
console.log("ReconnectingWebSocket close", e);
onWebsocketClose();

@@ -295,3 +434,3 @@ });

}
console.error("NetworkedDOMWebsocket error", e);
console.error("ReconnectingWebSocket error", e);
onWebsocketClose();

@@ -318,6 +457,13 @@ });

var UserNetworkingClient = class extends ReconnectingWebSocket {
constructor(url, websocketFactory, statusUpdateCallback, setIdentityCallback, clientUpdate) {
super(url, websocketFactory, statusUpdateCallback);
this.setIdentityCallback = setIdentityCallback;
this.clientUpdate = clientUpdate;
constructor(config) {
super(config.url, config.websocketFactory, (status) => {
if (status === 1 /* Connected */) {
this.sendMessage({
type: USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
sessionToken: config.sessionToken
});
}
config.statusUpdateCallback(status);
});
this.config = config;
}

@@ -328,2 +474,5 @@ sendUpdate(update) {

}
sendMessage(message) {
this.send(message);
}
handleIncomingWebsocketMessage(message) {

@@ -333,23 +482,28 @@ if (typeof message.data === "string") {

switch (parsed.type) {
case IDENTITY_MESSAGE_TYPE:
console.log(`Assigned ID: ${parsed.id}`);
this.setIdentityCallback(parsed.id);
case USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE:
console.error(`Server error: ${parsed.message}. errorType: ${parsed.errorType}`);
this.config.onServerError(parsed);
break;
case CONNECTED_MESSAGE_TYPE:
console.log(`Client ID: ${parsed.id} joined`);
break;
case DISCONNECTED_MESSAGE_TYPE:
case USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE:
console.log(`Client ID: ${parsed.id} left`);
this.clientUpdate(parsed.id, null);
this.config.clientUpdate(parsed.id, null);
break;
case PING_MESSAGE_TYPE: {
this.send({ type: "pong" });
case USER_NETWORKING_IDENTITY_MESSAGE_TYPE:
console.log(`Client ID: ${parsed.id} assigned to self`);
this.config.assignedIdentity(parsed.id);
break;
case USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE:
console.log(`Client ID: ${parsed.id} updated profile`);
this.config.clientProfileUpdated(parsed.id, parsed.username, parsed.characterDescription);
break;
case USER_NETWORKING_PING_MESSAGE_TYPE: {
this.sendMessage({ type: "pong" });
break;
}
default:
console.warn("unknown message type received", parsed);
console.error("Unhandled message", parsed);
}
} else if (message.data instanceof ArrayBuffer) {
const userNetworkingClientUpdate = UserNetworkingCodec.decodeUpdate(message.data);
this.clientUpdate(userNetworkingClientUpdate.id, userNetworkingClientUpdate);
this.config.clientUpdate(userNetworkingClientUpdate.id, userNetworkingClientUpdate);
} else {

@@ -361,8 +515,15 @@ console.error("Unhandled message type", message.data);

export {
CONNECTED_MESSAGE_TYPE,
DISCONNECTED_MESSAGE_TYPE,
IDENTITY_MESSAGE_TYPE,
PING_MESSAGE_TYPE,
PONG_MESSAGE_TYPE,
ReconnectingWebSocket,
USER_NETWORKING_AUTHENTICATION_FAILED_ERROR_TYPE,
USER_NETWORKING_CONNECTION_LIMIT_REACHED_ERROR_TYPE,
USER_NETWORKING_DISCONNECTED_MESSAGE_TYPE,
USER_NETWORKING_IDENTITY_MESSAGE_TYPE,
USER_NETWORKING_PING_MESSAGE_TYPE,
USER_NETWORKING_PONG_MESSAGE_TYPE,
USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
USER_NETWORKING_UNKNOWN_ERROR,
USER_NETWORKING_USER_AUTHENTICATE_MESSAGE_TYPE,
USER_NETWORKING_USER_PROFILE_MESSAGE_TYPE,
USER_NETWORKING_USER_UPDATE_MESSAGE_TYPE,
UserNetworkingClient,

@@ -369,0 +530,0 @@ UserNetworkingCodec,

import { ReconnectingWebSocket, WebsocketFactory, WebsocketStatus } from "./ReconnectingWebSocket";
import { UserNetworkingClientUpdate } from "./UserNetworkingCodec";
import { CharacterDescription, FromUserNetworkingClientMessage, UserNetworkingServerErrorType } from "./UserNetworkingMessages";
export type UserNetworkingClientConfig = {
url: string;
sessionToken: string;
websocketFactory: WebsocketFactory;
statusUpdateCallback: (status: WebsocketStatus) => void;
assignedIdentity: (clientId: number) => void;
clientUpdate: (id: number, update: null | UserNetworkingClientUpdate) => void;
clientProfileUpdated: (id: number, username: string, characterDescription: CharacterDescription) => void;
onServerError: (error: {
message: string;
errorType: UserNetworkingServerErrorType;
}) => void;
};
export declare class UserNetworkingClient extends ReconnectingWebSocket {
private setIdentityCallback;
private clientUpdate;
constructor(url: string, websocketFactory: WebsocketFactory, statusUpdateCallback: (status: WebsocketStatus) => void, setIdentityCallback: (id: number) => void, clientUpdate: (id: number, update: null | UserNetworkingClientUpdate) => void);
private config;
constructor(config: UserNetworkingClientConfig);
sendUpdate(update: UserNetworkingClientUpdate): void;
sendMessage(message: FromUserNetworkingClientMessage): void;
protected handleIncomingWebsocketMessage(message: MessageEvent): void;
}
import WebSocket from "ws";
import { UserData } from "./UserData";
import { UserNetworkingClientUpdate } from "./UserNetworkingCodec";
export type Client = {
import { UserIdentity, UserNetworkingServerError } from "./UserNetworkingMessages";
export type UserNetworkingServerClient = {
socket: WebSocket;
id: number;
lastPong: number;
update: UserNetworkingClientUpdate;
authenticatedUser: UserData | null;
};
export type UserNetworkingServerOptions = {
connectionLimit?: number;
onClientConnect: (clientId: number, sessionToken: string, userIdentity?: UserIdentity) => Promise<UserData | null> | UserData | null;
onClientUserIdentityUpdate: (clientId: number, userIdentity: UserIdentity) => Promise<UserData | null> | UserData | null;
onClientDisconnect: (clientId: number) => void;
};
export declare class UserNetworkingServer {
private clients;
private clientLastPong;
constructor();
heartBeat(): void;
pingClients(): void;
getId(): number;
private options;
private allClientsById;
private authenticatedClientsById;
private sendUpdatesIntervalTimer;
private pingClientsIntervalTimer;
private heartbeatIntervalTimer;
constructor(options: UserNetworkingServerOptions);
private heartBeat;
private pingClients;
private getId;
connectClient(socket: WebSocket): void;
sendUpdates(): void;
private handleDisconnectedClient;
private handleUserAuth;
updateUserCharacter(clientId: number, userData: UserData): void;
private internalUpdateUser;
private handleUserUpdate;
private sendUpdates;
dispose(clientCloseError?: UserNetworkingServerError): void;
}
{
"name": "@mml-io/3d-web-user-networking",
"version": "0.0.0-experimental-80967c9-20231110",
"version": "0.0.0-experimental-81deb84-20240724",
"publishConfig": {

@@ -22,13 +22,13 @@ "access": "public"

"dependencies": {
"ws": "^8.13.0"
"ws": "^8.18.0"
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/express-ws": "^3.0.1",
"@types/node": "^20.5.9",
"@types/ws": "^8.5.5",
"express": "4.18.2",
"@types/express": "^4.17.21",
"@types/express-ws": "^3.0.4",
"@types/node": "^20.14.10",
"@types/ws": "^8.5.10",
"express": "4.19.2",
"express-ws": "5.0.2"
},
"gitHead": "07d94306f124adeaff468773b82541c751a9dc61"
"gitHead": "36e3513bb3b51cd1822c8f9bf1afb2aceb8ac248"
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc