Sort out version info

The idea is to have the following version information included:

 - 1-component save version
   - 2-component under the hood but the minor component shouldn't ever change again
   - see currentVersionMajor in GameSave.cpp
 - 1-component website API version
   - again, currently 2-component because that's what the website code expects
   - see apiVersion in requestmanager/Common.cpp
 - 2-component display version, entirely cosmetic
   - exposed as meson options display_version_major and display_version_minor
   - see APP_VERSION in Config.template.h
 - 1-component business logic version aka build number
   - exposed as meson option build_num
   - see APP_VERSION in Config.template.h
 - variant id aka mod id, tightly coupled with the build number
   - exposed as meson option mod_id
   - see MOD_ID in Config.template.h
 - display and business logic versions repeated for the upstream
   - exposed as meson options upstream_version_major, upstream_version_minor, and upstream_build_num
   - we'll have to update these alongside display_version_major, display_version_minor, and build_num, but mod owners can just merge our changes
   - see UPSTREAM_VERSION in Config.template.h
 - update channel, makes sense in the context of the variant (and yes, this would later enable mod snapshots)
   - currently not exposed as a meson option but derived from meson options snapshot and mod_id
   - see IDENT_RELTYPE in Config.template.h
 - vcs tag aka git commit hash
   - set by build.sh in ghactions workflows
   - see VCS_TAG in VcsTag.tempalte.h

Rather importantly, the save and website API versions are now allowed to change independently of the display version.

These changes also allowed me to remove the ugly sed hacks in build.sh used to provision some manifest files; they are now provisioned by meson.

Also add version info for windows and android.
This commit is contained in:
Tamás Bálint Misius 2023-10-19 10:48:10 +02:00
parent 7f0f9ad132
commit 7fc3fb4c15
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
21 changed files with 229 additions and 111 deletions

36
.github/build.sh vendored
View File

