Skip to content

Conversation

Copy link

Copilot AI commented Feb 1, 2026

  • Investigate network lag issue when hosting server on VM
  • Analyze current client-server communication patterns
  • Identify and implement optimizations
  • Document changes comprehensively
  • Code review completed and all feedback addressed:
    • Fixed backwards tick_update_frequency comments
    • Integrated api_client.close() in quitApplication()
    • Documented entity movement visibility tradeoff
    • Added tests for updateTick optimization
  • Fixed failing PersistenceIntegrationTest (player stats not persisted)
  • Await user feedback on network performance
  • Consider WebSocket implementation for future improvements

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 8 commits January 16, 2026 05:58
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Add PlayerControllerTest with tests for food consumption incrementing foodEaten stat
- Update PersistenceIntegrationTest to verify player stats persistence
- Fix copyPlayerData method to include stats fields when loading player data
- All tests pass successfully

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Add visitedRooms Set to Player to track unique rooms visited
- Automatically increment roomsExplored when player enters new room
- Add visited_rooms column to players table for persistence
- Update PlayerService to call visitRoom() on room transitions
- Update PersistenceService to save/load visited rooms as semicolon-separated string
- Initialize new players with starting room (0,0) already visited
- All tests passing

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Remove score and number of deaths from stats screen display
- Keep only implemented stats: rooms explored and food eaten
- Update fetchStatsFromServer to only fetch implemented stats
- Simplify stats screen to show only working features

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Update comments to say 'semicolon-separated' instead of 'comma-separated'
- Code was already using semicolons correctly, only comments needed fixing

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copilot AI and others added 3 commits February 1, 2026 12:23
- Reduced tick update frequency from 20/sec to 4/sec (5x improvement)
- Eliminated redundant player data request by reusing tick response data
- Implemented smart room reloading (only on room change, not every tick)
- Added debounced room refresh after gather/place actions (500ms cooldown)
- Added HTTP connection pooling with persistent connections
- Added 5-second request timeout for faster failure detection
- Added comprehensive performance documentation

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
- Fixed misleading comment about retry logic
- Removed unnecessary hasattr check in close() method

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copilot AI changed the title [WIP] Transition to Spring Boot server and Pygame client Optimize network performance for remote server deployment Feb 1, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements significant network performance optimizations for remote server deployment scenarios. The game was experiencing severe lag when the server ran on a VM and clients connected over a network, primarily due to excessive HTTP requests (~60/sec during normal gameplay).

Changes:

  • Reduced tick update frequency from every 3 frames to every 15 frames, decreasing requests from ~20/sec to ~4/sec
  • Eliminated redundant player data requests by extracting player state from tick response instead of separate GET calls
  • Implemented smart room reloading that only fetches room data on room transitions rather than every tick
  • Added debounced room refresh mechanism with 500ms cooldown for gather/place actions to prevent request spam
  • Introduced HTTP connection pooling with persistent connections and 5-second request timeouts

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
src/screen/serverBackedWorldScreen.py Implements reduced tick frequency (15 frames), player data extraction from tick response, conditional room loading on transitions, and debounced room refresh with tracking variables for optimization
src/client/api_client.py Adds HTTP session with connection pooling (HTTPAdapter), configurable request timeout (5s default), and close() method for resource cleanup
docs/PERFORMANCE.md Comprehensive documentation of optimization strategies, impact measurements, configuration options, and future improvement opportunities
docs/OPTIMIZATION_SUMMARY.md User-friendly summary of optimizations with before/after comparisons, performance metrics, and testing recommendations

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

Tunable parameters in `serverBackedWorldScreen.py`:

