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
8 changes: 6 additions & 2 deletions example_community_patch_settings_en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -730,8 +730,6 @@ disable_escape_exit = true
# Maximum gap in milliseconds between Escape presses that opens the exit prompt; 0 disables double-tap exit
disable_escape_exit_timer = 0

# ⚠️ EXPERIMENTAL: Prevent the first popup advert from appearing
disable_first_popup = false

# ⚠️ EXPERIMENTAL: Prevent the standard move keys from working
disable_move_keys = false
Expand Down Expand Up @@ -925,6 +923,11 @@ show_station_cargo = true

[sync]

# Target changes apply on settings reload and receive the latest observed state.
# Failed uploads retry; pending changes to an entity use its latest value.
# Up to 300 recent battle journals are retained per session. Journals may replay
# after restarting or logging in again; the old sent-ID file is no longer used.

# Subgroup: Data
# --------------

Expand All @@ -944,6 +947,7 @@ buildings = true
haven = true

# Do you want to sync live fleet state (mining, docked, warping...)? Opt-in, off by default
# Every state change is sent; up to 300 recent changes are retained per session.
fleets = false

# Do you want to sync inventory data?
Expand Down
18 changes: 2 additions & 16 deletions libs/il2cpp/include/il2cpp/il2cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,22 +551,6 @@ inline Il2CppString* ToString(Object* object)
return object->Call<"ToString", Il2CppString*>();
}

// Instance fields of the object's class and its bases, most derived first. For reading game data whose field
// names drift between builds; bindings should name their fields.
template <typename Visit> void ForEachInstanceField(Il2CppObject* object, Visit visit)
{
for (auto* klass = object ? object->klass : nullptr; klass; klass = il2cpp_class_get_parent(klass)) {
void* iter = nullptr;
while (auto* field = il2cpp_class_get_fields(klass, &iter))
if (!(field->type->attrs & FIELD_ATTRIBUTE_STATIC))
visit(field);
}
}

// A field found by ForEachInstanceField. T must match the field's layout; nothing checks it here.
template <typename T> T ReadField(Il2CppObject* object, FieldInfo* field)
{ return detail::ReadField<T>(field, object); }

