Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6ca7423
Enable pthreads in Emscripten CI build
divinity76 Dec 15, 2025
5dc2680
fix
divinity76 Dec 15, 2025
8436bdb
Fix lua binder void member return handling
divinity76 Dec 15, 2025
eab46b1
sonarcube is being a dick
divinity76 Dec 15, 2025
b30b774
void default return for lua binder
divinity76 Dec 15, 2025
13c0a59
fix
divinity76 Dec 15, 2025
e942062
fix
divinity76 Dec 15, 2025
746cdba
CI http 504 gateway timeout retry...
divinity76 Dec 15, 2025
6ba3a40
Fix em++ regex
divinity76 Dec 15, 2025
93f0ba1
Stabilize em++ detection
divinity76 Dec 15, 2025
37a261a
Detect em++ via substring
divinity76 Dec 15, 2025
a8fea73
nektos/act support; fix act compliation
divinity76 Dec 15, 2025
8a7ef9d
asdfhjqwerdf
divinity76 Dec 15, 2025
3679f78
Sword Art Online Abridged
divinity76 Dec 16, 2025
d5f0e30
wtf
divinity76 Dec 16, 2025
be693fb
wtf
divinity76 Dec 16, 2025
9baf5f4
wtf
divinity76 Dec 16, 2025
3297dad
wtf
divinity76 Dec 16, 2025
f0d41f1
rerun..
divinity76 Dec 16, 2025
88274c3
fix(ci): checkout pr head for emscripten
divinity76 Dec 16, 2025
41efe85
Fix wasm CI cache. lua
divinity76 Dec 16, 2025
b8a09c7
wtf
divinity76 Dec 16, 2025
66e4766
wtf
divinity76 Dec 16, 2025
9679547
wasm longjmp
divinity76 Dec 16, 2025
4f52cbf
wtf
divinity76 Dec 16, 2025
2834804
network-error retry
divinity76 Dec 16, 2025
8b1924f
wtf
divinity76 Dec 16, 2025
1acb334
wtf
divinity76 Dec 16, 2025
3e99191
wtf
divinity76 Dec 16, 2025
80b332e
fsck
divinity76 Dec 17, 2025
4fdee00
fsckkk
divinity76 Dec 17, 2025
66fd6d6
Re-make the entire CI as a Dockerfile.
divinity76 Dec 18, 2025
b8c784c
sonarcube
divinity76 Dec 18, 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
134 changes: 20 additions & 114 deletions .github/workflows/build-browser.yml
Original file line number Diff line number Diff line change
@@ -1,140 +1,46 @@
name: Build - Emscripten
name: Build Browser

on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
paths:
- "Dockerfile.browser"
- "Dockerfile.browser.sh"
- "browser/**"
- "src/**"
- ".github/workflows/build-browser.yml"
push:
branches: [main]
paths:
- "Dockerfile.browser"
- "Dockerfile.browser.sh"
- "browser/**"
- "src/**"
- ".github/workflows/build-browser.yml"
branches:
- main

permissions:
contents: read
pull-requests: read

env:
LUKKA_RUN_VCPKG_SHA: "${{ vars.LUKKA_RUN_VCPKG_SHA != '' && vars.LUKKA_RUN_VCPKG_SHA || 'b3dd708d38df5c856fe1c18dc0d59ab771f93921' }}"
actions: write

jobs:
cancel-runs:
if: github.event_name == 'pull_request' && github.ref != 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- name: Cancel Previous Runs
uses: styfle/cancel-workflow-action@main
with:
access_token: ${{ github.token }}

build:
name: ${{ matrix.buildtype }}
docker-build:
runs-on: ubuntu-latest
env:
ACTIONS_RUNTIME_TOKEN: ${{ secrets.ACTIONS_RUNTIME_TOKEN }}
ACTIONS_CACHE_URL: ${{ secrets.ACTIONS_CACHE_URL }}
NODE_OPTIONS: "--max-old-space-size=4096"

concurrency:
group: otclient-emscripten-${{ github.workflow }}-${{ github.ref }}-${{ matrix.buildtype }}
cancel-in-progress: true

