Skip to content

Commit 514605f

Browse files
authored
Merge pull request #50 from apple1417/master
replace env var based configuration with toml
2 parents 33c70df + bb49b6c commit 514605f

25 files changed

+489
-288
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
[submodule "libs/fmt"]
55
path = libs/fmt
66
url = https://github.com/fmtlib/fmt.git
7+
[submodule "libs/tomlplusplus"]
8+
path = libs/tomlplusplus
9+
url = https://github.com/marzer/tomlplusplus.git
710
[submodule "common_cmake"]
811
path = common_cmake
912
url = ../../bl-sdk/common_cmake.git

CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ set(GIT_POST_CONFIGURE_FILE "${CONFIGURE_FILES_DIR}/unrealsdk/git.inl")
5555
include(common_cmake/git_watcher.cmake)
5656

5757
add_subdirectory(libs/minhook)
58+
add_subdirectory(libs/tomlplusplus)
5859
if(NOT supports_format)
5960
add_subdirectory(libs/fmt)
6061
endif()
@@ -63,13 +64,14 @@ endif()
6364
if((NOT CMAKE_HOST_WIN32)
6465
AND (CMAKE_SYSTEM_NAME STREQUAL "Windows")
6566
AND (CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
66-
# Disable minhook intrinsics - MSVC implicitly defines intrinsics, but Clang does not, so we get
67+
# Disable some intrinsics - MSVC implicitly defines intrinsics, but Clang does not, so we get
6768
# a linking error otherwise
6869
target_compile_definitions(minhook PRIVATE MINHOOK_DISABLE_INTRINSICS)
70+
target_compile_definitions(_unrealsdk_interface INTERFACE TOML_ENABLE_SIMD=0)
6971
endif()
7072

7173
target_include_directories(_unrealsdk_interface INTERFACE "src" ${CONFIGURE_FILES_DIR})
72-
target_link_libraries(_unrealsdk_interface INTERFACE minhook)
74+
target_link_libraries(_unrealsdk_interface INTERFACE minhook tomlplusplus::tomlplusplus)
7375
if(NOT ${supports_format})
7476
target_link_libraries(_unrealsdk_interface INTERFACE fmt)
7577
endif()

README.md

Lines changed: 81 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@
33

44
A library to help interact with unreal engine objects from another module in the same address space.
55

6-
# Usage Overview
7-
To start, you need to initialize the sdk. This is called with a reference to an `AbstractHook` - the
8-
sdk can work with multiple (somewhat similar) UE versions from a single binary, so you need to tell
9-
it how exactly to hook everything. The easiest way is to let it autodetect.
6+
What makes this library different?
7+
- All property accesses are performed and validated at runtime. There's no need to dump an SDK, and
8+
it's automatically compatible with any game updates adding new fields.
9+
- No dependencies on Epic, so actually open source, all code independently reverse engineered.
10+
- Supports multiple different compilers, cross compiling from Linux is a first class target.
11+
12+
Why shouldn't you use this library?
13+
- Very limited support for different Unreal Engine versions, it's primarily focused on the
14+
Borderlands series.
15+
- Some more complex property types (e.g. maps) are not supported, since they each need to be
16+
manually implemented.
17+
- Only supports Windows executables (though will work under Proton).
1018

11-
```cpp
12-
unrealsdk::init(unrealsdk::game::select_based_on_executable);
13-
```
14-
If this doesn't work correctly, you can always implement your own version (and then merge it back
15-
into this project).
16-
17-
If you link against the sdk as a shared library, it automatically initializes like this for you.
18-
19-
After initializing, you probably want to setup some hooks. The sdk can run callbacks whenever an
20-
unreal function is hooked, allowing you to interact with it's args, and mess with it's execution.
21-
Exact hook semantics are better documented in the `hook_manager.h` header.
19+
# Usage Overview
20+
Standard usage involves setting up hooks. The sdk can run callbacks whenever a given unreal function
21+
is called, allowing you to interact with it's args, and mess with it's execution. Exact hook
22+
semantics are better documented in the `hook_manager.h` header.
2223

2324
```cpp
2425
bool on_main_menu(unrealsdk::hook_manager::Details& hook) {
@@ -31,11 +32,9 @@ unrealsdk::hook_manager::add_hook(L"WillowGame.FrontendGFxMovie:Start",
3132
&on_main_menu);
3233
```
3334
34-
Once your hook runs, you start having access to unreal objects. You can generally interact with any
35-
unreal value (such as the properties on an object) through the templated `get` and `set` functions.
36-
These functions take the expected property type as a template arg (and will throw exceptions if it
37-
doesn't appear to line up). All property accesses are evaluated at runtime, meaning you don't need
38-
to generate an sdk specific to your game.
35+
Once your hook runs, you start having access to unreal objects. You can interact with unreal values
36+
through the `get` and `set` functions. These functions take the expected property type as a template
37+
arg, and will throw exceptions if it doesn't appear to line up.
3938
4039
```cpp
4140
auto paused = hook.args->get<UBoolProperty>(L"StartPaused"_fn);
@@ -48,46 +47,15 @@ auto op_string = hook.obj->get<UFunction, BoundFunction>(L"BuildOverpowerPromptS
4847
.call<UStrProperty, UIntProperty, UIntProperty>(1, 10);
4948
```
5049

51-
# Environment Variables
52-
A few environment variables adjust the sdk's behaviour. Note that not all variables are used in all
53-
build configurations.
54-
55-
| Environment Variable | Usage |
56-
| :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
57-
| `UNREALSDK_ENV_FILE` | A file containing environment variables to load, relative to the dll. Defaults to `unrealsdk.env`. More below. |
58-
| `UNREALSDK_EXTERNAL_CONSOLE` | If defined, creates an external console window mirroring what is written to the game's console. Always enabled in debug builds. |
59-
| `UNREALSDK_LOG_FILE` | The file to write log messages to, relative to the dll. Defaults to `unrealsdk.log`. |
60-
| `UNREALSDK_LOG_LEVEL` | Changes the default logging level used in the unreal console. May use either the level names or their numerical values. |
61-
| `UNREALSDK_GAME_OVERRIDE` | Override the executable name used for game detection. |
62-
| `UNREALSDK_UPROPERTY_SIZE` | Changes the size the `UProperty` class is assumed to have. |
63-
| `UNREALSDK_ALLOC_ALIGNMENT` | Changes the alignment used when calling the unreal memory allocation functions. |
64-
| `UNREALSDK_CONSOLE_KEY` | Changes the default console key which is set when one is not already bound. |
65-
| `UNREALSDK_UCONSOLE_CONSOLE_COMMAND_VF_INDEX` | Overrides the virtual function index used when hooking `UConsole::ConsoleCommand`. |
66-
| `UNREALSDK_UCONSOLE_OUTPUT_TEXT_VF_INDEX` | Overrides the virtual function index used when calling `UConsole::OutputText`. |
67-
| `UNREALSDK_LOCKING_PROCESS_EVENT` | If defined, locks simultaneous ProcessEvent calls from different threads. This is used both for hooks and for calling unreal functions - external code must take care wrt. deadlocks. |
68-
| `UNREALSDK_LOG_ALL_CALLS_FILE` | After enabling `unrealsdk::hook_manager::log_all_calls`, the file to write calls to. |
69-
70-
You can also define any of these in an env file, which will automatically be loaded when the sdk
71-
starts (excluding `UNREALSDK_ENV_FILE` of course). This file should contain lines of equals
72-
separated key-value pairs, noting that whitespace is *not* stripped (outside of the trailing
73-
newline). A line is ignored if it does not contain an equals sign, or if it defines a variable which
74-
already exists.
75-
76-
```ini
77-
UNREALSDK_LOG_LEVEL=MISC
78-
UNREALSDK_CONSOLE_KEY=Quote
79-
```
80-
81-
You can also use this file to load environment variables for other plugins (assuming they don't
82-
check them too early), it's not limited to just those used by the sdk.
50+
# Integrating the SDK into your project
51+
So there are a few extra steps to integrate the sdk into your project before you can start using
52+
hooks like above.
8353

84-
# Linking Against the SDK
85-
The sdk requires at least C++20, primarily for templated lambdas. It also makes great use of
86-
`std::format`, though if this is not available it tries to fall back to using fmtlib. Linking
87-
against the sdk thus requires your own projects to use at least C++20 too.
54+
The SDK requires at least C++20. This is primarily for templated lambdas - you may still be able to
55+
use it if your compiler doesn't yet fully support it (e.g. it falls back to fmtlib if `std::format`
56+
is not available).
8857

89-
To link against the sdk, simply clone the repo (including submodules), add it as a subdirectory,
90-
and link against the `unrealsdk` target.
58+
The recommended way to link against the sdk is as a submodule.
9159

9260
```
9361
git clone --recursive https://github.com/bl-sdk/unrealsdk.git
@@ -98,35 +66,70 @@ target_link_libraries(MyProject PRIVATE unrealsdk)
9866
```
9967

10068
You can configure the sdk by setting a few variables before including it:
101-
- `UNREALSDK_UE_VERSION` - The unreal engine version to build the SDK for. One of `UE3` or `UE4`.
69+
- `UNREALSDK_UE_VERSION` - The unreal engine version to build the SDK for, one of `UE3` or `UE4`.
10270
These versions are different enough that supporting them from a single binary is difficult.
10371
- `UNREALSDK_ARCH` - The architecture to build the sdk for. One of `x86` or `x64`. Will be double
10472
checked at compile time.
10573
- `UNREALSDK_SHARED` - If set, compiles as a shared library instead of as an object.
10674

107-
## Shared Library
108-
The sdk contains a decent amount of internal state, meaning it's not possible to inject twice into
109-
the same process. At it's simplest, any detours on unreal functions will change their signatures, so
110-
a second instance won't be able to find them again. If two programs both want to use the sdk in the
111-
same game process, they will have to link against the shared library.
75+
If you want to be able to run multiple projects using the sdk in the same game process, you *must*
76+
compile it as a shared library, there's a decent amount of internal state preventing initializing it
77+
twice.
11278

113-
The included shared library initializes based on executable. If you need custom initialization, you
114-
can create your own shared library by linking against the object library and defining the
115-
`UNREALSDK_SHARED` and `UNREALSDK_EXPORTING` macros.
79+
If you're linking against a static library, the easiest way to initialize it is:
80+
```cpp
81+
unrealsdk::init(unrealsdk::game::select_based_on_executable);
82+
```
83+
If you're linking against the shared library, it's automatically initialized in a thread. You'll
84+
instead need to blocking wait on it finishing before continuing.
85+
```cpp
86+
while (!unrealsdk::is_initialized()) {}
87+
```
88+
89+
At this point the SDK is ready, you can start setting your hooks.
90+
91+
You may want to further wait on the console being hooked, so that your log messages appear in the
92+
game's console. They will still be written to the log file before this point.
93+
```cpp
94+
LOG(INFO, "Some log message"); // Only in the log file
95+
while (!unrealsdk::is_console_ready()) {}
96+
LOG(INFO, "Some log message"); // Both in the log file and console
97+
```
98+
99+
## Configuration
100+
There are a few pieces of sdk behaviour you can configure, via an `unrealsdk.toml`. By default this
101+
should be placed next to the dll, though you can also specify a custom location via the
102+
`UNREALSDK_CONFIG_FILE` environment variable.
103+
104+
Since it's somewhat expected your project may have to ship with some default settings, an
105+
`unrealsdk.user.toml` can also be used to add some user specific settings. The two files are merged,
106+
fixing conflicts as follows:
107+
- If a value is a table in both files, it's recursively merged
108+
- Otherwise, whatever value is in the user file overwrites the base file.
109+
110+
See [`unrealsdk.toml.example`](unrealsdk.toml.example) for a full description of what settings are
111+
supported.
112+
113+
<br>
114+
115+
All the sdk's settings are under the top level `unrealsdk` table. In your own projects you may add
116+
additional settings to the same file, under a different header. The sdk exposes a few functions to
117+
let you read some basic values without needing to parse the files again.
118+
```cpp
119+
auto val = unrealsdk::config::get_str("my_project.my_field");
120+
if (val.has_value()) {
121+
do_something(*val);
122+
}
123+
```
116124

117125
## Cross-Compiler ABI
118126
One of the goals of the shared library implementation is have a stable cross-compiler ABI - i.e.
119127
allowing developing one program while also running another which you downloaded a precompiled
120128
version of.
121129

122-
In order to do this, the exported functions try to use a pure C interface. Since the sdk heavily
123-
relies on C++ features (e.g. all the templates), it's impractical to export everything this way.
124-
Instead, it only exports the bare minimum functions which interact with internal state. Some of
125-
these rely on private wrapper functions, which do things like decompose strings into pointer and
126-
length, not everything's exposed in the headers.
127-
128-
There is one assumption we rely on for these exported functions to work properly, where we can't
129-
quite stick with pure C:
130+
In order to do this, the sdk uses a number of private exported functions (those in the headers are
131+
automatically converted), which try to keep to a pure C interface. There is one assumption we still
132+
rely on however, where we can't quite stick with pure C:
130133

131134
- Both dlls share the same exception ABI. While none of the exported functions intentionally throw,
132135
it's impossible to completely avoid an exception travelling between modules - we can't stop a
@@ -137,10 +140,9 @@ This turns out to be a bit of a problem - MSVC and GNU have different exception
137140
both. Practically, this means when cross compiling, you should either compile everything from
138141
scratch, or setup Clang to build with the MSVC ABI. [See this blog post for more info](https://apple1417.dev/posts/2023-05-18-debugging-proton).
139142

140-
# Running Builds
141-
As previously mentioned, the sdk can be configured to create a shared library. This is useful when
142-
developing for the sdk itself, it's the minimal configuration to get it running. The CMake presets
143-
are set up to build this.
143+
# Running the SDK by itself
144+
The shared library is also useful when developing for the sdk itself, since it's the minimal
145+
configuration to get it running. The CMake presets are set up to build this.
144146

145147
Note that you will need to use some game specific plugin loader to get the dll loaded. It is not set
146148
up to alias any system dlls (since when actually using it as a library you don't want that), you

changelog.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
# Changelog
22

33
## Upcoming
4+
5+
- Completely reworked the configuration system.
6+
7+
Environment variables and the `unrealsdk.env` are no longer used, due to issues with them not fully
8+
propagating within the same process. The new configuration now uses an `unrealsdk.toml` instead.
9+
10+
Also added support for a user specific override file - `unrealsdk.user.toml`. This allows projects
11+
to ship their own `unrealsdk.toml`, without overwriting user's settings on update.
12+
13+
[4daecbde](https://github.com/bl-sdk/unrealsdk/commit/4daecbde)
14+
415
- `unrealsdk::hook_manager::inject_next_call` is now thread local.
516

617
[427c8734](https://github.com/bl-sdk/unrealsdk/commit/427c8734)

libs/tomlplusplus

Submodule tomlplusplus added at 3017243

0 commit comments

Comments
 (0)