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
38 changes: 3 additions & 35 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ env:
jobs:
build-win:
runs-on: windows-2025-vs2026
outputs:
run_attempt: ${{ steps.build_identity.outputs.run_attempt }}
steps:
- uses: actions/checkout@v7
- uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2
Expand All @@ -39,10 +37,8 @@ jobs:
run: |
if ($env:STFC_WINDOWS_TOOLCHAIN_URL) { zig build fetch-toolchains }
- name: Build and test
id: build_identity
shell: pwsh
run: |
"run_attempt=${{ github.run_attempt }}" >> $env:GITHUB_OUTPUT
zig build install test-toolchains test-codegen test-deps test-il2cpp test-mod test-sync test-spud `
-Dtarget=x86_64-windows-msvc -Doptimize=ReleaseFast -Dtest-optimize=Debug `
"-Dstfc_source_state_id=git:${{ github.sha }}" `
Expand All @@ -51,7 +47,9 @@ jobs:
-Dstfc_build_channel=ci
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
- name: Verify proxy binary contract
run: python scripts/verify-windows-binary.py
run: >-
python scripts/verify-windows-binary.py --identity
"stfc-identity-v1;distribution=netniv.stfc-community-mod;source=git:${{ github.sha }};base=${{ github.sha }};build=github:${{ github.run_id }}:${{ github.run_attempt }}:windows;mode=release;channel=ci"
- uses: actions/upload-artifact@v7.0.1
with:
name: stfc-community-mod
Expand Down Expand Up @@ -154,36 +152,6 @@ jobs:
name: stfc-community-mod
path: .artifacts/windows

- name: Verify DLL provenance
shell: pwsh
env:
STFC_SOURCE_COMMIT: ${{ github.sha }}
STFC_RUN_ID: ${{ github.run_id }}
STFC_BUILD_RUN_ATTEMPT: ${{ needs.build-win.outputs.run_attempt }}
run: |
$dll = (Resolve-Path -LiteralPath '.artifacts/windows/version.dll').Path
$commit = $env:STFC_SOURCE_COMMIT
$runId = $env:STFC_RUN_ID
$buildAttempt = $env:STFC_BUILD_RUN_ATTEMPT

if ($commit -notmatch '^[0-9a-f]{40}$') {
throw "Unable to resolve the workflow commit for DLL provenance."
}
if ($runId -notmatch '^[1-9][0-9]*$') {
throw "Unable to resolve the workflow run ID for DLL provenance."
}
if ($buildAttempt -notmatch '^[1-9][0-9]*$') {
throw "The producing build did not provide a valid run attempt."
}

$expected = "stfc-identity-v1;distribution=netniv.stfc-community-mod;source=git:$commit;" +
"base=$commit;build=github:${runId}:${buildAttempt}:windows;mode=release;channel=ci"
$comments = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($dll).Comments
if (-not [string]::Equals($comments, $expected, [System.StringComparison]::Ordinal)) {
throw "DLL provenance mismatch. Expected '$expected'; found '$comments'."
}
Write-Host "Verified DLL provenance: $comments"

- name: Attest Windows DLL
uses: actions/attest@v4.2.1
with:
Expand Down
17 changes: 4 additions & 13 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ It uses text matching. Runtime type checks and in-game validation remain necessa
| `sync/state.cc` | Comparisons, removals, and per-target acknowledgements |
| `sync/upload.cc` | Sessions, the record store, target workers, and HTTP delivery |
| `sync/battlelogs.cc` | Battle-journal fetching |
| `sync/fleets.cc` | Client fleet records: slot polling, baselines, state changes, snapshots, and late re-reads, on the game thread |
| `sync/fleets.cc` | Client fleet records: slot polling, state changes, snapshots, and late re-reads, on the game thread |

Payload construction and HTTP run outside the store lock.
Session generations prevent old responses and acknowledgements from changing a new login.
Expand Down Expand Up @@ -262,18 +262,9 @@ Review source and configuration changes, then build supported targets.
macOS uses Apple's system libcurl; Windows builds pinned curl with Schannel. Neither builds OpenSSL.
Librsync uses checked-in configuration files. Normal builds need no Perl or CMake.

Windows build-identity options have matching environment fallbacks:

| Zig option | Environment variable |
| --- | --- |
| `stfc_source_state_id` | `STFC_SOURCE_STATE_ID` |
| `stfc_base_commit` | `STFC_BASE_COMMIT` |
| `stfc_build_invocation_id` | `STFC_BUILD_INVOCATION_ID` |
| `stfc_build_channel` | `STFC_BUILD_CHANNEL` |

Prefix options with `-D`, as in `-Dstfc_build_channel=local`.
Zig 0.17 cannot track environment variables in its configure cache, so reading a fallback configures the build again on every run.
Windows builds take their identity from `-Dstfc_source_state_id`, `-Dstfc_base_commit`, `-Dstfc_build_invocation_id`
and `-Dstfc_build_channel`, as in `-Dstfc_build_channel=local`.
Values accept 1 to 160 ASCII letters, digits, or `._:+/-`.
The DLL version resource contains the same values.
They form the `stfc-identity-v1` marker in the DLL version resource, which CI checks with `scripts/verify-windows-binary.py --identity`.
Keep CI artifact names compatible with the upstream Windows attestation and macOS release jobs.
See [Windows DLL verification](docs/WINDOWS_DLL_PROVENANCE.md) and [launcher version generation](docs/VERSION_SUBSTITUTION.md).
2 changes: 1 addition & 1 deletion docs/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The [shortcut reference](KEYMAPPING.md) lists the defaults.
## Rewards and interface

- Skip chest reveals, open bulk gift claims, and leave the section after the supported gift-collection flow.
- Extend Alliance donations and, on Windows, supported tagged chest-purchase sliders.
- Extend Alliance donations and supported tagged chest-purchase sliders.
- Limit Daily Goals **Claim All** to selected Federation, Klingon, or Romulan goals. Its toggle can start enabled each session.
`ui.daily_bulk_claim_factions` lists the factions; blank means all three, and `none` by itself excludes all of them. Other dailies stay eligible.
- Hide Galaxy or Veil Chat, and player titles under chat names. Set supported HUD buttons to automatic, always shown, or always hidden.
Expand Down
7 changes: 1 addition & 6 deletions docs/IL2CPP.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ A missing or drifted class or member is reported once.
Calls then do nothing and return a value-initialized result.
A managed exception is contained at the binding call, reported once per method, and produces the same default result.
It cannot unwind through the hook and skip the game's code.
`TryCall` is the same call for code that undoes its own changes when the game's code fails.
It returns an empty `std::optional`, or `false` for a void method, when the method is missing or threw.

Receivers must be non-null. Check pointers returned by the game before calling methods through them.

Expand Down Expand Up @@ -180,17 +178,14 @@ Change hidden `MethodInfo*` handling only when runtime evidence points to it.
| `il2cpp::Allocate<T>()` | Allocate without a constructor, for classes whose parameterless constructor has no generated code |
| `NewString`, `ToUtf8`, `Equals` | Create, convert, or compare managed strings |
| `ToString` | A managed string for any object |
| `Box`, `Unbox` | Box a primitive, or read one of exactly that type |
| `Box` | Box a primitive |
| `Nullable<T>` | `System.Nullable<T>` by value |
| `Is<T>`, `As<T>` | Test or cast a receiver against a bound class |
| `InvokeBound` | Run a single-cast delegate's method directly; managed exceptions propagate so the caller can fall back |
| `NewDelegate` | Bind a delegate to a static method, with an optional native redirect |
| `Guarded` | Contain Windows access violations when reading layouts that may have drifted |

The GC scans native stacks, so managed pointers in local variables remain visible to it.
Pointers stored in C++ statics or heap objects need `GcHandle::Strong` roots.
`GcRoots` holds strong roots for the objects one operation touches and releases them together.
Its `healthy()` turns false once a root could not be taken.
Release roots before runtime shutdown. Roots do not destroy Unity native resources.

`unity::Track<T>()` tracks a class and its subclasses from hook installation onward.
Expand Down
102 changes: 0 additions & 102 deletions src/libs/il2cpp/include/il2cpp/il2cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@
#include <functional>
#include <initializer_list>
#include <iterator>
#include <optional>
#include <ranges>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

namespace il2cpp
{
Expand Down Expand Up @@ -93,7 +91,6 @@ namespace detail
Il2CppClass* TypeClass();
// System.Int32 and friends from corlib, by element type; null for anything else.
Il2CppClass* PrimitiveClass(Il2CppTypeEnum type);
void ReportFault(const char* what);
void ReportMissingICall(const char* name);

template <typename T>
Expand Down Expand Up @@ -287,34 +284,6 @@ namespace detail
return R();
}

// Invoke for TryCall: empty (false for void) when the method is missing, the receiver is null, or a managed
// exception stopped the call. Exceptions are reported as Invoke reports them.
template <typename R> using Tried = std::conditional_t<std::is_void_v<R>, bool, std::optional<R>>;
template <typename R, typename... A> Tried<R> TryInvoke(const MethodInfo* method, Il2CppObject* target, A... args)
{
const bool is_static = method && (method->flags & METHOD_ATTRIBUTE_STATIC);
if (!method || (!is_static && !target))
return {};
if (!is_static && (method->flags & METHOD_ATTRIBUTE_VIRTUAL)) {
method = il2cpp_object_get_virtual_method(target, method);
if (!method)
return {};
}
try {
if constexpr (std::is_void_v<R>) {
InvokeNative<R>(method, target, args...);
return true;
} else {
return InvokeNative<R>(method, target, args...);
}
} catch (const Il2CppExceptionWrapper& error) {
ReportException(method, error.ex);
} catch (...) {
ReportException(method, nullptr);
}
return {};
}

// Call<"Name", F>: F is either the result type, with parameters taken from the argument types (CallOf<R, A...>),
// or a full signature R(P...). Param converts each argument to P with brace initialization, which rejects
// narrowing, and turns a T& parameter into the std::reference_wrapper the deduced form uses for ref/out.
Expand All @@ -338,11 +307,6 @@ namespace detail
static_assert(sizeof...(A) == sizeof...(P), "argument count does not match the signature");
return Invoke<R, typename Param<P>::Type...>(method, target, Param<P>::From(std::forward<A>(args))...);
}
template <typename... A> static Tried<R> TryRun(const MethodInfo* method, Il2CppObject* target, A&&... args)
{
static_assert(sizeof...(A) == sizeof...(P), "argument count does not match the signature");
return TryInvoke<R, typename Param<P>::Type...>(method, target, Param<P>::From(std::forward<A>(args))...);
}
};
template <typename F, typename... A> struct CallOf : MethodCall<F(A...)> {
};
Expand Down Expand Up @@ -515,15 +479,6 @@ struct Object : Il2CppObject {
auto* method = detail::Lookup<Self>(self, [](Il2CppClass* klass) { return Sig::Resolve(klass, N.View(), false); });
return Sig::Run(method, &self, std::forward<A>(args)...);
}
// Call, except that a failed call is told apart from a result that reads as zero: empty, or false for a void
// method, when the method is missing or a managed exception (still reported) stopped it. For callers that undo
// their own changes when the game's code fails.
template <Name N, typename F = void, typename Self, typename... A> auto TryCall(this Self& self, A&&... args)
{
using Sig = detail::CallOf<F, std::decay_t<A>...>;
auto* method = detail::Lookup<Self>(self, [](Il2CppClass* klass) { return Sig::Resolve(klass, N.View(), false); });
return Sig::TryRun(method, &self, std::forward<A>(args)...);
}

template <typename T> bool Is() const
{
Expand Down Expand Up @@ -573,15 +528,6 @@ template <typename T> Il2CppObject* Box(T value)
return klass ? il2cpp_value_box(klass, &value) : nullptr;
}

// Reads a boxed primitive of exactly type T. False, leaving `value` alone, for null or any other type.
template <typename T> bool Unbox(const Il2CppObject* object, T& value)
{
if (!object || object->klass != PrimitiveClass<T>())
return false;
std::memcpy(&value, il2cpp_object_unbox(const_cast<Il2CppObject*>(object)), sizeof(T));
return true;
}

// System.Nullable<T> as the runtime lays it out, for members that take or return one: the flag, then the value.
template <typename T> struct Nullable {
bool has_value;
Expand Down Expand Up @@ -728,26 +674,6 @@ Il2CppObject* NewDelegate(Il2CppClass* delegate_class, Method target, Il2CppMeth
template <typename R = void, typename... A> R InvokeBound(Il2CppDelegate* delegate, A... args)
{ return detail::InvokeNative<R, A...>(delegate->method, delegate->target, args...); }

// --- Faults ---

// Runs `read` and, on Windows, survives an access violation inside it: reading game objects whose layout drifted
// between client builds should cost a log line, not the game. False, after reporting `what`, when one was caught.
template <typename Fn> bool Guarded(const char* what, Fn&& read)
{
#if _WIN32
__try {
read();
} __except (1 /* EXCEPTION_EXECUTE_HANDLER */) {
detail::ReportFault(what);
return false;
}
#else
(void)what;
read();
#endif
return true;
}

// --- GC roots ---

// Move-only managed root. Weak handles do not retain the managed object.
Expand Down Expand Up @@ -796,34 +722,6 @@ class GcHandle
Il2CppGCHandle handle_ = nullptr;
};

// Strong roots for the objects one operation touches, released together when it ends. Keep returns its argument,
// or null after marking the set unhealthy when the runtime could not root it. Null needs no root.
class GcRoots
{
public:
Il2CppObject* Keep(Il2CppObject* object)
{
if (!object)
return nullptr;
auto root = GcHandle::Strong(object);
if (root.Get() != object) {
healthy_ = false;
return nullptr;
}
roots_.push_back(std::move(root));
return object;
}
template <typename T> T* Keep(T* object)
{ return reinterpret_cast<T*>(Keep(reinterpret_cast<Il2CppObject*>(object))); }
// False once any Keep failed.
bool healthy() const
{ return healthy_; }

private:
std::vector<GcHandle> roots_;
bool healthy_ = true;
};

// --- Internal calls ---

// A Unity engine internal call. One that a Unity upgrade renamed is reported once, when resolved, and then returns
Expand Down
3 changes: 0 additions & 3 deletions src/libs/il2cpp/src/il2cpp.cc
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,6 @@ namespace detail
return klass;
}

void ReportFault(const char* what)
{ Report(std::format("[IL2CPP] access violation while reading {}", what)); }

Il2CppClass* FindClass(std::string_view assembly, std::string_view namespaze, std::string_view name)
{
const std::string namespace_name(namespaze);
Expand Down
8 changes: 0 additions & 8 deletions src/libs/unity/include/unity/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ struct UnityObject {
inline bool Alive(Il2CppObject* object)
{ return object && UnityObject::Class::Call<"op_Implicit", bool>(object); }

inline void Destroy(Il2CppObject* object)
{ UnityObject::Class::Call<"Destroy">(object); }

// The first loaded object of T. Scans every loaded object; keep it off per-frame paths.
template <typename T> T* FindObject()
{
Expand Down Expand Up @@ -121,9 +118,6 @@ struct GameObject : il2cpp::Object {
{ return Get<"transform", Transform*>(); }
void SetActive(bool active)
{ Call<"SetActive">(active); }
// Whether it and every parent are active.
bool activeInHierarchy()
{ return Get<"activeInHierarchy", bool>(); }

// Passing System.Type selects the (Type) overloads over the string and generic ones. A null Type would throw in
// managed code, so a missing class yields null instead.
Expand Down Expand Up @@ -325,8 +319,6 @@ struct Material : il2cpp::Object {
{ return il2cpp::ToUtf8(Get<"name", Il2CppString*>()); }
Shader* shader()
{ return Get<"shader", Shader*>(); }
bool HasProperty(Il2CppString* name)
{ return Call<"HasProperty", bool>(name); }
float GetFloat(Il2CppString* name)
{ return Call<"GetFloat", float>(name); }
void SetFloat(Il2CppString* name, float value)
Expand Down
Loading
Loading