strategy:
fail-fast: false
matrix:
buildtype: [emscripten-debug]
include:
- buildtype: emscripten-debug
cmake_build_type: Debug

BUILD_LOG: docker-browser-build.log
OUTPUT_DIR: build-emscripten-web
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
repository: ${{ github.event_name == 'push' && github.repository || (github.event.pull_request.head.repo.fork && github.repository || github.event.pull_request.head.repo.full_name) }}
ref: ${{ github.event_name == 'push' && github.ref || (github.event.pull_request.head.repo.fork && github.event.pull_request.base.ref || github.event.pull_request.head.ref) }}
fetch-depth: 0

- name: Setup Emscripten
uses: mymindstorm/setup-emsdk@v14
with:
version: "3.1.45"

- name: Setup Node.js for Emscripten
uses: actions/setup-node@v4
with:
node-version: '18'
- name: Build browser bundle with Docker
run: bash Dockerfile.browser.sh

- name: Get vcpkg commit ID
id: vcpkg-step
run: |
vcpkgCommitId=$(grep '.builtin-baseline' vcpkg.json | awk -F: '{print $2}' | tr -d '," ')
echo "vcpkgGitCommitId=$vcpkgCommitId" >> $GITHUB_OUTPUT

- name: Cache full vcpkg artifacts
uses: actions/cache@v4
with:
path: |
~/.cache/vcpkg/archives
${{ github.workspace }}/vcpkg/installed
${{ github.workspace }}/vcpkg/buildtrees
${{ github.workspace }}/vcpkg/downloads
key: vcpkg-${{ matrix.buildtype }}-${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}
restore-keys: |
vcpkg-${{ matrix.buildtype }}-
- name: Setup vcpkg
uses: lukka/run-vcpkg@a400452f634fe49e9f18d388aeb1809dcc642136
with:
vcpkgGitCommitId: ${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}


- name: Validate vcpkg baseline SHA
shell: pwsh
run: |
$SHAS = "${{ steps.vcpkg-step.outputs.vcpkgGitCommitId }}"
if ([string]::IsNullOrEmpty($SHAS)) {
echo "::error title=Missing vcpkg baseline::Provide a full 40-char vcpkgGitCommitId."
exit 1
}
if ($SHAS -notmatch '^[0-9a-f]{40}$') {
echo "::error title=Invalid vcpkg baseline::vcpkgGitCommitId must be a full 40-char commit SHA."
exit 1
}
- name: Install CMake and Ninja
uses: lukka/[email protected]

- name: Configure CMake
run: |
cmake -G Ninja -S . -B build-${{ matrix.buildtype }} \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$EMSDK/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
-DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten \
-DVCPKG_OVERLAY_PORTS=${{ github.workspace }}/browser/overlay-ports \
-DVCPKG_BUILD_TYPE=${{ matrix.cmake_build_type }} \
-DCMAKE_MAKE_PROGRAM=ninja \
-DOPTIONS_ENABLE_IPO=OFF \
-DTOGGLE_BIN_FOLDER=ON \
-DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \
-DTOGGLE_BOT_PROTECTION=OFF \
-DCMAKE_CXX_FLAGS="-fno-lto -std=c++20 -D_LIBCPP_DISABLE_AVAILABILITY -fexperimental-library" \
-DCMAKE_EXE_LINKER_FLAGS="-s DISABLE_EXCEPTION_CATCHING=0 -s ALLOW_MEMORY_GROWTH=1 -s USE_PTHREADS=0 -s WASM=1 -s NO_EXIT_RUNTIME=1 -s MALLOC=none -fno-lto" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
-DCMAKE_CXX_FLAGS_RELEASE="-O2 -DNDEBUG -fno-lto" \
-DCMAKE_CXX_FLAGS_DEBUG="-O0 -g -fno-lto" \
-DCMAKE_CXX_STANDARD=20

- name: Build
run: |
cmake --build build-${{ matrix.buildtype }} --target otclient --verbose

