Reputation: 61
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
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