Skip to content

Commit b3ef624

Browse files
rbtyingclaude
andauthored
[RFC] Support using a server-side fallback for browsers without WebAssembly support (#482)
* Enable plaintext JSON if requested * feat: Add support for environments without WebAssembly Detect WebAssembly availability and gracefully fallback to uncompressed JSON messages when WASM is not available. This enables the frontend to work in restricted environments where WASM or JavaScript JIT compilation is disabled. - Add WASM detection utility to check runtime support - Request uncompressed responses from server when WASM unavailable - Handle both binary (compressed) and text (uncompressed) WebSocket messages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Extract WASM RPC types into shared backend-types crate Move all request/response types from shengji_wasm into a shared wasm_rpc module in the backend-types crate. This allows the types to be reused between: - WASM module for client-side execution - Backend server for RPC fallback handlers - Frontend for making RPC calls Changes: - Create wasm_rpc.rs module with all RPC request/response types - Update shengji_wasm to use types from shengji_types::wasm_rpc - Fix lifetime issues by using String instead of &'static str - Add proper Serialize/Deserialize derives for all types - Create WasmRpcRequest/Response enums to wrap all RPC calls This sets up the foundation for implementing server-side RPC handlers when WASM is not available. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Extract shared WASM RPC implementations into separate crate Create a new wasm-rpc-impl crate that contains all the shared game logic implementations that can be used by both the WASM module and the backend server. This ensures consistent behavior between client-side WASM execution and server-side RPC fallback. Changes: - Create new wasm-rpc-impl crate with all RPC function implementations - Update shengji-wasm to use the shared implementations - Update json-schema-bin to import types from shengji-types instead of shengji-wasm - Regenerate TypeScript type definitions This architecture allows code reuse between WASM and backend, ensuring both execution paths behave identically. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * test: Add tests for /api/rpc endpoint - Add comprehensive test coverage for WASM RPC endpoints - Test sort_and_group_cards functionality - Test get_card_info retrieval - Test compute_deck_len calculation - Test find_viable_plays logic - Use axum-test for simplified HTTP testing - Note: Some complex tests temporarily disabled due to serialization complexity 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * feat: Add async provider with WASM/RPC fallback - Create WasmOrRpcProvider to handle both WASM and server RPC calls - Add AsyncWasmContext for async function access - Create useAsyncWasm hook for components - Update Play.tsx to use async functions for: - decomposeTrickFormat in TrickFormatHelper and HelperContents - findViablePlays for card selection - canPlayCards for play validation - nextThresholdReachable for early game ending - Implement automatic fallback to server RPC when WASM unavailable - Add loading states for async operations Note: sortAndGroupCards still needs async conversion for full compatibility 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: Resolve compilation errors in async implementation - Remove unused WasmContext import from Play.tsx - Fix type annotations for map functions in Play.tsx - Correct compute_deck_len return value in WasmOrRpcProvider - Temporarily simplify getCardsFromHand for async compatibility - Add TODO for proper async sorting implementation 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * feat: Update BidArea and Cards components to use async WASM functions - Convert BidArea to use async findValidBids with loading state - Convert Cards to use async sortAndGroupCards with loading state - Add proper error handling and fallback for card sorting - Show loading indicators while WASM functions execute - Update imports to use useAsyncWasm hook Both components now properly handle async operations and will work with server-side RPC fallback when WASM is unavailable. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * feat: Implement async getCardInfo with caching in Card component - Convert Card component to use async getCardInfo function - Add global cache for card info to avoid repeated async calls - Cache key includes both card and trump for accurate caching - Show basic card while loading card info - Display trump and point icons based on async card info - Graceful fallback if card info loading fails The Card component now works with both WASM and server RPC fallback, with efficient caching to minimize async calls for frequently displayed cards. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * fix: Improve Card component caching and reduce duplication - Create stable cache key based on trump suit and number - Fix cache key generation for both Standard and NoTrump variants - Refactor to avoid duplicate CardCanvas rendering - Use conditional rendering for labels and icons based on loading state - Fix TypeScript error with fallback card info The Card component now has more efficient caching that properly depends on the trump suit, and cleaner code with less duplication. 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * Convert frontend to async engine with RPC fallback for non-WASM environments - Replace synchronous WASM calls with async operations - Add HTTP RPC fallback when WebAssembly is unavailable - Implement batch API for getCardInfo to reduce network requests - Add caching for card info and scoring explanations - Rename AsyncWasmContext to EngineContext for clarity - Remove legacy synchronous WasmProvider - Prefill caches on game start to improve performance This enables the frontend to work in environments without WebAssembly or JIT compilation support by falling back to server-side execution. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Apply code formatting - Format TypeScript with prettier - Fix ESLint issues - Format Rust code with cargo fmt 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix ESLint config to align with Prettier formatting - Disable ESLint formatting rules that conflict with Prettier - Apply Prettier formatting to gen-types.d.ts - Ensure ESLint and Prettier work together without conflicts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix TypeScript any types introduced during async conversion - Replace all 'any' types with proper TypeScript types - Add proper type imports for RPC responses - Use 'unknown' instead of 'any' for truly dynamic values - Cast decoded wire format messages to GameMessage type - Add type assertions where necessary for Promise results All TypeScript errors resolved except third-party package issues. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Apply Prettier formatting - Format Points.tsx, WasmOrRpcProvider.tsx, and WebsocketProvider.tsx - Ensure consistent code style across the codebase 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix Rust clippy warnings - Use inline format arguments (uninlined_format_args) - Remove redundant closure in map operation - Update format strings to use new Rust syntax All clippy checks now pass with -D warnings flag. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix Docker build by copying wasm-rpc-impl directory The Docker build was failing because the wasm-rpc-impl crate was not being copied before the frontend build step. This crate is needed as a dependency for the build process. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5c1b41e commit b3ef624

38 files changed

+2501
-803
lines changed

CLAUDE.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
Shengji is an online implementation of the Chinese trick-taking card game 升级 ("Tractor" or "Finding Friends"). It features a Rust backend with WebSocket support and a React TypeScript frontend with WebAssembly integration.
8+
9+
## Commands
10+
11+
### Development
12+
```bash
13+
# Run frontend in development mode with hot reloading
14+
cd frontend && yarn watch
15+
16+
# Run backend in development mode
17+
cd backend && cargo run --features dynamic
18+
19+
# Full development setup (run in separate terminals)
20+
cd frontend && yarn watch
21+
cd backend && cargo run --features dynamic
22+
```
23+
24+
### Building
25+
```bash
26+
# Build production frontend
27+
cd frontend && yarn build
28+
29+
# Build release backend
30+
cargo build --release
31+
32+
# Full production build
33+
cd frontend && yarn build && cd ../backend && cargo run
34+
```
35+
36+
### Testing
37+
```bash
38+
# Run all Rust tests
39+
cargo test --all
40+
41+
# Run specific Rust test
42+
cargo test test_name
43+
44+
# Run frontend tests
45+
cd frontend && yarn test
46+
47+
# Run frontend tests in watch mode
48+
cd frontend && yarn test --watch
49+
```
50+
51+
### Code Quality
52+
```bash
53+
# Lint TypeScript
54+
cd frontend && yarn lint
55+
56+
# Fix TypeScript lint issues
57+
cd frontend && yarn lint --fix
58+
59+
# Lint Rust
60+
cargo clippy
61+
62+
# Format TypeScript
63+
cd frontend && yarn prettier --write
64+
65+
# Check TypeScript formatting
66+
cd frontend && yarn prettier --check
67+
68+
# Format Rust
69+
cargo fmt --all
70+
71+
# Check Rust formatting
72+
cargo fmt --all -- --check
73+
```
74+
75+
### Type Generation
76+
```bash
77+
# Generate TypeScript types from Rust schemas (run from frontend directory)
78+
cd frontend && yarn types && yarn prettier --write && yarn lint --fix
79+
```
80+
81+
## Architecture
82+
83+
### Rust Workspace Structure
84+
- **backend/**: Axum web server handling WebSocket connections and game API
85+
- **core/**: Game state management, message types, and serialization
86+
- **mechanics/**: Core game logic including bidding, tricks, and scoring
87+
- **storage/**: Storage abstraction layer supporting in-memory and Redis backends
88+
- **frontend/shengji-wasm/**: WebAssembly bindings for client-side game mechanics
89+
90+
### Frontend Structure
91+
- **frontend/src/**: React components and application logic
92+
- **frontend/src/state/**: WebSocket connection and state management
93+
- **frontend/src/ChatMessage.tsx**: In-game chat implementation
94+
- **frontend/src/Draw.tsx**: Card rendering and game board visualization
95+
- **frontend/src/Play.tsx**: Main gameplay component
96+
- **frontend/json-schema-bin/**: Utility for generating TypeScript types from Rust
97+
98+
### Type Safety Strategy
99+
The project maintains type safety between Rust and TypeScript by:
100+
1. Defining types in Rust using serde serialization
101+
2. Generating JSON schemas from Rust types
102+
3. Converting schemas to TypeScript definitions via json-schema-bin
103+
4. Sharing game logic through WebAssembly for client-side validation
104+
105+
### WebSocket Communication
106+
- All game state updates flow through WebSocket connections
107+
- Messages are typed and validated on both client and server
108+
- State synchronization happens automatically via the WebSocketProvider
109+
110+
## Development Notes
111+
112+
### When modifying game mechanics:
113+
1. Update logic in `mechanics/src/`
114+
2. If changing message types, update `core/src/message.rs`
115+
3. Regenerate TypeScript types with `yarn types`
116+
4. Update frontend components to handle new mechanics
117+
118+
### When adding new features:
119+
1. Implement server-side logic in appropriate Rust module
120+
2. Add message types if needed in `core/`
121+
3. Generate TypeScript types
122+
4. Implement UI in React components
123+
5. Ensure WebSocket message handling is updated
124+
125+
### Testing approach:
126+
- Unit test game mechanics in Rust (`mechanics/src/`)
127+
- Integration test API endpoints in `backend/`
128+
- Component testing for React UI elements
129+
- Manual testing for WebSocket interactions and gameplay flow

Cargo.lock

Lines changed: 111 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
members = [
33
"backend",
44
"frontend/json-schema-bin",
5-
"frontend/shengji-wasm"
5+
"frontend/shengji-wasm",
6+
"wasm-rpc-impl"
67
]
78
resolver = "2"
89

0 commit comments

Comments
 (0)