Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/workflows/ci-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ jobs:
body_path: ${{ needs.meta.outputs.channel == 'release' && 'release-notes.md' || '' }}
generate_release_notes: ${{ needs.meta.outputs.channel != 'release' }}

- name: Strip auto-generated 'New Contributors' from nightly notes
- name: Strip 'New Contributors' + author handles from nightly notes
if: needs.meta.outputs.channel == 'nightly'
shell: pwsh
env:
Expand All @@ -379,10 +379,13 @@ jobs:
# the regex below never match. Always -join "`n" first.
$body = (gh release view nightly --repo "$env:GITHUB_REPOSITORY" --json body --jq '.body') -join "`n"
$clean = [regex]::Replace($body, "(?s)\r?\n?## New Contributors\r?\n(?:\*.*\r?\n?)*\r?\n?(?=\*\*Full Changelog\*\*)", "`n`n")
# Drop the "by @author" credit from each "What's Changed" bullet - the notes
# should describe the change, not the committer.
$clean = [regex]::Replace($clean, ' by @[A-Za-z0-9-]+ in (https://\S+)', ' in $1')
Set-Content nightly-notes.md $clean -Encoding utf8
if ($clean -notmatch "## What's Changed") { throw "nightly notes look wrong after strip - aborting rather than publishing mangled notes" }
gh release edit nightly --repo "$env:GITHUB_REPOSITORY" --notes-file nightly-notes.md
Write-Host "Stripped 'New Contributors' from nightly notes"
Write-Host "Stripped 'New Contributors' + author handles from nightly notes"

# 32-bit static WinXP binary, MinGW cross-compiled on Linux via MXE. Shares no
# artifact with the x64 build, so it starts as soon as `meta` has the version and
Expand Down
103 changes: 90 additions & 13 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,100 @@ Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Newest first.
## [Unreleased]
- Notes for the next release accumulate here; rename to `## [x.y.z]` when tagging.

### Fixed
- Co-op transfers: fixed a use-after-free when transferring a craft with crew to
a co-op base — kept crew are now unassigned before the craft is freed, so their
craft pointer is never left dangling (could crash or corrupt saves).
- Co-op transfers: the receiver now validates that the target base exists before
acknowledging a transfer/purchase, so the sender no longer removes goods (or
deducts funds) for a transfer that can never be applied (silent item loss).
- Co-op transfers: restored the peer notification dropped in the purchase-sync
rework — an immediate soldier/equipment transfer to a co-op base again arrives
at the receiving base (fixes gear not moving with a transferred soldier).

<!--
## [8.4.3] - 2026-07-15
<!-- Template for the next release section (keep this comment ABOVE the newest
released section: the notes extractor reads from "## [<v>]" until the next
line starting with "## ", so a commented heading below a real section would
leak a dangling "<!-" into the release body):
## [2.0.1] - 2026-XX-XX
### Added
- ...
### Fixed
- ...
### Changed
- ...
-->

## [2.0.0] - 2026-07-28

The first official release since v1.8.4, and the biggest update the mod has
had: a brand-new SHARED campaign mode, host-authoritative saving, an automatic
save upgrader, in-game voting, and a long list of desync, crash and item-loss
fixes. Windows x64, Windows XP (32-bit) and Linux packages now ship together
with every release.

### Added
- **SHARED co-op campaign** — a second co-op mode where both players run one
world together: shared bases, funds, research, manufacturing and craft.
Either player can command any craft, fly interceptions and dogfights, and a
battle can mix soldiers from both players in a single squad. The campaign
type is chosen at NEW GAME (Solo / Co-op (Separate) / Co-op (Shared)).
Includes a periodic desync checksum with automatic repair, live economy
refresh, and a landing broker so simultaneous craft arrivals stay ordered.
- **Save upgrader** — saves from older versions (including legacy v1.8.4 co-op
saves) are detected on load and upgraded in-game, step by step, to the
current format; mid-battle saves are supported. Solo saves are never
touched.
- **Host-authoritative saving** — the host's save now embeds the client's
world, making it the single authority for both players' campaigns; clients
resume straight from the host's copy, so there is no more guessing which
machine has the "right" save.
- **Multiplayer voting** — in-game vote dialogs (for example, voting to abort
a battle), with matching lobby improvements.
- **Clipboard paste** — text fields accept paste (Ctrl+V) on Windows, Linux
and macOS.
- **Skirmish co-op lobby** — NEW BATTLE co-op is redesigned around a
host-driven lobby with a BATTLE SETTINGS screen and a join popup shown over
the lobby; battles in progress can be rejoined.
- **Soldier gifting and transfers** — gift soldiers to the other player
(optionally with their gear), including a reworked in-battle gifting flow,
and transfer soldiers between players with rollback if the receiver cannot
take them.
- **Server browser** — the server list can aggregate multiple rendezvous
servers.
- **Geoscape ally indicator** — your partner's craft and bases are marked on
the globe.
- **Off-turn scanner indicators** in battle while it is not your turn.
- **Crash reporting** — crash logs are actionable and minidumps are written
next to them.
- Text labels in the Add Server menu and a Hotseat tooltip in the Host menu.

