try{constvalue=JSON.parse(awaitfsp.readFile(playerPath(playerId),'utf8'));if(!value||typeofvalue!=='object')thrownewError('Invalid player record');value.name=cleanPlayerName(value.name,defaultPlayerName(playerId));value.purchases=normalizePlayerPurchases(value.purchases);value.generationBonuses=normalizeGenerationBonuses(value.generationBonuses);value.economyRevision=Number.isSafeInteger(value.economyRevision)?value.economyRevision:0;value.earnedScore=playerEarnedScore(value);returnvalue}
catch(error){if(error.code==='ENOENT')throwObject.assign(newError('Cloud profile not found'),{status:404});throwerror}
asyncfunctionreadWorldBoard(record,id){constrevision=record.boardVersions[id];if(!Number.isSafeInteger(revision))returnnull;try{constvalue=JSON.parse(awaitfsp.readFile(worldBoardVersionPath(id,revision),'utf8'));returnvalue&&typeofvalue==='object'?value:null}catch(error){if(error.code==='ENOENT')throwObject.assign(newError(`Shared board is missing: ${id}`),{status:500});throwerror}}
asyncfunctionreadJsonBody(req){constchunks=[];letbytes=0;forawait(constchunkofreq){bytes+=chunk.length;if(bytes>MAX_BODY_BYTES)throwObject.assign(newError('Request body is too large'),{status:413});chunks.push(chunk)}if(!chunks.length)return{};try{constparsed=JSON.parse(Buffer.concat(chunks).toString('utf8'));if(!parsed||typeofparsed!=='object'||Array.isArray(parsed))thrownewError();returnparsed}catch{throwObject.assign(newError('Invalid JSON body'),{status:400})}}
constpuzzle=meta.puzzle;if(!puzzle||typeofpuzzle!=='object'||!Array.isArray(puzzle.valid)||!Array.isArray(puzzle.g)||!Array.isArray(puzzle.n)||!Array.isArray(puzzle.solution))badRequest(`Missing puzzle for ${meta.id}`);
constvalid=newSet();for(constcellofpuzzle.valid){if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||valid.has(cellKey(cell)))badRequest(`Invalid puzzle cell for ${meta.id}`);valid.add(cellKey(cell))}
if(!valid.size||valid.size>meta.chunks.length*25)badRequest(`Invalid puzzle area for ${meta.id}`);
constobstacles=newSet();for(constcellofArray.isArray(puzzle.obstacles)?puzzle.obstacles:[]){if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||valid.has(cellKey(cell))||obstacles.has(cellKey(cell)))badRequest(`Invalid obstacle for ${meta.id}`);obstacles.add(cellKey(cell))}
if(valid.size+obstacles.size!==meta.chunks.length*25)badRequest(`Incomplete puzzle area for ${meta.id}`);
constgates=puzzle.g;if(!gates.length||gates.length%2!==0)badRequest(`Invalid gates for ${meta.id}`);
for(constgateofgates)if(!Array.isArray(gate)||gate.length!==3||!Number.isSafeInteger(gate[0])||!Number.isSafeInteger(gate[1])||!valid.has(`${gate[0]},${gate[1]}`)||!['N','S','W','E'].includes(gate[2]))badRequest(`Invalid gate for ${meta.id}`);
for(constclueofpuzzle.n)if(!Array.isArray(clue)||clue.length!==3||!Number.isSafeInteger(clue[0])||!Number.isSafeInteger(clue[1])||!Number.isSafeInteger(clue[2])||clue[2]<0||clue[2]>512||!valid.has(`${clue[0]},${clue[1]}`))badRequest(`Invalid clue for ${meta.id}`);
constwarpMap=newMap();for(constpairofpuzzle.specialCells?.warps||[]){if(!Array.isArray(pair?.a)||!Array.isArray(pair?.b)||!valid.has(cellKey(pair.a))||!valid.has(cellKey(pair.b)))badRequest(`Invalid warp for ${meta.id}`);warpMap.set(cellKey(pair.a),cellKey(pair.b));warpMap.set(cellKey(pair.b),cellKey(pair.a))}
constvalidateRoute=(route,label)=>{if(!route||typeofroute!=='object'||!Array.isArray(route.cells)||!route.cells.length||!Number.isInteger(route.startGate)||!Number.isInteger(route.endGate)||route.startGate<0||route.endGate<0||route.startGate>=gates.length||route.endGate>=gates.length)badRequest(`Invalid ${label} for ${meta.id}`);if(cellKey(route.cells[0])!==`${gates[route.startGate][0]},${gates[route.startGate][1]}`||cellKey(route.cells[route.cells.length-1])!==`${gates[route.endGate][0]},${gates[route.endGate][1]}`)badRequest(`Gate mismatch in ${label} for ${meta.id}`);constlocal=newSet();for(letindex=0;index<route.cells.length;index++){constcell=route.cells[index],key=Array.isArray(cell)?cellKey(cell):'';if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||!valid.has(key)||local.has(key))badRequest(`Invalid ${label} cell for ${meta.id}`);local.add(key);if(index){constprevious=route.cells[index-1],distance=Math.abs(previous[0]-cell[0])+Math.abs(previous[1]-cell[1]);if(distance!==1&&warpMap.get(cellKey(previous))!==key)badRequest(`Disconnected ${label} for ${meta.id}`)}}returnroute.cells};
for(constrouteofpuzzle.solution)for(constcellofvalidateRoute(route,'solution path')){constkey=cellKey(cell),count=(coverage.get(key)||0)+1;if(count>(crossingSet.has(key)?2:1))badRequest(`Overlapping solution for ${meta.id}`);coverage.set(key,count)}
if(coverage.size!==valid.size)badRequest(`Incomplete solution for ${meta.id}`);
if(!chunks.length||chunks.length>50)badRequest(`Invalid chunks for ${meta.id}`);constseen=newSet();for(constcellofchunks){if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1])||Math.abs(cell[0])>1000000||Math.abs(cell[1])>1000000||seen.has(cellKey(cell)))badRequest(`Invalid chunk coordinate for ${meta.id}`);seen.add(cellKey(cell))}
if(!Number.isSafeInteger(meta.x)||!Number.isSafeInteger(meta.y)||Math.abs(meta.x)>100000000||Math.abs(meta.y)>100000000)badRequest(`Invalid board coordinate for ${meta.id}`);
if(!Number.isSafeInteger(meta.seed)||meta.seed<0||meta.seed>0xffffffff)badRequest(`Invalid seed for ${meta.id}`);
if(!Number.isInteger(meta.level)||meta.level<1||meta.level>10||!Number.isInteger(meta.targetLevel)||meta.targetLevel<1||meta.targetLevel>10)badRequest(`Invalid level for ${meta.id}`);
if(meta.entrySide!=null&&!['N','S','W','E'].includes(meta.entrySide))badRequest(`Invalid entry side for ${meta.id}`);
if(meta.sealedSides!=null&&(!Array.isArray(meta.sealedSides)||meta.sealedSides.some(side=>!['N','S','W','E'].includes(side))))badRequest(`Invalid sealed sides for ${meta.id}`);
constderivedLevel=PuzzleCore.solverDifficulty(clean.puzzle,clean.targetLevel);if(clean.level!==derivedLevel)badRequest(`Unverified difficulty for ${clean.id}`);
if(verification.status!=='unique'||verification.signature!==PuzzleCore.puzzleSignature(clean.puzzle))badRequest(`Puzzle is not server-verified: ${clean.id}`);
if(!row||typeofrow!=='object'||!BOARD_RE.test(row.id)||!row.value||typeofrow.value!=='object')badRequest('Invalid board state');if(!meta)badRequest(`State without metadata: ${row.id}`);
conststate=JSON.parse(JSON.stringify(row.value)),puzzle=validatePuzzle(meta),paths=Array.isArray(state.paths)?state.paths:[];if(paths.length>MAX_PATHS_PER_BOARD)badRequest(`Too many paths for ${row.id}`);
for(constitemofpaths){if(!item||typeofitem!=='object'||!Array.isArray(item.cells)||item.cells.length>MAX_CELLS_PER_PATH)badRequest(`Invalid path for ${row.id}`);if(!Number.isInteger(item.startGate)||item.startGate<0||item.startGate>=puzzle.gates.length||item.endGate!=null&&(!Number.isInteger(item.endGate)||item.endGate<0||item.endGate>=puzzle.gates.length))badRequest(`Invalid path gates for ${row.id}`);constseen=newSet();for(letindex=0;index<item.cells.length;index++){constcell=item.cells[index];if(!Array.isArray(cell)||cell.length!==2||!Number.isSafeInteger(cell[0])||!Number.isSafeInteger(cell[1]))badRequest(`Invalid path cell for ${row.id}`);constkey=cellKey(cell);if(!puzzle.valid.has(key)||seen.has(key))badRequest(`Invalid path cell for ${row.id}`);seen.add(key);if(index){constprevious=item.cells[index-1],distance=Math.abs(previous[0]-cell[0])+Math.abs(previous[1]-cell[1]);if(distance!==1&&puzzle.warpMap.get(cellKey(previous))!==key)badRequest(`Disconnected path for ${row.id}`)}}}
if(state.solved===true){constsolvedKeys=paths.map(route=>route.endGate==null?'':normalizedRouteKey(route.cells)).sort();if(solvedKeys.length!==puzzle.solutionKeys.length||solvedKeys.some((key,index)=>!key||key!==puzzle.solutionKeys[index]))badRequest(`Solved state does not match puzzle for ${row.id}`)}
if(!BOARD_RE.test(String(boardId||'')))badRequest('Invalid store board');constitem=STORE_CATALOG.get(itemId);if(!item)badRequest('Invalid store item');
constrow=awaitreadWorldBoard(world,boardId);if(!row?.meta||row.state?.solved!==true||!row.state?.store)badRequest('Store is not available');
conststore=row.state.store,itemIds=Array.isArray(store.itemIds)?store.itemIds:[];if(!itemIds.includes(item.id))badRequest('Item is not sold by this store');
constcursor=Math.max(0,Math.floor(finiteNumber(parameters.get('cursor'),0))),at=Math.max(0,Math.floor(finiteNumber(parameters.get('at'),record.revision)));if(cursor&&at!==record.revision)return{status:409,body:{error:'World changed during paged pull',revision:record.revision,serverTime:serverTime()}};
constmetas=Array.isArray(body.metas)?body.metas:[],states=Array.isArray(body.states)?body.states:[],deleted=[];if(metas.length+states.length>MAX_BOARDS_PER_PUSH*2)throwObject.assign(newError('Too many board changes'),{status:413});
for(constrawMetaofmetas){constclean=validateMeta(rawMeta),row=awaitloadChangedBoard(clean.id);if(row.meta&&worldMetaFingerprint(row.meta)!==worldMetaFingerprint(clean))badRequest(`Existing board is immutable: ${clean.id}`);clean.rev=rowRevision;clean.revAuthor='shared-world';row.meta=clean}
if(record.revision>0&&newMetaIds.length){constgrant=record.expansionGrants?.[player.playerId];if(!grant||grant.expiresAt<serverTime())throwObject.assign(newError('Expansion grant is required'),{status:403});if(newMetaIds.length>Math.min(MAX_NEW_BOARDS_PER_GRANT,grant.maxBoards||0))badRequest('Too many generated boards');constexpectedStart=Math.max(1,Number(record.global?.nextId)||1),numbers=newMetaIds.map(id=>Number(id.slice(1))).sort((a,b)=>a-b);for(leti=0;i<numbers.length;i++)if(numbers[i]!==expectedStart+i)badRequest('Generated board ids are not contiguous');for(constidofnewMetaIds.sort((a,b)=>Number(a.slice(1))-Number(b.slice(1))))addMetaToWorldOccupancy(changedBoards.get(id).meta,occupancy,{requireTouch:true});grant.maxBoards-=newMetaIds.length;if(grant.maxBoards<=0)deleterecord.expansionGrants[player.playerId]}
for(constrawRowofstates){if(!BOARD_RE.test(rawRow?.id))badRequest('Invalid board state');constrow=awaitloadChangedBoard(rawRow.id);if(!row.meta)badRequest(`Board metadata is missing: ${rawRow.id}`);constincoming=validateStateRow(rawRow,row.meta),firstSolve=row.state?.solved!==true&&incoming.solved===true;if(firstSolve&&realtimeHub&&!realtimeHub.hasClaim(player.playerId,rawRow.id))throwObject.assign(newError('Board claim is required'),{status:423});starterRow||=rawRow.id==='B0'?row:awaitreadWorldBoard(record,'B0');constworldSeed=starterRow?.meta?.seed||0;constpublished=publicStateForWorld(incoming,row.state,playerRecord,nextRevision,rowRevision,row.meta,worldSeed);row.state=published.state;if(published.clearEvent){clearEvents.push(published.clearEvent);solved++;earnedScore=Math.min(Number.MAX_SAFE_INTEGER,earnedScore+published.clearEvent.scoreAwarded);playerRecord.earnedScore=Math.min(Number.MAX_SAFE_INTEGER,playerEarnedScore(playerRecord)+published.clearEvent.scoreAwarded);playerRecord.economyRevision=(playerRecord.economyRevision||0)+1;playerRecord.updatedAt=published.clearEvent.solvedAt;record.expansionGrants[player.playerId]={boardId:rawRow.id,expiresAt:published.clearEvent.solvedAt+EXPANSION_GRANT_TTL_MS,maxBoards:MAX_NEW_BOARDS_PER_GRANT}}}
for(const[id,row]ofchangedBoards){if(!row.meta)badRequest(`Board metadata is missing: ${id}`);if(!row.state)row.state=publicStateForWorld({},null,playerRecord,nextRevision,rowRevision,row.meta).state}
stream.on('error',error=>{console.error(error);if(!res.headersSent)json(res,error.code==='ENOENT'?404:500,{error:error.code==='ENOENT'?'Not found':'Internal server error'});elseres.destroy(error)});
asyncfunctionmain(){awaitfsp.mkdir(DATA_DIR,{recursive:true,mode:0o700});awaitrecoverPendingWorldCommit();awaitcollectRetiredBoardVersions();constserver=http.createServer(async(req,res)=>{try{consturl=newURL(req.url,`http://${req.headers.host||'localhost'}`);if(url.pathname.startsWith('/api/'))awaithandleApi(req,res,url);elseawaitserveStatic(req,res,url)}catch(error){console.error(error);if(!res.headersSent)json(res,error.status||500,{error:error.status?error.message:'Internal server error',serverTime:serverTime()});elseres.destroy()}});realtimeHub=createRealtimeHub({server,authenticate:authenticateRealtime,getBoardInfo:realtimeBoardInfo});server.listen(PORT,HOST,()=>{console.log(`BEND FIELD v${BuildMeta.APP_VERSION} shared world: http://${HOST}:${PORT}`);console.log(`Shared world data: ${DATA_DIR}`)})}