fuzzyworld

dao-game / commit

Multiplayer mostly working.

9e157243134fa7f981d7abfff440c59840666fe9 · Pavel Safronov · 2022-03-20 17:03:18 -0700 · parents 47dfe48abb60

Multiplayer mostly working.
 public/index.html                |  24 ++++++---
 public/online.html               |   2 +-
 public/styles.css                | 102 +--------------------------------------
 readme.md                        |   4 +-
 src/local-game.ts                |  16 +++---
 src/remote-game.ts               |  22 +++++++--
 tools/esbuild-build-and-serve.ts |  65 +++++++++++++------------
 7 files changed, 81 insertions(+), 154 deletions(-)

diff --git a/public/index.html b/public/index.html
index dc8de5a..14baf73 100644
--- a/public/index.html
+++ b/public/index.html
@@ -11,11 +11,14 @@
     </head>
 
     <body>
-        <div class="invisible">
+        <div id="placeholders" class="invisible">
             <p class="piece p1-piece" id="default-p1-piece"></p>
             <p class="piece p2-piece" id="default-p2-piece"></p>
+            <div id="other-mouse">
+                <img src="./cursor.png" alt="cursor" />
+            </div>
         </div>
-        <main>
+        <main class="invisible">
             <div class="controls">
                 <span class="p1-turn-text"> Red's turn </span>
                 <span class="divider">|</span>
@@ -34,6 +37,7 @@
                 </button>
             </div>
         </main>
+
         <hr />
 
         <div class="title">
@@ -47,7 +51,8 @@
             The game consists of only one simple rule and three ways to win.
             Pieces move in a straight line, including diagonally, as far as
             possible with no jumps. The three ways to win are arranging your
-            pieces in a straight line on the board (not diagonally), occupying
+            pieces in a straight line on the board (not diagonally),
+            occupying
             the four corners, or forming a 2x2 square anywhere on the board.
         </div>
         <hr />
@@ -55,15 +60,18 @@
         <div class="info">
             <p>
                 Here is the
-                <a href="https://gitlab.com/fuzzy-rp/dao-game" target="_blank"
+                <a href="https://gitlab.com/fuzzy-rp/dao-game"
+                    target="_blank"
                     rel="noopener">source code</a>
             </p>
         </div>
 
-        <div id="other-mouse">
-            <img src="./cursor.png" alt="cursor" />
-        </div>
-
+        <script type="module">
+            const bundle = import('./bundle.js');
+            debugger;
+            console.log('Hi!');
+            bundle.play();
+        </script>
     </body>
 
 </html>
\ No newline at end of file
diff --git a/public/online.html b/public/online.html
index 5c4d114..ad554d7 100644
--- a/public/online.html
+++ b/public/online.html
@@ -12,7 +12,7 @@
         <script defer src="bundle.js"></script>
     </head>
 
-    <body style="display: none;">
+    <body>
         <div class="invisible">
             <p class="piece p1-piece" id="default-p1-piece"></p>
             <p class="piece p2-piece" id="default-p2-piece"></p>
