Skip to content

add multiplayer and playing against bots#50

Open
aaravmaloo wants to merge 33 commits into
vyrx-dev:toofan-onlinefrom
aaravmaloo:master
Open

add multiplayer and playing against bots#50
aaravmaloo wants to merge 33 commits into
vyrx-dev:toofan-onlinefrom
aaravmaloo:master

Conversation

@aaravmaloo

@aaravmaloo aaravmaloo commented May 5, 2026

Copy link
Copy Markdown
Collaborator

Competitive Racing (Multiplayer and AI Bots)

This PR implements the complete competitive racing framework for Toofan, including a dedicated multiplayer server and an AI-driven bot system. These features transition the game from a solo typing tool to a real-time competitive platform.

Core Features

1. Multiplayer Racing System

Developed a high-performance, independent race server to handle real-time multiplayer sessions.

  • Architecture: A centralized Hub/Room management system built from scratch.
  • Protocol: Real-time communication via Server-Sent Events (SSE) for low-latency progress broadcasting.
  • Session Management: Implemented heartbeats and "kick-on-join" logic to prevent ghost sessions and ensure immediate reconnection stability.
  • Lobby System: Support for configurable room sizes and automatic matchmaking.

2. AI Ghost Racers

Introduced a bot system to provide a competitive experience even in offline mode.

  • Personalized Difficulty: Bots can automatically scale their speed based on the user's last 10 tests (Personalized Strategy) or fixed levels (Easy, Medium, Hard).
  • Realistic Movement: Implemented "jitter" and varying WPM profiles for bots to simulate human-like typing patterns.
  • Ghost Cursors: Visual parity with multiplayer sessions, showing real-time rankings and progress bars for all bot participants.

3. TUI Integration

Updated the Bubble Tea model and view dispatch logic to handle complex race states.

  • Race View: New real-time racing UI featuring live leaderboards and multi-track progress bars.
  • Results Screen: Detailed post-race breakdown showing placements, rank, and WPM for all participants.
  • Centralized Content: Integrated the race server with the project's internal wordlist logic, ensuring consistent test material between local and online modes.

Technical Improvements

  • Stability: Fixed critical double-unlock race conditions in the server's room logic.
  • Cleanup: Robust session termination using deferred cleanup and connection monitoring.
  • Independence: The race server is a standalone module with its own embedded assets, requiring zero external dependencies for deployment.

Verification

  • Verified multiplayer connectivity across multiple terminals using localhost and public IP addresses.
  • Validated bot scaling logic across various user skill levels.
  • Tested server resilience by simulating sudden client disconnects and rapid reconnects.

Deployment Status

  • The server-side code has been successfully deployed on my server, to save deployment headache and server costs. The server has a 99.99% uptime and is running continuously.
  • The current deployment on the server can support up to 50 players simultaneously.
  • The deployed server-code listens on the port 8525 and can fully run independently without any extra toofan code.
  • The port 8525 on the deployed server was exposed via cloudflare. The base url for the multiplayer server of Toofan is: http://toofan-race.pikapp.in
  • The tunnel is exposed via cloudflare tunnels, by the domain I own.

aaravmaloo added 11 commits May 6, 2026 03:30
the `main.go` is the actual path manager (somewhat of a API), protocol stores all the `structs` required and `room.go` keeps tracks of players and manages the core functionality of multiplayer
handles connections and rooms, while room.go handles the actual gameplay inside those rooms.
`usrname` str correctly identifies each player uniquely. serverURL needs to be stored for connections to the server while playing multiplayer
@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

Attach a video with it please

@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

I tried it locally and it looks so cool 🔥

@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

multiplayer mode is not working... have you tested it on your machine?

@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

I’ve created a toofan-online branch and updated the README with your changes. Make sure you create the PR against that branch only.

@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner
image

what is bot percentage here means? accuracy, completion or anything else?

@vyrx-dev vyrx-dev assigned vyrx-dev, aaravmaloo and Copilot and unassigned vyrx-dev May 6, 2026
@aaravmaloo

Copy link
Copy Markdown
Collaborator Author

So basically multiplayer won't work if your joining two sessions frkm the same device.
To test multiplayer, you need separate devices. I made it so because we don't have the same person playing as 2 or more players.

@aaravmaloo aaravmaloo changed the base branch from master to toofan-online May 6, 2026 14:10
@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

from the same device.

what if two different people using same wifi network?

@vyrx-dev

vyrx-dev commented May 6, 2026

Copy link
Copy Markdown
Owner

Btw, for letter tracking, which do you think looks cleaner: caret tracking with a different color or the current look?