- name: Create and Upload Artifact
if: ${{ github.event_name != 'pull_request' }}
- name: Upload browser artifact
uses: actions/upload-artifact@v4
with:
name: otclient-${{ matrix.buildtype }}-${{ github.sha }}
path: ${{ github.workspace }}/build-${{ matrix.buildtype }}/bin/
retention-days: 30
name: otclient-browser-${{ github.sha }}
path: |
${{ env.BUILD_LOG }}
${{ env.OUTPUT_DIR }}
retention-days: 14
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ static/
cmake-build-*/
vcpkg_installed/
vc17/
/build-emscripten-web/

# MSTest test Results
[Tt]est[Rr]esult*/
Expand Down Expand Up @@ -313,4 +314,4 @@ google-services.json

# Android libraries
android/app/libs
android/app/src/main/assets
android/app/src/main/assets
32 changes: 32 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,38 @@ include(MessageColors)
include(LoggingHelper)
include(GNUInstallDirs)

set(_OTCLIENT_EMSCRIPTEN_COMPILER FALSE)
if(DEFINED CMAKE_CXX_COMPILER)
string(FIND "${CMAKE_CXX_COMPILER}" "em++" _OTCLIENT_EMSCRIPTEN_COMPILER_POS)
if(NOT _OTCLIENT_EMSCRIPTEN_COMPILER_POS EQUAL -1)
set(_OTCLIENT_EMSCRIPTEN_COMPILER TRUE)
endif()
endif()

if(NOT DEFINED WASM)
if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten"
OR CMAKE_CXX_COMPILER_ID MATCHES "Emscripten"
OR _OTCLIENT_EMSCRIPTEN_COMPILER)
set(WASM ON CACHE BOOL "Build otclient for WebAssembly targets" FORCE)
else()
set(WASM OFF CACHE BOOL "Build otclient for WebAssembly targets" FORCE)
endif()
endif()

if(WASM)
message(STATUS "WASM: ON")
endif()

if(CMAKE_SYSTEM_NAME STREQUAL "Emscripten"
OR CMAKE_CXX_COMPILER_ID MATCHES "Emscripten"
OR _OTCLIENT_EMSCRIPTEN_COMPILER
OR WASM)
set(THREADS_PREFER_PTHREAD_FLAG ON)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
endif()

# *****************************************************************************
# Options
# *****************************************************************************
Expand Down
72 changes: 72 additions & 0 deletions Dockerfile.browser
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# run from the repository root, e.g.
# docker build -t otclient-web -f Dockerfile.browser .
# docker create --name otclient-web-tmp otclient-web:latest
# docker cp otclient-web-tmp:/otclient-web ./build-emscripten-web
# docker rm otclient-web-tmp
FROM ubuntu:24.04 AS builder
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update -y
RUN apt-get -y full-upgrade

