Skip to content

Commit 376efe7

Browse files
authored
Merge pull request #85 from gamultong/chore/upgrade-nextjs-15
Chore/upgrade nextjs 15
2 parents 989af80 + b7cead0 commit 376efe7

26 files changed

Lines changed: 2184 additions & 835 deletions

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,5 @@ next-env.d.ts
4343
/wasm/target/
4444
.cursor/debug.log
4545
.claude/settings.local.json
46+
.claude/settings.json
47+
.omc/

package-lock.json

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

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
},
1212
"dependencies": {
1313
"@pixi/react": "^7.1.2",
14+
"@xyflow/react": "^12.10.1",
1415
"next": "^14.2.35",
1516
"pixi.js": "^7.4.2",
1617
"qs": "^6.14.1",
17-
"react-flow-renderer": "^10.3.17",
1818
"sass": "^1.80.7",
1919
"showdown": "^2.1.0",
2020
"zustand": "^5.0.1"
Lines changed: 93 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,131 @@
11
# About Interactions
22

3-
You can interact with the client through `onClick` and `onMouseDown` events. All interactions are handled on the interaction canvas.
3+
## Overview
44

5-
---
5+
User input is handled by the `useInputHandlers` hook.
6+
It detects mouse/touch events, calculates tile coordinates, and executes appropriate actions based on tile state.
7+
8+
**File**: `src/hooks/useInputHandlers.ts`
69

7-
## Overview of Click Interactions
8-
Users can interact with the tile grid using mouse clicks, and there are two types:
10+
---
911

10-
- **Normal Click (Left Click)**: Used to open tiles or move the cursor.
11-
- **Special Click (Right Click)**: Used to set or remove flags on tiles.
12+
## Click Types
1213

13-
The system calculates the tile position and evaluates the tile state to perform the appropriate action.
14+
| Input | Event | Action |
15+
|-------|-------|--------|
16+
| Left click | `handleClick()` | Open tile or move |
17+
| Right click | `handleRightClick()` | Set/remove flag or install bomb |
18+
| Long press (500ms) | `handleLongPress()` | Dismantle flagged mine (DISMANTLE_MINE) |
1419

1520
---
1621

17-
## Coordinate System
18-
To determine the tile clicked by the user, the system performs the following coordinate transformations:
22+
## Coordinate Conversion
23+
24+
How screen click positions are converted to tile grid coordinates:
1925

20-
1. **Canvas Coordinates**
21-
Adjust the mouse click position relative to the screen to fit the canvas grid.
26+
```
27+
1. Canvas coordinates
28+
Convert mouse event clientX/clientY to canvas-relative position
2229
23-
2. **Relative Tile Coordinates**
24-
Convert the click position to a relative position within the tile grid. This is calculated by dividing the adjusted click position by the tile size, considering the spacing between tiles.
30+
2. Relative tile coordinates
31+
Divide click position by tile size to get grid-relative position
32+
(accounts for zoom level and tile padding)
2533
26-
3. **Absolute Tile Coordinates**
27-
Adjust the relative coordinates based on the origin to calculate the absolute position of the tile.
34+
3. Absolute world coordinates
35+
Adjust relative coordinates based on cursor origin (cursorPosition)
36+
→ Absolute tile position to send to server
37+
```
2838

2939
---
3040

31-
## Tile States and Content
32-
Each tile in the grid can have one of the following states:
41+
## Actions by Tile State
3342

34-
- **Closed Tile**: A tile that has not been interacted with yet.
35-
- **Flagged Tile**: A tile marked as a potential mine.
36-
- **Opened Tile**: A revealed tile (no mine).
37-
- **Out of Bounds Tile**: When a click is outside the grid.
43+
### Left Click
3844

39-
---
45+
| Tile State | Adjacent (within 1 tile) | Distant |
46+
|-----------|--------------------------|---------|
47+
| Closed | Send `OPEN_TILES` → open tile | A* pathfind → move then open |
48+
| Opened | Move (1 tile) | A* pathfind → move |
49+
| Flagged | No action | Move to nearest open neighbor |
50+
| Bomb | No action | A* pathfind → move |
51+
52+
### Right Click
53+
54+
| Tile State | Normal Mode | Bomb Mode |
55+
|-----------|-------------|-----------|
56+
| Closed | `SET_FLAG` → set flag | `INSTALL_BOMB` → install bomb |
57+
| Flagged | `SET_FLAG` → remove flag | No action |
58+
| Opened | No action | No action |
4059

41-
## Click Types
42-
The type of click interaction is determined by the mouse button:
60+
### Long Press (500ms)
4361

44-
| **Click Type** | **Mouse Button** | **Action** |
45-
|------------------|------------------|----------------------------------|
46-
| **Normal Click** | Left Click | Open tile or move cursor. |
47-
| **Special Click**| Right Click | Set or remove flag on tile. |
62+
| Tile State | Action |
63+
|-----------|--------|
64+
| Flagged | `DISMANTLE_MINE` → dismantle flag |
65+
| Other | No action |
4866

4967
---
5068

51-
## Actions Based on Tile State
69+
## A* Pathfinding
5270

53-
### Normal Click (Left Click)
71+
**File**: `src/utils/aStar.ts`
5472

55-
| **Tile State** | **Action** |
56-
|------------------|-------------------------------------------------|
57-
| **Closed Tile** | Open the tile to reveal its content. |
58-
| **Opened Tile** | Move the cursor to the tile if a path exists. |
59-
| **Flagged Tile** | No action. |
60-
| **Out of Bounds**| No action. |
73+
When clicking a distant tile, the A* algorithm calculates the optimal path.
6174

62-
### Special Click (Right Click)
75+
### Features
6376

64-
| **Tile State** | **Action** |
65-
|------------------|-------------------------------------------------|
66-
| **Closed Tile** | Set a flag on the tile (mark as suspected mine).|
67-
| **Flagged Tile** | Remove the flag (revert to closed state). |
68-
| **Opened Tile** | No action. |
69-
| **Out of Bounds**| No action. |
77+
- **8-directional movement**: Cardinal + diagonal
78+
- **Impassable tiles**: Flag tiles are treated as obstacles
79+
- **Early exit**: Skips full search if target is adjacent
80+
- **Cost calculation**: Straight 1.0, diagonal 1.414 (Euclidean distance)
7081

71-
---
82+
### Path Execution Flow
7283

73-
## Tile Movement
74-
When the user normal clicks on an opened or exploded tile:
84+
```
85+
A* path calculation
86+
→ Generate waypoints array [start, ..., destination]
87+
88+
useMovement:
89+
→ setInterval at MOVE_SPEED (ms) intervals
90+
→ Each step:
91+
1. Calculate direction to next waypoint
92+
2. Move cursor 1 tile
93+
3. Send MOVE event to server
94+
4. Call padtiles() (shift grid in movement direction)
95+
5. CSS transform animation
96+
→ Execute original action on arrival
97+
```
7598

76-
- The system attempts to move the cursor to the clicked tile.
77-
- Movement occurs only if there is a valid path from the current position to the target tile.
78-
- Movement speed is fixed at 5 tiles per second.
99+
### Movement Speed
79100

80-
---
101+
Base movement interval: `MOVE_SPEED` (ms/tile)
102+
103+
Movement speed changes based on skills purchased from the skill tree:
81104

82-
## Chat
83-
Pressing the Enter key activates the chat component. Typing a message and pressing Enter again sends the chat, making it visible to other users.
105+
```typescript
106+
// Speed multiplier calculation in useSkillTree
107+
const speedMultiplier = purchasedSkills
108+
.filter(skill => skill.type === 'speed')
109+
.reduce((mult, skill) => mult * skill.value, 1.0);
110+
```
84111

85112
---
86113

87-
## Event Handling
88-
When the user interacts with a tile:
114+
## Chat
89115

90-
1. The system calculates the tile position (relative and absolute).
91-
2. Retrieves the content of the clicked tile.
92-
3. Evaluates the click type (normal or special).
93-
4. Performs the appropriate action:
94-
- Normal Click: Open the tile.
95-
- Special Click: Set or remove a flag.
96-
- Normal Click (valid tile): Move the cursor.
116+
Toggle chat input mode with the Enter key:
117+
118+
1. Enter → Activate chat input
119+
2. Type message
120+
3. Enter → Send `CHAT` event to server
121+
4. Displayed as speech bubble for other users (8 seconds)
122+
123+
Movement/click inputs are disabled while chatting.
97124

98125
---
99126

100-
## Special Conditions
101-
- If an adjacent tile explodes during interaction, all tile controls are restricted for a certain period (e.g., 3 minutes).
102-
- Clicks outside the grid are ignored and marked as "Out of Bounds".
127+
## Special Conditions
128+
129+
- **Stun state**: All inputs disabled for 10 seconds when within 3x3 explosion range
130+
- **Revive countdown**: `inactive` component shows countdown overlay
131+
- **Out-of-grid click**: Ignored (early return after bounds check)
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Project Architecture
2+
3+
## Tech Stack
4+
5+
| Category | Technology | Version | Purpose |
6+
|----------|-----------|---------|---------|
7+
| Framework | Next.js | 14.x | App Router SSR/SSG |
8+
| UI | React | 18.x | Component-based UI |
9+
| Rendering | Pixi.js | 7.x | WebGL 2D tile rendering |
10+
| Rendering Bindings | @pixi/react | 7.x | React-Pixi integration |
11+
| State Management | Zustand | 5.x | Lightweight global state |
12+
| Graph Visualization | @xyflow/react | 12.x | Skill tree / Sitemap |
13+
| Markdown | Showdown | 2.x | Documentation rendering |
14+
| Styling | SASS | 1.x | SCSS modules |
15+
| Tile Processing | WASM (Rust) || High-performance hex→tile conversion |
16+
17+
---
18+
19+
## Directory Structure
20+
21+
```
22+
src/
23+
├── app/ # Next.js pages
24+
│ ├── page.tsx # Home (landing page)
25+
│ ├── layout.tsx # Root layout
26+
│ ├── play/ # Game page
27+
│ │ ├── page.tsx # Game orchestrator
28+
│ │ ├── layout.tsx # Play layout
29+
│ │ └── constants.ts # RENDER_RANGE, WS_URL, etc.
30+
│ ├── documents/ # Documentation pages
31+
│ ├── robots.ts # SEO robots.txt
32+
│ └── sitemap.ts # Sitemap XML
33+
34+
├── components/ # React components
35+
│ ├── canvas/ # Canvas orchestrator
36+
│ ├── tilemap/ # Pixi.js tile renderer
37+
│ ├── canvasDashboard/ # Zoom/stats UI
38+
│ ├── skilltree/ # Skill tree (ReactFlow)
39+
│ ├── chat/ # Chat overlay
40+
│ ├── scoreboard/ # Ranking display
41+
│ ├── inactive/ # Revive countdown
42+
│ └── ...
43+
44+
├── hooks/ # Custom hooks
45+
│ ├── useMessageHandler # WebSocket message routing
46+
│ ├── useTileProcessing # hex → Uint8Array conversion
47+
│ ├── useTileViewport # Viewport calc + SET_WINDOW
48+
│ ├── useInputHandlers # Mouse/touch input
49+
│ ├── useMovement # A* pathfinding + cursor animation
50+
│ ├── useCursorRenderer # Cursor/path Canvas 2D rendering
51+
│ ├── useExplosionManager # Explosion state management
52+
│ ├── useShockwaveAnimation # Explosion animation (RAF)
53+
│ ├── useSkillTree # Skill tree logic
54+
│ ├── useTilemapTextures # Pixi texture creation/caching
55+
│ └── useScreenSize # Window size tracking
56+
57+
├── store/ # Zustand state stores
58+
│ ├── websocketStore # WebSocket connection state
59+
│ ├── tileStore # Tile grid + view bounds
60+
│ ├── cursorStore # Cursor position/zoom/score
61+
│ ├── interactionStore # Click position/animation
62+
│ ├── skillTreeStore # Purchased skills list
63+
│ └── rankingStore # Leaderboard
64+
65+
├── utils/ # Utilities
66+
│ ├── tileGrid.ts # TileGrid class (Uint8Array)
67+
│ ├── tileCache.ts # World-coordinate LRU cache
68+
│ ├── wasmTileEngine.ts # WASM module loader
69+
│ ├── tiles.ts # Hex parsing LUT (vectorized)
70+
│ ├── aStar.ts # A* pathfinding algorithm
71+
│ ├── pixiSpritePool.ts # Sprite object pool
72+
│ ├── canvas.ts # Canvas drawing helpers
73+
│ └── makePath2d.ts # SVG → Path2D converter
74+
75+
├── types/ # TypeScript type definitions
76+
│ ├── message.ts # WebSocket protocol types
77+
│ ├── canvas.ts # Rendering types
78+
│ ├── position.ts # Coordinate/direction types
79+
│ └── ...
80+
81+
├── constants/ # Global constants
82+
│ └── cursor.ts # Cursor colors, 8-way offsets
83+
84+
├── assets/ # SVG vector paths
85+
│ └── renderPaths.json # Tile/cursor/flag/bomb vectors
86+
87+
└── wasm-pkg/ # WASM bindings (compiled from Rust)
88+
└── minesweeper_tile_engine.wasm
89+
```
90+
91+
---
92+
93+
## Zustand State Management
94+
95+
```
96+
┌──────────────────────────────────────┐
97+
│ Zustand Stores │
98+
├──────────────────────────────────────┤
99+
│ websocketStore → socket, msg buffer │
100+
│ tileStore → tile grid, bounds │
101+
│ cursorStore → position, zoom │
102+
│ interactionStore→ click, animation │
103+
│ skillTreeStore → purchased skills │
104+
│ rankingStore → ranking data │
105+
└──────────────────────────────────────┘
106+
↑ (setters) ↓ (selectors)
107+
Hooks/Events React Components
108+
```
109+
110+
**Unidirectional data flow:**
111+
1. WebSocket event → `useMessageHandler` reads store state
112+
2. Store setter called (e.g., `applyTileChanges`)
113+
3. Subscribed components re-render
114+
4. Components read fresh store state
115+
116+
---
117+
118+
## Module Responsibilities
119+
120+
### Hooks
121+
122+
| Hook | Responsibility |
123+
|------|---------------|
124+
| `useMessageHandler` | Routes incoming WebSocket messages to event-specific handlers |
125+
| `useTileProcessing` | Converts hex-encoded tile data to TileGrid (Uint8Array); WASM-first, JS fallback |
126+
| `useTileViewport` | Calculates viewport bounds from zoom & cursor; debounces SET_WINDOW requests |
127+
| `useInputHandlers` | Detects left-click, right-click, long-press and triggers tile interactions |
128+
| `useMovement` | A* pathfinding, animates cursor along path, sends MOVE events |
129+
| `useCursorRenderer` | Draws player cursor, other cursors, and path lines on Canvas 2D |
130+
| `useExplosionManager` | Tracks active explosions (position, startTime, ID) |
131+
| `useShockwaveAnimation` | RAF loop for explosion animation (flash, rays, rings, sparks) |
132+
| `useSkillTree` | Manages ReactFlow nodes/edges, skill purchase, speed calculation |
133+
| `useTilemapTextures` | Pre-renders number textures + SVG assets as Pixi Textures |
134+
| `useScreenSize` | Detects window resize events, returns viewport dimensions |
135+
136+
### Stores
137+
138+
| Store | Managed State |
139+
|-------|--------------|
140+
| `websocketStore` | Socket object, isOpen, message buffer, binary message, connect/disconnect/sendMessage |
141+
| `tileStore` | tiles (TileGrid), renderTiles, startPoint/endPoint, tileSize, padtiles/applyChanges |
142+
| `cursorStore` | Player: id, position, color, zoom, score, items; Other users' cursor list |
143+
| `interactionStore` | Click position (x, y, content), movecost, useAnimation toggle |
144+
| `skillTreeStore` | purchasedSkills array |
145+
| `rankingStore` | Leaderboard rankings |
146+
147+
### Key Components
148+
149+
| Component | Role |
150+
|-----------|------|
151+
| `canvas/` | Orchestrator: coordinates tilemap, cursor rendering, input, animations |
152+
| `tilemap/` | Pixi Stage + sprite pools (bg, closed, boom, flag, number layers) |
153+
| `canvasDashboard/` | Zoom buttons, score/bomb display, animation/bomb mode toggles |
154+
| `skilltree/` | ReactFlow graph + skill info panel + purchase button |
155+
156+
---
157+
158+
## Performance Optimizations
159+
160+
| Technique | Description |
161+
|-----------|-------------|
162+
| Sprite Pooling | Reuse Pixi sprites instead of create/destroy per frame |
163+
| Flat Uint8Array | O(1) native memcpy tile copy via `Uint8Array.slice()` |
164+
| Vectorized LUT | 16-bit hex→tile type O(1) conversion without branching |
165+
| WASM Processing | Hex tile decoding 10-100x faster than JS |
166+
| RAF Loop | Smooth 60fps animations without blocking main thread |
167+
| Texture Caching | Pre-render number textures + SVG assets once at startup |
168+
| Concurrency Limiter | Max 8 concurrent GPU operations to prevent stalls |
169+
| LRU Tile Cache | World-coordinate cache for instant restore on revisit |
170+
| SET_WINDOW Debounce | 200ms debounce to reduce server request flood |
171+
| A* Early Exit | Adjacent tiles bypass full pathfinding |

0 commit comments

Comments
 (0)