Add WASM version of the engine (#9406)

This commit is contained in:
ololoken
2025-01-05 17:51:56 +08:00
committed by GitHub
parent 33c6233d98
commit 076150ef42
107 changed files with 541 additions and 31 deletions
+41
View File
@@ -342,3 +342,44 @@ jobs:
artifactErrorsFailBuild: true
prerelease: true
replacesArtifacts: true
make-emscripten:
name: Make (Emscripten)
runs-on: ubuntu-latest
container: emscripten/emsdk:3.1.74
timeout-minutes: 30
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
sudo apt-get -y update
sudo apt-get -y install gettext p7zip-full
- name: Build
run: |
emmake make -f Makefile.emscripten -j "$(nproc)"
env:
FHEROES2_STRICT_COMPILATION: ON
- name: Create package
run: |
# Translations and H2D files are already included in fheroes2.data
7z a -bb1 -tzip -- fheroes2_emscripten.zip LICENSE changelog.txt fheroes2.data fheroes2.js fheroes2.wasm ./docs/README.txt ./files/emscripten/*
- uses: actions/upload-artifact@v4
if: ${{ github.event_name == 'pull_request' }}
with:
name: fheroes2_emscripten.zip
path: fheroes2_emscripten.zip
if-no-files-found: error
- uses: ncipollo/release-action@v1
if: ${{ github.event_name == 'push' }}
with:
artifacts: fheroes2_emscripten.zip
body: ${{ github.event.head_commit.message }}
token: ${{ secrets.GITHUB_TOKEN }}
name: Emscripten build (latest commit)
tag: fheroes2-emscripten-sdl2_dev
allowUpdates: true
artifactErrorsFailBuild: true
prerelease: true
replacesArtifacts: true
+8 -1
View File
@@ -92,7 +92,14 @@ android/.idea
android/.gradle
# Automatically created Android assets
android/app/src/main/assets/files/
android/app/src/main/assets/
# Mac magic folders
.DS_Store
# Web build artifacts
*.wasm
*.wasm.map
fheroes2.js
fheroes2.data
src/dist/web/dist/*
+43
View File
@@ -0,0 +1,43 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
# Copyright (C) 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
###########################################################################
# Options:
#
# FHEROES2_STRICT_COMPILATION: build in strict compilation mode (turns warnings into errors)
# FHEROES2_WITH_DEBUG: build in debug mode
# FHEROES2_DATA: set the built-in path to the fheroes2 data directory (e.g. /usr/share/fheroes2)
.PHONY: all clean translations
all: fheroes2.js
fheroes2.js: translations
$(MAKE) -C src/dist PLATFORM=emscripten
cp src/dist/fheroes2-ems/fheroes2.data .
cp src/dist/fheroes2-ems/fheroes2.js .
cp src/dist/fheroes2-ems/fheroes2.wasm .
translations:
$(MAKE) -C files/lang
clean:
$(MAKE) -C src/dist clean
$(MAKE) -C files/lang clean
rm -f fheroes2.data fheroes2.js fheroes2.wasm
+2 -2
View File
@@ -1,6 +1,6 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
# Copyright (C) 2021 - 2024 #
# Copyright (C) 2021 - 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
@@ -40,7 +40,7 @@ fheroes2.vpk: eboot.bin param.sfo translations
--add files/images/platform/psv/sce_sys/livearea/contents/template.xml=sce_sys/livearea/contents/template.xml \
--add files/data=files/data \
--add files/lang/vita_temp=files/lang \
fheroes2.vpk
fheroes2.vpk
rm -r files/lang/vita_temp
translations: fheroes2.elf
+9 -1
View File
@@ -99,6 +99,14 @@ tasks.register('copyH2D', Copy) {
into 'src/main/assets/files/data'
}
tasks.register('copyTimidity', Copy) {
from('../../files/timidity') {
include 'instruments/*.pat'
include 'timidity.cfg'
}
into 'src/main/assets'
}
tasks.register('copyTranslations', Copy) {
from('../../files/lang') {
include '*.mo'
@@ -107,5 +115,5 @@ tasks.register('copyTranslations', Copy) {
}
preBuild {
dependsOn copyH2D, copyTranslations
dependsOn copyH2D, copyTimidity, copyTranslations
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+210
View File
@@ -0,0 +1,210 @@
<html lang="en">
<head>
<title>fheroes2</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/progress.js/0.1.0/progress.min.js" integrity="sha512-CklrzCqwODBOEJHJq73SZrgWC7xcxssgg5M1xokosfDDz2/nLTCuMDyc51gbJtb8DriV4EYjJSlklPH5Ejn9XA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/progress.js/0.1.0/progressjs.min.css" integrity="sha512-ovRFHsWpnYUBNZd/8CmbaWAKBDO8xb0l5Y8PdIae1O5RXO2QyU3CZGNzuYuE0CQHKazIzWMQpnTn26WpPjexfw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<style>
body { margin: 0; height: 0; font-family: "Roboto", serif;}
#canvas {
height: 100vh;
width: 100vw;
display: block;
margin: 0 auto;
background: url(./fheroes2.jpeg) center center;
background-size: cover;
position: absolute;
z-index: 10;
}
#uploader, #launcher {
z-index: 100;
position: absolute;
color: #fff;
font-size: 30px;
text-stroke: 1px #000;
-webkit-text-stroke: 1px #000;
height: 100vh;
width: 100vw;
}
#launcher {
display: none;
text-stroke: 1px #fff;
-webkit-text-stroke: 1px #fff;
}
#uploader input[type=file] {
display: none
}
#uploader label {
text-align: center;
display: block;
cursor: pointer;
height: 100vh;
width: 100vw;
position: absolute;
}
#uploader label span {
padding: 10vh 0 0 0;
display: block;
}
#launcher button {
font-size: 30px;
}
#launcher .buttons {
text-align: center;
padding: 10vh 0 0 0;
}
</style>
</head>
<body>
<canvas id="canvas" width="640" height="400"></canvas>
<div id="uploader">
<label>
<span>Click to select your Heroes 2 folder</span>
<input
type="file"
directory="directory"
multiple
webkitdirectory="webkitdirectory"
id="files"
/>
</label>
</div>
<div id="launcher">
<div class="buttons">
<button onclick="Module.startGame()">Start game</button>
<button onclick="Module.deleteFiles()">Delete files</button>
</div>
</div>
<script>
const S_IFMT = 61440; /* 0x0170000 type of file */
const S_IFDIR = 16384; /* 0x0040000 directory */
const progressBar = progressJs("#canvas").setOptions({
theme: 'blueOverlayRadiusWithPercentBar',
overlayMode: true,
considerTransition: true
});
const canvas = document.querySelector('canvas');
canvas.oncontextmenu = event => event.preventDefault();
Object.assign(window, { Module: {
preRun: [
() => Object.assign(window.ENV, {
FHEROES2_DATA: `/fheroes2/data`,
HOME: '/fheroes2'
}),
() => {
addRunDependency('syncfs');
FS.mkdir('/fheroes2');
FS.mount(IDBFS, { root: '/' }, '/fheroes2');
FS.syncfs(true, (err) => {
if (err) throw err;
try {
if (Object.keys(FS.lookupPath(`${ENV.FHEROES2_DATA}`)).length === 0) return;
document.querySelector('#uploader').style.display = 'none';
document.querySelector('#launcher').style.display = 'block';
}
catch (ignore) {
FS.mkdir(`${ENV.FHEROES2_DATA}`);
}
});
},
() => {
document.querySelector('input[type=file]').addEventListener('input', e => {
const files = [...e.target.files];
const requiredFiles = ['data/HEROES2.AGG', 'data/HEROES2X.AGG']
if (files.length === 1) return Module.clearInput('zip archives are not supported yet, sorry');
if (files.length > 3000) return Module.clearInput('wrong directory, or your maps collection is really awesome.');
const requiredResolved = files.filter(({ webkitRelativePath: path }) =>
requiredFiles.some(rq => path.toLocaleLowerCase().endsWith(rq.toLocaleLowerCase()))).length === 2;
if (!requiredResolved) return Module.clearInput('wrong directory');
const whitListed = ['data', 'heroes2', 'maps', 'music'];
files.filter(file => {
const [fileName, ...path] = file.webkitRelativePath.split('/').reverse();
if (fileName.startsWith('.')) return false;
return path
.map(folder => folder.toLocaleLowerCase())
.some(folder => whitListed.includes(folder))
}).reduce((uploaded, file, _ignore, queue) => {
const [fileName, ...path] = file.webkitRelativePath.split('/').reverse();
const [ignore, ...relativePath] = path.reverse();
Module.mkdirWithParents(`${ENV.FHEROES2_DATA}/${relativePath.join('/')}`);
const reader = new FileReader();
reader.addEventListener('error', console.error);
reader.addEventListener('loadend', ({ target: { result } }) => {
FS.writeFile(`${ENV.FHEROES2_DATA}/${relativePath.join('/')}/${fileName}`, new Uint8Array(result), {
encoding: 'binary'
});
uploaded.push(file);
if (uploaded.length === queue.length) {
FS.syncfs(false, err => {
if (err) return Module.clearInput('Failed to sync FS');
document.querySelector('#uploader').style.display = 'none';
document.querySelector('#launcher').style.display = 'block';
})
}
});
reader.readAsArrayBuffer(file);
return uploaded;
}, []);
})
}
],
clearInput: (msg = '') => {
document.querySelector('input[type=file]').value = null;
msg && alert(msg);
},
mkdirWithParents: path => {
const parts = path.split('/');
try { FS.lookupPath(parts.join('/')) }
catch (ignore) {
parts.reduce((p, part) => {
p = [...p, part];
try { FS.lookupPath(p.join('/')) }
catch (ignore) { FS.mkdir(p.join('/')) }
return p;
}, []);
}
},
clearPath: path => {
try {
Object.keys(FS.lookupPath(path).node.contents).forEach(node =>
(FS.stat(`${path}/${node}`).mode & S_IFMT) === S_IFDIR
? Module.clearPath(`${path}/${node}`)
: FS.unlink(`${path}/${node}`));
FS.rmdir(`${path}`)
} catch (ignore) {}
},
startGame: () => {
removeRunDependency('syncfs');
document.querySelector('#uploader').style.display = 'none';
document.querySelector('#launcher').style.display = 'none';
},
deleteFiles: () => {
if (false === confirm('Are you sure?')) return;
Module.clearPath(ENV.FHEROES2_DATA);
FS.syncfs(false, ignore => {});
Module.clearInput();
document.querySelector('#uploader').style.display = 'block';
document.querySelector('#launcher').style.display = 'none';
},
setStatus: status => {
const dlProgressRE = /(?<progress>\d+)\/(?<total>\d+)/ig;
if (!status || !dlProgressRE.test(status)) {
if ((status ?? '').startsWith('Downloading data')) progressBar.start();
return;
}
dlProgressRE.lastIndex = 0;
const {groups: {progress, total}} = [...status.matchAll(dlProgressRE)][0];
const done = Math.round(progress / total * 100);
progressBar.set(done);
if (100 === done) progressBar.end();
},
canvas
}
});
document.head.append(Object.assign(document.createElement('script'), {src: './fheroes2.js'}));
</script>
</body>
+9 -1
View File
@@ -1,6 +1,6 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
# Copyright (C) 2021 - 2024 #
# Copyright (C) 2021 - 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
@@ -159,7 +159,11 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER
$(MAKE) -C thirdparty/libsmacker CCFLAGS="$(CCFLAGS_TP)" CFLAGS="$(CFLAGS_TP)" CXXFLAGS="$(CXXFLAGS_TP)" CPPFLAGS="$(CPPFLAGS_TP)"
endif
$(MAKE) -C engine
ifeq ($(PLATFORM),emscripten)
$(MAKE) -C fheroes2-ems
else
$(MAKE) -C fheroes2
endif
ifdef FHEROES2_WITH_TOOLS
$(MAKE) -C tools
endif
@@ -169,5 +173,9 @@ ifndef FHEROES2_WITH_SYSTEM_SMACKER
$(MAKE) -C thirdparty/libsmacker clean
endif
$(MAKE) -C engine clean
ifeq ($(PLATFORM),emscripten)
$(MAKE) -C fheroes2-ems clean
else
$(MAKE) -C fheroes2 clean
endif
$(MAKE) -C tools clean
+43
View File
@@ -0,0 +1,43 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
# Copyright (C) 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
###########################################################################
CCFLAGS := $(filter-out -pthread,$(CCFLAGS)) \
--use-port=sdl2 \
--use-port=sdl2_mixer \
--use-port=zlib \
-Wno-variadic-macro-arguments-omitted
LDFLAGS := $(filter-out -pthread,$(LDFLAGS)) \
--preload-file ../../../files/data/resurrection.h2d@/files/data/resurrection.h2d \
--preload-file ../../../files/lang/@/files/lang/ \
--preload-file ../../../files/timidity/@/etc/ \
-sASYNCIFY \
-sASYNCIFY_STACK_SIZE=20480 \
-sENVIRONMENT=web \
-sINITIAL_MEMORY=128mb \
-sNO_DISABLE_EXCEPTION_CATCHING \
-sSDL2_MIXER_FORMATS=mid,mp3,ogg \
-sSTACK_SIZE=262144 \
-lidbfs.js
ifdef FHEROES2_WITH_DEBUG
CCFLAGS := $(CCFLAGS) -gsource-map
LDFLAGS := $(LDFLAGS) -gsource-map
endif
+48
View File
@@ -0,0 +1,48 @@
###########################################################################
# fheroes2: https://github.com/ihhub/fheroes2 #
# Copyright (C) 2025 #
# #
# This program is free software; you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation; either version 2 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program; if not, write to the #
# Free Software Foundation, Inc., #
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #
###########################################################################
DEPLIBS := ../engine/libengine.a
CCFLAGS := $(CCFLAGS) -I../../engine
ifndef FHEROES2_WITH_SYSTEM_SMACKER
DEPLIBS := $(DEPLIBS) ../thirdparty/libsmacker/libsmacker.a
CCFLAGS := $(CCFLAGS) -I../../thirdparty/libsmacker
endif
SOURCEROOT := ../../fheroes2
SOURCEDIRS := $(filter %/,$(wildcard $(SOURCEROOT)/*/))
SOURCES := $(wildcard $(SOURCEROOT)/*/*.cpp)
VPATH := $(SOURCEDIRS)
.PHONY: all clean
all: fheroes2.js
fheroes2.js: $(notdir $(patsubst %.cpp, %.o, $(SOURCES))) $(DEPLIBS)
$(CXX) -o $@ $^ $(LIBS) $(LDFLAGS)
%.o: %.cpp
$(CXX) -c -MD $< $(addprefix -I, $(SOURCEDIRS)) $(CCFLAGS) $(CXXFLAGS) $(CPPFLAGS)
include $(wildcard *.d)
clean:
rm -f *.d *.o fheroes2.data fheroes2.js fheroes2.wasm

Some files were not shown because too many files have changed in this diff Show More