fuzzyworld

dao-game / commit

PeerGame2 update:

7313313d71d0fb9b8322d024c476acfc63b38f24 · Pavel Safronov · 2022-03-31 10:28:45 -0700 · parents ecef404bb612

PeerGame2 update:
- removed all DOM code
- introduced settings interface with callbacks and ways to get data in and out of the game
- haven't tried hooking this up yet to the React app yet
 src/game/remote-game-2.ts | 139 +++++++++++++---------------------------------
 1 file changed, 40 insertions(+), 99 deletions(-)

diff --git a/src/game/remote-game-2.ts b/src/game/remote-game-2.ts
index f570cad..11260dc 100644
--- a/src/game/remote-game-2.ts
+++ b/src/game/remote-game-2.ts
@@ -1,19 +1,10 @@
 import Peer, { PeerJSOption } from 'peerjs';
-import { showModal } from '../utils/modal';
-import { useSessionStorage } from '../utils/storage';
-import { showSnackbar } from "../utils/snackbar";
 import { Game, Move, MoveResult } from './game';
 import { Coord } from './coord';
 import { generateHash } from '../utils/hashing';
 import { delay } from '../utils/delay';
-import { copyToClipboard } from '../utils/clipboard';
-import { detect } from 'detect-browser';
 
-interface LocalData {
-    id?: string;
-    peerId?: string;
-}
-interface Pos {
+export interface Pos {
     x: number;
     y: number;
 }
@@ -23,10 +14,6 @@ interface PeerData {
 }
 
 const peerOptions: PeerJSOption = { debug: 0 };
-const { getter: getData, setter: setData } = useSessionStorage<LocalData>('dao-game-2', { id: undefined, peerId: undefined });
-const otherMouseElement = document.getElementById('other-mouse')!;
-const shareContainer = document.getElementById('share-container')!;
-const shareLinkButton = document.getElementById('share-link')!;
 const minString = (a: string, b: string): string => (a < b ? a : b);
 const maxString = (a: string, b: string): string => (a > b ? a : b);
 const getFirstPlayerId = (playerId1: string, playerId2: string) => {
@@ -37,18 +24,25 @@ const getFirstPlayerId = (playerId1: string, playerId2: string) => {
     const id = isEven ? minString(playerId1, playerId2) : maxString(playerId1, playerId2);
     return id;
 };
-const dropFileExtension = (path: string) => {
-    const lastSlashIndex = path.lastIndexOf('/');
-    const dirName = path.substring(0, lastSlashIndex);
-    return dirName;
+
+export class MousePosEventEmitter {
+    private _cbs: ((pos: Pos) => void)[] = [];
+    public onData(callback: (pos: Pos) => void): void {
+        this._cbs.push(callback);
+    }
+    public sendData(pos: Pos): void {
+        this._cbs.forEach((cb) => cb(pos));
+    }
 }
-const showMessageAndLeave = async (title: string, text: string) => {
-    await showModal({ title, text, willBackgroundCancel: false });
-    sessionStorage.clear();
-    const { protocol, host, pathname } = location;
-    const dir = dropFileExtension(pathname);
-    const newUrl = `${protocol}//${host}${dir}`
-    document.location = newUrl;
+
+export interface PeerGameSettings {
+    id?: string;
+    peerId?: string;
+    onError: (type: string, error: any) => void;
+    onSetup: (selfId: string) => void;
+    onStart: (selfId: string, peerId: string) => void;
+    onPeerMouseMove: (unitPos: Pos) => void;
+    mouseEventEmitter: MousePosEventEmitter;
 }
 
 export class PeerGame2 extends Game {
@@ -56,7 +50,7 @@ export class PeerGame2 extends Game {
     public selfId: string;
     public onMouseMoveCallback: (pos: Coord) => void = () => { };
     public peerMousePositionCallback: (pos: Coord) => void = () => { };
-    
+
     public constructor(player1Id: string, player1Name: string, player2Id: string, player2Name: string, selfId: string, conn: Peer.DataConnection) {
         super(player1Id, player1Name, player2Id, player2Name);
         this.selfId = selfId;
@@ -68,56 +62,35 @@ export class PeerGame2 extends Game {
         this.conn.send(JSON.stringify(peerData));
         return result;
     }
-    public static async createPeerGame(): Promise<PeerGame2> {
-        let { id, peerId } = getData();
-        const main = document.getElementsByTagName('main')[0];
+    public static async createPeerGame(settings: PeerGameSettings): Promise<PeerGame2> {
+        let id: string = settings.id ? settings.id : '';
+        let peerId: string = settings.peerId ? settings.peerId : '';
         let game: PeerGame2 | undefined = undefined;
 
         const handleConn = async (conn: Peer.DataConnection) => {
-            shareContainer.classList.add('invisible');
-
-            await new Promise<void>((resolve) => {
-                conn.on('open', () => resolve());
-            });
+            // wait for connection to 'open'
+            await new Promise<void>((resolve) => conn.on('open', resolve));
 
             id = peer.id;
             peerId = conn.peer;
-            setData({ id, peerId });
+            settings.onStart(id, peerId);
 
             const isLocalP1 = getFirstPlayerId(id, peerId) === id;
             game = isLocalP1 ?
                 new PeerGame2(id, "Local", peerId, "Remote", id, conn) :
                 new PeerGame2(peerId, "Remote", id, "Local", id, conn);
 
-
-            let lastTimerId: NodeJS.Timeout | undefined = undefined;
             conn.on('data', (data: string) => {
-                if (!game) return;
+                if (!game) {
+                    console.warn({ warning: 'Receiving data, but there is no game', data });
+                    return;
+                }
 
                 const peerData = JSON.parse(data) as PeerData;
                 const { mousePos, move } = peerData;
 
                 if (mousePos) {
-                    const isValidPos = mousePos.x !== -1 && mousePos.y !== -1;
-                    if (isValidPos) {
-                        const xRatio = mousePos.x;
-                        const yRatio = mousePos.y;
-                        const xTable = xRatio * table.clientWidth;
-                        const yTable = yRatio * table.clientHeight;
-                        const x = table.offsetLeft + xTable;
-                        const y = table.offsetTop + yTable;
-                        const style = `left:${x - otherMouseElement.clientWidth / 2}px;top:${y}px;display:block;`;
-                        otherMouseElement.setAttribute('style', style);
-                        if (lastTimerId !== undefined) {
-                            clearTimeout(lastTimerId);
-                        }
-                        lastTimerId = setTimeout(() => {
-                            otherMouseElement.setAttribute('style', '');
-                            lastTimerId = undefined;
-                        }, 3000);
-                    } else if (lastTimerId !== undefined) {
-                        otherMouseElement.setAttribute('style', '');
-                    }
+                    settings.onPeerMouseMove(mousePos);
                 }
 
                 const isWaitingForRemoteMove = peerId === game.currentPlayerId;
@@ -131,44 +104,34 @@ export class PeerGame2 extends Game {
                 }
             });
             conn.on('error', (err) => {
-                showMessageAndLeave(`Connection error`, err);
+                settings.onError(`Connection error`, err);
             });
             conn.on('close', () => {
-                showMessageAndLeave(`Connection closed`, 'Connection closed for some reason')
+                settings.onError(`Connection closed`, 'Connection closed for some reason')
+            });
+            settings.mouseEventEmitter.onData((pos) => {
+                const peerData: PeerData = { mousePos: pos };
+                conn.send(JSON.stringify(peerData));
             });
         };
 
-        const { host, protocol, pathname, search } = location;
-        const queryParams = new URLSearchParams(search);
-
         const peer = id === undefined ? new Peer(peerOptions) : new Peer(id, peerOptions);
-        window.onunload = () => peer.destroy();
         peer.on('connection', (conn) => handleConn(conn));
         peer.on('error', async (err) => {
-            await showMessageAndLeave('Peer error encountered', err);
+            settings.onError('Peer error encountered', err);
         });
         peer.on('open', (trueId) => {
             id = trueId;
-            peerId = queryParams.get('peerId') ?? peerId;
-            setData({ id, peerId });
+            settings.onSetup(id);
 
             if (peerId) {
                 console.log(`${id} connecting to peer "${peerId}"`);
                 const conn = peer.connect(peerId);
                 handleConn(conn);
-            } else {
-                queryParams.set('peerId', id);
-                const newSearch = queryParams.toString();
-                const newUrl = `${protocol}//${host}${pathname}?${newSearch}`;
-                shareLinkButton.onclick = () => {
-                    showSnackbar('Url copied to clipboard');
-                    copyToClipboard(newUrl);
-                };
-                shareContainer.classList.remove('invisible');
             }
         });
         peer.on('disconnected', async () => {
-            await showMessageAndLeave('Peer disconnected', 'For some reason')
+            settings.onError('Peer disconnected', 'For some reason')
         });
         while (game === undefined) {
             console.log('Waiting to connect to peer');
@@ -177,25 +140,3 @@ export class PeerGame2 extends Game {
         return game;
     }
 }
-
-const isBrowserSupported = () => {
-    const browser = detect();
-    switch (browser && browser.name) {
-        case 'chrome':
-        case 'firefox':
-            return true;
-        default:
-            return false;
-    }
-}
-export const playGame = async () => {
-    if (isBrowserSupported()) {
-        await PeerGame.createPeerGame();
-    } else {
-        showMessageAndLeave(
-            'Unsupported browser',
-            '<p>Your current browser is not yet supported.</p>' +
-            '<p>This PeerJs issue is tracking support in Microsoft Edge and Safari: </p>' +
-            '<p><a href="https://github.com/peers/peerjs/issues/899" target="_blank">https://github.com/peers/peerjs/issues/899</a></p>')
-    }
-};