Spaces:
Running
Running
Download patch_scene.mjs from bep40/hangmu-docker: direct link, hf CLI and curl.
- Browser
- Download file 9.67 kB
-
https://huggingface.co/spaces/bep40/hangmu-docker/resolve/main/patch_scene.mjs
- Command line
-
hf download hf://spaces/bep40/hangmu-docker/patch_scene.mjs
-
curl -L -o patch_scene.mjs https://huggingface.co/spaces/bep40/hangmu-docker/resolve/main/patch_scene.mjs
9.67 kB
| /* patch_scene.mjs — v17: sân trước cửa hang + cờ MU + áo MU + bảng điểm danh | |
| VẤN ĐỀ v16: cờ MU đặt z=66 nhưng sân ngoài (OutdoorGround) chỉ bắt đầu z~76 và | |
| walkR guard chặn z<55 → vùng z 55..78 là khoảng trống giữa cửa hang và sân | |
| ngoài: không đất nền, vật phẩm vô hình. | |
| FIX v17 (chạy trên SOURCE GỐC cave3d.js — bản chưa patch): | |
| (A) Forecourt — sân đất trước cửa hang (z 55..80) nối cửa với sân ngoài. | |
| (B) Cờ MU/GVMU 2 hàng hai bên lối ra trên forecourt (z=62, z=74). | |
| (C) Ghế gỗ nghỉ chân cạnh forecourt. | |
| (D) Bảng điểm danh CHUYEN CAN THANG NAY dựng trái cửa (z=62). | |
| (E) Giá treo áo MU #7 dựng phải cửa (z=62). | |
| (F) Kéo dài OutdoorPath (11x86) nối liền forecourt. | |
| LƯU Ý: OutdoorScene trong SOURCE GỐC chỉ gồm SkyDome/Ground/Path/Props/Light | |
| (không có Flags/Bench — những cái đó chỉ tồn tại trong bản build v16). | |
| Nếu pattern lệch -> exit(1) để build fail. */ | |
| import fs from 'node:fs' | |
| function patchFile(path, reps) { | |
| let s = fs.readFileSync(path, 'utf8') | |
| const failures = [] | |
| for (const [oldStr, newStr] of reps) { | |
| const count = s.split(oldStr).length - 1 | |
| if (count === 1) { s = s.replace(oldStr, newStr); continue } | |
| failures.push(`pattern not unique/found (count=${count}): ${oldStr.slice(0, 80)}`) | |
| } | |
| if (failures.length) { | |
| console.error(`PATCH SCENE FAILED (${path}):`) | |
| failures.forEach((f) => console.error(' -', f)) | |
| process.exit(1) | |
| } | |
| fs.writeFileSync(path, s) | |
| console.log('patched ok:', path) | |
| } | |
| /* ================= cave3d.js ================= */ | |
| patchFile('frontend/cave3d.js', [ | |
| /* ---- (A) Forecourt: san dat noi cua hang -> san ngoai ---- */ | |
| [ | |
| `function OutdoorGround() {`, | |
| `function Forecourt() { | |
| const geo = useMemo(() => { | |
| const m = new THREE.PlaneGeometry(64, 26, 40, 18) | |
| m.rotateX(-Math.PI / 2) | |
| const p = m.attributes.position | |
| const cols = new Float32Array(p.count * 3) | |
| const cA = new THREE.Color('#7d7258'), cB = new THREE.Color('#5f5540'), moss = new THREE.Color('#6d6a3e'), tmp = new THREE.Color() | |
| for (let i = 0; i < p.count; i++) { | |
| const x = p.getX(i), z = p.getZ(i) | |
| p.setY(i, fbm3(x * 0.09, z * 0.09, 0, 31) * 0.22 + 0.02) | |
| tmp.copy(cA).lerp(cB, fbm3(x * 0.16, z * 0.16, 3, 32) * 0.5 + 0.25) | |
| tmp.lerp(moss, Math.max(0, fbm3(x * 0.1, 9, z * 0.1, 33)) * 0.3) | |
| cols[i * 3] = tmp.r; cols[i * 3 + 1] = tmp.g; cols[i * 3 + 2] = tmp.b | |
| } | |
| m.setAttribute('color', new THREE.BufferAttribute(cols, 3)) | |
| m.computeVertexNormals() | |
| return m | |
| }, []) | |
| return ( | |
| <mesh geometry={geo} position={[0, 0, 67]} receiveShadow fog={false}> | |
| <meshLambertMaterial vertexColors fog={false} /> | |
| </mesh> | |
| ) | |
| } | |
| function OutdoorGround() {`, | |
| ], | |
| /* ---- (B2E) OutdoorScene mới: flags + bench + attendance + jersey ---- */ | |
| [ | |
| `function OutdoorScene() { | |
| return ( | |
| <group> | |
| <SkyDome /> | |
| <OutdoorGround /> | |
| <OutdoorPath /> | |
| <OutdoorProps /> | |
| <OutdoorLight /> | |
| </group> | |
| ) | |
| }`, | |
| `/* ---- Bang diem danh (chuyen can) truoc cua hang ---- */ | |
| function AttendanceForecourt() { | |
| const [rows, setRows] = React.useState(null) | |
| React.useEffect(() => { | |
| let alive = true | |
| fetch('/api/attendance').then(r => r.ok ? r.json() : Promise.reject()).then(d => { | |
| if (alive && d && d.rows) setRows(d.rows) | |
| }).catch(() => { | |
| if (!alive) return | |
| const r = [] | |
| for (let i = 0; i < 5; i++) { | |
| const row = [] | |
| for (let j = 0; j < 10; j++) row.push(Math.random() > 0.35 ? { name: '', number: 0 } : null) | |
| r.push(row) | |
| } | |
| setRows(r) | |
| }) | |
| return () => { alive = false } | |
| }, []) | |
| const tex = React.useMemo(() => { | |
| const c = document.createElement('canvas'); c.width = 640; c.height = 320 | |
| const g = c.getContext('2d') | |
| g.fillStyle = '#7a5f42'; g.fillRect(0, 0, 640, 320) | |
| g.strokeStyle = '#4a3522'; g.lineWidth = 4; g.strokeRect(4, 4, 632, 312) | |
| g.fillStyle = '#3a2a18'; g.font = '700 26px "Be Vietnam Pro", sans-serif'; g.textAlign = 'center' | |
| g.fillText('CHUYEN CAN THANG NAY', 320, 36) | |
| for (let r = 0; r < 5; r++) for (let c = 0; c < 10; c++) { | |
| const x = 16 + c * 61, y = 50 + r * 50 | |
| g.fillStyle = '#5a442c'; g.fillRect(x, y, 56, 42) | |
| if (rows && rows[r] && rows[r][c]) { | |
| g.fillStyle = '#c8102e'; g.fillRect(x + 3, y + 3, 50, 36) | |
| g.fillStyle = '#fff'; g.font = '600 18px "Be Vietnam Pro", sans-serif' | |
| g.fillText('v', x + 28, y + 27) | |
| } else { g.fillStyle = '#8a6f4e'; g.fillRect(x + 3, y + 3, 50, 36) } | |
| } | |
| const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace | |
| return t | |
| }, [rows]) | |
| if (!rows) return null | |
| return ( | |
| <group position={[-4.2, 0, 62]} rotation={[0, 0.85, 0]} fog={false}> | |
| <mesh position={[0, 1.2, -0.12]}> | |
| <boxGeometry args={[2.5, 2.6, 0.2]} /> | |
| <meshLambertMaterial color="#3d2a1a" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0, -0.9, -0.1]}> | |
| <boxGeometry args={[0.18, 1.9, 0.18]} /> | |
| <meshLambertMaterial color="#5a4328" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0, 1.35, 0.05]}> | |
| <planeGeometry args={[2.2, 2.3]} /> | |
| <meshBasicMaterial map={tex} transparent toneMapped={false} fog={false} /> | |
| </mesh> | |
| </group> | |
| ) | |
| } | |
| /* ---- Gia treo ao MU (jersey #7) canh cua ---- */ | |
| function JerseyStand() { | |
| const [tex, setTex] = React.useState(null) | |
| React.useEffect(() => { | |
| let alive = true | |
| const t = _jerseyTex() | |
| if (alive && t) setTex(t) | |
| return () => { alive = false } | |
| }, []) | |
| return ( | |
| <group position={[5.4, 0, 62]} rotation={[0, -0.7, 0]} fog={false}> | |
| <mesh position={[0, 1.3, 0]}> | |
| <cylinderGeometry args={[0.07, 0.09, 2.6, 6]} /> | |
| <meshLambertMaterial color="#4a3220" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0, 2.62, 0]}> | |
| <sphereGeometry args={[0.1, 8, 8]} /> | |
| <meshStandardMaterial color="#c9a24e" fog={false} /> | |
| </mesh> | |
| <mesh position={[0, 2.05, 0]} rotation-x={0.12}> | |
| <planeGeometry args={[1.15, 1.45]} /> | |
| <meshBasicMaterial map={tex ?? null} transparent side={THREE.DoubleSide} toneMapped={false} fog={false} /> | |
| </mesh> | |
| </group> | |
| ) | |
| } | |
| /* ---- Co phuon MU hai ben loi ra (forecourt) ---- */ | |
| function makePennantTex(text) { | |
| if (typeof document === 'undefined') return null | |
| const c = document.createElement('canvas'); c.width = 128; c.height = 128 | |
| const g = c.getContext('2d') | |
| g.fillStyle = '#c8102e'; g.fillRect(0, 0, 128, 128) | |
| g.strokeStyle = '#ffd76a'; g.lineWidth = 6; g.strokeRect(4, 4, 120, 120) | |
| g.fillStyle = '#ffffff'; g.font = '700 40px "Be Vietnam Pro", sans-serif' | |
| g.textAlign = 'center'; g.textBaseline = 'middle' | |
| g.fillText(text, 64, 66) | |
| const t = new THREE.CanvasTexture(c); t.colorSpace = THREE.SRGBColorSpace | |
| return t | |
| } | |
| function OutdoorFlags() { | |
| const texM = useMemo(() => makePennantTex('MU'), []) | |
| const texG = useMemo(() => makePennantTex('GGMU'), []) | |
| const flags = [ | |
| { x: -7, z: 62, tex: texM }, { x: 7, z: 62, tex: texG }, | |
| { x: -11, z: 74, tex: texG }, { x: 11, z: 74, tex: texM }, | |
| ] | |
| return ( | |
| <group> | |
| {flags.map((f, i) => ( | |
| <group key={i} position={[f.x, 0, f.z]} fog={false}> | |
| <mesh position={[0, 1.9, 0]} fog={false}> | |
| <cylinderGeometry args={[0.09, 0.12, 3.8, 6]} /> | |
| <meshLambertMaterial color="#5a4328" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0, 3.85, 0]} fog={false}> | |
| <sphereGeometry args={[0.12, 8, 8]} /> | |
| <meshLambertMaterial color="#c9a24e" fog={false} /> | |
| </mesh> | |
| <mesh position={[0.5, 3.0, 0]} fog={false}> | |
| <planeGeometry args={[1.4, 1.1]} /> | |
| <meshBasicMaterial map={f.tex} transparent side={THREE.DoubleSide} fog={false} toneMapped={false} /> | |
| </mesh> | |
| </group> | |
| ))} | |
| </group> | |
| ) | |
| } | |
| /* ---- Ghe go nghi chan ---- */ | |
| function OutdoorBench() { | |
| const bench = (x, z, rot) => ( | |
| <group key={`${x}-${z}`} position={[x, 0, z]} rotation-y={rot} fog={false}> | |
| <mesh position={[0, 0.5, 0]} fog={false}> | |
| <boxGeometry args={[1.9, 0.16, 0.55]} /> | |
| <meshLambertMaterial color="#6a4a2c" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0, 0.72, 0]} fog={false}> | |
| <boxGeometry args={[1.9, 0.12, 0.5]} /> | |
| <meshLambertMaterial color="#7a5a38" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[-0.72, 0.22, 0]} fog={false}> | |
| <boxGeometry args={[0.12, 0.44, 0.5]} /> | |
| <meshLambertMaterial color="#4a3220" flatShading fog={false} /> | |
| </mesh> | |
| <mesh position={[0.72, 0.22, 0]} fog={false}> | |
| <boxGeometry args={[0.12, 0.44, 0.5]} /> | |
| <meshLambertMaterial color="#4a3220" flatShading fog={false} /> | |
| </mesh> | |
| </group> | |
| ) | |
| return ( | |
| <group> | |
| {bench(-6, 60, 0.12)} | |
| {bench(6, 60, -0.1)} | |
| </group> | |
| ) | |
| } | |
| function OutdoorScene() { | |
| return ( | |
| <group> | |
| <SkyDome /> | |
| <OutdoorGround /> | |
| <OutdoorPath /> | |
| <OutdoorProps /> | |
| <OutdoorFlags /> | |
| <OutdoorBench /> | |
| <AttendanceForecourt /> | |
| <JerseyStand /> | |
| <OutdoorLight /> | |
| </group> | |
| ) | |
| }`, | |
| ], | |
| /* ---- (F) Keo dai OutdoorPath len forecourt nhin lien mach ---- */ | |
| [ | |
| ` <planeGeometry args={[11, 66]} />`, | |
| ` <planeGeometry args={[11, 86]} />`, | |
| ], | |
| ]) | |
| console.log('patch_scene.mjs OK — forecourt + MU flags + jersey stand + attendance board added') |