Skip to content

Commit 1550ad5

Browse files
committed
Merge branch 'master' of github.com:cloudwu/deepfuture
2 parents 30a170a + 5589f5f commit 1550ad5

File tree

2 files changed

+253
-88
lines changed

2 files changed

+253
-88
lines changed

.github/assets/index.html

Lines changed: 251 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,106 +4,269 @@
44
<meta charset="utf-8">
55
<title>Deep Future</title>
66
<style>
7-
body { margin: 0; background: #000; }
8-
canvas { display: block; width: 100vw; height: 100vh; }
7+
body {
8+
margin: 0;
9+
background: #000;
10+
font-family: "Segoe UI", "PingFang SC", "Hiragino Sans GB", Arial, sans-serif;
11+
color: #f5f5f5;
12+
}
13+
canvas {
14+
display: block;
15+
width: 100vw;
16+
height: 100vh;
17+
}
18+
#overlay {
19+
position: fixed;
20+
inset: 0;
21+
display: flex;
22+
align-items: center;
23+
justify-content: center;
24+
padding: 24px;
25+
box-sizing: border-box;
26+
background: radial-gradient(circle at top, rgba(36, 64, 128, 0.8), rgba(6, 10, 24, 0.95));
27+
text-align: center;
28+
transition: opacity 0.4s ease;
29+
z-index: 10;
30+
}
31+
#overlay.hidden {
32+
opacity: 0;
33+
pointer-events: none;
34+
}
35+
#overlay.error {
36+
background: rgba(16, 16, 16, 0.95);
37+
}
38+
.overlay-content {
39+
max-width: 640px;
40+
}
41+
#overlay-title {
42+
font-size: 2rem;
43+
letter-spacing: 0.08em;
44+
text-transform: uppercase;
45+
margin: 0 0 12px;
46+
}
47+
#overlay-intro {
48+
font-size: 1rem;
49+
line-height: 1.6;
50+
margin-bottom: 24px;
51+
}
52+
#overlay-intro p {
53+
margin: 0 0 12px;
54+
}
55+
#overlay-status {
56+
font-size: 0.95rem;
57+
opacity: 0.85;
58+
margin: 0;
59+
}
960
</style>
1061
<script src="./coi-serviceworker.min.js"></script>
1162
</head>
1263
<body>
64+
<div id="overlay">
65+
<div class="overlay-content">
66+
<h1 id="overlay-title"></h1>
67+
<div id="overlay-intro"></div>
68+
<p id="overlay-status"></p>
69+
</div>
70+
</div>
1371
<canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
1472