aaravmaloo added 5 commits May 6, 2026 21:03
switched to cloudflare tunnel, since safer, and faster
big changes made.
- now there are rooms
- rooms can be public or private.
- private rooms require a code to join
- the host can configure settings for the room
these changes were made by the feedback of @vyrx-dev
aaravmaloo added 5 commits May 7, 2026 20:04
- after multiplayer was over, toofan would just freeze. the fix involved in the server responding with a status of over and then the client displaying the correct results.
- added bot-like gameplay, with ghost cursors and colored list while playing multiplyer
@vyrx-dev

vyrx-dev commented May 7, 2026

Copy link
Copy Markdown
Owner

what's the status?

@aaravmaloo

Copy link
Copy Markdown
Collaborator Author

I think its ready for merging. you can check the code and it was testing locally. 👍

@aaravmaloo aaravmaloo added feature add New feature or request in-review tag indicating the PR is in review labels May 7, 2026
@programmersd21

Copy link
Copy Markdown

@vyrx-dev, I tried it out too, it's awesome af! pls merge it, would love it to be in upstream! ❤️🔥

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces competitive racing to Toofan by adding (1) a standalone SSE-based multiplayer race server, and (2) local AI bot racers, plus the TUI/client-side wiring to render races (ghost cursors, race bars, lobby/results views) and persist multiplayer settings.

Changes:

  • Added a standalone race_server/ Go module implementing rooms, lobby/countdown/race state, and an SSE join endpoint with HTTP progress updates.
  • Added a local AI bot system and race UI elements (bot picker, ghost cursors, race bars, bot results).
  • Added an in-app multiplayer flow (room creation/joining/quick-match, lobby UI, progress reporting, results) plus config persistence for username/server URL.

Reviewed changes