diff --git a/public/styles.css b/public/styles.css
index 6f9a281..12f0275 100644
--- a/public/styles.css
+++ b/public/styles.css
@@ -11,7 +11,7 @@
 }
 
 main {
-  display: flex;
+  /* display: flex; */
   flex-direction: column;
   justify-content: space-between;
   align-items: center;
@@ -27,19 +27,16 @@ body {
 div {
   display: flex;
   justify-content: space-between;
-  /* display: inline;
-    flex: 1; */
 }
 
 #other-mouse {
   position: absolute;
-  /* top: -16px;
-  left: -32px; */
   z-index: 1000;
   pointer-events: none;
 }
 
 .invisible {
+  /* visibility: hidden; */
   display: none;
 }
 
@@ -78,7 +75,6 @@ td {
   text-align: center;
   color: lightgray;
   background-color: white;
-  /* border: 1px solid black; */
   border-radius: 10px;
   padding: 10px;
   -ms-user-select: none;
@@ -204,9 +200,6 @@ td {
   }
 }
 
-/* .pulsing {
-    animation: pulsingSize 1.5s infinite;
-} */
 @keyframes activePulsing {
   from {
     width: var(--active-size);
@@ -221,94 +214,3 @@ td {
     height: var(--active-size);
   }
 }
-
-/* breaks */
-
-/* @media screen and (max-width: 560px) {
-    .red-piece {
-        height: 4.5vw;
-        width: 4.5vw;
-    }
-
-    .black-piece {
-        height: 4.5vw;
-        width: 4.5vw;
-    }
-
-    td {
-        width: 12vw;
-        height: 12vw;
-    }
-
-    .red-turn-text {
-        font-size: 40px;
-    }
-    
-    .black-turn-text {
-        font-size: 40px;
-    }
-
-    table {
-        margin: 0;
-    }
-
-    .mobile {
-        display: block;
-    }
-
-    .desktop {
-        display: none;
-    }
-
-    .red-turn-text {
-        -webkit-transform:rotate(-180deg);
-        -moz-transform:rotate(-180deg);
-        -o-transform:rotate(-180deg);
-        transform:rotate(-180deg);
-        filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);    
-    }
-}
-
-@media screen and (max-width: 380px) {
-    .red-piece {
-        height: 3vw;
-        width: 3vw;
-    }
-
-    .black-piece {
-        height: 3vw;
-        width: 3vw;
-    }
-}
-
-@media screen and (max-width: 345px) {
-    .red-piece {
-        height: 2.5vw;
-        width: 2.5vw;
-    }
-
-    .black-piece {
-        height: 2.5vw;
-        width: 2.5vw;
-    }
-}
-
-@media screen and (max-width: 300px) {
-    .red-piece {
-        height: 1.5vw;
-        width: 1.5vw;
-    }
-
-    .black-piece {
-        height: 1.5vw;
-        width: 1.5vw;
-    }
-
-    .red-turn-text {
-        font-size: 35px;
-    }
-    
-    .black-turn-text {
-        font-size: 35px;
-    }
-} */
diff --git a/readme.md b/readme.md
index 46f2a15..1646fb8 100644
--- a/readme.md
+++ b/readme.md
@@ -20,4 +20,6 @@ This project is using [esbuild](https://esbuild.github.io/getting-started/#build
 ## Todos
 
 1. Online multiplayer
-2. There's a bug where opening a new tab results in the same session data, look into this and figure out why and how to prevent this
+2. Figure out way to have different pages access different JS
+3. There's a bug where opening a new tab results in the same session data, look into this and figure out why and how to prevent this
+4. Publish versions of all branches
diff --git a/src/local-game.ts b/src/local-game.ts
index cfee804..396921d 100644
--- a/src/local-game.ts
+++ b/src/local-game.ts
@@ -14,15 +14,15 @@ export class LocalGame extends Game {
   listenerManager: ListenerManager = new ListenerManager();
 
   // DOM references
-  private readonly dom = {
+  readonly dom = {
     defaultP1Piece: document.getElementById('default-p1-piece'),
     defaultP2Piece: document.getElementById('default-p2-piece'),
     resetButton: document.getElementById('reset-button') as HTMLInputElement,
     undoButton: document.getElementById('undo-button') as HTMLInputElement,
     cells: () => Array.from(document.querySelectorAll('td')),
     allPieces: () => Array.from(document.querySelectorAll('.piece')),
-    p1TurnText: () => Array.from(document.querySelectorAll('.p1-turn-text')),
-    p2TurnText: () => Array.from(document.querySelectorAll('.p2-turn-text')),
+    p1TurnText: () => Array.from(document.querySelectorAll('.p1-turn-text'))[0],
+    p2TurnText: () => Array.from(document.querySelectorAll('.p2-turn-text'))[0],
   };
 
   private getPiecesAndCoords(playerId: string) {
@@ -110,14 +110,14 @@ export class LocalGame extends Game {
     const { dom, currentPlayerId, player1Id, canUndoMove, moveHistory, listenerManager, selectedPiece } = this;
 
     // reset text
-    dom.p1TurnText().forEach((t) => t.classList.remove(classNames.selectedPlayerText));
-    dom.p2TurnText().forEach((t) => t.classList.remove(classNames.selectedPlayerText));
+    dom.p1TurnText().classList.remove(classNames.selectedPlayerText);
+    dom.p2TurnText().classList.remove(classNames.selectedPlayerText);
 
     const isP1Turn = currentPlayerId === player1Id;
     if (isP1Turn) {
-      dom.p1TurnText().forEach((t) => t.classList.add(classNames.selectedPlayerText));
+      dom.p1TurnText().classList.add(classNames.selectedPlayerText);
     } else {
-      dom.p2TurnText().forEach((t) => t.classList.add(classNames.selectedPlayerText));
+      dom.p2TurnText().classList.add(classNames.selectedPlayerText);
     }
 
     dom.undoButton.disabled = canUndoMove;
@@ -231,7 +231,7 @@ export class LocalGame extends Game {
 
 // start!
 export const playGame = () => {
-  const lg = new LocalGame('Red', 'Blue', 'Red');
+  const lg = new LocalGame('Red', 'Blue');
   lg.resetUi();
   lg.setActiveUi();
 };
diff --git a/src/remote-game.ts b/src/remote-game.ts
index 2923ead..5f43230 100644
--- a/src/remote-game.ts
+++ b/src/remote-game.ts
@@ -16,6 +16,7 @@ interface PeerData {
   mousePos?: Pos;
   move?: Move;
 }
+const peerOptions: PeerJSOption = { debug: 4 };
 const { getter: getData, setter: setData } = useSessionStorage<LocalData>('dao-game', { id: undefined, peerId: undefined });
 const otherMouseElement = document.getElementById('other-mouse');
 const openInNewTab = (url: string) => {
@@ -52,9 +53,16 @@ export class PeerGame extends LocalGame {
     super(player1Id, player2Id);
     this.selfId = selfId;
     this.conn = conn;
+
+    const { dom } = this;
+    const isLocalP1 = player1Id === selfId;
+    (dom.p1TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Local turn` : `Remote turn`;
+    (dom.p2TurnText() as HTMLSpanElement).innerText = isLocalP1 ? `Remote turn` : `Local turn`;
   }
   public override setActiveUi(): void {
-    const isLocalTurn = this.currentPlayerId === this.selfId;
+    const { currentPlayerId, selfId } = this;
+
+    const isLocalTurn = currentPlayerId === selfId;
     if (isLocalTurn) {
       super.setActiveUi();
     }
@@ -68,18 +76,24 @@ export class PeerGame extends LocalGame {
 
   public static async createPeerGame(): Promise<PeerGame> {
     let { id, peerId } = getData();
-    const main = document.getElementsByTagName('body')[0];
+    const main = document.getElementsByTagName('main')[0];
     let game: PeerGame = undefined;
 
     const handleConn = (conn: Peer.DataConnection) => {
+      if (game !== undefined) {
+        conn.close();
+        return;
+      }
+
       id = peer.id;
       peerId = conn.peer;
       setData({ id, peerId });
 
       const isLocalP1 = getFirstPlayerId(id, peerId) === id;
-      const game = isLocalP1 ? new PeerGame(id, peerId, id, conn) : new PeerGame(peerId, id, id, conn);
+      game = isLocalP1 ? new PeerGame(id, peerId, id, conn) : new PeerGame(peerId, id, id, conn);
       game.resetUi();
       game.setActiveUi();
+      main.classList.remove('invisible');
 
       main.addEventListener('mousemove', (ev: MouseEvent) => {
         const peerData: PeerData = { mousePos: { x: ev.x, y: ev.y } };
@@ -110,7 +124,6 @@ export class PeerGame extends LocalGame {
     const { host, protocol, pathname, search } = location;
     const queryParams = new URLSearchParams(search);
 
-    const peerOptions: PeerJSOption = { debug: 0 };
     const peer = id === undefined ? new Peer(peerOptions) : new Peer(id, peerOptions);
     window.onunload = () => peer.destroy();
     peer.on('connection', (conn) => handleConn(conn));
@@ -133,6 +146,7 @@ export class PeerGame extends LocalGame {
     });
 
     while (game === undefined) {
+      console.log('Waiting to connect to peer');
       await delay(500);
     }
     return game;
diff --git a/tools/esbuild-build-and-serve.ts b/tools/esbuild-build-and-serve.ts
index 1f39f91..4632dc5 100644
--- a/tools/esbuild-build-and-serve.ts
+++ b/tools/esbuild-build-and-serve.ts
@@ -5,40 +5,41 @@ import http from 'http';
 const hostPort = 3000;
 
 const serveOptions: esbuild.ServeOptions = {
-    servedir: 'public'
+  servedir: 'public',
 };
 const buildOptions: esbuild.BuildOptions = {
-    entryPoints: ['src/app.ts'],
-    bundle: true,
-    outfile: 'public/bundle.js',
-    sourcemap: true
+  entryPoints: ['src/app.ts'],
+  bundle: true,
+  outfile: 'public/bundle.js',
+  sourcemap: true,
 };
-esbuild.serve(serveOptions, buildOptions)
-    .then(result => {
-        const { host, port } = result;
-        http
-            .createServer((req, res) => {
-                const options: http.RequestOptions = {
-                    hostname: host,
-                    port: port,
-                    path: req.url,
-                    method: req.method,
-                    headers: req.headers
-                };
-                const proxyReq = http.request(options, proxyRes => {
-                    if (proxyRes.statusCode === 404) {
-                        res.writeHead(404, { 'Content-Type': 'text/html' });
-                        res.end('<h1>A custom 404 page</h1>');
-                        return;
-                    }
+esbuild.serve(serveOptions, buildOptions).then((result) => {
+  const { host, port } = result;
+  http
+    .createServer((req, res) => {
+      const options: http.RequestOptions = {
+        hostname: host,
+        port: port,
+        path: req.url,
+        method: req.method,
+        headers: req.headers,
+      };
+      const proxyReq = http.request(options, (proxyRes) => {
+        if (proxyRes.statusCode === 404) {
+          res.writeHead(404, { 'Content-Type': 'text/html' });
+          res.end('<h1>A custom 404 page</h1>');
+          return;
+        }
 
-                    res.writeHead(proxyRes.statusCode!, proxyRes.headers);
-                    proxyRes.pipe(res, { end: true });
-                });
+        res.writeHead(proxyRes.statusCode!, proxyRes.headers);
+        proxyRes.pipe(res, { end: true });
+      });
 
-                req.pipe(proxyReq, { end: true });
-            })
-            .listen(hostPort, () => {
-                childProcess.exec(`start http://localhost:${hostPort}/`);
-            });
-    });
\ No newline at end of file
+      req.pipe(proxyReq, { end: true });
+    })
+    .listen(hostPort, () => {
+      const url = `http://localhost:${hostPort}/`;
+      // childProcess.exec(`start ${url}`);
+      console.log(`Server serving at ${url}`);
+    });
+});