Code-breaking game with Spring Boot backend and real-time WebSocket multiplayer. Players guess a secret number and get feedback (bulls for correct position, cows for wrong position). Five game modes including daily challenges and 1v1 battles.
I built this to learn WebSocket programming and real-time systems. Also wanted to practice complex state management without a frontend framework. Everything is vanilla JavaScript.

The app uses both REST and WebSocket. REST handles game logic, authentication, and data persistence. WebSocket handles real-time features like multiplayer sync, friend presence, and live notifications.
Spring Boot backend manages game sessions in memory using ConcurrentHashMap. Each game mode has its own session class with different logic. PostgreSQL stores user data, game history, achievements, and friendships.
WebSocket uses STOMP messaging protocol. Users subscribe to channels for their active games and friend updates. Server broadcasts game state changes to all connected players.
Session management uses composite keys (sessionId:tabId) so users can play multiple modes in different tabs. Scheduled cleanup removes expired sessions after 5-30 minutes depending on mode.
Problem
In multiplayer mode, two players compete on the same secret number. They make guesses concurrently from different browsers. Without proper synchronization, race conditions could corrupt game state.
Solution
Used ConcurrentHashMap for session storage (thread-safe by design). Server is the source of truth for game state. WebSocket events are sequenced to prevent out-of-order processing.
Impact
Zero race condition bugs in production. Multiplayer feels smooth and instant.
Problem
WebSocket connections need authentication but they don't use standard HTTP headers after the handshake. JWT tokens need validation but where do you check them?
Solution
Validate JWT during WebSocket handshake (initial HTTP upgrade). Store user info in WebSocket session attributes. Check authorization on every message. Disconnect invalid sessions immediately.
Impact
Secure real-time communication. No unauthorized access to multiplayer games or friend presence data.
Problem
Users can play multiple game modes simultaneously in different tabs. How do you track separate sessions without conflicts?
Solution
Composite keys: sessionId:tabId. Each mode has its own session class with different logic. ConcurrentHashMap handles concurrent access. Scheduled cleanup removes expired sessions. Mode-specific validation prevents cross-mode state bugs.
Impact
Users can play multiple modes at once without issues. Sessions stay isolated and clean up automatically.
WebSocket Programming: Real-time sync is harder than it looks. Need to think about connection drops, reconnection logic, and message ordering. STOMP makes it easier than raw WebSocket.
Thread-Safe Concurrent Programming: ConcurrentHashMap was essential. Learned to think about race conditions and use proper synchronization. Server as source of truth prevents most sync issues.
State Management Without Frameworks: Vanilla JavaScript taught me a lot. No framework magic means you understand every piece. Modular architecture with separate files per feature kept things organized.
Complex Database Relationships: 13 entities with many-to-many relationships (users, friends, achievements). JPA makes this manageable but query optimization matters.
What I'd Do Differently: Use Redis for session storage (would allow horizontal scaling). Add proper job queue for background tasks. Implement WebSocket scaling strategy from day one. More comprehensive testing. Mobile apps would be better than mobile web.