Copilot reviewed 90 out of 90 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
README.md Adds FAQ entries for online/bots and a contributors section.
race_server/go.mod Defines standalone race server module.
race_server/main.go Adds HTTP endpoints for join/progress/start/online/health.
race_server/hub.go Implements hub + SSE join handler + matchmaking logic.
race_server/protocol.go Defines JSON protocol types and message wrapper.
race_server/room.go Implements room state, countdown/start, progress + finish broadcasting.
race_server/words.go Embeds and loads word/snippet content; generates race text.
race_server/data/english/easy.txt Adds embedded easy English word list for races.
race_server/data/english/medium.txt Adds embedded medium English word list for races.
race_server/data/english/hard.txt Adds embedded hard English word list for races.
race_server/data/go/00_basics.go Adds Go snippet content for code-mode races.
race_server/data/go/01_http_server.go Adds Go snippet content for code-mode races.
race_server/data/go/02_concurrency.go Adds Go snippet content for code-mode races.
race_server/data/go/03_error_handling.go Adds Go snippet content for code-mode races.
race_server/data/go/04_interfaces.go Adds Go snippet content for code-mode races.
race_server/data/go/05_maps_slices.go Adds Go snippet content for code-mode races.
race_server/data/go/06_structs.go Adds Go snippet content for code-mode races.
race_server/data/go/07_closures.go Adds Go snippet content for code-mode races.
race_server/data/go/08_json.go Adds Go snippet content for code-mode races.
race_server/data/go/lessons/basics.go Adds Go lesson snippet content for code-mode races.
race_server/data/javascript/00_variables.js Adds JS snippet content for code-mode races.
race_server/data/javascript/01_fetch_api.js Adds JS snippet content for code-mode races.
race_server/data/javascript/02_array_methods.js Adds JS snippet content for code-mode races.
race_server/data/javascript/03_dom_selection.js Adds JS snippet content for code-mode races.
race_server/data/javascript/04_promises.js Adds JS snippet content for code-mode races.
race_server/data/javascript/05_classes.js Adds JS snippet content for code-mode races.
race_server/data/javascript/06_destructuring.js Adds JS snippet content for code-mode races.
race_server/data/javascript/07_arrow_closures.js Adds JS snippet content for code-mode races.
race_server/data/javascript/08_events.js Adds JS snippet content for code-mode races.
race_server/data/javascript/09_error_handling.js Adds JS snippet content for code-mode races.
race_server/data/javascript/10_storage.js Adds JS snippet content for code-mode races.
race_server/data/typescript/01_union_intersection.ts Adds TS snippet content for code-mode races.
race_server/data/typescript/02_generics_constraints.ts Adds TS snippet content for code-mode races.
race_server/data/typescript/03_mapped_conditional.ts Adds TS snippet content for code-mode races.
race_server/data/typescript/04_template_literal.ts Adds TS snippet content for code-mode races.
race_server/data/typescript/05_async_error_handling.ts Adds TS snippet content for code-mode races.
race_server/data/python/01_file_handling.py Adds Python snippet content for code-mode races.
race_server/data/python/02_requests_api.py Adds Python snippet content for code-mode races.
race_server/data/python/03_list_dict_comprehension.py Adds Python snippet content for code-mode races.
race_server/data/python/04_async_io.py Adds Python snippet content for code-mode races.
race_server/data/python/05_data_processing.py Adds Python snippet content for code-mode races.
race_server/data/rust/01_divide.rs Adds Rust snippet content for code-mode races.
race_server/data/rust/02_struct.rs Adds Rust snippet content for code-mode races.
race_server/data/rust/03_vectors.rs Adds Rust snippet content for code-mode races.
race_server/data/rust/04_files.rs Adds Rust snippet content for code-mode races.
race_server/data/rust/05_closures.rs Adds Rust snippet content for code-mode races.
race_server/data/java/01_stream_pipeline.java Adds Java snippet content for code-mode races.
race_server/data/java/02_optional_patterns.java Adds Java snippet content for code-mode races.
race_server/data/java/03_completable_future.java Adds Java snippet content for code-mode races.
race_server/data/java/04_record_pattern.java Adds Java snippet content for code-mode races.
race_server/data/java/05_concurrent_collections.java Adds Java snippet content for code-mode races.
race_server/data/c/01_file_io.c Adds C snippet content for code-mode races.
race_server/data/c/02_pointer_usage.c Adds C snippet content for code-mode races.
race_server/data/c/03_dynamic_memory.c Adds C snippet content for code-mode races.
race_server/data/c/04_struct_patterns.c Adds C snippet content for code-mode races.
race_server/data/c/05_standard_library.c Adds C snippet content for code-mode races.
race_server/data/cpp/01_file_io.cpp Adds C++ snippet content for code-mode races.
race_server/data/cpp/02_vector_patterns.cpp Adds C++ snippet content for code-mode races.
race_server/data/cpp/03_async_thread.cpp Adds C++ snippet content for code-mode races.
race_server/data/cpp/04_modern_features.cpp Adds C++ snippet content for code-mode races.
race_server/data/cpp/05_stl_algorithms.cpp Adds C++ snippet content for code-mode races.
race_server/data/csharp/01_linq_queries.cs Adds C# snippet content for code-mode races.
race_server/data/csharp/02_async_await.cs Adds C# snippet content for code-mode races.
race_server/data/csharp/03_file_handling.cs Adds C# snippet content for code-mode races.
race_server/data/csharp/04_dependency_injection.cs Adds C# snippet content for code-mode races.
race_server/data/csharp/05_collections.cs Adds C# snippet content for code-mode races.
race_server/data/dart/00_classes.dart Adds Dart snippet content for code-mode races.
race_server/data/dart/01_async.dart Adds Dart snippet content for code-mode races.
race_server/data/dart/02_collections.dart Adds Dart snippet content for code-mode races.
race_server/data/dart/03_mixins.dart Adds Dart snippet content for code-mode races.
race_server/data/lua/00_tables.lua Adds Lua snippet content for code-mode races.
race_server/data/lua/01_metatables.lua Adds Lua snippet content for code-mode races.
race_server/data/lua/02_file_io.lua Adds Lua snippet content for code-mode races.
race_server/data/lua/03_coroutines.lua Adds Lua snippet content for code-mode races.
race_server/data/shell/00_boilerplate.sh Adds shell snippet content for code-mode races.
race_server/data/shell/01_conditionals.sh Adds shell snippet content for code-mode races.
race_server/data/shell/02_loops.sh Adds shell snippet content for code-mode races.
race_server/data/shell/03_docker_git.sh Adds shell snippet content for code-mode races.
race_server/data/shell/04_text.sh Adds shell snippet content for code-mode races.
internal/game/bot.go Adds local bot generation + ticking + placement logic.
internal/game/client.go Adds multiplayer SSE client + HTTP progress/start calls.
internal/game/storage.go Persists multiplayer username/server URL in config.
internal/tui/model.go Adds state + update loop wiring for bots and multiplayer sessions.
internal/tui/typing.go Adds ghost cursors + race bars + keybinds for bots/multiplayer.
internal/tui/text.go Extends text rendering to support ghost cursor styling.
internal/tui/race.go Adds bot picker UI + bot race bar/results rendering.
internal/tui/online.go Adds multiplayer room flow (create/join/quick match) + lobby/results UI.
internal/tui/results.go Adds bot/multiplayer results integration + reset/disconnect logic.
internal/tui/picker.go Updates theme picker to match expanded config format.
internal/tui/profile.go Tweaks PB heading label in profile view.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/game/client.go
Comment on lines +161 to +183
func (c *RaceClient) SendProgress(progress float64, wpm float64) error {
c.mu.Lock()
if c.closed {
c.mu.Unlock()
return nil
}
c.mu.Unlock()

body := map[string]interface{}{
"name": c.name,
"room": c.room,
"progress": progress,
"wpm": wpm,
}
data, _ := json.Marshal(body)

resp, err := c.client.Post(c.serverURL+"/race/progress", "application/json", bytes.NewReader(data))
if err != nil {
return err
}
resp.Body.Close()
return nil
}
Comment thread internal/tui/typing.go
Comment on lines +259 to +266
// update local player's progress for immediate feedback
for i := range m.racePlayers {
if m.racePlayers[i].IsUser {
m.racePlayers[i].Progress = userProg
break
}
}
out = append(out, "", viewOnlineRaceBar(p, m.racePlayers, textWidth-4))
Comment thread internal/tui/online.go
Comment on lines +328 to +332
case "joined":
var payload game.LobbyPayload
if err := json.Unmarshal(msg.Payload, &payload); err != nil {
return m, nil
}
Comment thread internal/tui/online.go
Comment on lines +382 to +386
case "start":
var payload game.StartPayload
if err := json.Unmarshal(msg.Payload, &payload); err != nil {
return m, nil
}
Comment thread race_server/hub.go
Comment on lines +40 to +43
func (h *hub) removeClient(c *client) {
h.mu.Lock()
defer h.mu.Unlock()

Comment thread race_server/hub.go
Comment on lines +152 to +156
// auto-match in public rooms.
// no_rooms=true means queue for random 1v1 only (never join custom-sized rooms).
for _, ex := range h.rooms {
if ex.started || ex.pin != "" || len(ex.players) >= ex.maxPlayers {
continue
Comment thread internal/game/client.go
Comment on lines +95 to +109
func (c *RaceClient) Join(roomID, pin string, isCreate bool, size int, difficulty, mode, lang string, duration int, autoStart bool) error {
url := fmt.Sprintf("%s/race/join?name=%s", c.serverURL, c.name)
if !isCreate && roomID == "" {
url += "&no_rooms=true"
}
if roomID != "" {
url += "&room=" + roomID
}
if pin != "" {
url += "&pin=" + pin
}
if isCreate {
url += "&is_create=true"
url += fmt.Sprintf("&size=%d&difficulty=%s&mode=%s&lang=%s&duration=%d&auto_start=%t", size, difficulty, mode, lang, duration, autoStart)
}
Comment thread internal/game/client.go
Comment on lines +222 to +230
func (c *RaceClient) Close() {
c.mu.Lock()
defer c.mu.Unlock()
if c.closed {
return
}
c.closed = true
close(c.done)
}
Comment thread internal/tui/model.go
Comment on lines +146 to +153
if m.raceState == onlineRacing && m.raceClient != nil {
prog := 0.0
if len(m.game.Text()) > 0 {
prog = float64(len(m.game.Input())) / float64(len(m.game.Text()))
}
wpm := m.game.Stats().WPM
go m.raceClient.SendProgress(prog, wpm)
}
Comment thread README.md Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@vyrx-dev

vyrx-dev commented Jun 7, 2026

Copy link
Copy Markdown
Owner

@aaravmaloo i am not good with server side things so please check suggestions made by copilot especially those are labeled as high

@aaravmaloo

Copy link
Copy Markdown
Collaborator Author

@vyrx-dev well I think its suggestion are valid, but it does work perfectly and I see no need to consider those changes. If you really want to, feel free to take those suggestions, but its suggestions pretty negligible.

@programmersd21

Copy link
Copy Markdown

@vyrx-dev @aaravmaloo when is it comin' out guyzzz??? would be so cool af daymmmmm 😭

@vyrx-dev

vyrx-dev commented Jul 4, 2026

Copy link
Copy Markdown
Owner

It's available...you can just checkout the PR branch and try

As you can see it's not a small change so I have to review too many lines and it takes time (actually I am very busy with my freelance work)

You have to wait a little bit more 😅 for the package release

@aaravmaloo

Copy link
Copy Markdown
Collaborator Author

@vyrx-dev I thought we were gonna keep this on a toofan-online branch, right?

@vyrx-dev

vyrx-dev commented Jul 4, 2026

Copy link
Copy Markdown
Owner

@vyrx-dev I thought we were gonna keep this on a toofan-online branch, right?

Yes

@programmersd21

Copy link
Copy Markdown

It's available...you can just checkout the PR branch and try

As you can see it's not a small change so I have to review too many lines and it takes time (actually I am very busy with my freelance work)

You have to wait a little bit more 😅 for the package release

ok np

@aaravmaloo

Copy link
Copy Markdown
Collaborator Author

seems like a good choice. if you do have questions, on how this works, etc, lemme know!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature add New feature or request in-review tag indicating the PR is in review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants