Skip to main content

Create Your First Multiplayer Function

Let’s build a simple multiplayer cursor tracking game where players can see each other’s mouse positions in real-time.

Step 1: Create the Cloud Function

curl -X POST http://localhost:8000/projects/YOUR_PROJECT_ID/functions \
  -H "Content-Type: application/json" \
  -d '{
    "name": "cursor_game",
    "function_type": "websocket",
    "description": "Multiplayer cursor tracking game",
    "code": "YOUR_CODE_HERE"
  }'

Step 2: Define Event Handlers

Here’s the complete function code with all event handlers:
Python (Cloud Function)
async def on_connect():
    """Called when a player connects"""
    player_name = request.get('player_name', 'Guest')

    # Join a room (auto-created if doesn't exist)
    room.join(request.get('room_id', 'lobby'), max_players=10)

    # Initialize room state on first player
    if not room.state:
        room.state = {'players': {}}

    # Add player to state
    room.state['players'][session.player_id] = {
        'name': player_name,
        'x': 400,
        'y': 300,
        'color': '#ff6b6b'
    }

    # Broadcast join event to all players
    await room.broadcast({
        'type': 'player_joined',
        'player_id': session.player_id,
        'player_name': player_name
    })

    # Return welcome message to connecting player
    return {
        'type': 'welcome',
        'your_id': session.player_id,
        'players': room.state['players']
    }


async def on_message():
    """Called when a player sends a message"""
    action = request.get('action')

    if action == 'move':
        # Update player position
        x = request.get('x', 0)
        y = request.get('y', 0)

        if session.player_id in room.state['players']:
            room.state['players'][session.player_id]['x'] = x
            room.state['players'][session.player_id]['y'] = y

            # Broadcast movement to all other players
            await room.broadcast({
                'type': 'player_moved',
                'player_id': session.player_id,
                'x': x,
                'y': y
            }, exclude=[session.player_id])


async def on_disconnect():
    """Called when a player disconnects"""
    if session.player_id in room.state['players']:
        player_name = room.state['players'][session.player_id]['name']
        del room.state['players'][session.player_id]

        await room.broadcast({
            'type': 'player_left',
            'player_id': session.player_id,
            'player_name': player_name
        })

Step 3: Connect from Client

<!DOCTYPE html>
<html>
  <head>
    <title>Cursor Game</title>
    <style>
      body {
        margin: 0;
        overflow: hidden;
        background: #1a1a2e;
      }
      canvas {
        display: block;
      }
    </style>
  </head>
  <body>
    <canvas id="canvas"></canvas>

    <script>
      const canvas = document.getElementById("canvas");
      const ctx = canvas.getContext("2d");
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;

      // Connect to WebSocket
      const ws = new WebSocket(
        "wss://cloud.cocobase.buzz/ws/YOUR_PROJECT_ID/cursor_game"
      );

      let myId = null;
      let players = {};

      // Send initial connection message
      ws.onopen = () => {
        ws.send(
          JSON.stringify({
            room_id: "lobby",
            player_name: "Player" + Math.floor(Math.random() * 1000),
          })
        );
      };

      // Handle incoming messages
      ws.onmessage = (event) => {
        const data = JSON.parse(event.data);

        switch (data.type) {
          case "welcome":
            myId = data.your_id;
            players = data.players;
            break;

          case "player_joined":
            console.log(data.player_name + " joined!");
            break;

          case "player_moved":
            if (players[data.player_id]) {
              players[data.player_id].x = data.x;
              players[data.player_id].y = data.y;
            }
            break;

          case "player_left":
            delete players[data.player_id];
            console.log(data.player_name + " left");
            break;
        }
      };

      // Send cursor position on mouse move
      canvas.addEventListener("mousemove", (e) => {
        if (ws.readyState === WebSocket.OPEN && myId) {
          ws.send(
            JSON.stringify({
              action: "move",
              x: e.clientX,
              y: e.clientY,
            })
          );

          // Update local state immediately
          if (players[myId]) {
            players[myId].x = e.clientX;
            players[myId].y = e.clientY;
          }
        }
      });

      // Render loop
      function render() {
        ctx.fillStyle = "#1a1a2e";
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Draw all players
        for (const [id, player] of Object.entries(players)) {
          ctx.fillStyle = player.color || "#fff";
          ctx.beginPath();
          ctx.arc(player.x, player.y, 10, 0, Math.PI * 2);
          ctx.fill();

          // Draw name
          ctx.fillStyle = "#fff";
          ctx.font = "14px Arial";
          ctx.fillText(player.name, player.x + 15, player.y + 5);
        }

        requestAnimationFrame(render);
      }

      render();
    </script>
  </body>
</html>

Test It Out!

  1. Open the HTML file in multiple browser tabs
  2. Move your mouse - you should see cursors from other tabs
  3. Each player has a different color and name

Next Steps