```python
tick_update_frequency = 15 # Lower = fewer updates, higher = more responsive
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The comment states "Lower = fewer updates, higher = more responsive" but this is backwards. Lower values (e.g., 5) mean the tick updates MORE frequently (every 5 frames), making it more responsive but using more network. Higher values (e.g., 30) mean the tick updates LESS frequently (every 30 frames), using less network but being less responsive. The comment should be: "Lower = more frequent updates (more responsive, higher network usage), higher = less frequent updates (less responsive, lower network usage)".

Suggested change
tick_update_frequency = 15 # Lower = fewer updates, higher = more responsive
tick_update_frequency = 15 # Lower = more frequent updates (more responsive, higher network usage), higher = less frequent updates (less responsive, lower network usage)

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

Fixed in ae554b7. Comment now correctly states: "Lower = more frequent updates (more responsive, higher network usage), higher = less frequent updates (less responsive, lower network usage)".

Comment on lines +978 to +987
# OPTIMIZATION: Only reload room if player changed rooms or on demand
# Check if player moved to a different room since last tick
if self.player_data:
player_room_x = self.player_data.get('roomX', 0)
player_room_y = self.player_data.get('roomY', 0)
# Reload room every tick to show entity movements
self.load_room(player_room_x, player_room_y)

# Only reload if we're in a different room than currently displayed
if player_room_x != self.current_room_x or player_room_y != self.current_room_y:
logger.info(f"Player changed rooms from ({self.current_room_x}, {self.current_room_y}) to ({player_room_x}, {player_room_y})")
self.load_room(player_room_x, player_room_y)
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The optimization to only reload rooms on room transitions means that entity movements within the same room will no longer be visible in real-time. The previous implementation reloaded the room every tick specifically to show entity movements. This tradeoff significantly impacts gameplay visibility and should be clearly documented in the PR description and performance documentation. Consider adding a note about this limitation and potentially implementing a separate mechanism for entity position updates if real-time entity tracking is important for gameplay.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

Documented in ae554b7. Added clear warnings about this tradeoff in both PERFORMANCE.md and OPTIMIZATION_SUMMARY.md. The docs now explain that entity movements within the same room are not visible in real-time and recommend WebSocket integration for real-time entity tracking if needed.


```python
# Client: serverBackedWorldScreen.py
tick_update_frequency = 15 # Increase for less network usage, decrease for more responsive feel
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

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

The comment suggests to "Increase for less network usage, decrease for more responsive feel" but the relationship is inverted. Increasing tick_update_frequency (e.g., from 15 to 30) means updating LESS frequently (every 30 frames instead of 15), which reduces network usage. Decreasing it (e.g., from 15 to 5) means updating MORE frequently, which increases responsiveness but also network usage. The comment should be: "Increase for less frequent updates (lower network usage), decrease for more frequent updates (more responsive)".

Suggested change
tick_update_frequency = 15 # Increase for less network usage, decrease for more responsive feel
tick_update_frequency = 15 # Increase for less frequent updates (lower network usage), decrease for more frequent updates (more responsive)

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

Fixed in ae554b7. Comment now correctly states: "Increase for less frequent updates (lower network usage), decrease for more frequent updates (more responsive)".

@dmccoystephenson
Copy link
Member

@copilot address comments

- Fixed backwards comments about tick_update_frequency in both docs
- Added api_client.close() call in quitApplication() for proper cleanup
- Documented entity movement visibility tradeoff in both PERFORMANCE.md and OPTIMIZATION_SUMMARY.md
- Added comprehensive tests for updateTick optimization (player data extraction from tick response)
- Added test for fallback behavior when player data not in tick response

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
@dmccoystephenson
Copy link
Member

@copilot Create sub PR for this PR for the following:

### 1. WebSocket Integration (High Priority) 
  
 The server already has WebSocket support (`WebSocketMessageService`) but the client doesn't use it. 
  
 **Recommended approach**: 
 - Use WebSocket for server-to-client state updates (push instead of poll) 
 - Keep REST API for client-to-server actions 
 - Would eliminate periodic tick polling entirely 
  
 **Expected impact**: Additional 30-50% reduction in network overhead

Copy link
Author

Copilot AI commented Feb 1, 2026

@copilot Create sub PR for this PR for the following:

I cannot directly create pull requests, but I can help implement the WebSocket integration work on a new branch that you can then use to create a PR.

Would you like me to:

  1. Create a new branch off the current branch (copilot/sub-pr-266)
  2. Implement WebSocket client integration for real-time state updates
  3. Keep REST API for client actions (move, gather, place, etc.)
  4. Add configuration to switch between polling and WebSocket modes

Please confirm and I'll proceed with the implementation.

@dmccoystephenson
Copy link
Member

@copilot no, just provide a detailed ticket title/description for the future in a comment

Copy link
Author

Copilot AI commented Feb 1, 2026

@copilot no, just provide a detailed ticket title/description for the future in a comment

WebSocket Integration for Real-Time Game State Updates

Priority

High - This would provide an additional 30-50% reduction in network overhead on top of current optimizations

Summary

Implement WebSocket-based real-time communication for server-to-client state updates, eliminating the need for periodic tick polling while maintaining REST API for client-initiated actions.

Background

Current implementation uses REST API polling every 15 frames (~4 requests/sec) to fetch game state updates. The server already has full WebSocket support via WebSocketMessageService that broadcasts:

  • Player position updates (/topic/session/{id}/player)
  • Entity state updates (/topic/session/{id}/entity)
  • World events (/topic/session/{id}/world)
  • Tick updates (/topic/session/{id}/tick)

Technical Approach