RUN apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
cmake \
curl \
git \
ninja-build \
pkg-config \
python3 \
python3-pip \
unzip \
xz-utils \
zip \
&& rm -rf /var/lib/apt/lists/* # drop apt caches to save ~100MB in the final image

SHELL ["/bin/bash", "-lc"]

WORKDIR /opt
RUN git clone --filter=blob:none --single-branch --depth 1 https://github.com/emscripten-core/emsdk.git emsdk \
&& /opt/emsdk/emsdk install latest \
&& /opt/emsdk/emsdk activate latest

ENV VCPKG_ROOT=/opt/vcpkg
COPY vcpkg.json /tmp/vcpkg.json
RUN git clone --filter=blob:none https://github.com/microsoft/vcpkg.git ${VCPKG_ROOT} \
&& cd ${VCPKG_ROOT} \
&& git checkout "$(grep '.builtin-baseline' /tmp/vcpkg.json | awk -F: '{print $2}' | tr -d ',\" ')" \
&& ./bootstrap-vcpkg.sh

WORKDIR /workspace
# NOSONAR: .dockerignore constrains the build context to tracked source assets required for this build stage
COPY . /workspace

ARG BUILD_TYPE=Release
ENV BUILD_DIR=/workspace/build-emscripten
RUN mkdir -p ${BUILD_DIR}
RUN source /opt/emsdk/emsdk_env.sh \
&& cmake -G Ninja -S /workspace -B ${BUILD_DIR} \
-DWASM=ON \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/opt/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake \
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake \
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten \
-DVCPKG_OVERLAY_PORTS=/workspace/browser/overlay-ports \
-DVCPKG_BUILD_TYPE=${BUILD_TYPE} \
-DWASM_USE_WASM_EXCEPTIONS=OFF \
-DOPTIONS_ENABLE_IPO=OFF \
-DTOGGLE_BIN_FOLDER=ON \
-DTOGGLE_BOT_PROTECTION=OFF \
-DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_CXX_FLAGS="-fno-lto -std=c++20 -D_LIBCPP_DISABLE_AVAILABILITY -fexperimental-library" \
-DCMAKE_EXE_LINKER_FLAGS="-s ALLOW_MEMORY_GROWTH=1 -s USE_PTHREADS=1 -s WASM=1 -s NO_EXIT_RUNTIME=1 -fno-lto" \
-DCMAKE_CXX_FLAGS_RELEASE="-O2 -DNDEBUG -fno-lto" \
-DCMAKE_CXX_FLAGS_DEBUG="-O0 -g -fno-lto" \
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF \
&& cmake --build ${BUILD_DIR} --target otclient -j"$(nproc)"

# NOSONAR: minimal BusyBox stage used only to package static artifacts, root default is acceptable
FROM busybox:latest AS web-artifacts
WORKDIR /otclient-web
COPY --from=builder /workspace/build-emscripten/bin/ .
CMD ["sh"]
14 changes: 14 additions & 0 deletions Dockerfile.browser.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
set -euxo pipefail

cd "$(dirname "$0")"
export DOCKER_BUILDKIT=${DOCKER_BUILDKIT:-1}
BUILD_LOG=${BUILD_LOG:-docker-browser-build.log}
OUTPUT_DIR=${OUTPUT_DIR:-build-emscripten-web}
: > "${BUILD_LOG}"
rm -rf "${OUTPUT_DIR}"
docker build --progress=plain -t otclient-web -f Dockerfile.browser . 2>&1 | tee -a "${BUILD_LOG}"
docker rm -f otclient-web-tmp >/dev/null 2>&1 || true
docker create --name otclient-web-tmp otclient-web:latest
docker cp otclient-web-tmp:/otclient-web "${OUTPUT_DIR}"
docker rm otclient-web-tmp
40 changes: 35 additions & 5 deletions browser/shell.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,31 @@
></canvas>
<script type="text/javascript">
var canvas = document.getElementById("canvas");
function reportRuntimeError(prefix, error) {
const payload = [prefix, error]
.flat()
.filter(Boolean)
.map((entry) => {
if (!entry) return "";
if (typeof entry === "string") {
return entry;
}
if (entry.stack) {
return entry.stack;
}
try {
return JSON.stringify(entry);
} catch (_) {
return String(entry);
}
})
.join(": ");
console.error(payload);
const title = document.getElementById("title-text");
if (title) {
title.innerHTML = "Runtime error – check console";
}
}
function startGame() {
document.getElementById("start").style.display = "none";
callMain();
Expand Down Expand Up @@ -142,14 +167,16 @@
console.log(text);
};
})(),
printErr: function (text) {
text = Array.prototype.slice.call(arguments).join(" ");
console.error(text);
printErr: function () {
reportRuntimeError("stderr", Array.prototype.slice.call(arguments).join(" "));
},
canvas: (function () {
var canvas = document.getElementById("canvas");
return canvas;
})(),
onAbort: function (reason) {
reportRuntimeError("abort", reason || "unknown reason");
},
setStatus: function (text) {
console.log("status: " + text);
let display = "";
Expand All @@ -167,9 +194,12 @@
document.getElementById("title-text").innerHTML = "Initializing...";
},
};
window.onerror = function () {
console.log("onerror: " + event.message);
window.onerror = function (message, source, lineno, colno, error) {
reportRuntimeError("window.onerror", error || message);
};
window.addEventListener("unhandledrejection", function (event) {
reportRuntimeError("unhandledrejection", event.reason || "Promise rejected");
});
</script>
{{{ SCRIPT }}}
</body>
Expand Down
Loading
Loading