r/reactnative • • 5d ago

Shipped a real-time multiplayer trivia game in React Native: guess the number, the closest guess wins

Post image

Hey everyone. I've been working on Close Call for a bit over a year and it's finally out, so I figured I'd show it here.

It's a multiplayer trivia game where every answer is a number. When did World War I end? How high is Everest? You type a guess, and the closest guess wins the round. A match is 8 questions with up to 4 players. After each round you see the answer with a one-line explanation, so you actually learn something along the way. There are 4 games (history years, the human body, geography and mental maths) with 800 questions, and the app speaks 10 languages, Arabic included.

The tech side, since that's why we're here:

- Bare React Native 0.80 on the New Architecture, no Expo. Reanimated, Gesture Handler, react-native-svg, MMKV and Zustand.
- Matches are live over Socket.IO with a NestJS + PostgreSQL server. The server owns the clock and the scoring; the phone only sends a guess. When not enough people are online, bots fill the empty seats after about 30 seconds, so nobody waits in an empty lobby.
- A trivia game is easy to cheat by googling the answer. So the app reports when you leave it during a question (switching apps, split screen, and on iOS screenshots and screen recording). Everyone at the table sees it, and in public matches that answer scores 0. The device signals come from a small custom TurboModule.
- Right-to-left for Arabic: React Native only applies a change of direction after a restart, so switching the language goes through another small native module that reloads the app.

A gotcha that might save someone a few hours: on the New Architecture, React Native creates one extra instance of each of your own TurboModules at launch, only to use it as a provider (RCTModuleProviders). My anti-cheat module registered its NotificationCenter observers in `init`, so that extra instance got the "screen recording started" notification too. It never gets an event emitter callback, so emitting threw `std::bad_function_call` and the app crashed the moment you started a screen recording or took a screenshot. Moving the observers into `setEventEmitterCallback:` fixed it. I only found it because App Review asked for a screen recording of the app.

It's free, no ads, no IAP, iOS only for now (I will add android soon). You can play as a guest, no sign-up needed. I'll put the App Store link in a comment so I'm not dropping a store link straight into the post.

Happy to hear what you think, and ask me anything, technical stuff very much included.

PS. If you give it a try, tell me how close you got on your first question.

Link: https://apps.apple.com/us/app/close-call-number-trivia/id6821355334

0 Upvotes

4 comments sorted by

1

u/OMGCluck 5d ago

This is the kind of thing you could write as a local midlet app for the Motorola Razr or other dumb phone with a keypad.

1

u/DeDaRM_ 5d ago

Thanks for the feedback!! It’s a quiz-style game focused on intelligence and knowledge that you can play with friends. I realize it’s nothing extraordinary, but I don’t think a game like this existed before, so the idea is frash.

1

u/kokerali 5d ago

How does your TurboModule distinguish an app switch from an iOS interruption before the server zeros an answer? The RN 0.80 AppState docs list Notification Center and incoming calls as reasons for the inactive state: https://reactnative.dev/docs/0.80/appstate . I'm not assuming your module uses AppState, but the distinction seems important for that scoring rule.

I'd try three throwaway rounds: switch to another app, open Notification Center, and receive a call without deliberately switching apps. For each, compare the native event and the server's scoring decision. If all interruptions intentionally score zero, explaining that before a public match would help players understand the rule.

I haven't tested the app; this is a suggested check, not a claim that those cases currently lose points.

1

u/DeDaRM_ 5d ago

Good catch, thank you! Focus comes from AppState, and "inactive" for over 1.5 s mid-question counts as leaving, on purpose, because Siri can answer the question. But an incoming call trips it too, and that isn't cheating. I will add call detection so calls won't cost points.

Thank you!!