1573
<script>
16-
var Module = {
17-
arguments: ["zipfile=/data/main.zip:/data/font.zip"],
18-
canvas: document.getElementById('canvas'),
19-
preRun: [function () {
20-
const runDependency = 'load-main-zip';
21-
const fontDependency = 'load-font';
22-
23-
const originalLookup = MEMFS.lookup;
24-
MEMFS.lookup = function (parent, name) {
25-
try {
26-
return originalLookup.call(this, parent, name);
27-
} catch (e) {
28-
console.error('[MEMFS lookup failed]',
29-
'parent:', Module.FS.getPath(parent),
30-
'name:', name, e);
31-
throw e;
32-
}
33-
};
74+
(function () {
75+
const overlay = document.getElementById('overlay');
76+
const titleEl = document.getElementById('overlay-title');
77+
const introEl = document.getElementById('overlay-intro');
78+
const statusEl = document.getElementById('overlay-status');
79+
80+
const locale = (navigator.language || navigator.userLanguage || 'en').toLowerCase();
81+
const isChinese = locale.startsWith('zh');
82+
83+
const strings = isChinese ? {
84+
gameTitle: '深远未来',
85+
intro: [
86+
'拓殖星球,发展科技,永无止境地进化你银河系的深远未来。',
87+
'《深远未来》是桌游的数字化版本,带来“边玩边创造”的独特体验,每一局都能拓展属于你的银河文明。'
88+
],
89+
checkingWebGPU: '正在检查 WebGPU 支持…',
90+
webgpuMissingTitle: '当前浏览器未开启 WebGPU 支持',
91+
webgpuMissingDetail: '请在支持 WebGPU 的浏览器中打开,或者在浏览器设置中启用 WebGPU 后刷新页面。',
92+
loadingResources: '正在加载游戏资源…',
93+
loadingMainArchive: '正在加载核心资源包…',
94+
loadingFontArchive: '正在加载字体资源包…',
95+
runtimeReady: '引擎已就绪,正在启动…',
96+
runtimeFailedTitle: '运行时加载失败',
97+
runtimeFailedDetail: '加载运行时失败,请稍后重试。'
98+
} : {
99+
gameTitle: 'Deep Future',
100+
intro: [
101+
'Settle worlds, advance techs, and endlessly evolve your galaxy\'s deep future.',
102+
'Deep Future is the digital take on the make-as-you-play board game where every session grows your galactic civilization.'
103+
],
104+
checkingWebGPU: 'Checking for WebGPU support…',
105+
webgpuMissingTitle: 'WebGPU is not enabled in this browser',
106+
webgpuMissingDetail: 'Open the page in a browser with WebGPU support or enable it in your browser settings, then reload.',
107+
loadingResources: 'Loading game assets…',
108+
loadingMainArchive: 'Fetching core content…',
109+
loadingFontArchive: 'Fetching font resources…',
110+
runtimeReady: 'Runtime ready. Launching…',
111+
runtimeFailedTitle: 'Runtime failed to load',
112+
runtimeFailedDetail: 'We could not load the runtime. Please try again later.'
113+
};
114+
115+
titleEl.textContent = strings.gameTitle;
116+
introEl.innerHTML = strings.intro.map(text => '<p>' + text + '</p>').join('');
117+
118+
function setStatus(message) {
119+
statusEl.textContent = message;
120+
}
121+
122+
function showError(title, detail) {
123+
overlay.classList.remove('hidden');
124+
overlay.classList.add('error');
125+
titleEl.textContent = title;
126+
setStatus(detail);
127+
}
34128

35-
Module.FS_createPath('/', 'data', true, true);
129+
function hideOverlay() {
130+
overlay.classList.add('hidden');
131+
}
36132

133+
async function hasWebGPU() {
134+
if (!navigator.gpu || typeof navigator.gpu.requestAdapter !== 'function') {
135+
return false;
136+
}
37137
try {
38-
FS.mkdir('/persistent');
39-
FS.mount(IDBFS, {autoPersist: true}, '/persistent');
40-
FS.syncfs(true, err => {
41-
if (err) console.error('Failed to sync from IDBFS', err);
42-
else console.log('Synced from IDBFS');
43-
});
44-
45-
setInterval(() => {
46-
FS.syncfs(false, err => {
47-
if (err) console.error('Failed to sync to IDBFS', err);
48-
else console.log('Synced to IDBFS');
138+
const adapter = await navigator.gpu.requestAdapter();
139+
return Boolean(adapter);
140+
} catch (err) {
141+
console.error('WebGPU adapter request failed', err);
142+
return false;
143+
}
144+
}
145+
146+
function loadRuntimeScript() {
147+
return new Promise(function (resolve, reject) {
148+
const script = document.createElement('script');
149+
script.src = './soluna.js';
150+
script.onload = () => resolve();
151+
script.onerror = () => reject(new Error('Failed to load soluna.js'));
152+
document.head.appendChild(script);
153+
});
154+
}
155+
156+
setStatus(strings.checkingWebGPU);
157+
158+
hasWebGPU().then(function (supported) {
159+
if (!supported) {
160+
showError(strings.webgpuMissingTitle, strings.webgpuMissingDetail);
161+
return;
162+
}
163+
164+
setStatus(strings.loadingResources);
165+
166+
window.Module = {
167+
arguments: ["zipfile=/data/main.zip:/data/font.zip"],
168+
canvas: document.getElementById('canvas'),
169+
preRun: [function () {
170+
const runDependency = 'load-main-zip';
171+
const fontDependency = 'load-font';
172+
173+
const originalLookup = MEMFS.lookup;
174+
MEMFS.lookup = function (parent, name) {
175+
try {
176+
return originalLookup.call(this, parent, name);
177+
} catch (e) {
178+
console.error('[MEMFS lookup failed]',
179+
'parent:', Module.FS.getPath(parent),
180+
'name:', name, e);
181+
throw e;
182+
}
183+
};
184+
185+
Module.FS_createPath('/', 'data', true, true);
186+
187+
try {
188+
FS.mkdir('/persistent');
189+
FS.mount(IDBFS, { autoPersist: true }, '/persistent');
190+
FS.syncfs(true, err => {
191+
if (err) console.error('Failed to sync from IDBFS', err);
192+
else console.log('Synced from IDBFS');
49193
});
50-
}, 10000);
51-
} catch (e) {}
52-
53-
Module.addRunDependency(runDependency);
54-
fetch('./main.zip')
55-
.then(function (response) {
56-
if (!response.ok) {
57-
throw new Error('HTTP ' + response.status + ' while fetching main.zip');
58-
}
59-
return response.arrayBuffer();
60-
})
61-
.then(function (buffer) {
62-
const data = new Uint8Array(buffer);
63-
Module.FS.writeFile('/data/main.zip', data, { canOwn: true });
64-
console.log('main.zip loaded:', Module.FS.readdir('/data'));
65-
})
66-
.catch(function (err) {
67-
console.error('Failed to load main.zip', err);
68-
throw err;
69-
})
70-
.finally(function () {
71-
Module.removeRunDependency(runDependency);
72-
});
73-
Module.addRunDependency(fontDependency);
74-
fetch('./font.zip')
75-
.then(function (response) {
76-
if (!response.ok) {
77-
throw new Error('HTTP ' + response.status + ' while fetching font.zip');
194+
195+
setInterval(() => {
196+
FS.syncfs(false, err => {
197+
if (err) console.error('Failed to sync to IDBFS', err);
198+
else console.log('Synced to IDBFS');
199+
});
200+
}, 10000);
201+
} catch (e) {
202+
console.warn('Failed to init persistent storage', e);
78203
}
79-
return response.arrayBuffer();
80-
})
81-
.then(function (buffer) {
82-
const data = new Uint8Array(buffer);
83-
Module.FS.writeFile('/data/font.zip', data, { canOwn: true });
84-
console.log('font.zip loaded:', Module.FS.readdir('/data'));
85-
})
86-
.catch(function (err) {
87-
console.error('Failed to load font.zip', err);
88-
throw err;
89-
})
90-
.finally(function () {
91-
Module.removeRunDependency(fontDependency);
92-
});
93-
}],
94-
onRuntimeInitialized: function () {
95-
console.log('Soluna runtime ready');
96-
},
97-
onExit: function (status) {
98-
console.log('Program exited with status', status);
99-
},
100-
onAbort: function (what) {
101-
console.error('Program aborted:', what);
102-
},
103-
print: console.log,
104-
printErr: console.error
105-
};
204+
205+
Module.addRunDependency(runDependency);
206+
setStatus(strings.loadingMainArchive);
207+
fetch('./main.zip')
208+
.then(function (response) {
209+
if (!response.ok) {
210+
throw new Error('HTTP ' + response.status + ' while fetching main.zip');
211+
}
212+
return response.arrayBuffer();
213+
})
214+
.then(function (buffer) {
215+
const data = new Uint8Array(buffer);
216+
Module.FS.writeFile('/data/main.zip', data, { canOwn: true });
217+
console.log('main.zip loaded:', Module.FS.readdir('/data'));
218+
})
219+
.catch(function (err) {
220+
console.error('Failed to load main.zip', err);
221+
throw err;
222+
})
223+
.finally(function () {
224+
Module.removeRunDependency(runDependency);
225+
});
226+
Module.addRunDependency(fontDependency);
227+
setStatus(strings.loadingFontArchive);
228+
fetch('./font.zip')
229+
.then(function (response) {
230+
if (!response.ok) {
231+
throw new Error('HTTP ' + response.status + ' while fetching font.zip');
232+
}
233+
return response.arrayBuffer();
234+
})
235+
.then(function (buffer) {
236+
const data = new Uint8Array(buffer);
237+
Module.FS.writeFile('/data/font.zip', data, { canOwn: true });
238+
console.log('font.zip loaded:', Module.FS.readdir('/data'));
239+
})
240+
.catch(function (err) {
241+
console.error('Failed to load font.zip', err);
242+
throw err;
243+
})
244+
.finally(function () {
245+
Module.removeRunDependency(fontDependency);
246+
});
247+
}],
248+
onRuntimeInitialized: function () {
249+
console.log('Soluna runtime ready');
250+
setStatus(strings.runtimeReady);
251+
setTimeout(hideOverlay, 400);
252+
},
253+
onExit: function (status) {
254+
console.log('Program exited with status', status);
255+
},
256+
onAbort: function (what) {
257+
console.error('Program aborted:', what);
258+
showError(strings.runtimeFailedTitle, strings.runtimeFailedDetail);
259+
},
260+
print: console.log,
261+
printErr: console.error
262+
};
263+
264+
loadRuntimeScript().catch(function (err) {
265+
console.error(err);
266+
showError(strings.runtimeFailedTitle, strings.runtimeFailedDetail);
267+
});
268+
});
269+
})();
106270
</script>
107-
<script src="./soluna.js"></script>
108271
</body>
109272
</html>

.github/workflows/deploy.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ on:
1212
jobs:
1313
build:
1414
name: Build Soluna
15+
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/master'
1516
runs-on: ubuntu-latest
1617
strategy:
1718
fail-fast: false
@@ -73,6 +74,7 @@ jobs:
7374
path: build/
7475
deploy:
7576
name: Deploy to GitHub Pages
77+
if: github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/master'
7678
needs: [build]
7779
runs-on: ubuntu-latest
7880
permissions:

0 commit comments

Comments
 (0)