Spaces:
Running
Running
Fix crash vao hang (R3F Gold) + day leo + bang chuyen can dung; cache-bust v8
Browse files- patch_3d.mjs +94 -9
- patch_backend.mjs +2 -2
patch_3d.mjs
CHANGED
|
@@ -200,11 +200,98 @@ function NatureGround() {
|
|
| 200 |
</>
|
| 201 |
)
|
| 202 |
}` ],
|
| 203 |
-
/*
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
[ ` <CaveExterior />
|
| 205 |
<group position={layout.stele} rotation-y={layout.steleRotY}>`,
|
| 206 |
` <CaveExterior />
|
| 207 |
<NatureGround />
|
|
|
|
| 208 |
<group position={layout.stele} rotation-y={layout.steleRotY}>` ],
|
| 209 |
/* Bảng chuyên cần attendance trên bia đá */
|
| 210 |
[ `/* ---- Camera rig + layout (MA gốc) ---- */`,
|
|
@@ -293,8 +380,6 @@ patchFile('frontend/cave3d.js', [
|
|
| 293 |
CÚP VÀNG TRUNG TÂM (trophy) — trước vòng huyền thoại, kỷ vật MU
|
| 294 |
========================================================================= */
|
| 295 |
function Trophy() {
|
| 296 |
-
const gold = new THREE.MeshStandardMaterial({ color: '#c9a24e', metalness: 0.85, roughness: 0.25 })
|
| 297 |
-
const darkGold = new THREE.MeshStandardMaterial({ color: '#8a6a2c', metalness: 0.7, roughness: 0.35 })
|
| 298 |
return (
|
| 299 |
<group position={[0, 0, 0]} rotation-y={0.5}>
|
| 300 |
<mesh position={[0, 0.12, 0]} castShadow>
|
|
@@ -303,27 +388,27 @@ function Trophy() {
|
|
| 303 |
</mesh>
|
| 304 |
<mesh position={[0, 0.34, 0]} castShadow>
|
| 305 |
<cylinderGeometry args={[0.55, 0.7, 0.2, 8]} />
|
| 306 |
-
<
|
| 307 |
</mesh>
|
| 308 |
<mesh position={[0, 0.85, 0]} castShadow>
|
| 309 |
<cylinderGeometry args={[0.42, 0.5, 0.85, 10]} />
|
| 310 |
-
<
|
| 311 |
</mesh>
|
| 312 |
<mesh position={[0, 1.32, 0]} castShadow>
|
| 313 |
<torusGeometry args={[0.5, 0.09, 8, 18]} />
|
| 314 |
-
<
|
| 315 |
</mesh>
|
| 316 |
<mesh position={[-0.42, 0.85, 0]} rotation-z={0.3}>
|
| 317 |
<torusGeometry args={[0.26, 0.07, 8, 14, Math.PI]} />
|
| 318 |
-
<
|
| 319 |
</mesh>
|
| 320 |
<mesh position={[0.42, 0.85, 0]} rotation-z={-0.3}>
|
| 321 |
<torusGeometry args={[0.26, 0.07, 8, 14, Math.PI]} />
|
| 322 |
-
<
|
| 323 |
</mesh>
|
| 324 |
<mesh position={[0, 1.62, 0]} castShadow>
|
| 325 |
<sphereGeometry args={[0.2, 14, 14]} />
|
| 326 |
-
<
|
| 327 |
</mesh>
|
| 328 |
<pointLight position={[0, 2.6, 0]} color="#ffd76a" intensity={18} distance={9} decay={1.6} />
|
| 329 |
</group>
|
|
|
|
| 200 |
</>
|
| 201 |
)
|
| 202 |
}` ],
|
| 203 |
+
/* Dây leo quanh vòm cửa + bảng chuyên cần đứng — vines/board như gốc */
|
| 204 |
+
[ `function GroundGeom() {`,
|
| 205 |
+
`function Vines() {
|
| 206 |
+
const strands = useMemo(() => {
|
| 207 |
+
const out = []
|
| 208 |
+
const rand = mulberry(13)
|
| 209 |
+
for (let i = 0; i < 26; i++) {
|
| 210 |
+
const a = Math.PI + (i - 13) * 0.12
|
| 211 |
+
const r = 4.9
|
| 212 |
+
const x = Math.sin(a) * r * 1.15 + 1.2, z = Math.cos(a) * r - 1.5
|
| 213 |
+
const len = 1.6 + rand() * 2.2
|
| 214 |
+
out.push({ x, z, len, sway: rand() * Math.PI * 2, leaf: rand() * 5 })
|
| 215 |
+
}
|
| 216 |
+
return out
|
| 217 |
+
}, [])
|
| 218 |
+
return (
|
| 219 |
+
<group>
|
| 220 |
+
{strands.map((v, i) => (
|
| 221 |
+
<group key={i} position={[v.x, 3.4 - v.len / 2, v.z]}>
|
| 222 |
+
<mesh>
|
| 223 |
+
<cylinderGeometry args={[0.035, 0.05, v.len, 4]} />
|
| 224 |
+
<meshLambertMaterial color="#3d5a22" flatShading />
|
| 225 |
+
</mesh>
|
| 226 |
+
<mesh position={[0, -v.len / 2 + 0.1, 0]} rotation-x={0.5}>
|
| 227 |
+
<sphereGeometry args={[0.1, 5, 5]} />
|
| 228 |
+
<meshLambertMaterial color="#4c7a2e" flatShading />
|
| 229 |
+
</mesh>
|
| 230 |
+
</group>
|
| 231 |
+
))}
|
| 232 |
+
</group>
|
| 233 |
+
)
|
| 234 |
+
}
|
| 235 |
+
function AttendanceBoard() {
|
| 236 |
+
const [rows, setRows] = React.useState(null)
|
| 237 |
+
React.useEffect(() => {
|
| 238 |
+
let alive = true
|
| 239 |
+
fetch('/api/attendance').then(r => r.ok ? r.json() : Promise.reject()).then(d => {
|
| 240 |
+
if (alive && d && d.rows) setRows(d.rows)
|
| 241 |
+
}).catch(() => {
|
| 242 |
+
if (!alive) return
|
| 243 |
+
const r = []
|
| 244 |
+
for (let i = 0; i < 5; i++) {
|
| 245 |
+
const row = []
|
| 246 |
+
for (let j = 0; j < 10; j++) row.push(Math.random() > 0.35 ? { name: '', number: 0 } : null)
|
| 247 |
+
r.push(row)
|
| 248 |
+
}
|
| 249 |
+
setRows(r)
|
| 250 |
+
})
|
| 251 |
+
return () => { alive = false }
|
| 252 |
+
}, [])
|
| 253 |
+
const tex = React.useMemo(() => {
|
| 254 |
+
const c = document.createElement('canvas'); c.width = 640; c.height = 320
|
| 255 |
+
const g = c.getContext('2d')
|
| 256 |
+
g.fillStyle = '#7a5f42'; g.fillRect(0, 0, 640, 320)
|
| 257 |
+
g.strokeStyle = '#4a3522'; g.lineWidth = 4; g.strokeRect(4, 4, 632, 312)
|
| 258 |
+
g.fillStyle = '#3a2a18'; g.font = '700 26px "Be Vietnam Pro", sans-serif'; g.textAlign = 'center'
|
| 259 |
+
g.fillText('CHUYÊN CẦN THÁNG NÀY', 320, 36)
|
| 260 |
+
for (let r = 0; r < 5; r++) for (let c = 0; c < 10; c++) {
|
| 261 |
+
const x = 16 + c * 61, y = 50 + r * 50
|
| 262 |
+
g.fillStyle = '#5a442c'; g.fillRect(x, y, 56, 42)
|
| 263 |
+
if (rows && rows[r] && rows[r][c]) {
|
| 264 |
+
g.fillStyle = '#c8102e'; g.fillRect(x + 3, y + 3, 50, 36)
|
| 265 |
+
g.fillStyle = '#fff'; g.font = '600 18px "Be Vietnam Pro", sans-serif'
|
| 266 |
+
g.fillText('✓', x + 28, y + 27)
|
| 267 |
+
} else { g.fillStyle = '#8a6f4e'; g.fillRect(x + 3, y + 3, 50, 36) }
|
| 268 |
+
}
|
| 269 |
+
const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace
|
| 270 |
+
return t
|
| 271 |
+
}, [rows])
|
| 272 |
+
if (!rows) return null
|
| 273 |
+
return (
|
| 274 |
+
<mesh position={[0, 1.45, 0.02]} castShadow>
|
| 275 |
+
<planeGeometry args={[2.9, 1.45]} />
|
| 276 |
+
<meshStandardMaterial map={tex} roughness={0.85} />
|
| 277 |
+
</mesh>
|
| 278 |
+
)
|
| 279 |
+
}
|
| 280 |
+
function GroundGeom() {` ],
|
| 281 |
+
/* Wire AttendanceBoard vào PostAndPlaque (trụ bảng) */
|
| 282 |
+
[ ` <meshStandardMaterial map={tex} alphaTest={0.5} side={THREE.DoubleSide} roughness={0.85} />
|
| 283 |
+
</mesh>
|
| 284 |
+
)}`,
|
| 285 |
+
` <meshStandardMaterial map={tex} alphaTest={0.5} side={THREE.DoubleSide} roughness={0.85} />
|
| 286 |
+
</mesh>
|
| 287 |
+
)}
|
| 288 |
+
<AttendanceBoard />` ],
|
| 289 |
+
/* Wire NatureGround + Vines vào HomeScene */
|
| 290 |
[ ` <CaveExterior />
|
| 291 |
<group position={layout.stele} rotation-y={layout.steleRotY}>`,
|
| 292 |
` <CaveExterior />
|
| 293 |
<NatureGround />
|
| 294 |
+
<Vines />
|
| 295 |
<group position={layout.stele} rotation-y={layout.steleRotY}>` ],
|
| 296 |
/* Bảng chuyên cần attendance trên bia đá */
|
| 297 |
[ `/* ---- Camera rig + layout (MA gốc) ---- */`,
|
|
|
|
| 380 |
CÚP VÀNG TRUNG TÂM (trophy) — trước vòng huyền thoại, kỷ vật MU
|
| 381 |
========================================================================= */
|
| 382 |
function Trophy() {
|
|
|
|
|
|
|
| 383 |
return (
|
| 384 |
<group position={[0, 0, 0]} rotation-y={0.5}>
|
| 385 |
<mesh position={[0, 0.12, 0]} castShadow>
|
|
|
|
| 388 |
</mesh>
|
| 389 |
<mesh position={[0, 0.34, 0]} castShadow>
|
| 390 |
<cylinderGeometry args={[0.55, 0.7, 0.2, 8]} />
|
| 391 |
+
<meshStandardMaterial color="#8a6a2c" metalness={0.7} roughness={0.35} flatShading />
|
| 392 |
</mesh>
|
| 393 |
<mesh position={[0, 0.85, 0]} castShadow>
|
| 394 |
<cylinderGeometry args={[0.42, 0.5, 0.85, 10]} />
|
| 395 |
+
<meshStandardMaterial color="#c9a24e" metalness={0.85} roughness={0.25} flatShading />
|
| 396 |
</mesh>
|
| 397 |
<mesh position={[0, 1.32, 0]} castShadow>
|
| 398 |
<torusGeometry args={[0.5, 0.09, 8, 18]} />
|
| 399 |
+
<meshStandardMaterial color="#c9a24e" metalness={0.85} roughness={0.25} flatShading />
|
| 400 |
</mesh>
|
| 401 |
<mesh position={[-0.42, 0.85, 0]} rotation-z={0.3}>
|
| 402 |
<torusGeometry args={[0.26, 0.07, 8, 14, Math.PI]} />
|
| 403 |
+
<meshStandardMaterial color="#c9a24e" metalness={0.85} roughness={0.25} flatShading />
|
| 404 |
</mesh>
|
| 405 |
<mesh position={[0.42, 0.85, 0]} rotation-z={-0.3}>
|
| 406 |
<torusGeometry args={[0.26, 0.07, 8, 14, Math.PI]} />
|
| 407 |
+
<meshStandardMaterial color="#c9a24e" metalness={0.85} roughness={0.25} flatShading />
|
| 408 |
</mesh>
|
| 409 |
<mesh position={[0, 1.62, 0]} castShadow>
|
| 410 |
<sphereGeometry args={[0.2, 14, 14]} />
|
| 411 |
+
<meshStandardMaterial color="#c9a24e" metalness={0.85} roughness={0.25} flatShading />
|
| 412 |
</mesh>
|
| 413 |
<pointLight position={[0, 2.6, 0]} color="#ffd76a" intensity={18} distance={9} decay={1.6} />
|
| 414 |
</group>
|
patch_backend.mjs
CHANGED
|
@@ -19,7 +19,7 @@ if (!s.includes(oldVer)) {
|
|
| 19 |
console.error('PATCH FAILED: VERSION_ID marker not found')
|
| 20 |
process.exit(1)
|
| 21 |
}
|
| 22 |
-
s = s.replace(oldVer, 'VERSION_ID = "
|
| 23 |
|
| 24 |
// IMPORTANT: phải chèn TRƯỚC catch-all @app.get("/{path:path}") (serve_static) —
|
| 25 |
// nếu chèn sau, FastAPI match route theo thứ tự đăng ký → GET /api/logs bị
|
|
@@ -83,4 +83,4 @@ async def api_attendance():
|
|
| 83 |
`
|
| 84 |
s = s.slice(0, idx) + inject + s.slice(idx)
|
| 85 |
fs.writeFileSync(path, s)
|
| 86 |
-
console.log(`patched backend.py ok: VERSION_ID=
|
|
|
|
| 19 |
console.error('PATCH FAILED: VERSION_ID marker not found')
|
| 20 |
process.exit(1)
|
| 21 |
}
|
| 22 |
+
s = s.replace(oldVer, 'VERSION_ID = "v8"')
|
| 23 |
|
| 24 |
// IMPORTANT: phải chèn TRƯỚC catch-all @app.get("/{path:path}") (serve_static) —
|
| 25 |
// nếu chèn sau, FastAPI match route theo thứ tự đăng ký → GET /api/logs bị
|
|
|
|
| 83 |
`
|
| 84 |
s = s.slice(0, idx) + inject + s.slice(idx)
|
| 85 |
fs.writeFileSync(path, s)
|
| 86 |
+
console.log(`patched backend.py ok: VERSION_ID=v8, /api/log + /api/logs + /api/attendance (marker [${used}])`)
|