RealTime 1v1 quiz
17-10-2023
About the project
In my recent hobby project, I've created a platform to enhance the Hebrew-English vocabulary. One of the methods that instantly came to me is a 1v1 quiz of which users should answer the correct translation out of different options.
The tools I used
The project is based on react + Next.js. in addition, I had to somehow maintain a real time connection between the two players to have a shared state.
I used Supabase Presence system for this.
Process
- Basically, this is how it works: when one player clicks on find a match, it sends a fetch request into a route handler to find a room in the database of which there is only one player which didn't start yet (keeping track of it using stage number field inside a room row). If it finds one, it adds the player to the database room field and redirects the user to the duel page.
- When 2 players joined up, the first player sends a fetch request to update the stage of the match to be 1 (which means game started).
- The game works in a cycle: before a new round is starting, the players are waiting for each other to answer the current round. When both players answered the round (there is also a timer countdown), the players checking each other, if one of players has a wrong answer, there is a state of winners array that being changed. (array because if it's a tie both of them will get some points). Players will also get a UI update of whom won.
- End the match: the host player (the one who entered the room first) will fetch a request with the winner's array. The route handler detects, if there are multiple winners, it will give both some points and if there is one winner, it will give him more points instead.
Problems I encountered with
- The main problem was the syncing and tracking over the current round and see if one player failed before moving to the next round, it took a lot of effort and adjustments to keep it sturdy. But I came up with the cycle system which works ok
- Detect a leaver
- Supabase has built in subscription leave trigger which can call a function, but it called it every state change because of how the system is designed, so I came up with other solution:
- I kept track of the amount of the shared state keys, and when its 1, it means one user lost connection. But I couldn't declare win instantly, because it might be because of latency issue. So instead, a timer is being created on the remaining user, followed up with cleanup effect if the key's length are changed while the timer is on. If the timer runs out, the remaining user declared as the winner.
- Cleaning empty rooms - when there is 1 user left, and he leaves, beforeUnload event is triggered and fetch a request to delete the room.
Conclusion
the system isn't perfect, it was a challenging experience, but it works most of the time and turned out pretty well.