// Local component fixture, using the installed Cocos 2.4 engine and a real Asset Bundle. // No game login, network API or real payment is called. Run: node tools/coin-madness/preview.cjs const http = require('http'); const fs = require('fs'); const path = require('path'); const ts = require('typescript'); const root = path.resolve(__dirname, '../..'); const creator = process.env.COCOS_CREATOR_PATH || 'C:/ProgramData/cocos/editors/Creator/2.4.15'; const engine = path.join(creator, 'resources/engine/bin/cocos2d-js-for-preview.js'); const uuid = JSON.parse(fs.readFileSync(path.join(root, 'assets/coin_madness/prefab/CoinMadness.prefab.meta'))).uuid; const helpUuid = JSON.parse(fs.readFileSync(path.join(root, 'assets/coin_madness/prefab/CoinMadnessHelp.prefab.meta'))).uuid; const settlementUuid = JSON.parse(fs.readFileSync(path.join(root, 'assets/coin_madness/prefab/CoinMadnessSettlement.prefab.meta'))).uuid; const scriptUuid = JSON.parse(fs.readFileSync(path.join(root, 'assets/coin_madness/script/CoinMadnessPanel.ts.meta'))).uuid; const hex=scriptUuid.replaceAll('-',''), alphabet='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; const scriptId=hex.slice(0,5)+hex.slice(5).match(/.{3}/g).map(s=>alphabet[parseInt(s,16)>>6]+alphabet[parseInt(s,16)&63]).join(''); const textures = fs.readdirSync(path.join(root,'assets/coin_madness/texture'), { recursive: true }).map(n=>n.replaceAll('\\','/')).filter(n=>n.endsWith('.png') && fs.existsSync(path.join(root,'assets/coin_madness/texture',n+'.meta'))).map(file=>({file,...JSON.parse(fs.readFileSync(path.join(root,'assets/coin_madness/texture',file+'.meta')))})); const frames=textures.map(t=>({texture:t,meta:t.subMetas[path.basename(t.file, '.png')]})).filter(f=>f.meta); for (const file of fs.readdirSync(path.join(root,'assets/coin_madness/texture'), {recursive:true}).filter(f=>f.endsWith('.plist.meta'))) { const atlasMeta=JSON.parse(fs.readFileSync(path.join(root,'assets/coin_madness/texture',file))); frames.push(...Object.values(atlasMeta.subMetas).map(meta=>({texture:textures.find(t=>t.uuid===meta.rawTextureUuid),meta}))); } const html = `
`; function compile(file){return ts.transpileModule(fs.readFileSync(path.join(root,file),'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES5,module:ts.ModuleKind.CommonJS}}).outputText;} const server = http.createServer((req,res)=>{ const url = req.url.split('?')[0].replace(/^\/assets\//, '/'); if(url==='/favicon.ico'){res.statusCode=204;return res.end();} if(url==='/entry.png'){res.setHeader('Content-Type','image/png');return fs.createReadStream(path.join(root,'assets/resources/coin_madness_logo.png')).pipe(res);} if(url==='/'){res.setHeader('Content-Type','text/html; charset=utf-8');return res.end(html);} if(url==='/engine.js'){res.setHeader('Content-Type','text/javascript');return fs.createReadStream(engine).pipe(res);} if(url==='/host.js'){ const source=fs.readFileSync(path.join(root,'assets/Script/coin_madness/CoinMadnessHost.ts'),'utf8'); const output=ts.transpileModule(source,{compilerOptions:{target:ts.ScriptTarget.ES5,module:ts.ModuleKind.CommonJS,experimentalDecorators:true}}).outputText; res.setHeader('Content-Type','text/javascript');return res.end('(function(){var exports={};function require(name){if(name.includes("GoldMinerService"))return {default:{testEnabled:()=>!!window.testService?.testAvailable(),current:()=>window.testService||null}};if(name.includes("Utils"))return {default:{serverUrl:()=>"fixture://"}};if(name.includes("MiniGameSdk"))return {MiniGameSdk:{API:{showToast:console.log}}};return {default:{get:()=>({enqueue:(k,p,run)=>run(()=>{})})}};}cc._RF.push({},"CoinMadnessHost","CoinMadnessHost");'+output+';cc._RF.pop();})();'); } if(/^\/native\/[a-f0-9]{2}\/[a-f0-9-]+\.png$/.test(url)){ const target=path.join(root,'library/imports',url.slice('/native/'.length)); if(fs.existsSync(target)){res.setHeader('Content-Type','image/png');return fs.createReadStream(target).pipe(res);} } if(/^\/imports\/[a-f0-9]{2}\/[a-f0-9-]+\.json$/.test(url)){ const target=path.join(root,'library',url.slice(1)); if(fs.existsSync(target)){res.setHeader('Content-Type','application/json');return fs.createReadStream(target).pipe(res);} const config=JSON.parse(fs.readFileSync(path.join(creator,'resources/cocos2d-x/simulator/win32/assets/internal/config.json'))); const id=path.basename(url,'.json'), entry=config.paths[id]; if(entry && entry[1]==='cc.Material'){ const material=path.join(creator,'resources/static/default-assets/resources',entry[0]+'.mtl'); if(fs.existsSync(material)){res.setHeader('Content-Type','application/json');return fs.createReadStream(material).pipe(res);} } } if(url.startsWith('/internal/')){ const base=path.join(creator,'resources/cocos2d-x/simulator/win32/assets/internal'); const target=path.resolve(base,url.slice('/internal/'.length)); if(!target.startsWith(base+path.sep)||!fs.existsSync(target)){res.statusCode=404;return res.end();} res.setHeader('Content-Type',target.endsWith('.json')?'application/json':target.endsWith('.js')?'text/javascript':'application/octet-stream'); return fs.createReadStream(target).pipe(res); } if(url==='/coin_madness/config.json'){res.setHeader('Content-Type','application/json');return res.end(JSON.stringify({name:'coin_madness',debug:true,importBase:'import',nativeBase:'native',deps:[],uuids:[uuid,helpUuid,settlementUuid,...textures.map(t=>t.uuid),...frames.map(f=>f.meta.uuid)],paths:{[settlementUuid]:['prefab/CoinMadnessSettlement','cc.Prefab'],[uuid]:['prefab/CoinMadness','cc.Prefab'],[helpUuid]:['prefab/CoinMadnessHelp','cc.Prefab'],...Object.fromEntries(textures.map(t=>[t.uuid,['texture/'+t.file.slice(0,-4),'cc.Texture2D']])),...Object.fromEntries(frames.map(f=>[f.meta.uuid,['texture/'+f.texture.file.slice(0,-4),'cc.SpriteFrame']]))},scenes:{},packs:{},versions:{import:[],native:[]}}));} const frame=frames.find(f=>url==='/coin_madness/import/'+f.meta.uuid.slice(0,2)+'/'+f.meta.uuid+'.json'); if(frame){const m=frame.meta;res.setHeader('Content-Type','application/json');return res.end(JSON.stringify({__type__:'cc.SpriteFrame',content:{name:frame.texture.file.slice(0,-4),texture:frame.texture.uuid,rect:[m.trimX||0,m.trimY||0,m.width,m.height],offset:[0,0],originalSize:[m.width,m.height],capInsets:[m.borderLeft,m.borderTop,m.borderRight,m.borderBottom]}}));} const texture=textures.find(t=>url==='/coin_madness/import/'+t.uuid.slice(0,2)+'/'+t.uuid+'.json'||url==='/coin_madness/native/'+t.uuid.slice(0,2)+'/'+t.uuid+'.png'); if(texture){ if(url.endsWith('.png')){res.setHeader('Content-Type','image/png');return res.end(fs.readFileSync(path.join(root,'assets/coin_madness/texture',texture.file)));} res.setHeader('Content-Type','application/json');return res.end(JSON.stringify({__type__:'cc.Texture2D',content:'0,9729,9729,33071,33071,0,0,1'})); } if(url==='/coin_madness/index.js'){ const source=fs.readFileSync(path.join(root,'assets/coin_madness/script/CoinMadnessPanel.ts'),'utf8'); const output=ts.transpileModule(source,{compilerOptions:{target:ts.ScriptTarget.ES5,module:ts.ModuleKind.CommonJS,experimentalDecorators:true}}).outputText; const settlement = ts.transpileModule(fs.readFileSync(path.join(root,'assets/coin_madness/script/CoinMadnessSettlement.ts'),'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES5,module:ts.ModuleKind.CommonJS,experimentalDecorators:true}}).outputText; const settlementHex = JSON.parse(fs.readFileSync(path.join(root,'assets/coin_madness/script/CoinMadnessSettlement.ts.meta'))).uuid.replaceAll('-',''); const settlementId = settlementHex.slice(0,5)+settlementHex.slice(5).match(/.{3}/g).map(s=>alphabet[parseInt(s,16)>>6]+alphabet[parseInt(s,16)&63]).join(''); const digits=ts.transpileModule(fs.readFileSync(path.join(root,'assets/coin_madness/script/CoinMadnessDigits.ts'),'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES5,module:ts.ModuleKind.CommonJS,experimentalDecorators:true}}).outputText; const digitHex=JSON.parse(fs.readFileSync(path.join(root,'assets/coin_madness/script/CoinMadnessDigits.ts.meta'))).uuid.replaceAll('-',''); const digitId=digitHex.slice(0,5)+digitHex.slice(5).match(/.{3}/g).map(s=>alphabet[parseInt(s,16)>>6]+alphabet[parseInt(s,16)&63]).join(''); res.setHeader('Content-Type','text/javascript');return res.end('(function(){var digits=(function(){var exports={};cc._RF.push({},'+JSON.stringify(digitId)+',"CoinMadnessDigits");'+digits+';cc._RF.pop();return exports;})();function require(name){return name.includes("Digits")?digits:name.includes("BrownLoadingSpinner")?spinner:{};}var spinner=(function(){var exports={};'+compile('assets/Script/BrownLoadingSpinner.ts')+';return exports;})();var exports={};cc._RF.push({},'+JSON.stringify(scriptId)+',"CoinMadnessPanel");'+output+';cc._RF.pop();var exports={};cc._RF.push({},'+JSON.stringify(settlementId)+',"CoinMadnessSettlement");'+settlement+';cc._RF.pop();})();'); } if(url==='/coin_madness/import/'+helpUuid.slice(0,2)+'/'+helpUuid+'.json'){res.setHeader('Content-Type','application/json');return res.end(fs.readFileSync(path.join(root,'assets/coin_madness/prefab/CoinMadnessHelp.prefab')));} if(url==='/coin_madness/import/'+settlementUuid.slice(0,2)+'/'+settlementUuid+'.json'){res.setHeader('Content-Type','application/json');return res.end(fs.readFileSync(path.join(root,'assets/coin_madness/prefab/CoinMadnessSettlement.prefab')));} if(url==='/coin_madness/import/'+uuid.slice(0,2)+'/'+uuid+'.json'){ res.setHeader('Content-Type','application/json');return res.end(fs.readFileSync(path.join(root,'assets/coin_madness/prefab/CoinMadness.prefab'))); } res.statusCode=404;res.end('Not found'); }); server.listen(Number(process.env.PORT)||8765,'127.0.0.1',()=>console.log('Coin Madness fixture: http://127.0.0.1:'+server.address().port));