All case studies
FullstackTech Lead, fullstack

ESports Turbo Scout

An automated match protocol for the ESports Turbo tournament operator: Dota 2 events travel from the game into a live table next to the broadcast, the judge watches score and timer in real time and exports the finished protocol to Excel.

Under NDA — happy to walk through the architecture and the code in a call.

ESports Turbo Scout

Outcome

2
tournament formats: Dota 2 5×5 Turbo and 1×1 Solo
2
independent data sources: GSI and the custom game mode
16
types of match event in the protocol

The business problem

The operator ran daily Dota 2 tournaments in two formats — 5×5 Turbo and 1×1 Solo — and kept the match protocol by hand: a judge watched the stream and typed in first blood, the races to 5, 10 and 15 kills, destroyed towers and barracks, Roshan, the Tormentor, mega creeps and runes. The numbers drifted away from what was on screen, and at the end of the day the protocol had to be handed to the organisers as a file. The product had to pull events out of the game itself, show them beside the broadcast player, and stay out of the tournaments’ way: streams ran from 07:00 to 16:00 seven days a week, and the release window only opened after the last match.

My role

Web tech lead: owned the architecture and the build end to end — the NestJS backend, the Next.js judge panel, the data model and the deployment. Agreed the event contract with the developer of the Lua custom game mode and ran the contractor working on the GSI parser: wrote the data model, reviewed the code, accepted the work. Scheduled releases around the broadcast calendar and kept two environments so changes could be checked on staging while production served a live tournament.

The solution

Data arrives from two independent sources. The first is Game State Integration: the observer’s client posts the map state to a server route and the backend parses it directly, without third-party wrappers. The second is the Lua custom game mode: it catches events inside the game and sends them one by one, with separate requests for events, score and timer. Both sources write into one log entity keyed by match id, so a single match is assembled from two streams without duplicates, and if one source drops out the protocol keeps running on the other. The NestJS backend with TypeORM and PostgreSQL detects the start and the end of a match from the game states, force-closes the previous match when a new one starts, and clears the log a minute after the win. The Next.js judge panel keeps the event table, the score cards, the timer and the broadcast player on one page; updates arrive over a websocket and the Excel export is built on the client. Access is behind authentication, with staging and production kept apart. Tournaments, matches and teams were migrated from the old admin panel into Strapi on top of MongoDB, and the gap between stream and protocol was closed by a local resender that holds GSI packets back by a set number of seconds so the table never runs ahead of the picture.

Stack

  • Next.js
  • TypeScript
  • NestJS
  • TypeORM
  • PostgreSQL
  • WebSocket
  • Dota 2 GSI
  • Strapi
  • MongoDB
  • Docker