Curated with β€οΈ by Okkar Min, Ying Sheng and Jiayin
We are going to build a platform
- That has a button
- That button moves to a random location when another player press the button on their side
- Keep count of the the players score
You will understand
- Client - Server architecture
- WebSocket concept
- How to build and manipulate a webpage using HTML + CSS + JavaScript
- How to build Python websocket server
- Client REQUEST from server (Client β‘οΈ Server)
- Server RESPONSE to client (Server β‘οΈ Client)
- Client make use of data from Server (Text, Image, Video etc...)
- Repeat 1 through 3
Server RESPONSE with data when Client REQUEST
You visit YouTube
- Your laptop (client) request to YouTube (server) asking for data
- YouTube (server) respond to your laptop with data
- Your laptop (client) shows you the home page of YouTube
- You click on a video, repeat 1 through 3
Image taken from StackOverflow
- Client REQUEST for handshake (Client β‘οΈ Server)
- Sever RESPONSE with handshake confirmation (Server β‘οΈ Client)
- Client - Server socket connection established (Client
βοΈ Server) - Connect is established till Client or Server close the connection
Handshake : just like the name suggests, handshake occurs when two entities are connected. In our case client and server are connected
Server push data to Client, without Client asking for it
You visit Facebook
- Your laptop (client) request to Facebook (server) asking for handshake
- Facebook (server) respond to your laptop with handshake confirmation
- Your laptop (client) and Facebook (server) WebSocket connection established
- You get a notification when somebody message you, new post etc...
Image taken from StackOverflow
- Your laptop make request to server for handshake
- Server respond with handshake confirmation
- Server push data to your client when:
- a new player join
- the button was clicked
- a player has left
- Your laptop process on data received from server
Button page requirements
-
A Button for players to click
- Button should move randomly on every player screen when one player has clicked
-
Leaderboard that show list of connected players
- current player should be at the top of the list
- should show player score
<!-- index.html --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>π€‘ Crazy Button Workshop</title> </head> <!-- loads styling library --> <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet" type="text/css" /> <!-- loads socket.io javascript library --> <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" crossorigin="anonymous" ></script> <!-- start of helper functions --> <script> function clearPlayerList() { document.getElementById("playerListDisplay").textContent = ""; } function drawPlayerList(playerListData) { for (sid in playerListData) { // extract playerID and playerScore let playerID = sid.slice(0, 4); //sid is too long, we take the first 4 characters let playerScore = playerListData[sid]; // get the place we want to put the textToDislay let playerListDisplay = document.getElementById("playerListDisplay"); // create a list element to insert textToDisplay let listElement = document.createElement("li"); // craft the text to display let textToDisplay; // it is you! if (socket.id == sid) { textToDisplay = playerID + " π€ͺ " + "[" + playerScore + "]"; listElement.appendChild(document.createTextNode(textToDisplay)); // insert text to display into list element playerListDisplay.prepend(listElement); // insert at top of the list } else { // it is not you! textToDisplay = playerID + "[" + playerScore + "]"; listElement.appendChild(document.createTextNode(textToDisplay)); // insert text to display into list element playerListDisplay.append(listElement); // insert anywhere } } } function reDrawPlayerList(playerListData) { clearPlayerList(); drawPlayerList(playerListData); } // given randomTop and randomLeft values, // move button to new location using random values function moveButtonToLocation(randomTop, randomLeft) { // to adapt button position to screen size; let maxHeight = window.innerHeight; let maxWidth = window.innerWidth; // x and y offset of button relative to the browser // screen size // randomTop and randomLeft are value between [0, 1] sent from server // e.g for laptop with 1200 x 800 resolution and 0.5 for randomTop and randomLeft // top_offset = 800 * 0.5 = 400 // left_offset = 1200 * 0.5 = 600 // e.g for mobile with 400 x 800 resolution and 0.5 for randomTop and randomLeft // top_offset = 400 * 0.5 = 400 // left_offset = 800 * 0.5 = 600 let top_offset = maxHeight * randomTop; let left_offset = maxWidth * randomLeft; // set new button location let button = document.getElementById("button"); button.style.top = top_offset + "px"; button.style.left = left_offset + "px"; } </script> <!-- end of helper functions --> <!-- start of socket logic --> <script> const socket = io.connect("http://localhost:8080"); // new player connected socket.on("player_connected", (data) => { reDrawPlayerList(data.playerList); }); // current player disconnected socket.on("player_disconnected", (data) => { reDrawPlayerList(data.playerList); }); // button was pressed and server emits new_button_location event socket.on("new_button_location", (data) => { moveButtonToLocation(data.randomTop, data.randomLeft); reDrawPlayerList(data.playerList); }); function handleButtonClick() { socket.emit("button_pressed"); } </script> <!-- end of socket logic --> <body> <div class="container"> <button id="button" class=" absolute flex-auto bg-red-500 hover:bg-red-400 text-white font-bold py-2 px-4 border-b-4 border-red-700 hover:border-red-500 rounded " onclick="handleButtonClick()" > Catch Me! π€‘ </button> <p>Connected players π</p> <ul id="playerListDisplay"></ul> </div> </body> </html>WebSocket server requirements
-
Should emit
player_connectedevent when a new player connect to the server- should sent the following data to client
- current connected
playerList
- current connected
- should sent the following data to client
-
Should emit
player_disconnectedevent when a player disconnect from the server (player close tab|browser)- should sent the following data to client
- current connected
playerList
- current connected
- should sent the following data to client
-
Should emit
new_button_locatonevent when a player has clicked the button-
should keep track of which player clicked the button, so that we can show the score
-
should calculate random values for the button to move
-
should sent the following data to client
- randomTop, randomLeft a number between 0 to 1 [0, 1]
- current connected
playerList
-
-
Current connected
playerListshould contain- playerID and
- playerScore
### server.py ### # import libraries that will help us with creating the server import socketio from aiohttp import web from random import uniform # create a Socket.IO server sio = socketio.AsyncServer(cors_allowed_origins='*') app = web.Application() sio.attach(app) # keep track of list of connected players # { player1_id: score, # player2_id: score, # player3_id: score, # ... # } playerList = {} # start of event handlers # a player has joined @sio.event async def connect(sid, _): # print when new player has connected print('new player : ', sid) # add player to playerList with initial score of 0 playerList[sid] = 0 # announce to everybody connected that a new player has connected # with payload of playerList await sio.emit('player_connected', {'playerList': playerList}) # a player has left @sio.event async def disconnect(sid): # remove player from playerList playerList.pop(sid) # announce to everybody connected that a player has disconnected # with payload of playerList await sio.emit('player_disconnected', {'playerList': playerList}) # a player has pressed the button @sio.event async def button_pressed(sid): # randomTop and randonLeft is value between [0, 1] randomTop = uniform(0, 1) randomLeft = uniform(0, 1) # increase player score by 1 playerList[sid] += 1 # announce to everybody connected that a player has pressed the button # with payload of randomTop, randomLeft to move button on all players screen # and playerList to update the score on the player screen await sio.emit( 'new_button_location', { 'randomTop': randomTop, 'randomLeft': randomLeft, 'playerList': playerList, }) # end of event handlers # start server if __name__ == '__main__': web.run_app(app)put
index.htmlandserver.pyin same folder
pip install python-socketio #see https://python-socketio.readthedocs.io/en/latest/server.htmlpip install aiohttp #see https://docs.aiohttp.org/en/stable/cd [to-directory-where-index.html-is-in] python server.py # or python3 server.pycd [to-directory-where-server.py-is-in] python3 -m http.server 80go to http://localhost:80 to see index.html, our Crazy Button in action
Open two or more browser windows to see the button moving in realtime and scores being updated