### Changed
- The neutral side in battle is reported as "Outsiders Activity" instead of
"Alien Activity".
- The main menu shows a readable version block (coop version plus the OXCE
engine version), and official releases drop the channel suffix.
- Equipment stored at the other player's bases is hidden from your own
equip screens.
- Release packaging: the Windows x64, Windows XP i686 and Linux x86_64
packages are built, tested and published together, and every package now
includes the co-op art, a working official-server `rendezvous.json` and
`LICENSE.txt`.

### Fixed
- Geoscape sync no longer floods the connection: position updates ride a
last-write-wins lane, packet queues are thread-safe, and the "TX queue
full" / multi-second-ping stalls are gone.
- Purchases and transfers between players: fixed a use-after-free when
transferring a craft with crew; the receiver validates the target base
before acknowledging, so goods are no longer silently lost; restored the
dropped transfer notification; a transfer can no longer be applied twice;
and a client's out-craft keeps its co-op destination across a host save.
- Battlescape item sync: battles no longer lose or duplicate items across
machines, and a proximity-grenade blast no longer sweeps every loose item
off the other player's floor.
- Mid-battle disconnects: a peer dropping mid-battle no longer freezes the
game (or dumps you into a lobby), the host is no longer trapped behind the
reconnect dialog, and "Waiting for host to resume" can no longer strand a
client forever.
- Returning to the main menu tears the co-op session down cleanly, fixing
crashes and palette corruption after leaving a battle.
- SHARED mode: mission sites and waypoints keep a consistent lifecycle on
both machines, and UFO/mission detection stays in parity.
- SEPARATE mode: duplicate mission ids are de-duplicated at the snapshot
sender.
- Chryssalid zombies no longer double-spawn in co-op battles.
- The cursor no longer misbehaves during the other player's turn.
- Craft status stays in sync between players.
- Password-protected lobbies can be joined — and left — cleanly.
- Co-op menu entry points no longer crash when no save is active.
- Fixed a crash when unloading a weapon at a base.
2 changes: 1 addition & 1 deletion src/Engine/CrossPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1795,7 +1795,7 @@ std::array<int, 4> parseVersion(const std::string& newVersion)
*/
bool isHigherThanCurrentVersion(const std::string& newVersion)
{
return isHigherThanCurrentVersion(parseVersion(newVersion), { OPENXCOM_VERSION_NUMBER });
return isHigherThanCurrentVersion(parseVersion(newVersion), { OPENXCOM_VERSION_OXCE_NUMBER });
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/Engine/ModInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ struct EngineData
* List of engines that current version support.
*/
const EngineData supportedEngines[] = {
{ OPENXCOM_VERSION_ENGINE, { OPENXCOM_VERSION_NUMBER }},
{ OPENXCOM_VERSION_ENGINE, { OPENXCOM_VERSION_OXCE_NUMBER }},
{ "", { 0, 0, 0, 0 } }, // assume that every engine support mods from base game, remove if its not true.
};

Expand Down Expand Up @@ -314,11 +314,11 @@ static auto dummy = ([]
return std::array<int, 4>{{i, j, k, l}};
};

assert(findCompatibleEngine(supportedEngines, "Extended", create(OPENXCOM_VERSION_NUMBER)));
assert(findCompatibleEngine(supportedEngines, "Extended", create(OPENXCOM_VERSION_OXCE_NUMBER)));
assert(findCompatibleEngine(supportedEngines, "Extended", create(1, 0, 0, 0)));
assert(findCompatibleEngine(supportedEngines, "", create(0, 0, 0, 0)));
assert(!findCompatibleEngine(supportedEngines, "Extended", create(OPENXCOM_VERSION_NUMBER + 1)));
assert(!findCompatibleEngine(supportedEngines, "XYZ", create(OPENXCOM_VERSION_NUMBER)));
assert(!findCompatibleEngine(supportedEngines, "Extended", create(OPENXCOM_VERSION_OXCE_NUMBER + 1)));
assert(!findCompatibleEngine(supportedEngines, "XYZ", create(OPENXCOM_VERSION_OXCE_NUMBER)));
assert(!findCompatibleEngine(supportedEngines, "XYZ", create(0, 0, 0, 0)));


Expand Down
10 changes: 10 additions & 0 deletions src/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
// mod's own build version, e.g. 8.4.13201) - bump it by hand on each OXCE rebase.
#define OPENXCOM_VERSION_OXCE "8.4.2"

// Numeric form of OPENXCOM_VERSION_OXCE, for OXCE engine-compat checks (mod
// requiredExtendedVersion in ModInfo.cpp) and the update check
// (CrossPlatform::isHigherThanCurrentVersion). Like OPENXCOM_VERSION_OXCE it is
// NEVER stamped by CI - bump both together on each OXCE rebase. The stamped
// OPENXCOM_VERSION_NUMBER above is the coop mod's own version (e.g. 2,0,0,0)
// and must never be used for OXCE compatibility comparisons: a coop version
// numerically below the OXCE version would silently fail every mod's
// engine-version requirement.
#define OPENXCOM_VERSION_OXCE_NUMBER 8,4,2,0

// Release channel of this build; stamped by tools/ci/stamp_version.sh.
// Local/dev builds keep "dev".
#ifndef OPENXCOM_VERSION_CHANNEL
Expand Down
Loading