Client-Side Implementation:

  1. Add WebSocket client library (e.g., websocket-client or python-socketio)

  2. Create WebSocketClient class in src/client/websocket_client.py:

    • Connect to ws://server:8080/ws on session init
    • Subscribe to session-specific topics
    • Handle reconnection with exponential backoff
    • Parse and dispatch incoming messages to handlers
  3. Integrate with ServerBackedWorldScreen:

    • Remove periodic updateTick() calls from game loop
    • Add WebSocket message handlers for:
      • TickUpdate → Update server_tick
      • PlayerPositionUpdate → Update player position/state
      • EntityStateUpdate → Update entity positions in current room
      • WorldEventMessage → Handle world events
    • Keep REST API for player actions (move, gather, place, etc.)

Configuration:

  • Add use_websocket flag to Config (default: True)
  • Fallback to polling mode if WebSocket connection fails
  • Configurable WebSocket reconnection parameters

Error Handling:

  • Graceful degradation to REST polling on connection loss
  • Automatic reconnection with exponential backoff
  • Health check mechanism to detect stale connections

Implementation Tasks

  1. Add WebSocket client dependency to requirements.txt
  2. Implement WebSocketClient class with connection management
  3. Add message handlers for all WebSocket message types
  4. Integrate WebSocket client with ServerBackedWorldScreen
  5. Remove/disable tick polling when WebSocket is active
  6. Add configuration options for WebSocket vs REST modes
  7. Implement reconnection logic with exponential backoff
  8. Add comprehensive error handling and logging
  9. Write unit tests for WebSocket client
  10. Write integration tests for WebSocket message handling
  11. Update documentation (PERFORMANCE.md, README.md)
  12. Add metrics/logging for WebSocket connection health

Expected Impact

  • Network Requests: Reduce from ~8-12/sec to ~1-2/sec (actions only)
  • Latency: Immediate state updates (no polling delay)
  • Bandwidth: ~30-50% reduction in total network traffic
  • Responsiveness: Real-time entity movements visible without room reloads

Testing Strategy

  • Unit tests for WebSocket client connection/disconnection
  • Integration tests for message handling
  • Manual testing with server on remote VM
  • Network latency simulation testing
  • Reconnection scenario testing (server restart, network interruption)

Documentation Updates

  • Add WebSocket architecture diagram
  • Document configuration options
  • Update PERFORMANCE.md with new metrics
  • Add troubleshooting guide for WebSocket issues

Dependencies

  • WebSocket client library (Python)
  • Server WebSocket endpoints (already implemented)
  • Spring Boot STOMP WebSocket support (already configured)

Risks & Mitigation

  • Risk: WebSocket connection instability
    • Mitigation: Automatic fallback to REST polling
  • Risk: Message ordering issues
    • Mitigation: Use sequence numbers in messages
  • Risk: Increased server load
    • Mitigation: Connection pooling, message batching

Success Criteria

  • WebSocket connection establishes successfully on session init
  • All game state updates received via WebSocket (no polling)
  • Graceful fallback to REST mode on connection failure
  • No increase in perceived latency
  • 30-50% reduction in network requests measured
  • All existing tests pass with WebSocket mode enabled

…/roam-prototype into copilot/sub-pr-266

# Conflicts:
#	server/src/main/java/com/preponderous/roam/persistence/service/PersistenceService.java
#	server/src/main/java/com/preponderous/roam/service/PlayerService.java
#	src/roam.py
#	src/screen/statsScreen.py
@dmccoystephenson
Copy link
Member

@copilot fix failing unit test:

 Tests run: 4, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.109 s <<< FAILURE! -- in com.preponderous.roam.persistence.PersistenceIntegrationTest

- Added saving of player stats (score, roomsExplored, foodEaten, numberOfDeaths, visitedRooms) in savePlayer method
- Added loading of player stats in convertToPlayer method
- Added copying of player stats in copyPlayerData method
- Fixes failing test PersistenceIntegrationTest.testSaveAndLoadGameState

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Author

Copilot AI commented Feb 1, 2026

@copilot fix failing unit test:

Fixed in 18535bf. The test was failing because player stats (score, roomsExplored, foodEaten, numberOfDeaths, visitedRooms) were not being persisted to the database.

Root cause: The PersistenceService had three methods that needed updating:

  1. savePlayer() - wasn't saving the stats to PlayerEntityData
  2. convertToPlayer() - wasn't loading the stats from PlayerEntityData
  3. copyPlayerData() - wasn't copying the stats between Player objects

All player stats are now correctly persisted and restored. The test now passes with all 4 tests successful.

@dmccoystephenson dmccoystephenson marked this pull request as ready for review February 1, 2026 20:56
@dmccoystephenson dmccoystephenson merged commit b3d12c7 into feat/server-client Feb 1, 2026
2 checks passed
@dmccoystephenson dmccoystephenson deleted the copilot/sub-pr-266 branch February 1, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants