Compare commits

46 Commits

Author SHA1 Message Date
Itms 69461c90b7 Bump build date of A27.1 RC2
(cherry picked from commit 16bcc5a870)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-12 18:36:33 +02:00
Itms 6b763af4c8 Fixup SpiderMonkey DLL for Windows 7 on Win32
Pulling the DLL from SVN is performed separately to allow backporting
to A27.

Accepted-By: sera
Reviewed-On: https://gitea.wildfiregames.com/0ad/0ad/pulls/8175
(cherry picked from commit b362f0537a)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-12 15:19:15 +02:00
Ralph Sennhauser 0dbc1f604a Add support for specifying pkg-config via env
For cross compiling there is a need to specify an alternative binary for
pkg-config. It's customary to use the environment variable PKG_CONFIG for
this.

Ref: #8135
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 2b5830e82a)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-12 15:18:21 +02:00
Ralph Sennhauser bf4eee555f Don't export HOSTTYPE in update-workspace.sh
Exporting HOSTTYPE prevents cross compilation as HOSTTYPE is used to
override target host arch instead of detecting it from chosen compiler.
This then results in the wrong arch specific source files to be
included. So just don't set it by default and let it to the user to set
it if he so chooses.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 707abee34d)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-12 15:17:22 +02:00
Itms dae7a8c394 Bump engine version to 0.27.1 2025-07-12 15:14:45 +02:00
Itms 4633a2d522 Extend previous commit
Backport part of 0e1c881fef
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-12 15:14:34 +02:00
Dunedan aa3767d2f9 Fix pre-commit workflow to work with Ubuntu 24.04
(cherry picked from commit 782bab293b)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-07-08 11:16:31 +02:00
Itms 7b3d6ce04e New appdata entry for A27.1
Fixes #7986

(cherry picked from commit 406565f3c4)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-20 12:04:50 +02:00
elexis c98ae10e39 Fix change perspective in replays
Fixes change perspective features of multiplayer replays following cheat
protection in 023527e56e.

Fixes: #8020
(cherry picked from commit 67eaa8f1a9)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-19 16:59:26 +02:00
Lancelot de Ferrière 655a444847 Always allow last-ditch GCs
As noted on #7979, we run into OOMs since commit af32d386b9. The reason is that the default incremental GC budget is unlimited, which actually doesn't perform incremental GCs. Our settings can lead to situation where the incremental GCs don't actually sweep, thus not freeing memory.
SM has a mechanism to avoid OOM anyways with LAST_DITCH GCs, but by default these can only occur ever 30 seconds. Turn this off.

Reported by: langbart

(cherry picked from commit e3e542b1f9)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-19 16:58:32 +02:00
Ralph Sennhauser 182b1e3ee6 Fix memory leak wrt renderer instance
The renderer instance is created with new, so use delete.

Direct leak of 80 byte(s) in 1 object(s) allocated from:
    #0 0x7fd3c932870b in operator new(unsigned long) (/usr/lib/gcc/x86_64-pc-linux-gnu/15/libasan.so.8+0x12870b)
    #1 0x55bbedf25fb0 in InitGraphics(CmdLineArgs const&, int, std::vector<CStr8, std::allocator<CStr8> > const&, ScriptContext&, ScriptInterface&) ../../../source/ps/GameSetup/GameSetup.cpp:654
    #2 0x55bbed2bb95e in RunGameOrAtlas ../../../source/main.cpp:684
    #3 0x55bbed271b28 in main ../../../source/main.cpp:759
    #4 0x7fd3c665f3ed in __libc_start_call_main (/lib64/libc.so.6+0x263ed)

Fixes: #7951
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 03fd1acd5d)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-19 16:58:03 +02:00
Ralph Sennhauser 60a44dfc26 Never strip binaries automatically
Striping is trivial, getting the debug symbols if premake just strips
them silently is a pain tho.

Stripping should optionally be done during install, as we don't have an
install target nor an install-strip target, make it a configure option
which is off by default.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 1fac3461f9)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-19 16:57:03 +02:00
Dunedan 38e4cdc755 Fix password change for certain usernames
This fixes the ability for users with uppercase letters in their
username to change their passwords, which wasn't possible before on
non-Windows platforms. The underlying issue for that is
https://github.com/processone/ejabberd/issues/4377 and in addition some
inconsistent normalization of usernames in password change requests
by gloox. This commit works around that by always using the local JID
part as username for password requests, which got the nodeprep string
profile already applied.

It also fixes a problem that Windows users which were able to change
their passwords, weren't able to login afterwards anymore, unless they
typed their username in all lowercase in the login form. This was caused
by using the all lowercase username as input for the password hash
function, instead of using the username in the user supplied case.

Fixes #7796

(cherry picked from commit 638391d7ab)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-06-19 16:53:33 +02:00
Lancelot de Ferrière 68d99b3944 Fix no-pch build following #7829
(cherry picked from commit dd74892463)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 22:29:09 +02:00
Itms 5ffddd0cc4 Implement pipeline for generating patch releases
The new patch-release pipeline takes a git branch originating from a
release tag, applies the changes since the tag onto the nightly build of
the release, and rebuilds the Windows binary.

All resulting changes are sent to the bundling pipeline which builds for
macOS and creates the tarballs and installers.

There will be two instances of the bundles pipeline: the main one will
keep tracking the main branch and providing biweekly bundles of the next
release, while a new patched-bundles pipeline will be manually used to
generate patch releases.

(cherry picked from commit 51ab929926)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:22:12 +02:00
Itms 2721d642b8 Allow downloading Windows libraries into nightly
(cherry picked from commit c38b40cda8)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:13:10 +02:00
Lancelot de Ferrière e1daff1869 Iterate the component map only once on serialization
(cherry picked from commit 9eccf1f27d)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:05:53 +02:00
Lancelot de Ferrière e5df3e1ad8 Skip UTF8 conversions in prototype name
(cherry picked from commit ea34960249)
(fixed build on 520c489b68 by moving a change to this commit)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:05:00 +02:00
Lancelot de Ferrière 5ab58f82f0 BinarySerializer: avoid creating unnecessary ScriptRequest
(cherry picked from commit bcd0e12cc3)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:02:58 +02:00
Lancelot de Ferrière 520c489b68 Cache property keys during component serialization
(cherry picked from commit ab6a420f78)
(fixed build on this commit by moving a change to commit originally ea34960249)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 14:00:56 +02:00
Lancelot de Ferrière 0fdd6005b8 Skip redundant HasProperty in GetObjectClassName
(cherry picked from commit 83bf2eb22e)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:59:21 +02:00
Itms 3fce9a37cc Generate signatures and hashes in the bundles job
(cherry picked from commit 1adab34511)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:50:14 +02:00
Lancelot de Ferrière e4b604982a Stop running shrinking gcs & simplify GC logic.
Shrinking GCs dump the JITted code, which leads to redundant recompilations, lowers performance, and makes profiling JS more difficult.
They may still happen if the runtime is at risk of OOM.

(cherry picked from commit af32d386b9)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:49:41 +02:00
phosit b78b666797 Catch all std::exception from a map in atlas
Since 0eed117e6d exceptions from the map generation script are rethrown
in the main thread but not all of them are catched.

This defect is already fixed in the engine with 82513c9104

Fixes: #7620
(cherry picked from commit 56abf84da2)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:48:54 +02:00
Dunedan 2fcce77ad4 Properly terminate the XMPP connection
This ensures the XMPP connection gets properly terminated when leaving
the lobby.

Fixes #7504

(cherry picked from commit 312c6e8165)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:48:33 +02:00
Dunedan 83c8ec1b4a Create port forwarding for correct port with UPnP
Up to know the UPnP logic ignored the port a user was hosting a game on
and always added a port forwarding for the default port UDP 20595. This
commit fixes that, so a port forwarding is added for the actual port a
game is hosted on.

(cherry picked from commit fcd3fc2aa3)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:47:43 +02:00
Dunedan a7694ef8b3 Fix adding port forwardings using UPnP
The UPnP implementation included a combination of two subtle bugs, which
resulted in failure to create port forwardings every time after the
first one.

When using UPnP, the internet gateway to create the port forwardings at
needs to discovered. As that takes a while, the its root descriptor URL
was supposed to be cached after successful discovery in the user config
in "network.upnprootdescurl". However, instead of caching the root
descriptor URL, the control URL got cached. That caused following
requests to the root descriptor URL to fail, as they ended up at the
control URL instead. As such requests might also fail when the network
topology changed, the code was supposed to fall back to discovering the
internet gateway again when the cached one didn't work. However, due to
the inner workings of miniupnpc the request using the cached root
descriptor URL didn't result in an error, so the new discovery was never
triggered. As the wrong value was persisted in the user config there was
also no way to get out of this situation again.

This commit fixes both of these bugs.

As far as I can tell these bugs existed since the introduction of the
caching of the root descriptor URL in 0ba25e9968, which means creating
port forwardings using UPnP has been broken since Alpha 15.

(cherry picked from commit 18d7746c84)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-15 13:44:35 +02:00
fiftydinar 5012d1f1c0 Add StartupWMClass to desktop file
This fixes the potential edge-cases of icon not showing, like in Gnome's dash for example.

(cherry picked from commit 26acb9df22)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:57:43 +02:00
Ralph Sennhauser 73dec93113 Cmake-4 support for nvtt
Fix nvtt build to support cmake-4, while at it also fix other
related outstanding cmake issues in nvtt.

Fixes: #7538
Fixes: #7764
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 263b481442)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:57 +02:00
Vladislav Belov c4d10eedfd Fixes runtime switch of the GPU skinning.
Fixes #7572, #7630.

(cherry picked from commit 2264807ad0)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:56 +02:00
Vladislav Belov 21ec2f0ab8 Allows compute shaders for GL only since 4.3.
It seems current checks for GL 4.2 aren't enough so just disable
compute shaders for GL 4.2. Fixes #7734.

(cherry picked from commit fa3fb5d064)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:55 +02:00
Vladislav Belov 85cc968be0 Fixes out of bounds during GL buffer binding.
There was an out of bounds access during binding a uniform buffer on
GL. Fixes #7567, #7598.

(cherry picked from commit bce6e2c238)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:54 +02:00
Dunedan ef9ea478ec Add workaround to turn off nursery size heuristic
SpiderMonkey 98 introduced a size heuristic for the nursery GC region
(https://phabricator.services.mozilla.com/D136637). As this heuristic
uses a wall-clock time duration, it results in a severe performance
regression on slower systems for our use case.

This commit adds a workaround to turn off that heuristic, by telling
SpiderMonkey that a "page load" (something which doesn't have a meaning
in the context of pyrogenesis) is in progress, as that heuristic is
disabled for page loads.

Co-Authored by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
Fixes #7714

(cherry picked from commit 11dd480b67)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:53 +02:00
Ralph Sennhauser 417af009ea Don't map unknown scancodes to hotkeys
Users may generate key presses that don't map to a distinct SDL scancode
and will be mapped SDL_SCANCODE_UNKNOWN instead. Using the same ID for
unmapped hotkeys means any such key press will execute unset hotkeys. As
luck would have it in #7644 this leads to calling "Custom exit to
desktop" if the hotkey is unbound as is the default.

So simply use a code for unused hotkeys that doesn't map to any SDL
scancodes.

Fixes: #7644
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit f10526284f)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:52 +02:00
Itms fa5f81ba6f Fix Jenkins pipelines on Linux agent
The Linux agent now runs Docker in rootless mode for additional
security. Operations inside containers must be run as root to match the
underprivileged user running Docker.

(cherry picked from commit b831b1f559)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:51 +02:00
Ralph Sennhauser 30fe4f7fdc Remove root check in update-workspace.sh
If you run as root then created files will be owned by root, this is
expected behaviour and not messing with permissions as stated in the
error message.

Running in a container the root user may map to the user starting the
container while all other users would need mapping to be able to work
with a bind mounted a checkout.

Further Debian patches out the root check to be able to build on their
builder.

Given the above remove the check.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 6738fdbab7)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:50 +02:00
Ralph Sennhauser 1ec2709236 Bump Windows libs for premake5 beta4
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 8baff65e95)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:49 +02:00
Ralph Sennhauser fa1f474d6e Use new premake lto api if available
Premake v5.0.0-beta4 replaces the LinkTimeOptimization flag with a
function linktimeoptimization, use it if available to avoid deprecation
warning.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit df38d4e899)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:48 +02:00
Ralph Sennhauser e2d4ad6c40 Update premake to v5.0.0-beta4
https://github.com/premake/premake-core/releases/tag/v5.0.0-beta4

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit 84b407aa35)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:47 +02:00
AFCMS 851bbf07ac Improve the software center display of the application
- OARS content rating have been updated to version 1.1
- Touch Screens, Keyboard, Mouse have been marked as supported
- Laptop screens and bigger are marked as required to the minimum width of 1024 required by the game
- Fixes the device compatibility page on GNOME Software.
- added a "0AD" keyword to  fix an issue with Flathub search feature where searching for "0AD" instead of "0 A.D." returns no results.