@ -87,18 +87,6 @@ function inplace_sed() {
fi
}
function subst_version() {
local path=$1
if [[ $BSH_HOST_PLATFORM == darwin ]]; then
inplace_sed "s|SUBST_MACOS_MIN_VER|$macos_min_ver|g" $path
else
inplace_sed "s|SUBST_DATE|$(date --iso-8601)|g" $path
fi
inplace_sed "s|SUBST_SAVE_VERSION|$save_version|g" $path
inplace_sed "s|SUBST_MINOR_VERSION|$minor_version|g" $path
inplace_sed "s|SUBST_BUILD_NUM|$build_num|g" $path
}
if [[ $BSH_HOST_PLATFORM == android ]]; then
android_platform=android-30
if [[ -z "${JAVA_HOME_8_X64-}" ]]; then
@ -189,6 +177,11 @@ meson_configure=meson$'\t'setup
if [[ $BSH_DEBUG_RELEASE == release ]]; then
meson_configure+=$'\t'-Dbuildtype=debugoptimized
fi
if [[ $BSH_HOST_PLATFORM == darwin ]]; then
meson_configure+=$'\t'-Dmanifest_macos_min_ver=$macos_min_ver
else
meson_configure+=$'\t'-Dmanifest_date=$(date --iso-8601)
fi
meson_configure+=$'\t'-Dapp_name=$APP_NAME
meson_configure+=$'\t'-Dapp_comment=$APP_COMMENT
meson_configure+=$'\t'-Dapp_exe=$APP_EXE
@ -251,19 +244,10 @@ fi
if [[ $RELEASE_TYPE == stable ]]; then
stable_or_beta=yes
fi
set +e
save_version=$(cat src/Config.template.h | sed -n 's/constexpr int SAVE_VERSION * = \([^;]*\);/\1/p')
minor_version=$(cat src/Config.template.h | sed -n 's/constexpr int MINOR_VERSION * = \([^;]*\);/\1/p')
build_num=$(cat src/Config.template.h | sed -n 's/constexpr int BUILD_NUM * = \([^;]*\);/\1/p')
if [[ -z ${save_version-} ]] || [[ -z ${minor_version-} ]] || [[ -z ${build_num-} ]]; then
>&2 echo "failed to extract version from Config.template.h"
exit 1
fi
set -e
if [[ $stable_or_beta == yes ]] && [[ $MOD_ID != 0 ]]; then
save_version=$(echo $RELEASE_NAME | cut -d '.' -f 1)
minor_version=$(echo $RELEASE_NAME | cut -d '.' -f 2)
build_num=$(echo $RELEASE_NAME | cut -d '.' -f 3)
if [[ $stable_or_beta == yes ]]; then
meson_configure+=$'\t'-Ddisplay_version_major=$(echo $RELEASE_NAME | cut -d '.' -f 1)
meson_configure+=$'\t'-Ddisplay_version_minor=$(echo $RELEASE_NAME | cut -d '.' -f 2)
meson_configure+=$'\t'-Dbuild_num=$(echo $RELEASE_NAME | cut -d '.' -f 3)
fi
if [[ $RELEASE_TYPE == snapshot ]]; then
meson_configure+=$'\t'-Dsnapshot=true
@ -426,7 +410,6 @@ if [[ $PACKAGE_MODE == dmg ]]; then
mkdir $appdir
mkdir $appdir/Contents
cp resources/Info.plist $appdir/Contents/Info.plist
subst_version $appdir/Contents/Info.plist
mkdir $appdir/Contents/MacOS
cp $APP_EXE $appdir/Contents/MacOS/$APP_EXE
mkdir $appdir/Contents/Resources
@ -471,7 +454,6 @@ elif [[ $PACKAGE_MODE == appimage ]]; then
cp ../resources/icon_exe.svg $appdir/$APP_VENDOR-$APP_EXE.svg
cp resources/powder.desktop $appdir/$APP_ID.desktop
cp resources/appdata.xml $appdir/usr/share/metainfo/$APP_ID.appdata.xml
subst_version $appdir/usr/share/metainfo/$APP_ID.appdata.xml
cp $appdir/$APP_VENDOR-$APP_EXE.svg $appdir/usr/share/icons/$APP_VENDOR-$APP_EXE.svg
cp $appdir/$APP_ID.desktop $appdir/usr/share/applications/$APP_ID.desktop
./appimagetool $appdir $ASSET_PATH

View File

@ -2,8 +2,8 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="@APPID@"
android:versionCode="1"
android:versionName="1.0"
android:versionCode="@BUILD_NUM@"
android:versionName="@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.@BUILD_NUM@"
android:installLocation="auto"
>
<uses-sdk

View File

