@webgamekit/multiplayer-p2p 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data.d.ts +17 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +151 -0
- package/dist/index.umd.cjs +1 -0
- package/dist/lobby.d.ts +49 -0
- package/dist/matchmaker.d.ts +20 -0
- package/dist/players.d.ts +29 -0
- package/dist/room.d.ts +39 -0
- package/dist/types.d.ts +37 -0
- package/package.json +35 -3
- package/README.md +0 -3
package/dist/data.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { DataPayload } from 'trystero/nostr';
|
|
2
|
+
import type { P2PSession } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Send typed data to all peers on a named channel.
|
|
5
|
+
* @param session - The active P2P session
|
|
6
|
+
* @param channel - Channel name (must match receiver's channel name)
|
|
7
|
+
* @param payload - Serializable data to broadcast
|
|
8
|
+
*/
|
|
9
|
+
export declare const p2pSendData: <T extends DataPayload>(session: P2PSession, channel: string, payload: T) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to typed data arriving on a named channel from any peer.
|
|
12
|
+
* @param session - The active P2P session
|
|
13
|
+
* @param channel - Channel name to listen on
|
|
14
|
+
* @param callback - Called with the payload and the sender's peer ID
|
|
15
|
+
* @returns Unsubscribe function (stops the listener)
|
|
16
|
+
*/
|
|
17
|
+
export declare const p2pOnData: <T extends DataPayload>(session: P2PSession, channel: string, callback: (payload: T, peerId: string) => void) => (() => void);
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { PlayerPosition, PlayerRotation, PlayerState, PlayerAction, P2PConfig, P2PSession } from './types';
|
|
2
|
+
export { p2pJoin, p2pLeave, p2pOnPeerJoin, p2pOnPeerLeave, p2pIsSupported, p2pGetPeerIds } from './room';
|
|
3
|
+
export { p2pSendPosition, p2pOnPlayers, p2pSendAction, p2pOnAction } from './players';
|
|
4
|
+
export { p2pSendData, p2pOnData } from './data';
|
|
5
|
+
export { p2pMatchmake } from './matchmaker';
|
|
6
|
+
export type { MatchmakeStatus, MatchmakeHandle } from './matchmaker';
|
|
7
|
+
export { p2pLobbyJoin } from './lobby';
|
|
8
|
+
export type { MatchRequest, LobbyCallbacks, LobbyHandle } from './lobby';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { joinRoom as h, selfId as k } from "trystero/nostr";
|
|
2
|
+
const M = () => {
|
|
3
|
+
var t;
|
|
4
|
+
const e = typeof window < "u" && typeof ((t = window.crypto) == null ? void 0 : t.subtle) < "u";
|
|
5
|
+
return e || console.warn(
|
|
6
|
+
`[p2p] crypto.subtle is not available. P2P requires a secure context (HTTPS or localhost). Current origin: ${typeof window < "u" ? window.location.origin : "unknown"}`
|
|
7
|
+
), e;
|
|
8
|
+
}, A = (e, t) => {
|
|
9
|
+
if (!M())
|
|
10
|
+
throw new Error(
|
|
11
|
+
`P2P is not supported on this origin (${window.location.origin}). Serve the app over HTTPS or use localhost.`
|
|
12
|
+
);
|
|
13
|
+
const o = (t == null ? void 0 : t.appId) ?? "webgamekit", n = h({ appId: o }, e);
|
|
14
|
+
return {
|
|
15
|
+
get peerId() {
|
|
16
|
+
return k;
|
|
17
|
+
},
|
|
18
|
+
room: n,
|
|
19
|
+
destroy: () => {
|
|
20
|
+
n.leave();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}, R = (e) => Object.keys(e.room.getPeers()), q = (e) => {
|
|
24
|
+
e.destroy();
|
|
25
|
+
}, S = (e, t) => {
|
|
26
|
+
e.room.onPeerJoin((o) => {
|
|
27
|
+
t(o);
|
|
28
|
+
});
|
|
29
|
+
}, y = (e, t) => {
|
|
30
|
+
e.room.onPeerLeave((o) => {
|
|
31
|
+
t(o);
|
|
32
|
+
});
|
|
33
|
+
}, T = 30, w = /* @__PURE__ */ new WeakMap(), f = /* @__PURE__ */ new WeakMap(), l = /* @__PURE__ */ new WeakMap(), I = /* @__PURE__ */ new WeakMap(), v = (e, t) => {
|
|
34
|
+
l.has(e) || l.set(e, /* @__PURE__ */ new Map());
|
|
35
|
+
const o = l.get(e);
|
|
36
|
+
if (!o.has(t)) {
|
|
37
|
+
const [n] = e.room.makeAction(t);
|
|
38
|
+
o.set(t, n);
|
|
39
|
+
}
|
|
40
|
+
return o.get(t);
|
|
41
|
+
}, N = (e, t) => {
|
|
42
|
+
I.has(e) || I.set(e, /* @__PURE__ */ new Set());
|
|
43
|
+
const o = I.get(e);
|
|
44
|
+
if (o.has(t)) return null;
|
|
45
|
+
o.add(t);
|
|
46
|
+
const [, n] = e.room.makeAction(t);
|
|
47
|
+
return n;
|
|
48
|
+
}, C = (e, t, o, n) => {
|
|
49
|
+
const r = (n == null ? void 0 : n.throttleMs) ?? T, c = v(e, "pos"), d = performance.now(), u = f.get(e) ?? 0, m = () => {
|
|
50
|
+
f.set(e, performance.now()), c({ position: t, rotation: o });
|
|
51
|
+
};
|
|
52
|
+
if (d - u >= r) {
|
|
53
|
+
const a = w.get(e);
|
|
54
|
+
a !== void 0 && clearTimeout(a), m();
|
|
55
|
+
} else {
|
|
56
|
+
const a = w.get(e);
|
|
57
|
+
a !== void 0 && clearTimeout(a), w.set(
|
|
58
|
+
e,
|
|
59
|
+
setTimeout(
|
|
60
|
+
() => {
|
|
61
|
+
w.delete(e), m();
|
|
62
|
+
},
|
|
63
|
+
r - (d - u)
|
|
64
|
+
)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}, H = (e, t) => {
|
|
68
|
+
const o = N(e, "pos");
|
|
69
|
+
return o && o((n, r) => {
|
|
70
|
+
const c = n, d = c.position, u = c.rotation;
|
|
71
|
+
t({ id: r, position: d, rotation: u });
|
|
72
|
+
}), () => {
|
|
73
|
+
};
|
|
74
|
+
}, J = (e, t) => {
|
|
75
|
+
v(e, "action")({ name: t });
|
|
76
|
+
}, x = (e, t) => {
|
|
77
|
+
const o = N(e, "action");
|
|
78
|
+
return o && o((n, r) => {
|
|
79
|
+
t(r, n);
|
|
80
|
+
}), () => {
|
|
81
|
+
};
|
|
82
|
+
}, D = (e, t, o) => {
|
|
83
|
+
const [n] = e.room.makeAction(t);
|
|
84
|
+
n(o);
|
|
85
|
+
}, W = (e, t, o) => {
|
|
86
|
+
const [, n] = e.room.makeAction(t);
|
|
87
|
+
return n((r, c) => o(r, c)), () => {
|
|
88
|
+
};
|
|
89
|
+
}, _ = (e, t, o) => {
|
|
90
|
+
const n = A(e, t), r = new Set(R(n)), c = () => {
|
|
91
|
+
o(r.size > 0 ? "matched" : "searching", r.size);
|
|
92
|
+
};
|
|
93
|
+
return S(n, (d) => {
|
|
94
|
+
r.add(d), c();
|
|
95
|
+
}), y(n, (d) => {
|
|
96
|
+
r.delete(d), c();
|
|
97
|
+
}), c(), {
|
|
98
|
+
session: n,
|
|
99
|
+
stop: () => q(n)
|
|
100
|
+
};
|
|
101
|
+
}, E = "lobby-req", L = "lobby-res", O = "lobby-name", j = (e, t, o) => {
|
|
102
|
+
const n = A(e, t), [r, c] = n.room.makeAction(E), [d, u] = n.room.makeAction(L), [m, a] = n.room.makeAction(O);
|
|
103
|
+
let g = "";
|
|
104
|
+
return S(n, (s) => {
|
|
105
|
+
g && m({ name: g }, s), o.onPeerJoin(s);
|
|
106
|
+
}), y(n, o.onPeerLeave), c((s, p) => {
|
|
107
|
+
o.onRequest(
|
|
108
|
+
{ requestId: s.requestId, gameRoomId: s.gameRoomId },
|
|
109
|
+
p
|
|
110
|
+
);
|
|
111
|
+
}), u((s) => {
|
|
112
|
+
s.accepted === "true" ? o.onAccepted(s.requestId) : o.onIgnored(s.requestId);
|
|
113
|
+
}), a((s, p) => {
|
|
114
|
+
o.onPeerName(p, s.name);
|
|
115
|
+
}), {
|
|
116
|
+
session: n,
|
|
117
|
+
getPeerIds: () => R(n),
|
|
118
|
+
setName: (s) => {
|
|
119
|
+
g = s, m({ name: s });
|
|
120
|
+
},
|
|
121
|
+
sendRequest: (s, p) => {
|
|
122
|
+
const i = `${n.peerId}-${Date.now()}`, P = p ?? `${e}-game-${i}`;
|
|
123
|
+
return r({ requestId: i, gameRoomId: P }, s), { requestId: i, gameRoomId: P };
|
|
124
|
+
},
|
|
125
|
+
acceptRequest: (s, p) => {
|
|
126
|
+
const i = { requestId: s.requestId, accepted: "true" };
|
|
127
|
+
d(i, p);
|
|
128
|
+
},
|
|
129
|
+
ignoreRequest: (s, p) => {
|
|
130
|
+
const i = { requestId: s.requestId, accepted: "false" };
|
|
131
|
+
d(i, p);
|
|
132
|
+
},
|
|
133
|
+
stop: () => q(n)
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
R as p2pGetPeerIds,
|
|
138
|
+
M as p2pIsSupported,
|
|
139
|
+
A as p2pJoin,
|
|
140
|
+
q as p2pLeave,
|
|
141
|
+
j as p2pLobbyJoin,
|
|
142
|
+
_ as p2pMatchmake,
|
|
143
|
+
x as p2pOnAction,
|
|
144
|
+
W as p2pOnData,
|
|
145
|
+
S as p2pOnPeerJoin,
|
|
146
|
+
y as p2pOnPeerLeave,
|
|
147
|
+
H as p2pOnPlayers,
|
|
148
|
+
J as p2pSendAction,
|
|
149
|
+
D as p2pSendData,
|
|
150
|
+
C as p2pSendPosition
|
|
151
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(s,m){typeof exports=="object"&&typeof module<"u"?m(exports,require("trystero/nostr")):typeof define=="function"&&define.amd?define(["exports","trystero/nostr"],m):(s=typeof globalThis<"u"?globalThis:s||self,m(s.WebGameToolkitMultiplayerP2P={},s.trysteroNostr))})(this,function(s,m){"use strict";const h=()=>{var t;const e=typeof window<"u"&&typeof((t=window.crypto)==null?void 0:t.subtle)<"u";return e||console.warn(`[p2p] crypto.subtle is not available. P2P requires a secure context (HTTPS or localhost). Current origin: ${typeof window<"u"?window.location.origin:"unknown"}`),e},w=(e,t)=>{if(!h())throw new Error(`P2P is not supported on this origin (${window.location.origin}). Serve the app over HTTPS or use localhost.`);const o=(t==null?void 0:t.appId)??"webgamekit",n=m.joinRoom({appId:o},e);return{get peerId(){return m.selfId},room:n,destroy:()=>{n.leave()}}},y=e=>Object.keys(e.room.getPeers()),I=e=>{e.destroy()},g=(e,t)=>{e.room.onPeerJoin(o=>{t(o)})},S=(e,t)=>{e.room.onPeerLeave(o=>{t(o)})},P=new WeakMap,v=new WeakMap,A=new WeakMap,q=new WeakMap,T=(e,t)=>{A.has(e)||A.set(e,new Map);const o=A.get(e);if(!o.has(t)){const[n]=e.room.makeAction(t);o.set(t,n)}return o.get(t)},M=(e,t)=>{q.has(e)||q.set(e,new Set);const o=q.get(e);if(o.has(t))return null;o.add(t);const[,n]=e.room.makeAction(t);return n},O=(e,t,o,n)=>{const p=(n==null?void 0:n.throttleMs)??30,c=T(e,"pos"),d=performance.now(),f=v.get(e)??0,l=()=>{v.set(e,performance.now()),c({position:t,rotation:o})};if(d-f>=p){const a=P.get(e);a!==void 0&&clearTimeout(a),l()}else{const a=P.get(e);a!==void 0&&clearTimeout(a),P.set(e,setTimeout(()=>{P.delete(e),l()},p-(d-f)))}},k=(e,t)=>{const o=M(e,"pos");return o&&o((n,p)=>{const c=n,d=c.position,f=c.rotation;t({id:p,position:d,rotation:f})}),()=>{}},b=(e,t)=>{T(e,"action")({name:t})},L=(e,t)=>{const o=M(e,"action");return o&&o((n,p)=>{t(p,n)}),()=>{}},E=(e,t,o)=>{const[n]=e.room.makeAction(t);n(o)},J=(e,t,o)=>{const[,n]=e.room.makeAction(t);return n((p,c)=>o(p,c)),()=>{}},D=(e,t,o)=>{const n=w(e,t),p=new Set(y(n)),c=()=>{o(p.size>0?"matched":"searching",p.size)};return g(n,d=>{p.add(d),c()}),S(n,d=>{p.delete(d),c()}),c(),{session:n,stop:()=>I(n)}},$="lobby-req",C="lobby-res",H="lobby-name",W=(e,t,o)=>{const n=w(e,t),[p,c]=n.room.makeAction($),[d,f]=n.room.makeAction(C),[l,a]=n.room.makeAction(H);let R="";return g(n,r=>{R&&l({name:R},r),o.onPeerJoin(r)}),S(n,o.onPeerLeave),c((r,i)=>{o.onRequest({requestId:r.requestId,gameRoomId:r.gameRoomId},i)}),f(r=>{r.accepted==="true"?o.onAccepted(r.requestId):o.onIgnored(r.requestId)}),a((r,i)=>{o.onPeerName(i,r.name)}),{session:n,getPeerIds:()=>y(n),setName:r=>{R=r,l({name:r})},sendRequest:(r,i)=>{const u=`${n.peerId}-${Date.now()}`,N=i??`${e}-game-${u}`;return p({requestId:u,gameRoomId:N},r),{requestId:u,gameRoomId:N}},acceptRequest:(r,i)=>{const u={requestId:r.requestId,accepted:"true"};d(u,i)},ignoreRequest:(r,i)=>{const u={requestId:r.requestId,accepted:"false"};d(u,i)},stop:()=>I(n)}};s.p2pGetPeerIds=y,s.p2pIsSupported=h,s.p2pJoin=w,s.p2pLeave=I,s.p2pLobbyJoin=W,s.p2pMatchmake=D,s.p2pOnAction=L,s.p2pOnData=J,s.p2pOnPeerJoin=g,s.p2pOnPeerLeave=S,s.p2pOnPlayers=k,s.p2pSendAction=b,s.p2pSendData=E,s.p2pSendPosition=O,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})});
|
package/dist/lobby.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { P2PConfig, P2PSession } from './types';
|
|
2
|
+
export interface MatchRequest {
|
|
3
|
+
requestId: string;
|
|
4
|
+
gameRoomId: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LobbyCallbacks {
|
|
7
|
+
/** A new peer appeared in the lobby */
|
|
8
|
+
onPeerJoin: (peerId: string) => void;
|
|
9
|
+
/** A peer left the lobby */
|
|
10
|
+
onPeerLeave: (peerId: string) => void;
|
|
11
|
+
/** Another peer sent us a match request */
|
|
12
|
+
onRequest: (request: MatchRequest, fromPeerId: string) => void;
|
|
13
|
+
/** The peer we requested accepted the match */
|
|
14
|
+
onAccepted: (requestId: string) => void;
|
|
15
|
+
/** The peer we requested ignored the match */
|
|
16
|
+
onIgnored: (requestId: string) => void;
|
|
17
|
+
/** A peer broadcast their display name */
|
|
18
|
+
onPeerName: (peerId: string, name: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export interface LobbyHandle {
|
|
21
|
+
session: P2PSession;
|
|
22
|
+
/** IDs of all peers currently in the lobby */
|
|
23
|
+
getPeerIds: () => string[];
|
|
24
|
+
/** Broadcast a display name to all current peers and auto-send it to future joiners */
|
|
25
|
+
setName: (name: string) => void;
|
|
26
|
+
/**
|
|
27
|
+
* Send a match request to a specific peer — returns the request for tracking.
|
|
28
|
+
* Pass `gameRoomId` to advertise the caller's existing room; omit to auto-generate one.
|
|
29
|
+
*/
|
|
30
|
+
sendRequest: (targetPeerId: string, gameRoomId?: string) => MatchRequest;
|
|
31
|
+
/** Accept an incoming match request */
|
|
32
|
+
acceptRequest: (request: MatchRequest, fromPeerId: string) => void;
|
|
33
|
+
/** Ignore an incoming match request */
|
|
34
|
+
ignoreRequest: (request: MatchRequest, fromPeerId: string) => void;
|
|
35
|
+
/** Leave the lobby and clean up */
|
|
36
|
+
stop: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Join a lobby room for peer discovery and match negotiation.
|
|
40
|
+
*
|
|
41
|
+
* Peers in the lobby can broadcast a display name, send targeted match
|
|
42
|
+
* requests to each other, and accept or ignore each request individually.
|
|
43
|
+
* On acceptance, both sides receive the shared `gameRoomId` to join.
|
|
44
|
+
* @param lobbyRoomId - Well-known room ID all players join to discover each other
|
|
45
|
+
* @param config - Optional P2P configuration
|
|
46
|
+
* @param callbacks - Event handlers for peer, name, and request lifecycle events
|
|
47
|
+
* @returns A handle to set a name, send requests, respond, and leave the lobby
|
|
48
|
+
*/
|
|
49
|
+
export declare const p2pLobbyJoin: (lobbyRoomId: string, config: P2PConfig | undefined, callbacks: LobbyCallbacks) => LobbyHandle;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { P2PConfig, P2PSession } from './types';
|
|
2
|
+
export type MatchmakeStatus = 'searching' | 'matched';
|
|
3
|
+
export interface MatchmakeHandle {
|
|
4
|
+
/** The underlying P2P session — use this to send/receive game data */
|
|
5
|
+
session: P2PSession;
|
|
6
|
+
/** Leave the room and stop matchmaking */
|
|
7
|
+
stop: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Join a shared room and report status changes as peers arrive or leave.
|
|
11
|
+
*
|
|
12
|
+
* - Calls `onStatusChange('searching', 0)` immediately if no peers are present.
|
|
13
|
+
* - Calls `onStatusChange('matched', n)` whenever peer count rises above zero.
|
|
14
|
+
* - Reverts to `'searching'` if all peers disconnect.
|
|
15
|
+
* @param roomId - Shared room to join (same value for all players in the session)
|
|
16
|
+
* @param config - Optional P2P configuration
|
|
17
|
+
* @param onStatusChange - Fired on initial join and on every peer join/leave
|
|
18
|
+
* @returns A handle with the active session and a `stop()` function to leave
|
|
19
|
+
*/
|
|
20
|
+
export declare const p2pMatchmake: (roomId: string, config: P2PConfig | undefined, onStatusChange: (status: MatchmakeStatus, peerCount: number) => void) => MatchmakeHandle;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { PlayerPosition, PlayerRotation, PlayerState, PlayerAction, P2PConfig, P2PSession } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Broadcast the local player's position and rotation to all peers, throttled.
|
|
4
|
+
* @param session - The active P2P session
|
|
5
|
+
* @param position - Current player position {x, y, z}
|
|
6
|
+
* @param rotation - Current player rotation {x, y, z}
|
|
7
|
+
* @param config - Optional config carrying throttleMs
|
|
8
|
+
*/
|
|
9
|
+
export declare const p2pSendPosition: (session: P2PSession, position: PlayerPosition, rotation: PlayerRotation, config?: P2PConfig) => void;
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to position updates from other peers.
|
|
12
|
+
* @param session - The active P2P session
|
|
13
|
+
* @param callback - Called with the sender's PlayerState on each update
|
|
14
|
+
* @returns Unsubscribe function
|
|
15
|
+
*/
|
|
16
|
+
export declare const p2pOnPlayers: (session: P2PSession, callback: (player: PlayerState) => void) => (() => void);
|
|
17
|
+
/**
|
|
18
|
+
* Broadcast a named animation action to all peers.
|
|
19
|
+
* @param session - The active P2P session
|
|
20
|
+
* @param actionName - Animation clip name to play (e.g. "wave", "attack")
|
|
21
|
+
*/
|
|
22
|
+
export declare const p2pSendAction: (session: P2PSession, actionName: string) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Subscribe to animation action broadcasts from other peers.
|
|
25
|
+
* @param session - The active P2P session
|
|
26
|
+
* @param callback - Called with the sender's peer ID and action name
|
|
27
|
+
* @returns Unsubscribe function
|
|
28
|
+
*/
|
|
29
|
+
export declare const p2pOnAction: (session: P2PSession, callback: (peerId: string, action: PlayerAction) => void) => (() => void);
|
package/dist/room.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { P2PConfig, P2PSession } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Check whether the browser exposes crypto.subtle (requires HTTPS or localhost).
|
|
4
|
+
* @returns true if the environment supports P2P
|
|
5
|
+
*/
|
|
6
|
+
export declare const p2pIsSupported: () => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Join a P2P room using WebRTC via Trystero (torrent signaling — no server required).
|
|
9
|
+
* All peers in the same roomId can communicate directly.
|
|
10
|
+
* Requires a secure context (HTTPS or localhost) — throws if crypto.subtle is unavailable.
|
|
11
|
+
* @param roomId - Shared identifier for the game room (e.g. "level-1-abc123")
|
|
12
|
+
* @param config - Optional configuration overrides
|
|
13
|
+
* @returns A P2PSession with the room handle and destroy function
|
|
14
|
+
*/
|
|
15
|
+
export declare const p2pJoin: (roomId: string, config?: P2PConfig) => P2PSession;
|
|
16
|
+
/**
|
|
17
|
+
* Return the IDs of all peers currently connected in the room.
|
|
18
|
+
* Use this immediately after joining to catch peers that joined before you.
|
|
19
|
+
* @param session - The active P2P session
|
|
20
|
+
* @returns Array of peer IDs already in the room
|
|
21
|
+
*/
|
|
22
|
+
export declare const p2pGetPeerIds: (session: P2PSession) => string[];
|
|
23
|
+
/**
|
|
24
|
+
* Leave a P2P room and clean up all connections.
|
|
25
|
+
* @param session - The session returned by p2pJoin
|
|
26
|
+
*/
|
|
27
|
+
export declare const p2pLeave: (session: P2PSession) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Subscribe to a callback fired when a remote peer joins the room.
|
|
30
|
+
* @param session - The active P2P session
|
|
31
|
+
* @param callback - Called with the peer's ID when they join
|
|
32
|
+
*/
|
|
33
|
+
export declare const p2pOnPeerJoin: (session: P2PSession, callback: (peerId: string) => void) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Subscribe to a callback fired when a remote peer leaves the room.
|
|
36
|
+
* @param session - The active P2P session
|
|
37
|
+
* @param callback - Called with the peer's ID when they leave
|
|
38
|
+
*/
|
|
39
|
+
export declare const p2pOnPeerLeave: (session: P2PSession, callback: (peerId: string) => void) => void;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Room, DataPayload } from 'trystero/nostr';
|
|
2
|
+
export type { DataPayload };
|
|
3
|
+
export interface P2PConfig {
|
|
4
|
+
/** App identifier — must be unique to your game (default: "webgamekit") */
|
|
5
|
+
appId?: string;
|
|
6
|
+
/** Minimum ms between position broadcasts (default: 30) */
|
|
7
|
+
throttleMs?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface P2PSession {
|
|
10
|
+
/** Local peer ID assigned by Trystero */
|
|
11
|
+
peerId: string;
|
|
12
|
+
/** The underlying Trystero Room */
|
|
13
|
+
room: Room;
|
|
14
|
+
/** Leave the room and clean up */
|
|
15
|
+
destroy: () => void;
|
|
16
|
+
}
|
|
17
|
+
/** Serializable position — compatible with Trystero DataPayload */
|
|
18
|
+
export type PlayerPosition = {
|
|
19
|
+
x: number;
|
|
20
|
+
y: number;
|
|
21
|
+
z: number;
|
|
22
|
+
} & Record<string, number>;
|
|
23
|
+
/** Serializable rotation — compatible with Trystero DataPayload */
|
|
24
|
+
export type PlayerRotation = {
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
z: number;
|
|
28
|
+
} & Record<string, number>;
|
|
29
|
+
export interface PlayerState {
|
|
30
|
+
id: string;
|
|
31
|
+
position: PlayerPosition;
|
|
32
|
+
rotation: PlayerRotation;
|
|
33
|
+
}
|
|
34
|
+
/** Serializable animation action — compatible with Trystero DataPayload */
|
|
35
|
+
export type PlayerAction = {
|
|
36
|
+
name: string;
|
|
37
|
+
} & Record<string, string>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webgamekit/multiplayer-p2p",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "git+https://github.com/cnotv/generative-art.git",
|
|
7
|
+
"directory": "packages/multiplayer-p2p"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"main": "./dist/index.umd.cjs",
|
|
14
|
+
"module": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"require": "./dist/index.umd.cjs",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"trystero": "^0.23.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"trystero": "^0.23.0",
|
|
31
|
+
"typescript": "^5.0.0",
|
|
32
|
+
"vite": "^5.0.0",
|
|
33
|
+
"vitest": "^4.0.18"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "vite build && tsc"
|
|
37
|
+
}
|
|
6
38
|
}
|
package/README.md
DELETED