feat: initialize sunrise with 100-player shared rooms

This commit is contained in:
2026-10-30 16:36:35 +09:00
commit 31d6eac4c2
257 changed files with 66063 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
import { createHash } from 'node:crypto';
import { readFileSync,writeFileSync } from 'node:fs';
import { gzipSync,gunzipSync } from 'node:zlib';
import { createTerrainGeometry } from '../src/scene/terrainGeometry.ts';
import { applySummitTerrain } from '../src/scene/summitTerrain.ts';
import { createSummitWalking } from '../src/controls/summitWalking.ts';
import { buildSummitGeometry } from '../src/scene/summitGeometry.ts';
import { encodeSummitGeometry,decodeSummitGeometry } from '../src/scene/summitAssets.ts';
import { SUMMIT_ASSETS } from '../src/constants/summitAssets.ts';
const file=(path:string)=>new URL('../'+path,import.meta.url);
const hash=(data:Uint8Array)=>createHash('sha256').update(data).digest('hex');
const inputs=['public/terrain/gwanaksan.json','public/terrain/gwanaksan.f32','src/scene/terrainGeometry.ts',
'src/scene/summitTerrain.ts','src/scene/summitGeometry.ts','src/scene/summitAssets.ts','src/scene/structures.ts',
'src/controls/summitWalking.ts','src/controls/summitCoordinates.ts','src/constants/summitAssets.ts','src/constants/summitMaterials.ts',
'src/constants/summitTerrain.ts','src/constants/summitTrails.ts','src/constants/structures.ts','src/constants/terrain.ts',
'src/constants/summitPlateau.ts','scripts/prepare-summit-assets.ts','package-lock.json'];
const digest=createHash('sha256');
for(const path of inputs) {const bytes=readFileSync(file(path));digest.update(path).update('\0').update(path.endsWith('.f32')?bytes:bytes.toString('utf8').replace(/\r\n/g,'\n'));}
const sourceHash=digest.digest('hex');
const manifestPath=file('public'+SUMMIT_ASSETS.manifestUrl),binaryPath=file('public'+SUMMIT_ASSETS.bufferUrl),gzipPath=file('public'+SUMMIT_ASSETS.bufferUrl+'.gz');
if(process.argv.includes('--check')) {
try {
const manifest=JSON.parse(readFileSync(manifestPath,'utf8')),bytes=readFileSync(binaryPath);
if(manifest.version!==SUMMIT_ASSETS.version||manifest.sourceHash!==sourceHash||manifest.bufferHash!==hash(bytes)||manifest.byteLength!==bytes.length||!gunzipSync(readFileSync(gzipPath)).equals(bytes))throw new Error('stale');
console.log('Prebuilt summit is up to date.');
} catch {console.error('정상 지면 사전 생성 파일이 오래되었습니다. npm run prepare:summit 을 실행해 주세요.');process.exitCode=1;}
} else {
const meta=JSON.parse(readFileSync(file('public/terrain/gwanaksan.json'),'utf8'));
const bytes=readFileSync(file('public/terrain/gwanaksan.f32'));
const terrain=createTerrainGeometry(meta,new Float32Array(bytes.buffer,bytes.byteOffset,bytes.byteLength/4),true);
applySummitTerrain(terrain,meta);
const started=performance.now();
const original=buildSummitGeometry(createSummitWalking(meta.origin),terrain,true);
const generateMs=performance.now()-started;
const encoded=encodeSummitGeometry(original),buffer=Buffer.from(encoded),compressed=gzipSync(buffer,{level:9});
const decodedAt=performance.now(),restored=decodeSummitGeometry(encoded),decodeMs=performance.now()-decodedAt;
// Verify all triangles, including every normal, UV and material distance, after indexing.
for(const [name,size] of SUMMIT_ASSETS.attributes) {
const a=original.getAttribute(name),b=restored.getAttribute(name),index=restored.getIndex()!;
for(let i=0;i<a.count;i++)for(let j=0;j<size;j++)if(a.array[i*size+j]!==b.array[index.getX(i)*size+j])throw new Error('Attribute changed: '+name);
}
const manifest={version:SUMMIT_ASSETS.version,sourceHash,bufferHash:hash(buffer),byteLength:buffer.length,gzipBytes:compressed.length,
originalVertices:original.getAttribute('position').count,vertices:restored.getAttribute('position').count,
triangles:restored.getIndex()!.count/3,originalAttributeBytes:Object.values(original.attributes).reduce((n,a)=>n+a.array.byteLength,0),generateMs,decodeMs};
writeFileSync(binaryPath,buffer);writeFileSync(gzipPath,compressed);writeFileSync(manifestPath,JSON.stringify(manifest,null,2)+'\n');
console.log(JSON.stringify(manifest,null,2));original.dispose();restored.dispose();terrain.dispose();
}