The ReBel
The ReBel

Reputation: 61

Socket.io Operation timed out error when emitting an event to the room

I'm trying to create an asynchronous tic-tac-toe game. But whenever I try to emit an event "play_game" to both the client from the server, it throws an opearation timed out error.

Error: operation has timed out at Timeout._onTimeout (F:\tic-tac-toe-socketIO\server\node_modules\socket.io\dist\broadcast-operator.js:181:30)
at listOnTimeout (node:internal/timers:564:17) at process.processTimers (node:internal/timers:507:7)

I tried sending the same event multiple times with some delay when the error occurs but it was of no use.

I also tried increasing the timeout but it wasn't working either.

Server Code

const express = require("express");
const app = express();
const server = require("http").createServer(app);

io.on("connection", (socket) => {
  socket.on("join-room", (data) => {
    // checks if a room already exists and, if it does it joins the existing room
    // else it creates a new one
  });

  socket.on("game-start", (payload) => {
    // assigns Cross and Circle to players
    socket
      .to(payload.roomname)
      .emit("game-start", { piece: payload.piece, xIsNext: payload.xIsNext });
  });

  socket.on("getRoomMembers", (roomName) => {
    const room = io.sockets.adapter.rooms.get(roomName);
    // emits the members in the room 'roomName'
  });

  socket.on("username", (payload) => {
    io.to(payload.roomname).emit("username", { username: payload.username });
  });

  socket.on("move", (payload, callback) => { 
    const { squares, X, roomname } = payload;

    const retrySendMessage = (attemptCount) => {
        try {
          io
            .timeout(20000)
            .in(roomname)
            .emit(
              "play_game",
              { squares, X, statement: "emitter to other" },
              (err, response) => {
                console.log(err, "error");
                console.log(response);
                console.log("play_game event failed");
                const room = socket.adapter.rooms.get(roomname);

                if(err){
                  if (attemptCount < 5) {
                    retrySendMessage(attemptCount + 1); // Retry with an incremented attempt count
                    attemptCount++;
                  } else {
                    callback({
                      status: "not ok"
                    }); // Notify the client about the failure
                  }
                }
              }
            ); // Send the message to all clients in the room
          callback({
            status: "ok"
          }); // Acknowledge the successful message delivery
        } catch (error) {
          console.error("Error sending message:", error);
  
        }
    };

    try {
      if (socket.adapter.rooms.has(roomname)) {
        const room = socket.adapter.rooms.get(roomname);
        retrySendMessage(0);
      } else {
        console.log(`Error: Room "${roomname}" does not exist.`);
      }
    } catch (error) {
      console.log(error);
    }
  });

  socket.on("create-game", (payload) => {
    socket.join(payload.userEmail);
    socket.emit("create-confirmation", { success: true });
  });

  socket.on("disconnect", function () {
    console.log("SOCKET DISCONNECTING " + socket.id);
  });

  socket.on("disconnecting", (reason) => {
    console.log(socket.rooms); // Set { ... }
  });
});


server.listen(3000, () => console.log("server listening at port 3000"));
app.listen(4000, () => console.log("server listening at port 4000"));

Client Side