// Runs the method a delegate is bound to on its target directly, skipping the delegate's invoke stub. Unlike
// Call, a managed exception is not caught here, so the caller can fall back: catch Il2CppExceptionWrapper.
template <typename R = void, typename... A> R InvokeBound(Il2CppDelegate* delegate, A... args)
Expand Down Expand Up @@ -627,6 +611,8 @@ template <typename T> struct List : Object {
{ return Call<"get_Count", int32_t>(); }
T operator[](int32_t index)
{ return Call<"get_Item", T>(index); }
void Set(int32_t index, T value)
{ Call<"set_Item">(index, value); }
void Add(T value)
{ Call<"Add">(value); }
void RemoveAt(int32_t index)
Expand Down
3 changes: 3 additions & 0 deletions libs/unity/include/unity/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ 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
3 changes: 1 addition & 2 deletions libs/unity/include/unity/input.h
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,7 @@ struct InputActionCallbackContext {
};
static_assert(sizeof(InputActionCallbackContext) == 16);

// This frame's keyboard. Unity answers each key from its own per-frame state; the snapshot only skips asking on
// idle frames and caches the derived checks.
// This frame's keyboard. Idle frames skip key queries; otherwise each key/state is read from Unity at most once.
namespace Key
{
// Starts a frame's reads. Every hook that reads keys calls it first, since hooks run in no fixed order within a
Expand Down
13 changes: 6 additions & 7 deletions libs/unity/include/unity/tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <il2cpp/hook.h>
#include <il2cpp/il2cpp.h>

#include <ranges>
#include <vector>

namespace unity
Expand Down Expand Up @@ -51,13 +52,11 @@ template <typename T> void Track(std::string_view created = ".ctor", std::string
template <typename T> T* Newest()
{ return static_cast<T*>(detail::Newest(T::Class::Raw())); }

// Every live instance of T, oldest first. A copy, so the caller may call into the game while the GC's finalizer
// thread keeps updating the tracker.
template <typename T> std::vector<T*> Instances()
// Every live instance of T, oldest first. The range owns a snapshot of borrowed pointers, so the caller may call
// into the game while the GC's finalizer thread keeps updating the tracker.
template <typename T> auto Instances()
{
std::vector<T*> result;
for (auto* object : detail::Instances(T::Class::Raw()))
result.push_back(static_cast<T*>(object));
return result;
return detail::Instances(T::Class::Raw())
| std::views::transform([](Il2CppObject* object) { return static_cast<T*>(object); });
}
} // namespace unity
33 changes: 23 additions & 10 deletions libs/unity/src/input.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@ namespace
bool any_down = false;
int8_t focused = 0; // 0 = not read yet, 1 = true, -1 = false
int8_t modified = 0;
std::array<std::array<int8_t, static_cast<size_t>(KeyCode::Max)>, 2> keys{};

bool Read(KeyCode key, bool down)
{
if (!(down ? any_down : any_held))
return false;
auto& value = keys[down][static_cast<size_t>(key)];
if (value == 0)
value = (down ? Input::GetKeyDown(key) : Input::GetKey(key)) ? 1 : -1;
return value == 1;
}
} frame;

TMP_InputField* FocusedInputField()
bool InputFocused(EventSystem* event_system)
{
auto* event_system = EventSystem::current();
auto* selected = event_system ? event_system->currentSelectedGameObject() : nullptr;
auto* field = selected ? selected->GetComponent<TMP_InputField>() : nullptr;
return field && field->isFocused() ? field : nullptr;
auto* selected = event_system ? event_system->currentSelectedGameObject() : nullptr;
auto* field = selected ? selected->GetComponent<TMP_InputField>() : nullptr;
return field && field->isFocused();
}
} // namespace

Expand All @@ -40,10 +50,10 @@ namespace Key
}

bool Pressed(KeyCode key)
{ return frame.any_held && Input::GetKey(key); }
{ return frame.Read(key, false); }

bool Down(KeyCode key)
{ return frame.any_down && Input::GetKeyDown(key); }
{ return frame.Read(key, true); }

bool IsModified()
{
Expand All @@ -60,14 +70,17 @@ namespace Key
bool IsInputFocused()
{
if (frame.focused == 0)
frame.focused = FocusedInputField() ? 1 : -1;
frame.focused = InputFocused(EventSystem::current()) ? 1 : -1;
return frame.focused == 1;
}

void ClearInputFocus()
{
if (FocusedInputField())
EventSystem::current()->SetSelectedGameObject(nullptr);
auto* event_system = EventSystem::current();
if (InputFocused(event_system))
event_system->SetSelectedGameObject(nullptr);
// Selection callbacks can focus another field. Re-read the actual focus on the next query this frame.
frame.focused = 0;
}
} // namespace Key
} // namespace unity
11 changes: 3 additions & 8 deletions libs/unity/src/tracker.cc
Original file line number Diff line number Diff line change
Expand Up @@ -113,15 +113,10 @@ namespace
{
original(state);

std::scoped_lock lock(registry.mutex);
std::vector<Il2CppObject*> released;
// Every object sits in its own class's list, so checking those lists alone visits each object once.
std::scoped_lock lock(registry.mutex);
// Filter each class list once, including inherited entries; untracking one object at a time rescans them.
for (auto& [klass, instances] : registry.objects)
for (auto* object : instances)
if (ClassOf(object) == klass && IsMarked(object))
released.push_back(object);
for (auto* object : released)
Untrack(object);
std::erase_if(instances, IsMarked);
}

RegisterFinalizer FindRegisterFinalizer()
Expand Down
Loading
Loading