Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add build target "web" #9406

Merged
merged 27 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fbe8630
Add build target "web"
ololoken Dec 31, 2024
7e491d7
Deal with AsyncManager
oleg-derevenetz Jan 3, 2025
0c31b7c
Move the FS.syncfs() call to the serialize.cpp
oleg-derevenetz Jan 3, 2025
70910d3
Merge branch master into master-ololoken
oleg-derevenetz Jan 3, 2025
188897e
Update the copyright headers
oleg-derevenetz Jan 3, 2025
3473526
Apply the IWYU suggestions
oleg-derevenetz Jan 3, 2025
00bab79
Ignore the clang-format for JS code
oleg-derevenetz Jan 3, 2025
7ba3604
Style nits
oleg-derevenetz Jan 3, 2025
214fe9a
Move the sync operation to deleter
oleg-derevenetz Jan 3, 2025
a5aff48
remove pthread related patch; update web build description; add pipeline
ololoken Jan 3, 2025
82ef673
Rename "web" to "emscripten", simplify the build, add CI
oleg-derevenetz Jan 3, 2025
87b0f78
Merge remote-tracking branch 'oleg/emscripten-tmp' into emscipten-no-…
ololoken Jan 3, 2025
a4f870f
Don't run Docker manually
oleg-derevenetz Jan 3, 2025
5fb5bf2
Merge remote-tracking branch 'oleg/emscripten-tmp' into emscipten-no-…
ololoken Jan 3, 2025
1b03f36
fix whitelisted folders resolve
ololoken Jan 3, 2025
ed1f299
Forcibly reset the track position if music playback is started from t…
oleg-derevenetz Jan 3, 2025
3382fe4
Update the copyright date
oleg-derevenetz Jan 3, 2025
885de6e
Add comment
oleg-derevenetz Jan 3, 2025
37d06f1
use heroes2-pixed font for wasm launcher
ololoken Jan 3, 2025
c3a007f
remove texture from original game
ololoken Jan 4, 2025
9cbb468
Call the FS.syncfs() only when the corresponding file has been opened…
oleg-derevenetz Jan 4, 2025
120c27d
Style nits
oleg-derevenetz Jan 4, 2025
b310179
Make the static_assert stricter
oleg-derevenetz Jan 4, 2025
ae788dd
Add support for the MIDI playback
oleg-derevenetz Jan 4, 2025
2595be3
Merge branch master into master-ololoken
oleg-derevenetz Jan 4, 2025
99ef992
Update the background image
ihhub Jan 5, 2025
0f1f6d5
Revert changes done in 37d06f16e17c6dd4f981a904c06fff7205ea4614 commit
ihhub Jan 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/make.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 43 additions & 0 deletions Makefile.emscripten
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions Makefile.vita
Original file line number Diff line number Diff line change
@@ -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 #
Expand Down Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand All @@ -107,5 +115,5 @@ tasks.register('copyTranslations', Copy) {
}

preBuild {
dependsOn copyH2D, copyTranslations
dependsOn copyH2D, copyTimidity, copyTranslations
}
Binary file added files/emscripten/fheroes2.jpeg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
210 changes: 210 additions & 0 deletions files/emscripten/index.html
Original file line number Diff line number Diff line change
@@ -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>
ihhub marked this conversation as resolved.
Show resolved Hide resolved
<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>
File renamed without changes.
10 changes: 9 additions & 1 deletion src/dist/Makefile
Original file line number Diff line number Diff line change
@@ -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 #
Expand Down Expand Up @@ -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
Expand All @@ -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
Loading
Loading