(cherry picked from commit 51bbdc6537)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:38 +02:00
Stan 8a593904d5 Patch metainfo file to to match the new requirements
(cherry picked from commit cf0c285d8e)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-05-01 08:56:24 +02:00
Vincent Cheng ef42efe1e1 Make sure mozjs patch FixFpNormIssue.diff applies cleanly on i386
(cherry picked from commit 026a668a0f)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-04-27 21:31:27 +02:00
Martijn van Duren 8a31db1659 OpenBSD supports pkg-config
(cherry picked from commit 064ab14577)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-04-27 21:04:53 +02:00
Martijn van Duren 50127606b3 Make libraries/*/build.sh use "${TAR}" instead of tar directly.
This allows systems using a tar version not supporting all the required
flags to easily switch to gnu tar.

(cherry picked from commit 2338473da1)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-04-27 21:03:12 +02:00
Ștefan Talpalaru 8053860a5b build-source-libs.sh: fix "--with-system-premake"
(cherry picked from commit 3ff3f72890)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-04-27 21:02:41 +02:00
Ralph Sennhauser 6746f380f7 Add missing header in KeyName
Reported-by: @svenstaro
Ref: #7564
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
(cherry picked from commit bccd3db231)
Signed-off-by: Itms <itms@wildfiregames.com>
2025-04-27 20:59:22 +02:00
67 changed files with 629 additions and 530 deletions
+1
View File
@@ -66,6 +66,7 @@ Before moving on with Full Freeze, make sure that:
### Full Freeze
- [ ] [Update appdata.xml](wiki/ReleaseProcess#update-appdataxml)
- [ ] [Freeze Jenkins](wiki/ReleaseProcess#freeze-jenkins)
- [ ] [Confirm full freeze](wiki/ReleaseProcess#confirm-full-freeze)
- [ ] [Package East Asian mods](wiki/ReleaseProcess#package-east-asian-mods)
+2
View File
@@ -9,6 +9,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install lxml
run: pip3 install lxml
- name: Workaround for authentication problem with LFS
+2
View File
@@ -9,6 +9,8 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- id: restore-pip-cache
uses: actions/cache/restore@v4
with:
@@ -98,14 +98,6 @@ var AccountSettingsPage = {
if (Engine.GetGUIObjectByName("as_PasswordInputConfirm").caption !== newPass)
throw new SetPasswordError(translate("Passwords do not match"));
let usn = Engine.LobbyGetJID();
let atIndex = usn.indexOf("@");
if (atIndex == -1)
{
// Probably can't happen too easily, so error out.
error("Invalid JID");
throw new SetPasswordError(translate("Invalid JID, cannot change password."));
}
return Engine.EncryptPassword(newPass, usn.substring(0, atIndex).toLowerCase());
return Engine.EncryptPassword(newPass, Engine.LobbyGetUsername());
}
};
@@ -1,7 +1,5 @@
FROM debian:bullseye-slim
RUN useradd -ms /bin/bash --uid 1006 builder
# 0 A.D. dependencies.
ARG DEBIAN_FRONTEND=noninteractive
ARG DEBCONF_NOWARNINGS="yes"
@@ -45,9 +43,7 @@ RUN apt-get -qqy update && apt-get install -qqy --no-install-recommends git-lfs
RUN git lfs install --system --skip-smudge
# Install rust and Cargo via rustup
USER builder
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.66.0 -y
ENV PATH="${PATH}:/home/builder/.cargo/bin"
USER root
ENV PATH="${PATH}:/root/.cargo/bin"
ENV SHELL=/bin/bash
@@ -1,7 +1,5 @@
FROM debian:buster-slim
RUN useradd -ms /bin/bash --uid 1006 builder
# 0 A.D. dependencies.
ARG DEBIAN_FRONTEND=noninteractive
ARG DEBCONF_NOWARNINGS="yes"
@@ -47,10 +45,8 @@ RUN apt-get -qqy update && apt-get install -qqy --no-install-recommends git-lfs
RUN git lfs install --system --skip-smudge
# Install rust and Cargo via rustup
USER builder
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain 1.66.0 -y
ENV PATH="${PATH}:/home/builder/.cargo/bin"
USER root
ENV PATH="${PATH}:/root/.cargo/bin"
ENV SHELL=/bin/bash
@@ -4,8 +4,6 @@ ARG DEBIAN_FRONTEND=noninteractive
ARG DEBCONF_NOWARNINGS="yes"
RUN apt-get update && apt-get install -qqy llvm-9 clang-9 lld-9 libclang-9-dev --no-install-recommends
USER builder
ENV CC=clang-9
ENV CXX=clang++-9
ENV LDFLAGS="-fuse-ld=lld-9"
@@ -4,8 +4,6 @@ ARG DEBIAN_FRONTEND=noninteractive
ARG DEBCONF_NOWARNINGS="yes"
RUN apt-get install -qqy gcc-8 g++-8 --no-install-recommends
USER builder
ENV LIBCC=gcc-8
ENV LIBCXX=g++-8
ENV CC=gcc-8
+30 -5
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -32,6 +32,8 @@ pipeline {
parameters {
string(name: 'BUNDLE_VERSION', defaultValue: '0.27.0dev', description: 'Bundle Version')
string(name: 'NIGHTLY_REVISION', defaultValue: 'HEAD', description: 'Nightly SVN revision from which to build the bundles')
booleanParam(name: 'PATCH_BUILD', defaultValue: false, description: 'Apply patch generated from upstream job patch-release onto the nightly build')
booleanParam(name: 'DO_GZIP', defaultValue: true, description: 'Create .gz unix tarballs as well as .xz')
}
@@ -44,10 +46,20 @@ pipeline {
steps {
checkout changelog: false, poll: false, scm: [
$class: 'SubversionSCM',
locations: [[local: '.', remote: 'https://svn.wildfiregames.com/nightly-build/trunk']],
locations: [[local: '.', remote: "https://svn.wildfiregames.com/nightly-build/trunk@${NIGHTLY_REVISION}"]],
quietOperation: false,
workspaceUpdater: [$class: 'UpdateWithCleanUpdater']]
sh "svn cleanup"
}
}
stage("Patch Nightly Build") {
when {
expression { return params.PATCH_BUILD }
}
steps {
copyArtifacts projectName: '0ad-patch-release', selector: upstream()
sh "svn patch ${BUNDLE_VERSION}.patch"
untar file: "${params.BUNDLE_VERSION}-nightly-patch.tar.gz", keepPermissions: false
}
}
@@ -130,12 +142,25 @@ pipeline {
sh "BUNDLE_VERSION=${params.BUNDLE_VERSION} DO_GZIP=${params.DO_GZIP} source/tools/dist/build-unix-win32.sh"
}
}
stage("Generate Signatures and Checksums") {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'minisign-releases-key', keyFileVariable: 'MINISIGN_KEY', passphraseVariable: 'MINISIGN_PASS')]) {
sh 'echo ${MINISIGN_PASS} | minisign -s ${MINISIGN_KEY} -Sm *.{dmg,exe,tar.gz,tar.xz}'
}
sh 'for file in *.{dmg,exe,tar.gz,tar.xz}; do md5sum "${file}" > "${file}".md5sum; done'
sh 'for file in *.{dmg,exe,tar.gz,tar.xz}; do sha1sum "${file}" > "${file}".sha1sum; done'
}
}
}
post {
success {
archiveArtifacts '*.dmg,*.exe,*.tar.gz,*.tar.xz'
sh "shasum -a 1 *.{dmg,exe,tar.gz,tar.xz}"
archiveArtifacts '*.dmg,*.exe,*.tar.gz,*.tar.xz,*.minisig,*.md5sum,*.sha1sum'
}
cleanup {
sh 'svn revert -R .'
sh 'svn cleanup --remove-unversioned'
}
}
}
+4 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -57,6 +57,9 @@ pipeline {
customWorkspace "workspace/${JENKINS_COMPILER}-pch"
dir 'build/jenkins/dockerfiles'
filename "${JENKINS_COMPILER}.Dockerfile"
// Prevent Jenkins from running commands with the UID of the host's jenkins user
// https://stackoverflow.com/a/42822143
args '-u root'
}
}
@@ -0,0 +1,94 @@
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. 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.
*
* 0 A.D. 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 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// This pipeline is used to generate patched releases, suitable to be bundled and distributed.
def visualStudioPath = '"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe"'
def buildOptions = '/p:PlatformToolset=v141_xp /p:XPDeprecationWarning=false /t:pyrogenesis /t:AtlasUI %JOBS% /nologo -clp:Warningsonly -clp:ErrorsOnly'
pipeline {
agent {
node {
label 'WindowsAgent'
}
}
parameters {
string(name: 'BUNDLE_VERSION', description: 'Bundle Version')
string(name: 'NIGHTLY_REVISION', description: 'Revision of the nightly build corresponding to the release')
string(name: 'RELEASE_TAG', description: 'Git tag from which the point release originates')
}
stages {
stage('Generate Patch') {
steps {
checkout scmGit(branches: [[name: "${GIT_BRANCH}"]], extensions: [cleanAfterCheckout(), localBranch()])
bat 'cd build\\build_version && build_version.bat'
stash includes: 'build/build_version/build_version.txt', name: 'build_version'
bat "git diff ${RELEASE_TAG}..HEAD > ${BUNDLE_VERSION}.patch"
stash includes: "${params.BUNDLE_VERSION}.patch", name: 'patch'
archiveArtifacts artifacts: "${params.BUNDLE_VERSION}.patch"
}
}
stage('Patch Windows Build') {
steps {
ws('workspace/patch-release-svn') {
checkout changelog: false, poll: false, scm: [
$class: 'SubversionSCM',
locations: [[local: '.', remote: "https://svn.wildfiregames.com/nightly-build/trunk@${NIGHTLY_REVISION}"]],
quietOperation: false,
workspaceUpdater: [$class: 'UpdateWithCleanUpdater']]
unstash 'patch'
bat "svn patch ${params.BUNDLE_VERSION}.patch"
unstash 'build_version'
bat 'cd libraries && get-windows-libs.bat'
bat '(robocopy E:\\wxWidgets-3.2.6\\lib libraries\\win32\\wxwidgets\\lib /MIR /NDL /NJH /NJS /NP /NS /NC) ^& IF %ERRORLEVEL% LEQ 1 exit 0'
bat '(robocopy E:\\wxWidgets-3.2.6\\include libraries\\win32\\wxwidgets\\include /MIR /NDL /NJH /NJS /NP /NS /NC) ^& IF %ERRORLEVEL% LEQ 1 exit 0'
bat 'cd build\\workspaces && update-workspaces.bat --without-pch --without-tests'
bat "cd build\\workspaces\\vs2017 && ${visualStudioPath} pyrogenesis.sln /p:Configuration=Release ${buildOptions}"
script {
def modifiedFiles = bat(script:'@svn status', returnStdout: true).split('\n').collect { l -> l.drop(8).trim() }.join(', ')
tar archive: true, compress: true, exclude: '*.orig, binaries/system/*.exp, binaries/system/*.lib, build/workspaces/vs2017, libraries/win32/wxwidgets/**', file: "${params.BUNDLE_VERSION}-nightly-patch.tar.gz", glob: modifiedFiles
}
}
}
post {
cleanup {
ws('workspace/patch-release-svn') {
bat 'svn revert -R .'
bat 'svn cleanup --remove-unversioned'
}
}
}
}
stage('Bundle Patched Release') {
steps {
build job: '0ad-patch-bundles', wait: false, waitForStart: true, parameters: [
string(name: 'BUNDLE_VERSION', value: "${params.BUNDLE_VERSION}"),
string(name: 'NIGHTLY_REVISION', value: "${params.NIGHTLY_REVISION}"),
booleanParam(name: 'PATCH_BUILD', value: true)
]
}
}
}
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -21,9 +21,12 @@ pipeline {
agent {
dockerfile {
label 'LinuxAgent'
customWorkspace "workspace/technical-docs"
customWorkspace 'workspace/technical-docs'
dir 'build/jenkins/dockerfiles'
filename "docs-tools.Dockerfile"
filename 'docs-tools.Dockerfile'
// Prevent Jenkins from running commands with the UID of the host's jenkins user
// https://stackoverflow.com/a/42822143
args '-u root'
}
}
-6
View File
@@ -719,12 +719,6 @@ extern_lib_defs = {
add_default_links({
win_names = { "libvorbisfile" },
})
elseif os.getversion().description == "OpenBSD" then
-- TODO: We need to force linking with these as currently
-- they need to be loaded explicitly on execution
add_default_links({
unix_names = { "ogg", "vorbis" },
})
else
pkgconfig.add_links("vorbisfile")
end
+8 -3
View File
@@ -1,9 +1,14 @@
local m = {}
m._VERSION = "1.2.0-dev"
m._VERSION = "1.3.0-dev"
m.additional_pc_path = nil
m.static_link_libs = false
local pkg_config_command = "pkg-config"
if os.getenv("PKG_CONFIG") then
pkg_config_command = os.getenv("PKG_CONFIG")
end
local function os_capture(cmd)
return io.popen(cmd, 'r'):read('*a'):gsub("\n", " ")
end
@@ -12,7 +17,7 @@ local function parse_pkg_config_includes(lib, alternative_cmd, alternative_flags
local result
if not alternative_cmd then
local pc_path = m.additional_pc_path and "PKG_CONFIG_PATH="..m.additional_pc_path or ""
result = os_capture(pc_path.." pkg-config --cflags "..lib)
result = os_capture(pc_path .. " " .. pkg_config_command .. " --cflags " .. lib)
else
if not alternative_flags then
result = os_capture(alternative_cmd.." --cflags")
@@ -81,7 +86,7 @@ function m.add_links(lib, alternative_cmd, alternative_flags)
if not alternative_cmd then
local pc_path = m.additional_pc_path and "PKG_CONFIG_PATH="..m.additional_pc_path or ""
local static = m.static_link_libs and " --static " or ""
result = os_capture(pc_path.." pkg-config --libs "..static..lib)
result = os_capture(pc_path .. " " .. pkg_config_command .. " --libs " .. static .. lib)
else
if not alternative_flags then
result = os_capture(alternative_cmd.." --libs")
+10 -2
View File
@@ -5,6 +5,7 @@ newoption { trigger = "icc", description = "Use Intel C++ Compiler (Linux only;
newoption { trigger = "jenkins-tests", description = "Configure CxxTest to use the XmlPrinter runner which produces Jenkins-compatible output" }
newoption { trigger = "minimal-flags", description = "Only set compiler/linker flags that are really needed. Has no effect on Windows builds" }
newoption { trigger = "outpath", description = "Location for generated project files", default="../workspaces/default" }
newoption { trigger = "strip-binaries", description = "Strip created binaries" }
newoption { trigger = "with-system-cxxtest", description = "Search standard paths for cxxtest, instead of using bundled copy" }
newoption { trigger = "with-lto", description = "Enable Link Time Optimization (LTO)" }
newoption { trigger = "with-system-mozjs", description = "Search standard paths for libmozjs115, instead of using bundled copy" }
@@ -186,7 +187,9 @@ function project_set_build_flags()
editandcontinue "Off"
if not _OPTIONS["minimal-flags"] then
if _OPTIONS['strip-binaries'] then
symbols "Off"
else
symbols "On"
end
@@ -209,7 +212,12 @@ function project_set_build_flags()
optimize "Speed"
end
if _OPTIONS["with-lto"] then
flags { "LinkTimeOptimization" }
if linktimeoptimization then
linktimeoptimization("On")
else
-- deprecated since v5.0.0-beta4
flags { "LinkTimeOptimization" }
end
end
defines { "NDEBUG", "CONFIG_FINAL=1" }
+79 -12
View File
@@ -1,12 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2018 Wildfire Games <webmaster@wildfiregames.com> -->
<!-- Copyright 2025 Wildfire Games <webmaster@wildfiregames.com> -->
<component type="desktop-application">
<id>com.play0ad.zeroad</id>
<developer id="com.wildfiregames">
<name>Wildfire Games</name>
</developer>
<launchable type="desktop-id">0ad.desktop</launchable>
<project_license>GPL-2.0+ and CC-BY-SA</project_license>
<metadata_license>CC-BY-SA-3.0</metadata_license>
<name>0 A.D.</name>
<summary>Real-Time Strategy Game of Ancient Warfare</summary>
<summary>Real-time strategy game</summary>
<branding>
<color type="primary" scheme_preference="light">#d6c49a</color>
<color type="primary" scheme_preference="dark">#313131</color>
</branding>
<description>
<p>
0 A.D. is a real-time strategy (RTS) game of ancient warfare.
@@ -15,10 +22,10 @@
at their peak of economic growth and military prowess.
</p>
<p>
The thirteen factions are: Three of the Hellenic States (Athens, Sparta and
The fourteen factions are: Three of the Hellenic States (Athens, Sparta and
Macedonia), two of the kingdoms of Alexander the Great's successors
(Seleucids and Ptolemaic Egyptians), two Celtic tribes (Britons and
Gauls), the Romans, the Persians, the Iberians, the Carthaginians, the
Gauls), the Romans, the Persians, the Iberians, the Carthaginians, the Han, the
Mauryas and the Kushites.
Each civilization is complete with substantially unique artwork,
technologies and civilization bonuses.
@@ -32,15 +39,61 @@
<url type="bugtracker">https://gitea.wildfiregames.com/0ad/0ad/issues/</url>
<url type="translate">https://gitea.wildfiregames.com/0ad/0ad/wiki/Localization</url>
<url type="donation">https://play0ad.com/community/donate/</url>
<releases>
<release version="0.27.1" date="2025-07-13">
<url type="details">https://play0ad.com/patch-release-27-1-for-0-a-d/</url>
<description>
<p>Wildfire Games proudly announces the first "patch release" of 0 A.D., 0.27.1 for Alpha 27: "Agni".</p>
<ul>
<li>Critical performance fixes.</li>
<li>Graphics fixes for GPU Skinning and Vulkan Renderer.</li>
<li>Fix changing perspective in replays.</li>
<li>Port Forwarding and Multiplayer Lobby password fixes.</li>
<li>Various crash fixes, build fixes for Linux.</li>
</ul>
</description>
</release>
<release version="0.27.0" date="2025-01-30">
<url type="details">https://play0ad.com/new-release-0-a-d-alpha-27-agni/</url>
<description>
<p>Wildfire Games proudly announces the release of 0 A.D. Alpha 27: "Agni", the last alpha version of the free, open-source real-time strategy game of ancient warfare.</p>
<ul>
<li>Vulkan Renderer for improved performance.</li>
<li>Multiplayer Savegames support.</li>
<li>New Tips &amp; Tricks page for enhanced guidance.</li>
<li>Naval overhaul with categorized warships.</li>
<li>New and updated random and skirmish maps.</li>
<li>Gameplay enhancements: Civilization differentiation, melee balance updates, and improved capture mechanics.</li>
<li>Engine improvements: GPU Skinning, FSR upscaling, SpiderMonkey upgrades.</li>
<li>Observer flares and improved anti-cheat measures.</li>
<li>Migration to a git-based development environment.</li>
<li>Various bug fixes, UI enhancements, and performance optimizations.</li>
</ul>
</description>
</release>
</releases>
<screenshots>
<screenshot type="default">https://play0ad.com/wp-content/gallery/screenshots/screenshot0072.jpg</screenshot>
<screenshot>https://play0ad.com/wp-content/gallery/screenshots/water-specular.jpg</screenshot>
<screenshot>https://play0ad.com/wp-content/gallery/screenshots/screenshot0050.jpg</screenshot>
<screenshot>https://play0ad.com/wp-content/gallery/screenshots/cycladic_arcgipelago_6.jpg</screenshot>
<screenshot type="default">
<image>https://play0ad.com/wp-content/gallery/screenshots/screenshot0072.jpg</image>
<caption>Maurya village with buildings and farms</caption>
</screenshot>
<screenshot>
<image>https://play0ad.com/wp-content/gallery/screenshots/water-specular.jpg</image>
<caption>Carthaginian biremes in a tranquil cove</caption>
</screenshot>
<screenshot>
<image>https://play0ad.com/wp-content/gallery/screenshots/screenshot0050.jpg</image>
<caption>Republican Roman village in a desert setting</caption>
</screenshot>
<screenshot>
<image>https://play0ad.com/wp-content/gallery/screenshots/cycladic_arcgipelago_6.jpg</image>
<caption>Cycladic archipelago with six small islands</caption>
</screenshot>
</screenshots>
<update_contact>webmaster@wildfiregames.com</update_contact>
<keywords>
<keyword>RTS</keyword>
<keyword>0AD</keyword>
<keyword>Realtime Strategy</keyword>
<keyword>Economic Simulation Game</keyword>
<keyword>History</keyword>
@@ -55,6 +108,7 @@
<keyword>Britons</keyword>
<keyword>Carthaginians</keyword>
<keyword>Gauls</keyword>
<keyword>Han</keyword>
<keyword>Iberians</keyword>
<keyword>Kushites</keyword>
<keyword>Macedonians</keyword>
@@ -65,12 +119,23 @@
<keyword>Seleucids</keyword>
<keyword>Spartans</keyword>
</keywords>
<content_rating type="oars-1.0">
<requires>
<display_length compare="ge">1024</display_length>
</requires>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
<internet>offline-only</internet>
</supports>
<content_rating type="oars-1.1">
<content_attribute id="violence-cartoon">none</content_attribute>
<content_attribute id="violence-fantasy">none</content_attribute>
<content_attribute id="violence-realistic">intense</content_attribute>
<content_attribute id="violence-bloodshed">moderate</content_attribute>
<content_attribute id="violence-sexual">none</content_attribute>
<content_attribute id="violence-desecration">mild</content_attribute>
<content_attribute id="violence-slavery">mild</content_attribute>
<content_attribute id="drugs-alcohol">none</content_attribute>
<content_attribute id="drugs-narcotics">none</content_attribute>
<content_attribute id="drugs-tobacco">none</content_attribute>
@@ -85,9 +150,11 @@
<content_attribute id="social-location">none</content_attribute>
<content_attribute id="social-contacts">none</content_attribute>
<content_attribute id="money-purchasing">none</content_attribute>
<content_attribute id="money-advertising">none</content_attribute>
<content_attribute id="money-gambling">none</content_attribute>
</content_rating>
<mimetypes>
<mimetype>application/x-pyromod+zip</mimetype>
</mimetypes>
<provides>
<mediatype>application/x-pyromod+zip</mediatype>
<binary>pyrogenesis</binary>
</provides>
</component>
+1
View File
@@ -3,6 +3,7 @@ Version=1.4
Name=0 A.D.
Exec=0ad %F
Icon=0ad
StartupWMClass=pyrogenesis
Terminal=false
MimeType=application/x-pyromod+zip;
Type=Application
-7
View File
@@ -1,10 +1,5 @@
#!/bin/sh
if [ "$(id -u)" = "0" ]; then
echo "Running as root will mess up file permissions. Aborting ..." 1>&2
exit 1
fi
die()
{
echo ERROR: "$*"
@@ -47,8 +42,6 @@ fi
echo
# If we're in bash then make HOSTTYPE available to Premake, for primitive arch-detection
export HOSTTYPE="$HOSTTYPE"
# Now run Premake to create the makefiles
echo "Premake args: ${premake_args}"
if [ "$OS" != "Darwin" ]; then
+1 -1
View File
@@ -62,7 +62,7 @@ while [ "$#" -gt 0 ]; do
--with-system-cxxtest) with_system_cxxtest=true ;;
--with-system-nvtt) with_system_nvtt=true ;;
--with-system-mozjs) with_system_mozjs=true ;;
--with-system-premake) with_system_mozjs=true ;;
--with-system-premake) with_system_premake=true ;;
--with-spirv-reflect) with_spirv_reflect=true ;;
-j*) JOBS="$1" ;;
*)
+12 -2
View File
@@ -2,9 +2,19 @@ rem **Download sources and binaries of libraries**
rem **SVN revision to checkout for windows-libs**
rem **Update this line when you commit an update to windows-libs**
set "svnrev=28243"
set "svnrev=28256"
svn co https://svn.wildfiregames.com/public/windows-libs/trunk@%svnrev% win32
svn co https://svn.wildfiregames.com/public/windows-libs/trunk@%svnrev% win32 || ^
svn export --force https://svn.wildfiregames.com/public/windows-libs/trunk@%svnrev% win32
rem **Fixup SpiderMonkey for Windows 7 on Win32**
rem This change is performed separately to allow backporting to A27
set "smrev=28263"
if "%LIBS_PATH%" == "win32" (
svn up -r %smrev% win32/spidermonkey/bin || ^
svn export --force https://svn.wildfiregames.com/public/windows-libs/trunk/spidermonkey/bin@%smrev% win32/spidermonkey/bin || ^
exit /b 1
)
rem **Copy dependencies' binaries to binaries/system/**
+3 -1
View File
@@ -1,6 +1,8 @@
#!/bin/sh
set -e
: "${TAR:=tar}"
cd "$(dirname "$0")"
PV=4.4
@@ -40,7 +42,7 @@ fi
# unpack
rm -Rf "cxxtest-${PV}"
tar -xf "cxxtest-${PV}.tar.gz"
"${TAR}" -xf "cxxtest-${PV}.tar.gz"
# patch
patch -d "cxxtest-${PV}" -p1 <patches/0001-Add-Debian-python3-patch.patch
+4 -2
View File
@@ -1,6 +1,8 @@
#!/bin/sh
set -e
: "${TAR:=tar}"
cd "$(dirname "$0")"
PV=28209
@@ -10,7 +12,7 @@ fetch()
{
rm -Rf fcollada-${PV}
svn export https://svn.wildfiregames.com/public/source-libs/trunk/fcollada@${PV} fcollada-${PV}
tar cJf fcollada-${PV}.tar.xz fcollada-${PV}
"${TAR}" cJf fcollada-${PV}.tar.xz fcollada-${PV}
rm -R fcollada-${PV}
}
@@ -42,7 +44,7 @@ fi
# unpack
rm -Rf fcollada-${PV}
tar xf fcollada-${PV}.tar.xz
"${TAR}" xf fcollada-${PV}.tar.xz
# build
(
+10 -3
View File
@@ -1,16 +1,18 @@
#!/bin/sh
set -e
: "${TAR:=tar}"
cd "$(dirname "$0")"
PV=28209
LIB_VERSION=${PV}
LIB_VERSION=${PV}+wfg1
fetch()
{
rm -Rf nvtt-${PV}
svn export https://svn.wildfiregames.com/public/source-libs/trunk/nvtt@${PV} nvtt-${PV}
tar cJf nvtt-${PV}.tar.xz nvtt-${PV}
"${TAR}" cJf nvtt-${PV}.tar.xz nvtt-${PV}
rm -R nvtt-${PV}
}
@@ -42,7 +44,12 @@ fi
# unpack
rm -Rf nvtt-${PV}
tar xf nvtt-${PV}.tar.xz
"${TAR}" xf nvtt-${PV}.tar.xz
# patch
patch -d nvtt-${PV} -p1 <patches/0001-Don-t-overspecify-flags.patch
patch -d nvtt-${PV} -p1 <patches/0002-Bump-cmake-min-version-to-3.10.patch
patch -d nvtt-${PV} -p1 <patches/0003-Use-execute_process-insted-of-exec_program.patch
# build
(
@@ -0,0 +1,41 @@
From fee966a8d44afc26005df9b539b16a5ee7f99107 Mon Sep 17 00:00:00 2001
From: Ralph Sennhauser <ralph.sennhauser@gmail.com>
Date: Sun, 27 Apr 2025 07:53:42 +0200
Subject: [PATCH] Don't overspecify flags
Cmake picks flags like CFALGS CXXFLAGS and LDFLAGS from environment. The
use of CMAKE_LINK_FLAGS even triggers a warning.
Fixes: #7538
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
---
build.sh | 6 ------
1 file changed, 6 deletions(-)
diff --git a/build.sh b/build.sh
index dae3bcf..935c6ff 100755
--- a/build.sh
+++ b/build.sh
@@ -29,9 +29,6 @@ if [ "$(uname -s)" = "Darwin" ]; then
# but they're not as flexible for cross-compiling
# Disable png support (avoids some conflicts with MacPorts)
cmake .. \
- -DCMAKE_LINK_FLAGS="$LDFLAGS" \
- -DCMAKE_C_FLAGS="$CFLAGS" \
- -DCMAKE_CXX_FLAGS="$CXXFLAGS" \
-DCMAKE_BUILD_TYPE=Release \
$CMAKE_FLAGS \
-DBINDIR=bin \
@@ -40,9 +37,6 @@ if [ "$(uname -s)" = "Darwin" ]; then
-G "Unix Makefiles"
else
cmake .. \
- -DCMAKE_LINK_FLAGS="$LDFLAGS" \
- -DCMAKE_C_FLAGS="$CFLAGS" \
- -DCMAKE_CXX_FLAGS="$CXXFLAGS" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
$CMAKE_FLAGS \
--
2.49.0
@@ -0,0 +1,27 @@
From cc07839eff9be49e2c3df4e68ce18f3134caafd2 Mon Sep 17 00:00:00 2001
From: Ralph Sennhauser <ralph.sennhauser@gmail.com>
Date: Sun, 27 Apr 2025 07:57:10 +0200
Subject: [PATCH] Bump cmake min version to 3.10
Cmake-4 removes support for cmake < cmake-3.5 and deprecates cmake <
cmake-3.10
Fixes: #7764
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
---
src/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index b18b791..b517b52 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
+CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(NV)
ENABLE_TESTING()
--
2.49.0
@@ -0,0 +1,26 @@
From 324f50d1ccf5ca38e0f3817c8d18208bd72e01f2 Mon Sep 17 00:00:00 2001
From: Ralph Sennhauser <ralph.sennhauser@gmail.com>
Date: Sun, 27 Apr 2025 08:05:59 +0200
Subject: [PATCH] Use execute_process insted of exec_program
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
---
src/cmake/DetermineProcessor.cmake | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/cmake/DetermineProcessor.cmake b/src/cmake/DetermineProcessor.cmake
index 7db7a95..13d39de 100644
--- a/src/cmake/DetermineProcessor.cmake
+++ b/src/cmake/DetermineProcessor.cmake
@@ -8,7 +8,7 @@ IF(UNIX)
#EXEC_PROGRAM(uname ARGS -p OUTPUT_VARIABLE NV_SYSTEM_PROCESSOR RETURN_VALUE val)
#IF("${val}" GREATER 0 OR NV_SYSTEM_PROCESSOR STREQUAL "unknown")
- EXEC_PROGRAM(uname ARGS -m OUTPUT_VARIABLE NV_SYSTEM_PROCESSOR RETURN_VALUE val)
+ execute_process(COMMAND uname -m OUTPUT_VARIABLE NV_SYSTEM_PROCESSOR ERROR_VARIABLE val)
#ENDIF("${val}" GREATER 0 OR NV_SYSTEM_PROCESSOR STREQUAL "unknown")
IF(NV_SYSTEM_PROCESSOR STREQUAL "Power Macintosh")
--
2.49.0
+5 -12
View File
@@ -4,11 +4,12 @@ set -e
: "${OS:=$(uname -s || true)}"
: "${MAKE:=make}"
: "${JOBS:=-j1}"
: "${TAR:=tar}"
cd "$(dirname "$0")"
PV=5.0.0-beta3
LIB_VERSION=${PV}+wfg1
PV=5.0.0-beta4
LIB_VERSION=${PV}+wfg0
fetch()
{
@@ -44,19 +45,11 @@ fi
# unpack
rm -Rf "premake-core-${PV}"
tar -xf "premake-core-${PV}.tar.gz"
"${TAR}" -xf "premake-core-${PV}.tar.gz"
# patch
# ffcb7790f013bdceacc14ba5fda1c5cd107aac08
patch -d "premake-core-${PV}" -p1 <patches/0001-Use-_SC_NPROCESSORS_ONLN-for-CPU-detection-in-BSDs.-.patch
# https://github.com/premake/premake-core/issues/2338
patch -d "premake-core-${PV}" -p1 <patches/0002-Make-clang-default-toolset-for-BSD.patch
# 82c9d90495940e2d0d574e1c7849e9698f23b090
patch -d "premake-core-${PV}" -p1 <patches/0003-Add-support-for-riscv64-2356.patch
# 928397f72c00979d57ec4688cb1fb26ec7f2449b
patch -d "premake-core-${PV}" -p1 <patches/0004-Add-support-for-loongarch64-2363.patch
# 5c524b6d53307bcb4ba7b02c9dba20100df68943
patch -d "premake-core-${PV}" -p1 <patches/0005-premake.h-added-e2k-definition-2349.patch
patch -d "premake-core-${PV}" -p1 <patches/0001-Make-clang-default-toolset-for-BSD.patch
#build
(
@@ -1,4 +1,4 @@
From 0641fc981d664d1fd7202f145a9c846ee0089b20 Mon Sep 17 00:00:00 2001
From 4f02eba3f7279de3693aa2405e1c0700fcade23a Mon Sep 17 00:00:00 2001
From: Ralph Sennhauser <ralph.sennhauser@gmail.com>
Date: Mon, 25 Nov 2024 15:57:10 +0100
Subject: [PATCH] Make clang default toolset for *BSD
@@ -11,10 +11,10 @@ Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/_premake_init.lua b/src/_premake_init.lua
index a1043e25..ca901ffc 100644
index 69f05fc9..c42c514f 100644
--- a/src/_premake_init.lua
+++ b/src/_premake_init.lua
@@ -1597,7 +1597,7 @@
@@ -1441,7 +1441,7 @@
filter { "kind:SharedLib", "system:not Windows" }
pic "On"
@@ -22,7 +22,7 @@ index a1043e25..ca901ffc 100644
+ filter { "system:darwin or bsd" }
toolset "clang"
filter { "platforms:Win32" }
filter { "system:emscripten" }
--
2.45.2
@@ -1,48 +0,0 @@
From 6b52a675b7ec13619a64836a7d50ebcb7e0a09d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Matos?= <joao@tritao.eu>
Date: Thu, 14 Nov 2024 13:42:21 +0000
Subject: [PATCH] Use `_SC_NPROCESSORS_ONLN` for CPU detection in BSDs. (#2329)
Fixes https://github.com/premake/premake-core/issues/2328.
---
src/host/os_getnumcpus.c | 23 +----------------------
1 file changed, 1 insertion(+), 22 deletions(-)
diff --git a/src/host/os_getnumcpus.c b/src/host/os_getnumcpus.c
index f9b2fa93..57c478d5 100644
--- a/src/host/os_getnumcpus.c
+++ b/src/host/os_getnumcpus.c
@@ -46,29 +46,8 @@ int do_getnumcpus()
{
return 0;
}
-#elif PLATFORM_SOLARIS | PLATFORM_AIX | PLATFORM_MACOSX
+#elif PLATFORM_SOLARIS | PLATFORM_AIX | PLATFORM_MACOSX | PLATFORM_BSD
return sysconf(_SC_NPROCESSORS_ONLN);
-#elif PLATFORM_BSD
- int mib[4];
- int numCPU;
- size_t len = sizeof(numCPU);
-
- /* set the mib for hw.ncpu */
- mib[0] = CTL_HW;
- mib[1] = HW_AVAILCPU; // alternatively, try HW_NCPU;
-
- /* get the number of CPUs from the system */
- sysctl(mib, 2, &numCPU, &len, NULL, 0);
-
- if (numCPU < 1)
- {
- mib[1] = HW_NCPU;
- sysctl(mib, 2, &numCPU, &len, NULL, 0);
- if (numCPU < 1)
- return 0;
- }
-
- return numCPU;
#else
#warning do_getnumcpus is not implemented for your platform yet
return 0;
--
2.45.2
@@ -1,68 +0,0 @@
From 4a4491c96feb182d219759de0d9e7655d4ab39ec Mon Sep 17 00:00:00 2001
From: Levi Zim <rsworktech@outlook.com>
Date: Thu, 5 Dec 2024 23:52:42 +0800
Subject: [PATCH] Add support for riscv64 (#2356)
* Add support for riscv64
* Make error message clear when architecture is unknown
---
src/_premake_init.lua | 1 +
src/base/_foundation.lua | 1 +
src/host/premake.h | 4 ++++
website/docs/architecture.md | 1 +
4 files changed, 7 insertions(+)
diff --git a/src/_premake_init.lua b/src/_premake_init.lua
index ca901ffc..184f0c43 100644
--- a/src/_premake_init.lua
+++ b/src/_premake_init.lua
@@ -28,6 +28,7 @@
p.X86_64,
p.ARM,
p.ARM64,
+ p.RISCV64,
},
aliases = {
i386 = p.X86,
diff --git a/src/base/_foundation.lua b/src/base/_foundation.lua
index 0c19a861..411eb536 100644
--- a/src/base/_foundation.lua
+++ b/src/base/_foundation.lua
@@ -61,6 +61,7 @@
premake.X86_64 = "x86_64"
premake.ARM = "ARM"
premake.ARM64 = "ARM64"
+ premake.RISCV64 = "RISCV64"
diff --git a/src/host/premake.h b/src/host/premake.h
index 9bf20380..788f8a38 100644
--- a/src/host/premake.h
+++ b/src/host/premake.h
@@ -60,6 +60,10 @@
#elif defined(__arm__) || defined(__thumb__) || defined(__TARGET_ARCH_ARM) || defined(__TARGET_ARCH_THUMB) || \
defined(__ARM) || defined(_M_ARM) || defined(_M_ARM_T) || defined(__ARM_ARCH)
#define PLATFORM_ARCHITECTURE "ARM"
+#elif defined(_M_RISCV64) || (defined(__riscv) && __riscv_xlen == 64)
+#define PLATFORM_ARCHITECTURE "RISCV64"
+#elif !defined(RC_INVOKED)
+#error Unknown architecture detected
#endif
/* Pull in platform-specific headers required by built-in functions */
diff --git a/website/docs/architecture.md b/website/docs/architecture.md
index fb530453..0dbe352e 100644
--- a/website/docs/architecture.md
+++ b/website/docs/architecture.md
@@ -13,6 +13,7 @@ architecture ("value")
* `x86_64`
* `ARM`
* `ARM64`
+* `RISCV64`
* `armv5`: Only supported in VSAndroid projects
* `armv7`: Only supported in VSAndroid projects
* `aarch64`: Only supported in VSAndroid projects
--
2.45.2
@@ -1,64 +0,0 @@
From 64a8e8ae350a734b2b8512c4bcbba6e56a8e814e Mon Sep 17 00:00:00 2001
From: Leo <114008189+Leoforever123@users.noreply.github.com>
Date: Sat, 7 Dec 2024 11:21:24 +0800
Subject: [PATCH] Add support for loongarch64 (#2363)
---
src/_premake_init.lua | 1 +
src/base/_foundation.lua | 1 +
src/host/premake.h | 2 ++
website/docs/architecture.md | 1 +
4 files changed, 5 insertions(+)
diff --git a/src/_premake_init.lua b/src/_premake_init.lua
index 184f0c43..d40558df 100644
--- a/src/_premake_init.lua
+++ b/src/_premake_init.lua
@@ -29,6 +29,7 @@
p.ARM,
p.ARM64,
p.RISCV64,
+ p.LOONGARCH64
},
aliases = {
i386 = p.X86,
diff --git a/src/base/_foundation.lua b/src/base/_foundation.lua
index 411eb536..4581d2c0 100644
--- a/src/base/_foundation.lua
+++ b/src/base/_foundation.lua
@@ -62,6 +62,7 @@
premake.ARM = "ARM"
premake.ARM64 = "ARM64"
premake.RISCV64 = "RISCV64"
+ premake.LOONGARCH64 = "loongarch64"
diff --git a/src/host/premake.h b/src/host/premake.h
index 788f8a38..4e15b863 100644
--- a/src/host/premake.h
+++ b/src/host/premake.h
@@ -62,6 +62,8 @@
#define PLATFORM_ARCHITECTURE "ARM"
#elif defined(_M_RISCV64) || (defined(__riscv) && __riscv_xlen == 64)
#define PLATFORM_ARCHITECTURE "RISCV64"
+#elif (defined(__loongarch__) && __loongarch_grlen == 64) || defined(__loongarch64)
+#define PLATFORM_ARCHITECTURE "loongarch64"
#elif !defined(RC_INVOKED)
#error Unknown architecture detected
#endif
diff --git a/website/docs/architecture.md b/website/docs/architecture.md
index 0dbe352e..e7f4283f 100644
--- a/website/docs/architecture.md
+++ b/website/docs/architecture.md
@@ -14,6 +14,7 @@ architecture ("value")
* `ARM`
* `ARM64`
* `RISCV64`
+* `loongarch64`
* `armv5`: Only supported in VSAndroid projects
* `armv7`: Only supported in VSAndroid projects
* `aarch64`: Only supported in VSAndroid projects
--
2.45.2
@@ -1,25 +0,0 @@
From a4fa1da72cc969bb85e575be25acd5aaef2edc49 Mon Sep 17 00:00:00 2001
From: r-a-sattarov <51679282+r-a-sattarov@users.noreply.github.com>
Date: Sun, 8 Dec 2024 22:39:47 +0300
Subject: [PATCH] premake.h - added e2k definition (#2349)
---
src/host/premake.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/host/premake.h b/src/host/premake.h
index 4e15b863..25ccf33a 100644
--- a/src/host/premake.h
+++ b/src/host/premake.h
@@ -64,6 +64,8 @@
#define PLATFORM_ARCHITECTURE "RISCV64"
#elif (defined(__loongarch__) && __loongarch_grlen == 64) || defined(__loongarch64)
#define PLATFORM_ARCHITECTURE "loongarch64"
+#elif defined(__e2k__)
+#define PLATFORM_ARCHITECTURE "e2k"
#elif !defined(RC_INVOKED)
#error Unknown architecture detected
#endif
--
2.45.2
+4 -2
View File
@@ -1,12 +1,14 @@
#!/bin/sh
set -e
: "${TAR:=tar}"
cd "$(dirname "$0")"
# This should match the version in config/milestone.txt
FOLDER="mozjs-115.16.1"
# If same-version changes are needed, increment this.
LIB_VERSION="115.16.1+1"
LIB_VERSION="115.16.1+a27.1"
LIB_NAME="mozjs115"
fetch()
@@ -47,7 +49,7 @@ fi
# unpack
rm -Rf "${FOLDER}"
tar xfJ "${FOLDER}.tar.xz"
"${TAR}" xfJ "${FOLDER}.tar.xz"
# patch
(
@@ -1,6 +1,6 @@
--- a/modules/fdlibm/src/math_private.h
+++ b/modules/fdlibm/src/math_private.h
@@ -30,8 +30,13 @@
@@ -30,7 +30,11 @@
* Adapted from https://github.com/freebsd/freebsd-src/search?q=__double_t
*/
@@ -10,10 +10,9 @@
typedef double __double_t;
+#endif
typedef __double_t double_t;
+typedef float __float_t;
typedef float __float_t;
/*
* The original fdlibm code used statements like:
@@ -630,6 +634,53 @@
return ((double)(x + 0x1.8p52) - 0x1.8p52);
}
+3 -1
View File
@@ -1,6 +1,8 @@
#!/bin/sh
set -e
: "${TAR:=tar}"
cd "$(dirname "$0")"
PV=1.3.290.0
@@ -40,7 +42,7 @@ fi
# unpack
rm -Rf "SPIRV-Reflect-vulkan-sdk-${PV}"
tar xf "vulkan-sdk-${PV}.tar.gz"
"${TAR}" xf "vulkan-sdk-${PV}.tar.gz"
# configure
cmake -B build -S "SPIRV-Reflect-vulkan-sdk-${PV}" \
+5 -7
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -236,12 +236,10 @@ void CModel::ValidatePosition()
// For CPU skinning, we precompute as much as possible so that the only
// per-vertex work is a single matrix*vec multiplication.
// For GPU skinning, we try to minimise CPU work by doing most computation
// in the vertex shader instead.
// Using g_RenderingOptions to detect CPU vs GPU is a bit hacky,
// and this doesn't allow the setting to change at runtime, but there isn't
// an obvious cleaner way to determine what data needs to be computed.
bool worldSpaceBoneMatrices = !g_RenderingOptions.GetGPUSkinning();
bool computeBlendMatrices = !g_RenderingOptions.GetGPUSkinning();
// in the compute shader instead.
const bool isGPUSkinningEnabled{g_RenderingOptions.GetGPUSkinning()};
const bool worldSpaceBoneMatrices{!isGPUSkinningEnabled};
const bool computeBlendMatrices{!isGPUSkinningEnabled};
if (m_BoneMatrices && worldSpaceBoneMatrices)
{
+1 -1
View File
@@ -370,7 +370,7 @@ void CGUIManager::TickObjects()
// We share the script context with everything else that runs in the same thread.
// This call makes sure we trigger GC regularly even if the simulation is not running.
m_ScriptContext.MaybeIncrementalGC(1.0f);
m_ScriptContext.MaybeIncrementalGC();
// Save an immutable copy so iterators aren't invalidated by tick handlers
PageStackType pageStack = m_PageStack;
+3 -3
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -20,6 +20,6 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define PYROGENESIS_VERSION "0.27.0"
#define PYROGENESIS_VERSION_WORD 0,27,0,0
#define PYROGENESIS_VERSION "0.27.1"
#define PYROGENESIS_VERSION_WORD 0,27,1,0
extern wchar_t build_version[];
+2 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -43,6 +43,7 @@ public:
virtual void SetNick(const std::string& nick) = 0;
virtual std::string GetNick() const = 0;
virtual std::string GetJID() const = 0;
virtual std::string GetUsername() const = 0;
virtual void ChangePassword(const std::string& newPassword) = 0;
virtual void kick(const std::string& nick, const std::string& reason) = 0;
virtual void ban(const std::string& nick, const std::string& reason) = 0;
+17 -4
View File
@@ -169,6 +169,8 @@ XmppClient::XmppClient(const ScriptInterface* scriptInterface, const std::string
*/
XmppClient::~XmppClient()
{
this->disconnect();
DbgXMPP("XmppClient destroyed");
delete m_registration;
delete m_mucRoom;
@@ -1183,9 +1185,9 @@ const std::wstring& XmppClient::GetSubject()
}
/**
* Request nick change, real change via mucRoomHandler.
* Request MUC nick change, real change via mucRoomHandler.
*
* @param nick Desired nickname
* @param nick Desired MUC nickname
*/
void XmppClient::SetNick(const std::string& nick)
{
@@ -1193,7 +1195,7 @@ void XmppClient::SetNick(const std::string& nick)
}
/**
* Get current nickname.
* Get current MUC nickname.
*/
std::string XmppClient::GetNick() const
{
@@ -1205,6 +1207,17 @@ std::string XmppClient::GetJID() const
return m_client->jid().full();
}
/**
* Get the XMPP username.
*
* @return current XMPP username
*/
std::string XmppClient::GetUsername() const
{
return m_username;
}
/**
* Change password for authenticated user.
*
@@ -1212,7 +1225,7 @@ std::string XmppClient::GetJID() const
*/
void XmppClient::ChangePassword(const std::string& newPassword)
{
m_registration->changePassword(m_username, newPassword);
m_registration->changePassword(m_client->jid().username(), newPassword);
}
/**
+2 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -88,6 +88,7 @@ public:
void SetNick(const std::string& nick) override;
std::string GetNick() const override;
std::string GetJID() const override;
std::string GetUsername() const override;
void ChangePassword(const std::string& newPassword) override;
void kick(const std::string& nick, const std::string& reason) override;
void ban(const std::string& nick, const std::string& reason) override;
+2 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -218,6 +218,7 @@ void RegisterScriptFunctions(const ScriptRequest& rq)
REGISTER_XMPP(SetNick, "LobbySetNick");
REGISTER_XMPP(GetNick, "LobbyGetNick");
REGISTER_XMPP(GetJID, "LobbyGetJID");
REGISTER_XMPP(GetUsername, "LobbyGetUsername");
REGISTER_XMPP(ChangePassword, "LobbyChangePassword");
REGISTER_XMPP(kick, "LobbyKick");
REGISTER_XMPP(ban, "LobbyBan");
+8 -8
View File
@@ -193,20 +193,20 @@ bool CNetServerWorker::SetupConnection(const u16 port)
#if CONFIG2_MINIUPNPC
// Launch the UPnP thread
m_UPnPThread = std::thread(Threading::HandleExceptions<SetupUPnP>::Wrapper);
m_UPnPThread = std::thread(Threading::HandleExceptions<SetupUPnP>::Wrapper, port);
#endif
return true;
}
#if CONFIG2_MINIUPNPC
void CNetServerWorker::SetupUPnP()
void CNetServerWorker::SetupUPnP(const u16 port)
{
debug_SetThreadName("UPnP");
// Values we want to set.
char psPort[6];
sprintf_s(psPort, ARRAY_SIZE(psPort), "%d", PS_DEFAULT_PORT);
sprintf_s(psPort, ARRAY_SIZE(psPort), "%d", port);
const char* leaseDuration = "0"; // Indefinite/permanent lease duration.
const char* description = "0AD Multiplayer";
const char* protocall = "UDP";
@@ -242,7 +242,7 @@ void CNetServerWorker::SetupUPnP()
int ret = 0;
// Try a cached URL first
if (!rootDescURL.empty() && UPNP_GetIGDFromUrl(rootDescURL.c_str(), &urls, &data, internalIPAddress, sizeof(internalIPAddress)))
if (!rootDescURL.empty() && UPNP_GetIGDFromUrl(rootDescURL.c_str(), &urls, &data, internalIPAddress, sizeof(internalIPAddress)) && strlen(data.first.controlurl) != 0)
{
LOGMESSAGE("Net server: using cached IGD = %s", urls.controlURL);
ret = 1;
@@ -339,9 +339,9 @@ void CNetServerWorker::SetupUPnP()
externalIPAddress, psPort, protocall, intClient, intPort, duration);
// Cache root descriptor URL to try to avoid discovery next time.
g_ConfigDB.SetValueString(CFG_USER, "network.upnprootdescurl", urls.controlURL);
g_ConfigDB.WriteValueToFile(CFG_USER, "network.upnprootdescurl", urls.controlURL);
LOGMESSAGE("Net server: cached UPnP root descriptor URL as %s", urls.controlURL);
g_ConfigDB.SetValueString(CFG_USER, "network.upnprootdescurl", urls.rootdescURL);
g_ConfigDB.WriteValueToFile(CFG_USER, "network.upnprootdescurl", urls.rootdescURL);
LOGMESSAGE("Net server: cached UPnP root descriptor URL as %s", urls.rootdescURL);
freeUPnP();
}
@@ -424,7 +424,7 @@ bool CNetServerWorker::RunStep()
// (Do as little work as possible while the mutex is held open,
// to avoid performance problems and deadlocks.)
m_ScriptInterface->GetContext().MaybeIncrementalGC(0.5f);
m_ScriptInterface->GetContext().MaybeIncrementalGC();
ScriptRequest rq(m_ScriptInterface);
+1 -1
View File
@@ -436,7 +436,7 @@ private:
/**
* Try to find a UPnP root on the network and setup port forwarding.
*/
static void SetupUPnP();
static void SetupUPnP(const u16 port);
std::thread m_UPnPThread;
#endif
+6
View File
@@ -43,6 +43,7 @@
#include "renderer/TimeManager.h"
#include "renderer/WaterManager.h"
#include "scriptinterface/FunctionWrapper.h"
#include "scriptinterface/ScriptContext.h"
#include "scriptinterface/ScriptInterface.h"
#include "scriptinterface/JSON.h"
#include "simulation2/Simulation2.h"
@@ -331,6 +332,11 @@ PSRETURN CGame::ReallyStartGame()
// all be invisible)
Interpolate(0, 0);
// Run a shrinking GC to reset the memory before starting the game proper.
// This also clears JIT code, which seems like a good idea as the game init
// might have different patterns to the game itself.
m_Simulation2->GetScriptInterface().GetContext().ShrinkingGC();
m_GameStarted = true;
// Preload resources to avoid blinking on a first game frame.
+2 -2
View File
@@ -348,7 +348,7 @@ void ShutdownNetworkAndUI()
if (hasRenderer)
{
TIMER_BEGIN(L"shutdown Renderer");
g_Renderer.~CRenderer();
delete &g_Renderer;
TIMER_END(L"shutdown Renderer");
}
@@ -535,7 +535,7 @@ bool Init(const CmdLineArgs& args, int flags)
// Using a global object for the context is a workaround until Simulation and AI use
// their own threads and also their own contexts.
const int contextSize = 384 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 12 * 1024 * 1024;
g_ScriptContext = ScriptContext::CreateContext(contextSize, heapGrowthBytesGCTrigger);
// On the first Init (INIT_MODS), check for command-line arguments
+1 -2
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -71,7 +71,6 @@ namespace {
static_assert(std::is_integral<std::underlying_type<SDL_Scancode>::type>::value, "SDL_Scancode is not an integral enum.");
static_assert(SDL_USEREVENT_ == SDL_USEREVENT, "SDL_USEREVENT_ is not the same type as the real SDL_USEREVENT");
static_assert(UNUSED_HOTKEY_CODE == SDL_SCANCODE_UNKNOWN);
// Look up each key binding in the config file and set the mappings for
// all key combinations that trigger it.
+3 -2
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -49,7 +49,8 @@ const uint SDL_HOTKEYUP = SDL_USEREVENT_ + 2;
const uint SDL_HOTKEYPRESS_SILENT = SDL_USEREVENT_ + 3;
const uint SDL_HOTKEYUP_SILENT = SDL_USEREVENT_ + 4;
constexpr SDL_Scancode_ UNUSED_HOTKEY_CODE = 0; // == SDL_SCANCODE_UNKNOWN
// Value not mapping to any valid SDL_SCANCODE_*
constexpr SDL_Scancode_ UNUSED_HOTKEY_CODE{-1};
struct SKey
{
+2 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -25,6 +25,7 @@
#include "ps/CStr.h"
#include <algorithm>
#include <cstring>
#include <unordered_map>
#include <vector>
+1 -1
View File
@@ -197,7 +197,7 @@ void CReplayPlayer::Replay(const bool serializationtest, const int rejointesttur
g_ProfileViewer.AddRootTable(g_ScriptStatsTable);
const int contextSize = 384 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 20 * 1024 * 1024;
const int heapGrowthBytesGCTrigger = 12 * 1024 * 1024;
g_ScriptContext = ScriptContext::CreateContext(contextSize, heapGrowthBytesGCTrigger);
std::vector<SimulationCommand> commands;
+3 -3
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -76,7 +76,7 @@ void SetPlayerID(const ScriptRequest& rq, int id)
if (currentID == id)
return;
if (g_Game->CheatsEnabled())
if (g_Game->CheatsEnabled() || g_Game->IsVisualReplay())
g_Game->SetPlayerID(id);
else
ScriptException::Raise(rq, "Changing player ID with cheats disabled is prohibited");
@@ -89,7 +89,7 @@ void SetViewedPlayer(const ScriptRequest& rq, int id)
int playerID = g_Game->GetPlayerID();
// Forbid active players to reveal the map by changing perspective, unless cheats are allowed.
if (playerID == -1 || g_Game->CheatsEnabled() || g_Game->PlayerFinished(playerID))
if (playerID == -1 || g_Game->CheatsEnabled() || g_Game->PlayerFinished(playerID) || g_Game->IsVisualReplay())
g_Game->SetViewedPlayerID(id);
else
ScriptException::Raise(rq, "Changing the perspective with cheats disabled is prohibited");
+2 -1
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -219,6 +219,7 @@ void ShaderModelRenderer::Submit(int cullGroup, CModel* model)
const void* key = m->vertexRenderer.get();
if (!rdata || rdata->GetKey() != key)
{
model->InvalidatePosition();
rdata = m->vertexRenderer->CreateModelData(key, model);
model->SetRenderData(rdata);
model->SetDirty(~0u);
+3
View File
@@ -262,6 +262,9 @@ void CRenderingOptions::ReadConfigAndSetupHooks()
m_GPUSkinning = true;
else
LOGMESSAGE("GPU skinning isn't supported on the current hardware.");
if (CRenderer::IsInitialised())
g_Renderer.MakeShadersDirty();
}
});
+1 -2
View File
@@ -362,8 +362,7 @@ std::unique_ptr<IDevice> CDevice::Create(SDL_Window* window, const bool arb)
capabilities.ARBShaders = !ogl_HaveExtensions(0, "GL_ARB_vertex_program", "GL_ARB_fragment_program", nullptr);
if (capabilities.ARBShaders)
capabilities.ARBShadersShadow = ogl_HaveExtension("GL_ARB_fragment_program_shadow");
capabilities.computeShaders = !device->m_ARB &&
(ogl_HaveVersion(4, 3) || (ogl_HaveVersion(4, 2) && ogl_HaveExtension("GL_ARB_compute_shader") && ogl_HaveExtension("GL_ARB_shader_image_load_store")));
capabilities.computeShaders = !device->m_ARB && ogl_HaveVersion(4, 3);
#if CONFIG2_GLES
// Some GLES implementations have GL_EXT_texture_compression_dxt1
// but that only supports DXT1 so we can't use it.
@@ -246,6 +246,9 @@ CDeviceCommandContext::CDeviceCommandContext(CDevice* device)
for (size_t index = 0; index < m_BoundBuffers.size(); ++index)
{
const CBuffer::Type type = static_cast<CBuffer::Type>(index);
// Currently we don't support upload buffers for GL.
if (type == CBuffer::Type::UPLOAD)
continue;
const GLenum target = BufferTypeToGLTarget(type);
const GLuint handle = 0;
m_BoundBuffers[index].first = target;
@@ -1456,6 +1459,7 @@ CDeviceCommandContext::ScopedBufferBind::ScopedBufferBind(
{
ENSURE(buffer);
m_CacheIndex = static_cast<size_t>(buffer->GetType());
ENSURE(m_CacheIndex < m_DeviceCommandContext->m_BoundBuffers.size());
const GLenum target = BufferTypeToGLTarget(buffer->GetType());
const GLuint handle = buffer->GetHandle();
if (m_DeviceCommandContext->m_BoundBuffers[m_CacheIndex].first == target &&
@@ -221,7 +221,7 @@ private:
};
using BoundBuffer = std::pair<GLenum, GLuint>;
std::array<BoundBuffer, 2> m_BoundBuffers;
std::array<BoundBuffer, 4> m_BoundBuffers;
class ScopedBufferBind
{
public:
+1 -1
View File
@@ -135,7 +135,7 @@ template<typename T>
inline bool GetObjectClassName(const ScriptRequest& rq, JS::HandleObject obj, T& name)
{
JS::RootedValue constructor(rq.cx, JS::ObjectOrNullValue(JS_GetConstructor(rq.cx, obj)));
return constructor.isObject() && Script::HasProperty(rq, constructor, "name") && Script::GetProperty(rq, constructor, "name", name);
return constructor.isObject() && Script::GetProperty(rq, constructor, "name", name);
}
/**
+82 -93
View File
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -27,6 +27,8 @@
#include "scriptinterface/ScriptEngine.h"
#include "scriptinterface/ScriptInterface.h"
#include "js/friend/PerformanceHint.h"
void GCSliceCallbackHook(JSContext* UNUSED(cx), JS::GCProgress progress, const JS::GCDescription& UNUSED(desc))
{
/**
@@ -57,7 +59,8 @@ void GCSliceCallbackHook(JSContext* UNUSED(cx), JS::GCProgress progress, const J
if (progress == JS::GCProgress::GC_CYCLE_BEGIN)
printf("starting cycle ===========================================\n");
const char16_t* str = desc.formatMessage(cx);
//const char16_t* str = desc.formatMessage(cx);
const char16_t* str = desc.formatSliceMessage(cx);
int len = 0;
for(int i = 0; i < 10000; i++)
@@ -75,15 +78,22 @@ void GCSliceCallbackHook(JSContext* UNUSED(cx), JS::GCProgress progress, const J
}
printf("---------------------------------------\n: %ls \n---------------------------------------\n", outstring);
const uint32_t gcBytes = JS_GetGCParameter(cx, JSGC_BYTES);
printf("gcBytes: %i KB\n", gcBytes / 1024);
if (progress == JS::GCProgress::GC_SLICE_END)
printf("ending cycle ===========================================\n");
#endif
}
std::shared_ptr<ScriptContext> ScriptContext::CreateContext(int contextSize, int heapGrowthBytesGCTrigger)
std::shared_ptr<ScriptContext> ScriptContext::CreateContext(int contextSize, uint32_t heapGrowthBytesGCTrigger)
{
return std::make_shared<ScriptContext>(contextSize, heapGrowthBytesGCTrigger);
}
ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
ScriptContext::ScriptContext(int contextSize, uint32_t heapGrowthBytesGCTrigger):
m_JobQueue{std::make_unique<Script::JobQueue>()},
m_ContextSize{contextSize},
m_HeapGrowthBytesGCTrigger{heapGrowthBytesGCTrigger}
@@ -110,6 +120,12 @@ ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
JS_SetGCParameter(m_cx, JSGC_INCREMENTAL_GC_ENABLED, true);
JS_SetGCParameter(m_cx, JSGC_PER_ZONE_GC_ENABLED, false);
// Set a low time budget to avoid lag spikes, but allow any number of last ditch GCs
// to avoid OOM errors.
JS_SetGCParameter(m_cx, JSGC_SLICE_TIME_BUDGET_MS, 10);
JS_SetGCParameter(m_cx, JSGC_MIN_LAST_DITCH_GC_PERIOD, 0);
JS_SetOffthreadIonCompilationEnabled(m_cx, true);
// For GC debugging:
@@ -129,6 +145,10 @@ ScriptContext::ScriptContext(int contextSize, int heapGrowthBytesGCTrigger):
JS::ContextOptionsRef(m_cx).setStrictMode(true);
// Workaround to turn off nursery size heuristic.
// See https://gitea.wildfiregames.com/0ad/0ad/issues/7714 for details.
js::gc::SetPerformanceHint(m_cx, js::gc::PerformanceHint::InPageLoad);
ScriptEngine::GetSingleton().RegisterContext(m_cx);
JS::SetJobQueue(m_cx, m_JobQueue.get());
@@ -139,6 +159,9 @@ ScriptContext::~ScriptContext()
{
ENSURE(ScriptEngine::IsInitialised() && "The ScriptEngine must be active (initialized and not yet shut down) when destroying a ScriptContext!");
// Switch back to normal performance mode to avoid assertion in debug mode.
js::gc::SetPerformanceHint(m_cx, js::gc::PerformanceHint::Normal);
JS_DestroyContext(m_cx);
ScriptEngine::GetSingleton().UnRegisterContext(m_cx);
}
@@ -159,105 +182,71 @@ void ScriptContext::UnRegisterRealm(JS::Realm* realm)
}
#define GC_DEBUG_PRINT 0
void ScriptContext::MaybeIncrementalGC(double delay)
void ScriptContext::MaybeIncrementalGC()
{
PROFILE2("MaybeIncrementalGC");
if (JS::IsIncrementalGCEnabled(m_cx))
if (!JS::IsIncrementalGCEnabled(m_cx))
return;
// The idea is to get the heap size after a completed GC and trigger the next GC
// when the heap size has reached m_LastGCBytes + X.
// Spidermonkey allocates memory arenas of 4KB for JS heap data.
// At the end of a GC, any such arena that became empty is freed.
// On shrinking GCs, spidermonkey further defragments the arenas, which effectively frees more memory but costs time.
// In practice, shrinking GCs also dump JITted code and the defragmentation is not worth it for 0 A.D.
// The regular GCs also free quite a bit of memory anyways, and non-full arenas get used for new objects.
const uint32_t gcBytes = JS_GetGCParameter(m_cx, JSGC_BYTES);
#if GC_DEBUG_PRINT
printf("gcBytes: %i KB, last of %i KB\n", gcBytes / 1024, m_LastGCBytes / 1024);
#endif
// The memory freeing happens mostly in the background, so we can't rely on the value on the last incremental slice.
// To fix that, just remember a 'minimum' value.
if (m_LastGCBytes > gcBytes || m_LastGCBytes == 0)
{
// The idea is to get the heap size after a completed GC and trigger the next GC when the heap size has
// reached m_LastGCBytes + X.
// In practice it doesn't quite work like that. When the incremental marking is completed, the sweeping kicks in.
// The sweeping actually frees memory and it does this in a background thread (if JS_USE_HELPER_THREADS is set).
// While the sweeping is happening we already run scripts again and produce new garbage.
const js::SliceBudget GCSliceTimeBudget = js::SliceBudget(js::TimeBudget(30)); // Milliseconds an incremental slice is allowed to run
// Have a minimum time in seconds to wait between GC slices and before starting a new GC to distribute the GC
// load and to hopefully make it unnoticeable for the player. This value should be high enough to distribute
// the load well enough and low enough to make sure we don't run out of memory before we can start with the
// sweeping.
if (timer_Time() - m_LastGCCheck < delay)
return;
m_LastGCCheck = timer_Time();
int gcBytes = JS_GetGCParameter(m_cx, JSGC_BYTES);
#if GC_DEBUG_PRINT
std::cout << "gcBytes: " << gcBytes / 1024 << " KB" << std::endl;
printf("Setting m_LastGCBytes: %d KB \n", gcBytes / 1024);
#endif
m_LastGCBytes = gcBytes;
}
// Run an additional incremental GC slice if the currently running incremental GC isn't over yet
// ... or
// start a new incremental GC if the JS heap size has grown enough for a GC to make sense
if (JS::IsIncrementalGCInProgress(m_cx) || (gcBytes - m_LastGCBytes > m_HeapGrowthBytesGCTrigger))
{
#if GC_DEBUG_PRINT
if (JS::IsIncrementalGCInProgress(m_cx))
printf("An incremental GC cycle is in progress. \n");
else
printf("GC needed because JSGC_BYTES - m_LastGCBytes > m_HeapGrowthBytesGCTrigger \n"
" JSGC_BYTES: %d KB \n m_LastGCBytes: %d KB \n m_HeapGrowthBytesGCTrigger: %d KB \n",
gcBytes / 1024,
m_LastGCBytes / 1024,
m_HeapGrowthBytesGCTrigger / 1024);
#endif
if (m_LastGCBytes > gcBytes || m_LastGCBytes == 0)
{
#if GC_DEBUG_PRINT
printf("Setting m_LastGCBytes: %d KB \n", gcBytes / 1024);
#endif
m_LastGCBytes = gcBytes;
}
// Run an additional incremental GC slice if the currently running incremental GC isn't over yet
// ... or
// start a new incremental GC if the JS heap size has grown enough for a GC to make sense
if (JS::IsIncrementalGCInProgress(m_cx) || (gcBytes - m_LastGCBytes > m_HeapGrowthBytesGCTrigger))
{
#if GC_DEBUG_PRINT
if (JS::IsIncrementalGCInProgress(m_cx))
printf("An incremental GC cycle is in progress. \n");
else
printf("GC needed because JSGC_BYTES - m_LastGCBytes > m_HeapGrowthBytesGCTrigger \n"
" JSGC_BYTES: %d KB \n m_LastGCBytes: %d KB \n m_HeapGrowthBytesGCTrigger: %d KB \n",
gcBytes / 1024,
m_LastGCBytes / 1024,
m_HeapGrowthBytesGCTrigger / 1024);
if (!JS::IsIncrementalGCInProgress(m_cx))
printf("Starting incremental GC \n");
else
printf("Running incremental GC slice \n");
#endif
// A hack to make sure we never exceed the context size because we can't collect the memory
// fast enough.
if (gcBytes > m_ContextSize / 2)
{
if (JS::IsIncrementalGCInProgress(m_cx))
{
#if GC_DEBUG_PRINT
printf("Finishing incremental GC because gcBytes > m_ContextSize / 2. \n");
#endif
PrepareZonesForIncrementalGC();
JS::FinishIncrementalGC(m_cx, JS::GCReason::API);
}
else
{
if (gcBytes > m_ContextSize * 0.75)
{
ShrinkingGC();
#if GC_DEBUG_PRINT
printf("Running shrinking GC because gcBytes > m_ContextSize * 0.75. \n");
#endif
}
else
{
#if GC_DEBUG_PRINT
printf("Running full GC because gcBytes > m_ContextSize / 2. \n");
#endif
JS_GC(m_cx);
}
}
}
else
{
#if GC_DEBUG_PRINT
if (!JS::IsIncrementalGCInProgress(m_cx))
printf("Starting incremental GC \n");
else
printf("Running incremental GC slice \n");
#endif
PrepareZonesForIncrementalGC();
if (!JS::IsIncrementalGCInProgress(m_cx))
JS::StartIncrementalGC(m_cx, JS::GCOptions::Normal, JS::GCReason::API, GCSliceTimeBudget);
else
JS::IncrementalGCSlice(m_cx, JS::GCReason::API, GCSliceTimeBudget);
}
m_LastGCBytes = gcBytes;
}
// There is a tradeoff between this time and the number of frames we must run GCs on, but overall we should prioritize smooth framerates.
const js::SliceBudget GCSliceTimeBudget = js::SliceBudget(js::TimeBudget(6)); // Milliseconds an incremental slice is allowed to run. SM respects this fairly well.
PrepareZonesForIncrementalGC();
if (!JS::IsIncrementalGCInProgress(m_cx))
JS::StartIncrementalGC(m_cx, JS::GCOptions::Normal, JS::GCReason::API, GCSliceTimeBudget);
else
JS::IncrementalGCSlice(m_cx, JS::GCReason::API, GCSliceTimeBudget);
// Reset this here so that the minimum gets cleared.
m_LastGCBytes = gcBytes;
}
}
+20 -15
View File
@@ -25,7 +25,7 @@
// Those are minimal defaults. The runtime for the main game is larger and GCs upon a larger growth.
constexpr int DEFAULT_CONTEXT_SIZE = 16 * 1024 * 1024;
constexpr int DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER = 2 * 1024 * 1024;
constexpr uint32_t DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER = 2 * 1024 * 1024;
namespace Script
{
@@ -45,7 +45,7 @@ class JobQueue;
class ScriptContext
{
public:
ScriptContext(int contextSize, int heapGrowthBytesGCTrigger);
ScriptContext(int contextSize, uint32_t heapGrowthBytesGCTrigger);
~ScriptContext();
/**
@@ -57,20 +57,26 @@ public:
*/
static std::shared_ptr<ScriptContext> CreateContext(
int contextSize = DEFAULT_CONTEXT_SIZE,
int heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER);
uint32_t heapGrowthBytesGCTrigger = DEFAULT_HEAP_GROWTH_BYTES_GCTRIGGER);
/**
* MaybeIncrementalGC tries to determine whether a context-wide garbage collection would free up enough memory to
* be worth the amount of time it would take. It does this with our own logic and NOT some predefined JSAPI logic because
* such functionality currently isn't available out of the box.
* It does incremental GC which means it will collect one slice each time it's called until the garbage collection is done.
* This can and should be called quite regularly. The delay parameter allows you to specify a minimum time since the last GC
* in seconds (the delay should be a fraction of a second in most cases though).
* It will only start a new incremental GC or another GC slice if this time is exceeded. The user of this function is
* responsible for ensuring that GC can run with a small enough delay to get done with the work.
* MaybeIncrementalGC checks if running a GC is worth the time that will take.
* The logic is custom as Spidermonkey tends to assume 'idle time' will exist,
* which is a thing in websites but not really in 0 A.D.
* This can have a few behaviours:
* - doing nothing
* - starting a new incremental GC
* - running a GC slice
* - finishing the incremental GC
* For details, check the SM doc in e.g. GC.cpp and GCapi.cpp
*/
void MaybeIncrementalGC();
/**
* Does a non-incremental, shrinking GC.
* A shrinking GC dumps JIT code and tries to defragment memory.
*/
void MaybeIncrementalGC(double delay);
void ShrinkingGC();
/**
@@ -105,9 +111,8 @@ private:
std::list<JS::Realm*> m_Realms;
int m_ContextSize;
int m_HeapGrowthBytesGCTrigger;
int m_LastGCBytes{0};
double m_LastGCCheck{0.0};
uint32_t m_HeapGrowthBytesGCTrigger;
uint32_t m_LastGCBytes{0};
};
// Using a global object for the context is a workaround until Simulation, AI, etc,
+1 -12
View File
@@ -489,20 +489,9 @@ void CSimulation2Impl::Update(int turnLength, const std::vector<SimulationComman
}
}
// Run the GC occasionally
// No delay because a lot of garbage accumulates in one turn and in non-visual replays there are
// much more turns in the same time than in normal games.
// Every 500 turns we run a shrinking GC, which decommits unused memory and frees all JIT code.
// Based on testing, this seems to be a good compromise between memory usage and performance.
// Also check the comment about gcPreserveCode in the ScriptInterface code and this forum topic:
// http://www.wildfiregames.com/forum/index.php?showtopic=18466&p=300323
//
// (TODO: we ought to schedule this for a frame where we're not
// running the sim update, to spread the load)
if (m_TurnNumber % 500 == 0)
scriptInterface.GetContext().ShrinkingGC();
else
scriptInterface.GetContext().MaybeIncrementalGC(0.0f);
scriptInterface.GetContext().MaybeIncrementalGC();
if (m_EnableOOSLog)
DumpState();
@@ -62,6 +62,8 @@ CBinarySerializerScriptImpl::CBinarySerializerScriptImpl(const ScriptInterface&
{
ScriptRequest rq(m_ScriptInterface);
JS_AddExtraGCRootsTracer(rq.cx, Trace, this);
m_SerializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Serialize"));
m_DeserializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Deserialize"));
}
CBinarySerializerScriptImpl::~CBinarySerializerScriptImpl()
@@ -70,10 +72,13 @@ CBinarySerializerScriptImpl::~CBinarySerializerScriptImpl()
JS_RemoveExtraGCRootsTracer(rq.cx, Trace, this);
}
void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
void CBinarySerializerScriptImpl::PutScriptVal(JS::HandleValue val)
{
ScriptRequest rq(m_ScriptInterface);
HandleScriptVal(ScriptRequest(m_ScriptInterface), val);
}
void CBinarySerializerScriptImpl::HandleScriptVal(const ScriptRequest& rq, JS::HandleValue val)
{
switch (JS_TypeOfValue(rq.cx, val))
{
case JSTYPE_UNDEFINED:
@@ -92,7 +97,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
JS::RootedObject obj(rq.cx, &val.toObject());
// If we've already serialized this object, just output a reference to it
u32 tag = GetScriptBackrefTag(obj);
u32 tag = GetScriptBackrefTag(rq, obj);
if (tag != 0)
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_BACKREF);
@@ -129,7 +134,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
// Now handle its array buffer
// this may be a backref, since ArrayBuffers can be shared by multiple views
JS::RootedValue bufferVal(rq.cx, JS::ObjectValue(*JS_GetArrayBufferViewBuffer(rq.cx, obj, &sharedMemory)));
HandleScriptVal(bufferVal);
HandleScriptVal(rq, bufferVal);
break;
}
else if (JS::IsArrayBufferObject(obj))
@@ -177,8 +182,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
ENSURE(JS_GetElement(rq.cx, keyValuePairObj, 0, &key));
ENSURE(JS_GetElement(rq.cx, keyValuePairObj, 1, &value));
HandleScriptVal(key);
HandleScriptVal(value);
HandleScriptVal(rq, key);
HandleScriptVal(rq, value);
}
break;
}
@@ -206,7 +211,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
if (done)
break;
HandleScriptVal(value);
HandleScriptVal(rq, value);
}
break;
}
@@ -227,14 +232,14 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
if (!JS_GetPrototype(rq.cx, obj, &proto))
throw PSERROR_Serialize_ScriptError("JS_GetPrototype failed");
SPrototypeSerialization protoInfo = GetPrototypeInfo(rq, proto);
SPrototypeSerialization protoInfo = GetPrototypeInfo(rq, proto, m_SerializePropId, m_DeserializePropId);
if (protoInfo.name == "Object")
if (protoInfo.name == L"Object")
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT);
else
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_OBJECT_PROTOTYPE);
m_Serializer.String("proto", wstring_from_utf8(protoInfo.name), 0, 256);
m_Serializer.String("proto", protoInfo.name, 0, 256);
// Does it have custom Serialize function?
// if so, we serialize the data it returns, rather than the object's properties directly
@@ -281,7 +286,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
JS::RootedString str(rq.cx, JS::ToString(rq.cx, val));
if (!str)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
ScriptString("value", str);
ScriptString(rq, "value", str);
break;
}
else if (protokey == JSProto_Boolean)
@@ -330,12 +335,12 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
if (!idstr)
throw PSERROR_Serialize_ScriptError("JS_ValueToString failed");
ScriptString("prop name", idstr);
ScriptString(rq, "prop name", idstr);
if (!JS_GetPropertyById(rq.cx, obj, id, &propval))
throw PSERROR_Serialize_ScriptError("JS_GetPropertyById failed");
HandleScriptVal(propval);
HandleScriptVal(rq, propval);
}
break;
@@ -376,7 +381,7 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
{
m_Serializer.NumberU8_Unbounded("type", SCRIPT_TYPE_STRING);
JS::RootedString stringVal(rq.cx, val.toString());
ScriptString("string", stringVal);
ScriptString(rq, "string", stringVal);
break;
}
case JSTYPE_NUMBER:
@@ -425,10 +430,8 @@ void CBinarySerializerScriptImpl::HandleScriptVal(JS::HandleValue val)
}
}
void CBinarySerializerScriptImpl::ScriptString(const char* name, JS::HandleString string)
void CBinarySerializerScriptImpl::ScriptString(const ScriptRequest& rq, const char* name, JS::HandleString string)
{
ScriptRequest rq(m_ScriptInterface);
#if BYTE_ORDER != LITTLE_ENDIAN
#error TODO: probably need to convert JS strings to little-endian
#endif
@@ -436,8 +439,8 @@ void CBinarySerializerScriptImpl::ScriptString(const char* name, JS::HandleStrin
size_t length;
JS::AutoCheckCannotGC nogc;
// Serialize strings directly as UTF-16 or Latin1, to avoid expensive encoding conversions
bool isLatin1 = JS::StringHasLatin1Chars(string);
m_Serializer.Bool("isLatin1", isLatin1);
u8 isLatin1 = JS::StringHasLatin1Chars(string);
m_Serializer.NumberU8_Unbounded("isLatin1", isLatin1);
if (isLatin1)
{
const JS::Latin1Char* chars = JS_GetLatin1StringCharsAndLength(rq.cx, nogc, string, &length);
@@ -463,15 +466,13 @@ void CBinarySerializerScriptImpl::Trace(JSTracer *trc, void *data)
serializer->m_ScriptBackrefTags.trace(trc);
}
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(JS::HandleObject obj)
u32 CBinarySerializerScriptImpl::GetScriptBackrefTag(const ScriptRequest& rq, JS::HandleObject obj)
{
// To support non-tree structures (e.g. "var x = []; var y = [x, x];"), we need a way
// to indicate multiple references to one object(/array). So every time we serialize a
// new object, we give it a new tag; when we serialize it a second time we just refer
// to that tag.
ScriptRequest rq(m_ScriptInterface);
ObjectTagMap::Ptr ptr = m_ScriptBackrefTags.lookup(JS::Heap<JSObject*>(obj.get()));
if (!ptr.found())
{
@@ -1,4 +1,4 @@
/* Copyright (C) 2024 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "lib/byte_order.h"
#include "scriptinterface/ScriptExtraHeaders.h"
#include "scriptinterface/ScriptForward.h"
#include <ostream>
#include <streambuf>
@@ -86,9 +87,10 @@ public:
CBinarySerializerScriptImpl(const ScriptInterface& scriptInterface, ISerializer& serializer);
~CBinarySerializerScriptImpl();
void ScriptString(const char* name, JS::HandleString string);
void HandleScriptVal(JS::HandleValue val);
void PutScriptVal(JS::HandleValue val);
private:
void ScriptString(const ScriptRequest& rq, const char* name, JS::HandleString string);
void HandleScriptVal(const ScriptRequest& rq, JS::HandleValue val);
static void Trace(JSTracer* trc, void* data);
const ScriptInterface& m_ScriptInterface;
@@ -97,7 +99,11 @@ private:
using ObjectTagMap = JS::GCHashMap<JS::Heap<JSObject*>, u32, js::StableCellHasher<JSObject*>, js::SystemAllocPolicy>;
ObjectTagMap m_ScriptBackrefTags;
u32 m_ScriptBackrefsNext;
u32 GetScriptBackrefTag(JS::HandleObject obj);
u32 GetScriptBackrefTag(const ScriptRequest& rq, JS::HandleObject obj);
JS::PropertyKey m_SerializePropId;
JS::PropertyKey m_DeserializePropId;
};
/**
@@ -201,7 +207,7 @@ protected:
virtual void PutScriptVal(const char* UNUSED(name), JS::MutableHandleValue value)
{
m_ScriptImpl->HandleScriptVal(value);
m_ScriptImpl->PutScriptVal(value);
}
virtual void PutRaw(const char* name, const u8* data, size_t len)
@@ -1,4 +1,4 @@
/* Copyright (C) 2023 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -57,13 +57,13 @@ enum
struct SPrototypeSerialization
{
std::string name = "";
std::wstring name;
bool hasCustomSerialize = false;
bool hasCustomDeserialize = false;
bool hasNullSerialize = false;
};
inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::HandleObject prototype)
inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::HandleObject prototype, JS::PropertyKey ser, JS::PropertyKey deser)
{
SPrototypeSerialization ret;
@@ -71,27 +71,26 @@ inline SPrototypeSerialization GetPrototypeInfo(const ScriptRequest& rq, JS::Han
throw PSERROR_Serialize_ScriptError("Could not get constructor name.");
// Nothing to do for basic Object objects.
if (ret.name == "Object")
if (ret.name == L"Object")
return ret;
if (!JS_HasProperty(rq.cx, prototype, "Serialize", &ret.hasCustomSerialize) ||
!JS_HasProperty(rq.cx, prototype, "Deserialize", &ret.hasCustomDeserialize))
throw PSERROR_Serialize_ScriptError("JS_HasProperty failed");
JS::RootedValue serialize(rq.cx);
if (!JS_GetPropertyById(rq.cx, prototype, JS::Handle<jsid>::fromMarkedLocation(&ser), &serialize))
throw PSERROR_Serialize_ScriptError("JS_GetProperty failed");
if (ret.hasCustomSerialize)
if (serialize.isUndefined())
return ret;
ret.hasCustomSerialize = true;
if (serialize.isNull())
ret.hasNullSerialize = true;
if (!JS_HasPropertyById(rq.cx, prototype, JS::Handle<jsid>::fromMarkedLocation(&deser), &ret.hasCustomDeserialize) ||
(!ret.hasNullSerialize && !ret.hasCustomDeserialize))
{
JS::RootedValue serialize(rq.cx);
if (!JS_GetProperty(rq.cx, prototype, "Serialize", &serialize))
throw PSERROR_Serialize_ScriptError("JS_GetProperty failed");
if (serialize.isNull())
ret.hasNullSerialize = true;
else if (!ret.hasCustomDeserialize)
{
// Don't throw for this error: mods might need updating and this crashes as exceptions are not correctly handled.
LOGERROR("Error serializing object '%s': non-null Serialize() but no matching Deserialize().", ret.name);
}
// Don't throw for this error: mods might need updating and this crashes as exceptions are not correctly handled.
LOGERROR("Error serializing object '%s': non-null Serialize() but no matching Deserialize().", utf8_from_wstring(ret.name));
}
return ret;
}
@@ -1,4 +1,4 @@
/* Copyright (C) 2022 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -35,7 +35,11 @@
CStdDeserializer::CStdDeserializer(const ScriptInterface& scriptInterface, std::istream& stream) :
m_ScriptInterface(scriptInterface), m_Stream(stream)
{
JS_AddExtraGCRootsTracer(ScriptRequest(scriptInterface).cx, CStdDeserializer::Trace, this);
ScriptRequest rq(m_ScriptInterface);
JS_AddExtraGCRootsTracer(rq.cx, CStdDeserializer::Trace, this);
m_SerializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Serialize"));
m_DeserializePropId = JS::PropertyKey::fromPinnedString(JS_AtomizeAndPinString(rq.cx, "Deserialize"));
// Insert a dummy object in front, as valid tags start at 1.
m_ScriptBackrefs.emplace_back(nullptr);
}
@@ -168,9 +172,9 @@ JS::Value CStdDeserializer::ReadScriptVal(const char* UNUSED(name), JS::HandleOb
JS::RootedObject prototype(rq.cx);
JS_GetPrototype(rq.cx, obj, &prototype);
SPrototypeSerialization info = GetPrototypeInfo(rq, prototype);
SPrototypeSerialization info = GetPrototypeInfo(rq, prototype, m_SerializePropId, m_DeserializePropId);
if (preexistingObject != nullptr && prototypeName != wstring_from_utf8(info.name))
if (preexistingObject != nullptr && prototypeName != info.name)
throw PSERROR_Deserialize_ScriptError("Deserializer failed: incorrect pre-existing object");
@@ -1,4 +1,4 @@
/* Copyright (C) 2021 Wildfire Games.
/* Copyright (C) 2025 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@@ -52,6 +52,8 @@ private:
virtual void AddScriptBackref(JS::HandleObject obj);
virtual void GetScriptBackref(size_t tag, JS::MutableHandleObject ret);
std::vector<JS::Heap<JSObject*> > m_ScriptBackrefs;
JS::PropertyKey m_SerializePropId;
JS::PropertyKey m_DeserializePropId;
const ScriptInterface& m_ScriptInterface;
@@ -31,6 +31,7 @@
#include "simulation2/components/ICmpTemplateManager.h"
#include "ps/CLogger.h"
#include "ps/Profiler2.h"
std::string SerializeRNG(const boost::random::rand48& rng)
{
@@ -97,6 +98,7 @@ bool CComponentManager::DumpDebugState(std::ostream& stream, bool includeDebugIn
bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) const
{
PROFILE2("ComputeStateHash");
// Hash serialization: this includes the minimal data necessary to detect
// differences in the state, and ignores things like counts and names
@@ -117,27 +119,17 @@ bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) const
continue;
// Only emit component types if they have a component that will be serialized
bool needsSerialization = false;
for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
{
// Don't serialize local entities
if (ENTITY_IS_LOCAL(eit->first))
continue;
needsSerialization = true;
break;
}
if (!needsSerialization)
continue;
serializer.NumberI32_Unbounded("component type id", cit->first);
bool hasEmittedComponent = false;
for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
{
// Don't serialize local entities
if (ENTITY_IS_LOCAL(eit->first))
continue;
if (!hasEmittedComponent)
{
serializer.NumberI32_Unbounded("component type id", cit->first);
hasEmittedComponent = true;
}
serializer.NumberU32_Unbounded("entity id", eit->first);
eit->second->Serialize(serializer);
@@ -127,7 +127,7 @@ QUERYHANDLER(GenerateMap)
msg->status = 0;
}
catch (PSERROR_Game_World_MapLoadFailed&)
catch (std::exception&)
{
// Cancel loading
LDR_Cancel();