@ -43,6 +43,48 @@ option(
value: 0,
description: 'Snapshot ID, only relevant if \'snapshot\' is true'
)
option(
'display_version_major',
type: 'integer',
min: 0,
value: 97,
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
)
option(
'display_version_minor',
type: 'integer',
min: 0,
value: 0,
description: 'Minor component of the display version, should more or less map to the PATCH version in semantic versioning'
)
option(
'build_num',
type: 'integer',
min: 0,
value: 352,
description: 'Build number, should be strictly monotonously increasing across public releases'
)
option(
'upstream_version_major',
type: 'integer',
min: 0,
value: 97,
description: 'Major component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_version_minor',
type: 'integer',
min: 0,
value: 0,
description: 'Minor component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_build_num',
type: 'integer',
min: 0,
value: 352,
description: 'Upstream build number, mod owners should not change this but merge upstream changes to it'
)
option(
'mod_id',
type: 'integer',
@ -227,3 +269,21 @@ option(
value: 'static_release_only',
description: 'Enable VCS tag resolution, introduces an always-stale custom target'
)
option(
'manifest_copyright',
type: 'string',
value: 'Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1',
description: 'Copyright string, don\'t take too seriously, subject to change'
)
option(
'manifest_macos_min_ver',
type: 'string',
value: '',
description: 'MacOS minimum allowed platform version string, used by ghactions workflows, not useful otherwise'
)
option(
'manifest_date',
type: 'string',
value: '',
description: 'Build date string, used by ghactions workflows, not useful otherwise'
)

View File

@ -18,7 +18,7 @@
<key>CFBundleIconFile</key>
<string>icon_exe.icns</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1</string>
<string>@MANIFEST_COPYRIGHT@</string>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleDocumentTypes</key>
@ -57,13 +57,13 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>SUBST_SAVE_VERSION.SUBST_MINOR_VERSION</string>
<string>@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>SUBST_SAVE_VERSION.SUBST_MINOR_VERSION (build SUBST_BUILD_NUM)</string>
<string>@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@ (build @BUILD_NUM@)</string>
<key>LSMinimumSystemVersion</key>
<string>SUBST_MACOS_MIN_VER</string>
<string>@MANIFEST_MACOS_MIN_VER@</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>

View File

@ -29,6 +29,6 @@
<url type="bugtracker">https://github.com/The-Powder-Toy/The-Powder-Toy/issues</url>
<url type="help">https://powdertoy.co.uk/Wiki/W/Main_Page.html</url>
<releases>
<release date="SUBST_DATE" version="SUBST_SAVE_VERSION.SUBST_MINOR_VERSION.SUBST_BUILD_NUM" />
<release date="@MANIFEST_DATE@" version="@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.@BUILD_NUM@" />
</releases>
</component>

View File

@ -52,8 +52,18 @@ if host_platform == 'windows'
command: command,
) }
endforeach
rc_conf_data = configuration_data()
rc_conf_data.merge_from(conf_data)
rc_conf_data.set('RESOUCE_H', join_paths(meson.current_source_dir(), 'resource.h'))
rc_conf_data.set('WINUTF8_XML', join_paths(meson.current_source_dir(), 'winutf8.xml'))
rc_conf_data.set('ICON_EXE_ICO', join_paths(meson.current_build_dir(), 'icon_exe.ico'))
rc_conf_data.set('ICON_CPS_ICO', join_paths(meson.current_build_dir(), 'icon_cps.ico'))
powder_files += windows_mod.compile_resources(
'powder-res.rc',
configure_file(
input: 'powder-res.template.rc',
output: 'powder-res.rc',
configuration: rc_conf_data,
),
depends: [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],

View File

@ -1,6 +0,0 @@
#include "resource.h"
#include <winuser.h>
IDI_ICON ICON DISCARDABLE "icon_exe.ico"
IDI_DOC_ICON ICON DISCARDABLE "icon_cps.ico"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "winutf8.xml"

View File

@ -0,0 +1,32 @@
#include "@RESOUCE_H@"
#include <winuser.h>
#include <winver.h>
#include <ntdef.h>
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
PRODUCTVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
{
BLOCK "StringFileInfo"
{
BLOCK "040904b0"
{
VALUE "CompanyName", "https://powdertoy.co.uk/\0"
VALUE "FileDescription", "@APPCOMMENT@\0"
VALUE "FileVersion", "@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.0.@BUILD_NUM@\0"
VALUE "OriginalFilename", "@APPEXE@.exe\0"
VALUE "LegalCopyright", "@MANIFEST_COPYRIGHT@\0"
VALUE "ProductName", "@APPNAME@\0"
VALUE "ProductVersion", "@DISPLAY_VERSION_MAJOR@.@DISPLAY_VERSION_MINOR@.0.@BUILD_NUM@\0"
VALUE "InternalName", "@APPID@\0"
}
}
BLOCK "VarFileInfo"
{
VALUE "Translation", 0x409, 65001
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include "VcsTag.h"
#include "common/Version.h"
constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@;
constexpr bool DEBUG = @DEBUG@;
@ -44,13 +45,18 @@ constexpr char APPID[] = "@APPID@";
constexpr char APPDATA[] = "@APPDATA@";
constexpr char APPVENDOR[] = "@APPVENDOR@";
constexpr int SAVE_VERSION = 97;
constexpr int MINOR_VERSION = 0;
constexpr int BUILD_NUM = 352;
constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@;
constexpr int MOD_ID = @MOD_ID@;
constexpr int FUTURE_SAVE_VERSION = 98;
constexpr int FUTURE_MINOR_VERSION = 0;
struct DisplayVersionWithBuild
{
Version<2> displayVersion;
size_t build;
};
constexpr DisplayVersionWithBuild APP_VERSION = { { @DISPLAY_VERSION_MAJOR@, @DISPLAY_VERSION_MINOR@ }, @BUILD_NUM@ };
constexpr DisplayVersionWithBuild UPSTREAM_VERSION = { { @UPSTREAM_VERSION_MAJOR@, @UPSTREAM_VERSION_MINOR@ }, @UPSTREAM_BUILD_NUM@ };
constexpr auto DISPLAY_VERSION = APP_VERSION.displayVersion;
constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');

View File

@ -325,8 +325,8 @@ ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
}
saveData->authors = stampInfo;
auto [ fromNewerVersion, gameData ] = saveData->Serialise();
(void)fromNewerVersion;
std::vector<char> gameData;
std::tie(std::ignore, gameData) = saveData->Serialise();
if (!gameData.size())
return "";

View File

@ -4,7 +4,6 @@
#include "simulation/Simulation.h"
#include "simulation/ElementClasses.h"
#include "common/tpt-compat.h"
#include "common/Version.h"
#include "bson/BSON.h"
#include "graphics/Renderer.h"
#include "Config.h"
@ -16,6 +15,9 @@
#include <cmath>
#include <algorithm>
constexpr auto currentVersionMajor = 97;
constexpr auto nextVersionMajor = currentVersionMajor + 1;
static void ConvertJsonToBson(bson *b, Json::Value j, int depth = 0);
static void ConvertBsonToJson(bson_iterator *b, Json::Value *j, int depth = 0);
static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen);
@ -326,6 +328,9 @@ static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *se
void GameSave::readOPS(const std::vector<char> &data)
{
constexpr auto currentVersion = Version(currentVersionMajor, 0);
constexpr auto nextVersion = Version(nextVersionMajor, 0);
Renderer::PopulateTables();
unsigned char *inputData = (unsigned char*)&data[0], *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
@ -333,9 +338,8 @@ void GameSave::readOPS(const std::vector<char> &data)
unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen;
unsigned partsCount = 0;
int savedVersion = inputData[4];
majorVersion = savedVersion;
minorVersion = 0;
unsigned int savedVersion = inputData[4];
version = { savedVersion, 0 };
bool fakeNewerVersion = false; // used for development builds only
bson b;
@ -354,7 +358,7 @@ void GameSave::readOPS(const std::vector<char> &data)
auto partS = blockS * CELL;
//From newer version
if (savedVersion > SAVE_VERSION)
if (savedVersion > currentVersion[0])
{
fromNewerVersion = true;
//throw ParseException(ParseException::WrongVersion, "Save from newer version");
@ -470,7 +474,7 @@ void GameSave::readOPS(const std::vector<char> &data)
if (!strcmp(bson_iterator_key(&signiter), "text") && bson_iterator_type(&signiter) == BSON_STRING)
{
tempSign.text = format::CleanString(ByteString(bson_iterator_string(&signiter)).FromUtf8(), true, true, true).Substr(0, 45);
if (majorVersion < 94 || (majorVersion == 94 && minorVersion < 2))
if (version < Version(94, 2))
{
if (tempSign.text == "{t}")
{
@ -582,7 +586,7 @@ void GameSave::readOPS(const std::vector<char> &data)
{
if (!strcmp(bson_iterator_key(&subiter), "minorVersion"))
{
minorVersion = bson_iterator_int(&subiter);
version[1] = bson_iterator_int(&subiter);
}
}
}
@ -596,27 +600,30 @@ void GameSave::readOPS(const std::vector<char> &data)
{
if (bson_iterator_type(&iter) == BSON_OBJECT)
{
int major = INT_MAX, minor = INT_MAX;
bson_iterator subiter;
bson_iterator_subiterator(&iter, &subiter);
while (bson_iterator_next(&subiter))
Version<2> version;
{
if (bson_iterator_type(&subiter) == BSON_INT)
int major = INT_MAX, minor = INT_MAX;
bson_iterator subiter;
bson_iterator_subiterator(&iter, &subiter);
while (bson_iterator_next(&subiter))
{
if (!strcmp(bson_iterator_key(&subiter), "major"))
major = bson_iterator_int(&subiter);
else if (!strcmp(bson_iterator_key(&subiter), "minor"))
minor = bson_iterator_int(&subiter);
if (bson_iterator_type(&subiter) == BSON_INT)
{
if (!strcmp(bson_iterator_key(&subiter), "major"))
major = bson_iterator_int(&subiter);
else if (!strcmp(bson_iterator_key(&subiter), "minor"))
minor = bson_iterator_int(&subiter);
}
}
version = Version(major, minor);
}
auto majorToCheck = ALLOW_FAKE_NEWER_VERSION ? FUTURE_SAVE_VERSION : SAVE_VERSION;
auto minorToCheck = ALLOW_FAKE_NEWER_VERSION ? FUTURE_MINOR_VERSION : MINOR_VERSION;
if (major > majorToCheck || (major == majorToCheck && minor > minorToCheck))
auto versionToCheck = ALLOW_FAKE_NEWER_VERSION ? nextVersion : currentVersion;
if (versionToCheck < version)
{
String errorMessage = String::Build("Save from a newer version: Requires version ", major, ".", minor);
String errorMessage = String::Build("Save from a newer version: Requires version ", version[0], ".", version[1]);
throw ParseException(ParseException::WrongVersion, errorMessage);
}
else if (ALLOW_FAKE_NEWER_VERSION && (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION)))
else if (ALLOW_FAKE_NEWER_VERSION && currentVersion < version)
{
fakeNewerVersion = true;
}
@ -642,8 +649,8 @@ void GameSave::readOPS(const std::vector<char> &data)
}
}
auto paletteRemap = [this, saveVersion = Version(majorVersion, minorVersion)](auto maxVersion, ByteString from, ByteString to) {
if (saveVersion <= maxVersion)
auto paletteRemap = [this](auto maxVersion, ByteString from, ByteString to) {
if (version <= maxVersion)
{
auto it = std::find_if(palette.begin(), palette.end(), [&from](auto &item) {
return item.first == from;
@ -1182,7 +1189,7 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
unsigned char * saveData = (unsigned char *)&dataVec[0];
auto dataLength = int(dataVec.size());
int q,p=0, ver, pty, ty, legacy_beta=0;
int q,p=0, pty, ty, legacy_beta=0;
Vec2<int> blockP = { 0, 0 };
int new_format = 0, ttv = 0;
@ -1202,11 +1209,10 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
if (saveData[2]==0x76 && saveData[1]==0x53 && saveData[0]==0x50) {
new_format = 1;
}
if (saveData[4]>SAVE_VERSION)
if (saveData[4]>97) // this used to respect currentVersion but no valid PSv will ever have a version > 97 so it's ok to hardcode
throw ParseException(ParseException::WrongVersion, "Save from newer version");
ver = saveData[4];
majorVersion = saveData[4];
minorVersion = 0;
version = { saveData[4], 0 };
auto ver = version[0];
if (ver<34)
{
@ -1818,16 +1824,18 @@ void GameSave::readPSv(const std::vector<char> &dataVec)
std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{
constexpr auto currentVersion = Version(currentVersionMajor, 0);
// minimum version this save is compatible with
// when building, this number may be increased depending on what elements are used
// or what properties are detected
int minimumMajorVersion = 90, minimumMinorVersion = 2;
auto RESTRICTVERSION = [&minimumMajorVersion, &minimumMinorVersion](int major, int minor) {
auto minimumVersion = Version(90, 2);
auto RESTRICTVERSION = [&minimumVersion](auto major, auto minor = 0) {
// restrict the minimum version this save can be opened with
if (major > minimumMajorVersion || ((major == minimumMajorVersion && minor > minimumMinorVersion)))
auto version = Version(major, minor);
if (minimumVersion < version)
{
minimumMajorVersion = major;
minimumMinorVersion = minor;
minimumVersion = version;
}
};
@ -2295,7 +2303,7 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
}
// Mark save as incompatible with latest release
bool fakeFromNewerVersion = ALLOW_FAKE_NEWER_VERSION && (minimumMajorVersion > SAVE_VERSION || (minimumMajorVersion == SAVE_VERSION && minimumMinorVersion > MINOR_VERSION));
bool fakeFromNewerVersion = ALLOW_FAKE_NEWER_VERSION && currentVersion < minimumVersion;
bson b;
b.data = NULL;
@ -2306,9 +2314,9 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
set_bson_err_handler([](const char* err) { throw BuildException("BSON error when parsing save: " + ByteString(err).FromUtf8()); });
bson_init(&b);
bson_append_start_object(&b, "origin");
bson_append_int(&b, "majorVersion", SAVE_VERSION);
bson_append_int(&b, "minorVersion", MINOR_VERSION);
bson_append_int(&b, "buildNum", BUILD_NUM);
bson_append_int(&b, "majorVersion", int(currentVersion[0]));
bson_append_int(&b, "minorVersion", int(currentVersion[1]));
bson_append_int(&b, "buildNum", APP_VERSION.build);
bson_append_int(&b, "snapshotId", SNAPSHOT_ID);
bson_append_int(&b, "modId", MOD_ID);
bson_append_string(&b, "releaseType", ByteString(1, IDENT_RELTYPE).c_str());
@ -2322,8 +2330,8 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
RESTRICTVERSION(97, 0);
}
bson_append_start_object(&b, "minimumVersion");
bson_append_int(&b, "major", minimumMajorVersion);
bson_append_int(&b, "minor", minimumMinorVersion);
bson_append_int(&b, "major", int(minimumVersion[0]));
bson_append_int(&b, "minor", int(minimumVersion[1]));
bson_append_finish_object(&b);
@ -2467,7 +2475,7 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
header[1] = 'P';
header[2] = 'S';
header[3] = '1';
header[4] = SAVE_VERSION;
header[4] = currentVersion[0];
header[5] = CELL;
header[6] = blockS.X;
header[7] = blockS.Y;

View File

@ -2,6 +2,7 @@
#include "common/Plane.h"
#include "common/String.h"
#include "common/tpt-rand.h"
#include "common/Version.h"
#include "simulation/Sign.h"
#include "simulation/Particle.h"
#include "Misc.h"
@ -64,8 +65,7 @@ class GameSave
public:
Vec2<int> blockSize = { 0, 0 };
bool fromNewerVersion = false;
int majorVersion = 0;
int minorVersion = 0;
Version<2> version{};
bool hasPressure = false;
bool hasAmbientHeat = false;
bool hasBlockAirMaps = false; // only written by readOPS, never read

View File

@ -82,12 +82,12 @@ namespace http
else
{
parseUpdate("Stable", UpdateInfo::channelStable, [](int build) -> bool {
return build > BUILD_NUM;
return size_t(build) > APP_VERSION.build;
});
if (!startupInfo.updateInfo.has_value())
{
parseUpdate("Beta", UpdateInfo::channelBeta, [](int build) -> bool {
return build > BUILD_NUM;
return size_t(build) > APP_VERSION.build;
});
}
}

View File

@ -10,13 +10,14 @@ namespace http
capath(newCapath),
disableNetwork(newDisableNetwork)
{
auto apiVersion = Version(97, 0);
userAgent = ByteString::Build(
"PowderToy/", SAVE_VERSION, ".", MINOR_VERSION,
"PowderToy/", DISPLAY_VERSION[0], ".", DISPLAY_VERSION[1],
" (", IDENT_PLATFORM,
"; NO", // Unused, used to be SSE level.
"; M", MOD_ID,
"; ", IDENT,
") TPTPP/", SAVE_VERSION, ".", MINOR_VERSION, ".", BUILD_NUM, IDENT_RELTYPE, ".", SNAPSHOT_ID
") TPTPP/", apiVersion[0], ".", apiVersion[1], ".", APP_VERSION.build, IDENT_RELTYPE, ".", SNAPSHOT_ID
);
}

View File

@ -26,6 +26,16 @@ struct Version
{
return *this < other || *this == other;
}
constexpr size_t operator [](size_t index) const
{
return components[index];
}
size_t &operator [](size_t index)
{
return components[index];
}
};
template<class ...Args>

View File

@ -1204,10 +1204,10 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
gameSave->authors = localSaveInfo;
Platform::MakeDirectory(LOCAL_SAVE_DIR);
auto [ fromNewerVersion, saveData ] = gameSave->Serialise();
std::vector<char> saveData;
std::tie(std::ignore, saveData) = gameSave->Serialise();
tempSave->SetGameSave(std::move(gameSave));
gameModel->SetSaveFile(std::move(tempSave), gameView->ShiftBehaviour());
(void)fromNewerVersion;
if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data.");
else if (!Platform::WriteFile(saveData, gameModel->GetSaveFile()->GetName()))
@ -1611,11 +1611,11 @@ void GameController::NotifyUpdateAvailable(Client * sender)
}
else if constexpr (BETA)
{
updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Beta, Build " << BUILD_NUM;
updateMessage << DISPLAY_VERSION[0] << "." << DISPLAY_VERSION[1] << " Beta, Build " << APP_VERSION.build;
}
else
{
updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Stable, Build " << BUILD_NUM;
updateMessage << DISPLAY_VERSION[0] << "." << DISPLAY_VERSION[1] << " Stable, Build " << APP_VERSION.build;
}
updateMessage << "\nNew version:\n ";

View File

@ -5,7 +5,7 @@
inline ByteString VersionInfo()
{
ByteStringBuilder sb;
sb << SAVE_VERSION << "." << MINOR_VERSION << "." << BUILD_NUM << " " << IDENT;
sb << DISPLAY_VERSION[0] << "." << DISPLAY_VERSION[1] << "." << APP_VERSION.build << " " << IDENT;
if constexpr (SNAPSHOT)
{
sb << " SNAPSHOT " << SNAPSHOT_ID;
@ -39,7 +39,7 @@ inline ByteString VersionInfo()
inline ByteString IntroText()
{
ByteStringBuilder sb;
sb << "\bl\bU" << APPNAME << "\bU - Version " << SAVE_VERSION << "." << MINOR_VERSION << " - https://powdertoy.co.uk, irc.libera.chat #powder, https://tpt.io/discord\n"
sb << "\bl\bU" << APPNAME << "\bU - Version " << DISPLAY_VERSION[0] << "." << DISPLAY_VERSION[1] << " - https://powdertoy.co.uk, irc.libera.chat #powder, https://tpt.io/discord\n"
"\n"
"\n"
"\bgControl+C/V/X are Copy, Paste and cut respectively.\n"

View File

@ -115,8 +115,8 @@ void LocalSaveActivity::saveWrite(ByteString finalFilename)
gameSave->authors = localSaveInfo;
save->SetGameSave(std::move(gameSave));
}
auto [ fromNewerVersion, saveData ] = save->GetGameSave()->Serialise();
(void)fromNewerVersion;
std::vector<char> saveData;
std::tie(std::ignore, saveData) = save->GetGameSave()->Serialise();
if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data.");
else if (!Platform::WriteFile(saveData, finalFilename))

View File

@ -403,12 +403,18 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m):
lua_newtable(l);
tptPropertiesVersion = lua_gettop(l);
lua_pushinteger(l, SAVE_VERSION);
lua_pushinteger(l, DISPLAY_VERSION[0]);
lua_setfield(l, tptPropertiesVersion, "major");
lua_pushinteger(l, MINOR_VERSION);
lua_pushinteger(l, DISPLAY_VERSION[1]);
lua_setfield(l, tptPropertiesVersion, "minor");
lua_pushinteger(l, BUILD_NUM);
lua_pushinteger(l, APP_VERSION.build);
lua_setfield(l, tptPropertiesVersion, "build");
lua_pushinteger(l, UPSTREAM_VERSION.displayVersion[0]);
lua_setfield(l, tptPropertiesVersion, "upstream_major");
lua_pushinteger(l, UPSTREAM_VERSION.displayVersion[1]);
lua_setfield(l, tptPropertiesVersion, "upstream_minor");
lua_pushinteger(l, UPSTREAM_VERSION.build);
lua_setfield(l, tptPropertiesVersion, "upstream_build");
if constexpr (SNAPSHOT || MOD)
{
lua_pushinteger(l, SNAPSHOT_ID);

View File

@ -12,6 +12,15 @@ conf_data.set('DEBUG', is_debug.to_string())
conf_data.set('MOD', is_mod.to_string())
conf_data.set('SNAPSHOT', is_snapshot.to_string())
conf_data.set('SNAPSHOT_ID', get_option('snapshot_id'))
conf_data.set('DISPLAY_VERSION_MAJOR', get_option('display_version_major'))
conf_data.set('DISPLAY_VERSION_MINOR', get_option('display_version_minor'))
conf_data.set('BUILD_NUM', get_option('build_num'))
conf_data.set('UPSTREAM_VERSION_MAJOR', get_option('upstream_version_major'))
conf_data.set('UPSTREAM_VERSION_MINOR', get_option('upstream_version_minor'))
conf_data.set('UPSTREAM_BUILD_NUM', get_option('upstream_build_num'))
conf_data.set('MANIFEST_COPYRIGHT', get_option('manifest_copyright'))
conf_data.set('MANIFEST_MACOS_MIN_VER', get_option('manifest_macos_min_ver'))
conf_data.set('MANIFEST_DATE', get_option('manifest_date'))
conf_data.set('ALLOW_FAKE_NEWER_VERSION', (is_snapshot or is_beta or is_debug or is_mod).to_string())
conf_data.set('IDENT_PLATFORM', ident_platform)
conf_data.set('IDENT', '@0@-@1@-@2@'.format(host_arch, host_platform, host_libc).to_upper())

View File

@ -151,8 +151,8 @@ std::vector<ByteString> Simulation::Load(const GameSave *save, bool includePress
player.spwn = 1;
player.elem = PT_DUST;
if ((save->majorVersion < 93 && parts[i].ctype == SPC_AIR) ||
(save->majorVersion < 88 && parts[i].ctype == OLD_SPC_AIR))
if ((save->version < Version(93, 0) && parts[i].ctype == SPC_AIR) ||
(save->version < Version(88, 0) && parts[i].ctype == OLD_SPC_AIR))
{
player.fan = true;
}
@ -165,8 +165,8 @@ std::vector<ByteString> Simulation::Load(const GameSave *save, bool includePress
Element_STKM_init_legs(this, &player2, i);
player2.spwn = 1;
player2.elem = PT_DUST;
if ((save->majorVersion < 93 && parts[i].ctype == SPC_AIR) ||
(save->majorVersion < 88 && parts[i].ctype == OLD_SPC_AIR))
if ((save->version < Version(93, 0) && parts[i].ctype == SPC_AIR) ||
(save->version < Version(88, 0) && parts[i].ctype == OLD_SPC_AIR))
{
player2.fan = true;
}
@ -189,8 +189,8 @@ std::vector<ByteString> Simulation::Load(const GameSave *save, bool includePress
if (parts[i].tmp >= 0)
{
bool fan = false;
if ((save->majorVersion < 93 && parts[i].ctype == SPC_AIR)
|| (save->majorVersion < 88 && parts[i].ctype == OLD_SPC_AIR))
if ((save->version < Version(93, 0) && parts[i].ctype == SPC_AIR)
|| (save->version < Version(88, 0) && parts[i].ctype == OLD_SPC_AIR))
{
fan = true;
parts[i].ctype = 0;