export default function Play() {
  const [history, setHistory] = useState(Array(9).fill(null));
  const [squares, setSquares] = useState(Array(9).fill(null));
  const [renderSquares, setRenderSquares] = useState(Array(9).fill(null));
  const [currentMove, setCurrentMove] = useState(0);
  const [piece, setPiece] = useState("X"); // X or O
  const [xIsNext, setXIsNext] = useState(true);
  const { LogOut, socket, userEmail, rivalEmail, opponent, secondPlayer } =
    useContext(FormContextProvider);

  const roomnameRef = useRef(secondPlayer ? rivalEmail : userEmail);

  // to receive the play_game event from the server
  socket.on("play_game", (payload) => {
    setXIsNext(payload.X);
    payload.squares ? setSquares(payload.squares) : null;
  });


  // when the game starts, assign the pieces to first and second players
  useEffect(() => {
    if (secondPlayer) {
      setPiece("O");
    } else {
      setPiece("X");
    }
  }, []);

  // render the <Cross /> and <Circle /> components on the 3X3 table
  useEffect(() => {
    console.log("squares", squares);
    const newSquares = squares.map((square) => {
      if (!square) return square;
      else if (square === "X") return <Cross />;
      else return <Circle />;
    });
    setRenderSquares(newSquares);
  }, [squares]);


  // triggers whenever you click on the button
  // sends an event "move" to the server 
  function handleClick(i) {
    try {
      console.log("handling");
      // socket.emit("getRoomMembers", roomnameRef.current);
      if (calculateWinner(squares) || squares[i]) {
        return;
      }
      const renderSquaresCopy = renderSquares.slice();
      const squaresCopy = squares.slice();
      if (xIsNext) {
        renderSquaresCopy[i] = <Cross />;
        squaresCopy[i] = "X";
      } else {
        squaresCopy[i] = "O";
        renderSquaresCopy[i] = <Circle />;
      }
      setSquares(squaresCopy);
      setXIsNext(!xIsNext);
      console.log("handling");
      console.log(roomnameRef.current.value)
      console.log(roomnameRef.current)
      const payload = {
        squares: squaresCopy,
        X: !xIsNext,
        piece,
        roomname: roomnameRef.current,
      };
      console.log(payload);
      socket.emit("move", payload, (err) => {
        console.log(err.status)
      });
    } catch (error) {
      console.log("error on axios", error);
    }
  }

  const winner = calculateWinner(squares);
  let status;
  if (winner) {
    status = "Winner: " + winner;
  } else {
    status = "Next player: " + (xIsNext ? "X" : "O");
  }

  function jumpTo(nextMove) {
    setCurrentMove(nextMove);
  }

  // function to calculate winner
  function calculateWinner(squares) {
  }

  // function to make a btn disabled
  const disabled = (index) => {
  };

  return (
    <form className="card flex flex-col text-left relative h-full w-full">
      <div>
        <div className="flex flex-col">
          <div className="flex justify-between">
            <button
              disabled={disabled(0)}
              onClick={(e) => {
                e.preventDefault();
                handleClick(0);
              }}
            >
              <Square>{renderSquares[0]}</Square>
            </button>
            <button>
              <Square>{renderSquares[1]}</Square>
            </button>
            <button>
              <Square>{renderSquares[2]}</Square>
            </button>
          </div>
          <div className="flex justify-between">
            <button>
              <Square>{renderSquares[3]}</Square>
            </button>
            <button>
              <Square>{renderSquares[4]}</Square>
            </button>
            <button>
              <Square>{renderSquares[5]}</Square>
            </button>
          </div>
          <div className="flex justify-between">
            <button>
              <Square>{renderSquares[6]}</Square>
            </button>
            <button>
              <Square>{renderSquares[7]}</Square>
            </button>
            <button>
              <Square>{renderSquares[8]}</Square>
            </button>
          </div>
        </div>
      </div>
      <a>
        {!((piece === "X" && !xIsNext) || (piece === "O" && xIsNext))
          ? "Submit!"
          : "Waiting for player..."}
      </a>
    </form>
  );
}

Whenever a button is click, handleClick() event gets triggered. It emits move event to the server.

On the server side, move event is caught and then emitted another event play_game to all the clients in the room 'roomname'

Upvotes: 0

Views: 508

Answers (1)

deepanshu
deepanshu

Reputation: 663

The error has occured because you haven't provided a callback to the on event named "play_game" on the client side.

So you have to replace that part of your code with this:

socket.on("play_game", (payload,callback) => {
    setXIsNext(payload.X);
    payload.squares ? setSquares(payload.squares) : null;
    callback({
        status: "ok"
      }); // Acknowledge the successful message delivery
  });

Upvotes: 0

Related Questions