Bugfix for wayland

This commit is contained in:
2026-04-12 23:08:09 -04:00
parent ea74f5fe76
commit 85a6fad139
7 changed files with 124 additions and 95 deletions

View File

@@ -1,17 +1,18 @@
# Integration Project # Integration Project
This is **Luprex**, a game engine built on top of Unreal that uses Lua as its scripting language. ## Behavior Rules
The system consists of two parts:
1. The Luprex DLL. Stores the state of the world. Handles networking and lua scripting. The networking is automatic, scripters don't write networking code. The luprex DLL is event-driven, deterministic, OS-independent, no I/O. Pure standard-compliant C++. Organized as a library with struct `EngineWrapper` as the top-level API. - Be Concise!
* Do not output multiple paragraphs. Doing so is very rude.
* You are having a conversation, give the other person a chance to speak.
2. The Unreal-based Driver. Its job is to peer into the luprex DLL and render whatever it sees. It is also responsible for sending OS events (like TCP/IP events) into the Luprex DLL. Uses struct EngineWrapper access the Luprex DLL. There's also a command-line driver for the game's server. - Do not use git to make changes (no commit, no push, no stash, etc.)
The luprex DLL never calls into the Unreal driver. Output goes through polled buffers. This separation enables deterministic replay for debugging: the driver logs all events, and can replay them to reproduce crashes. - Work at the user's pace. Do not start coding until the user says it is time.
## Build System ## Build System
- Use `build.py` for all builds. Do NOT follow Epic's standard build instructions. - Use `build.py` for all builds. Do NOT use UBT directly.
- `build.py all` — full rebuild (engine, game, intellisense, project files) - `build.py all` — full rebuild (engine, game, intellisense, project files)
- `build.py c++` — lightweight rebuild (use if you've only edited c++ files) - `build.py c++` — lightweight rebuild (use if you've only edited c++ files)
- Lua and Blueprint edits don't require any kind of build. - Lua and Blueprint edits don't require any kind of build.
@@ -19,78 +20,24 @@ The luprex DLL never calls into the Unreal driver. Output goes through polled bu
## Directory Structure ## Directory Structure
- `luprex/` — The Luprex DLL. - `luprex/` — The Luprex DLL.
- `Source/Integration/`Game module C++ source (Unreal-side driver code) - `Source/Integration/`Unreal interface to Luprex.
- `Content/` — Unreal assets - `Content/` — Unreal assets
- `Docs/` — Documentation. When trying to understand this system, start with the markdown files in the Docs directory. - `Docs/` — Documentation.
- `Config/` — Unreal config files - `Config/` — Unreal config files
- `EnginePatches/` — Custom engine modifications - `EnginePatches/` — Custom engine modifications
- `Plugins/UEWingman/' - An MCP that gives you control over the unreal editor. - `Plugins/UEWingman/' - An MCP that gives you control over the unreal editor.
- `../integration.UE/` - the unreal engine source tree - `../integration.UE/` - the unreal engine source tree
## Architecture: World Models and Predictive Reexecution
There are four types of world models, each a separate `World` instance with its own Lua interpreter:
- **Master** (server, one) — authoritative state, executes commands immediately when they arrive.
- **Server-synchronous** (server, one per client) — executes commands when the acknowledgement is issued.
- **Client-synchronous** (client, one) — executes commands when the same acknowledgement arrives; determinism keeps it in perfect sync with its server-synchronous counterpart.
- **Asynchronous** (client, one) — snapshot of client-synchronous with predictions applied for responsive rendering; rolled back when server confirms.
The synchronous models lag behind the master but stay in lockstep with each other. The asynchronous model fills the latency gap for the player.
Two update channels flow into the synchronous models:
1. **Command acknowledgements** — for the client's own actions, keeping the two synchronous models in lockstep.
2. **Difference transmission** — for everything else (other players' actions, server-side events, tangibles entering/leaving visibility).
See `Docs/Predictive-Reexecution.md` for the full explanation.
## Architecture: Lua / Unreal Separation
Lua scripts have no access to the Unreal API whatsoever. The scripter works with plain Lua tables, animation queues of key-value tuples, and coroutines. There are no "unreal bindings." The Luprex DLL is engine-agnostic — Unreal (or any other front end) interprets the animation queues and renders accordingly.
## Architecture: Tangibles
Tangibles are game objects. Each has:
- A **Lua table** — the scripter stores arbitrary game data here.
- An **animation queue** — a fixed-length sequence of key-value animation steps.
- A **C++ tangible** — holds the ID, animation queue, positional tracker, etc.
- A **metatable** — engine-reserved; contains __id, __index (class), __threads.
Animation steps contain **transient** values (like `action`) that don't propagate, and **persistent** values (like `xyz`, `facing`, `plane`) that carry forward automatically.
On the Unreal side, **Tangible Actor blueprints** (TangibleStaticMesh, TangibleSkeletalMesh, TangibleCharacter) monitor the animation queue and perform the visual animations. Custom blueprints can interpret the queue in any way they want.
## Architecture: Lua Environment
- **Patched Lua runtime** — deterministic table iteration, deterministic table length, flag bits on tables, generalized less-than, C++ exceptions instead of longjmp, and more. See `Docs/A-Summary-of-our-Lua-Patches.md`.
- **LuaStack API** — custom C++ API replacing the standard Lua C API. Uses `LuaDefStack`/`LuaExtStack` with `LuaArg`/`LuaVar`/`LuaRet` slots mapped to stack positions. See `Docs/Our-In-House-Lua-API.md`.
- **LuaDefine macro** — declares Lua-callable C++ functions and auto-registers them in a global registry for automatic insertion into the Lua environment.
- **eng::malloc heap** — custom deterministic memory allocator for the driven portion, ensuring reproducible addresses during replay.
## Architecture: GUI System
Blueprints call into Lua via two mechanisms:
- **Invokes** — change world state, forwarded to server, executed in order per predictive reexecution rules.
- **Probes** — read-only, return data to blueprints, run locally on client.
Look-at widgets, hotkeys, and menus are built on top of this. The menu system is implemented entirely in "user space" Lua and blueprint code. See `Docs/Displaying-Widget-Blueprints.md`.
## Key Documentation
Look in the Docs directory for important documentation.
## Workflow
- Do not use git to make changes (commit, push, branch, etc.). Read-only git commands (status, log, diff, etc.) are fine.
- Work at the user's pace. Do not start coding until the user says it is time.
- If an instruction ends with an ellipsis (`...`), the user has more to say. Wait for the next message before acting.
- Do not output multiple paragraphs. Doing so is very rude. You are having a conversation, give the other person a
chance to speak. For most questions, 3-4 sentences is the maximum, unless you've been asked to give a
detailed explanation.
## Coding Conventions ## Coding Conventions
- Prefer early returns and `continue` to reduce nesting (never-nester style). - Prefer early returns and `continue` to reduce nesting (never-nester style).
- Do not use static functions in Unreal code. Use class methods instead. - Do not use static functions in Unreal code. Use class methods instead.
- Use `LogLuprexIntegration` for log messages inside Source/. Use LogTemp inside Plugins/.
## Special commands.
You must learn these shorthands:
- "bc" - be concise. You must rephrase your most recent
response in a more concise manner, and then, going forward, be
more concise in general.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,59 @@
# The Luprex System
This is **Luprex**, a game engine built on top of Unreal that uses Lua as its scripting language.
The system consists of two parts:
1. The Luprex DLL. Stores the state of the world. Handles networking and lua scripting. The networking is automatic, scripters don't write networking code. The luprex DLL is event-driven, deterministic, OS-independent, no I/O. Pure standard-compliant C++. Organized as a library with struct `EngineWrapper` as the top-level API.
2. The Unreal-based Driver. Its job is to peer into the luprex DLL and render whatever it sees. It is also responsible for sending OS events (like TCP/IP events) into the Luprex DLL. Uses struct EngineWrapper access the Luprex DLL. There's also a command-line driver for the game's server.
The luprex DLL never calls into the Unreal driver. Output goes through polled buffers. This separation enables deterministic replay for debugging: the driver logs all events, and can replay them to reproduce crashes.
## Architecture: World Models and Predictive Reexecution
There are four types of world models, each a separate `World` instance with its own Lua interpreter:
- **Master** (server, one) — authoritative state, executes commands immediately when they arrive.
- **Server-synchronous** (server, one per client) — executes commands when the acknowledgement is issued.
- **Client-synchronous** (client, one) — executes commands when the same acknowledgement arrives; determinism keeps it in perfect sync with its server-synchronous counterpart.
- **Asynchronous** (client, one) — snapshot of client-synchronous with predictions applied for responsive rendering; rolled back when server confirms.
The synchronous models lag behind the master but stay in lockstep with each other. The asynchronous model fills the latency gap for the player.
Two update channels flow into the synchronous models:
1. **Command acknowledgements** — for the client's own actions, keeping the two synchronous models in lockstep.
2. **Difference transmission** — for everything else (other players' actions, server-side events, tangibles entering/leaving visibility).
See `Docs/Predictive-Reexecution.md` for the full explanation.
## Architecture: Lua / Unreal Separation
Lua scripts have no access to the Unreal API whatsoever. The scripter works with plain Lua tables, animation queues of key-value tuples, and coroutines. There are no "unreal bindings." The Luprex DLL is engine-agnostic — Unreal (or any other front end) interprets the animation queues and renders accordingly.
## Architecture: Tangibles
Tangibles are game objects. Each has:
- A **Lua table** — the scripter stores arbitrary game data here.
- An **animation queue** — a fixed-length sequence of key-value animation steps.
- A **C++ tangible** — holds the ID, animation queue, positional tracker, etc.
- A **metatable** — engine-reserved; contains __id, __index (class), __threads.
Animation steps contain **transient** values (like `action`) that don't propagate, and **persistent** values (like `xyz`, `facing`, `plane`) that carry forward automatically.
On the Unreal side, **Tangible Actor blueprints** (TangibleStaticMesh, TangibleSkeletalMesh, TangibleCharacter) monitor the animation queue and perform the visual animations. Custom blueprints can interpret the queue in any way they want.
## Architecture: Lua Environment
- **Patched Lua runtime** — deterministic table iteration, deterministic table length, flag bits on tables, generalized less-than, C++ exceptions instead of longjmp, and more. See `Docs/A-Summary-of-our-Lua-Patches.md`.
- **LuaStack API** — custom C++ API replacing the standard Lua C API. Uses `LuaDefStack`/`LuaExtStack` with `LuaArg`/`LuaVar`/`LuaRet` slots mapped to stack positions. See `Docs/Our-In-House-Lua-API.md`.
- **LuaDefine macro** — declares Lua-callable C++ functions and auto-registers them in a global registry for automatic insertion into the Lua environment.
- **eng::malloc heap** — custom deterministic memory allocator for the driven portion, ensuring reproducible addresses during replay.
## Architecture: GUI System
Blueprints call into Lua via two mechanisms:
- **Invokes** — change world state, forwarded to server, executed in order per predictive reexecution rules.
- **Probes** — read-only, return data to blueprints, run locally on client.
Look-at widgets, hotkeys, and menus are built on top of this. The menu system is implemented entirely in "user space" Lua and blueprint code. See `Docs/Displaying-Widget-Blueprints.md`.

View File

@@ -1,6 +1,5 @@
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py /home/jyelon/integration.UE/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py --- Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py.orig 2026-04-12 22:58:33.989318455 -0400
--- /home/jyelon/integration.UE.orig/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-11-10 23:34:18.481538118 -0500
+++ /home/jyelon/integration.UE/Engine/Extras/LLDBDataFormatters/UEDataFormatters_2ByteChars.py 2025-09-08 15:15:35.242987722 -0400
@@ -32,7 +32,7 @@ @@ -32,7 +32,7 @@
if DataVal == 0: if DataVal == 0:
Val = 'NULL' Val = 'NULL'
@@ -22,9 +21,8 @@ diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--
return Val return Val
def UESignedCharSummaryProvider(valobj,dict): def UESignedCharSummaryProvider(valobj,dict):
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp /home/jyelon/integration.UE/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp --- Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp.orig 2026-04-12 22:58:34.075320964 -0400
--- /home/jyelon/integration.UE.orig/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2026-04-12 23:03:40.139226303 -0400
+++ /home/jyelon/integration.UE/Engine/Plugins/Developer/VisualStudioCodeSourceCodeAccess/Source/VisualStudioCodeSourceCodeAccess/Private/VisualStudioCodeSourceCodeAccessor.cpp 2026-03-02 15:28:34.593675093 -0500
@@ -149,7 +149,7 @@ @@ -149,7 +149,7 @@
FString SolutionDir = GetSolutionPath(); FString SolutionDir = GetSolutionPath();
TArray<FString> Args; TArray<FString> Args;
@@ -34,9 +32,8 @@ diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--
return Launch(Args); return Launch(Args);
} }
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp /home/jyelon/integration.UE/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp --- Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp.orig 2026-04-12 22:58:34.169323705 -0400
--- /home/jyelon/integration.UE.orig/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp 2026-04-12 23:04:02.208867909 -0400
+++ /home/jyelon/integration.UE/Engine/Source/Editor/UnrealEd/Private/SourceCodeNavigation.cpp 2026-03-02 15:37:59.934459864 -0500
@@ -463,7 +463,7 @@ @@ -463,7 +463,7 @@
ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess"); ISourceCodeAccessModule& SourceCodeAccessModule = FModuleManager::LoadModuleChecked<ISourceCodeAccessModule>("SourceCodeAccess");
ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor(); ISourceCodeAccessor& SourceCodeAccessor = SourceCodeAccessModule.GetAccessor();
@@ -57,9 +54,8 @@ diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--
} }
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp /home/jyelon/integration.UE/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp --- Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp.orig 2026-04-12 22:58:34.254326184 -0400
--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-11-10 23:34:18.481701126 -0500
+++ /home/jyelon/integration.UE/Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxPlatformApplicationMisc.cpp 2025-09-08 15:15:35.242987722 -0400
@@ -299,6 +299,9 @@ @@ -299,6 +299,9 @@
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // When relative mouse mode is active, don't hide cursor. SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE, "1"); // When relative mouse mode is active, don't hide cursor.
SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0"); // Don't warp the cursor to the center in relative mouse mode. SDL_SetHint(SDL_HINT_MOUSE_RELATIVE_MODE_WARP, "0"); // Don't warp the cursor to the center in relative mouse mode.
@@ -70,9 +66,37 @@ diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--
// If we're rendering offscreen, use the "dummy" SDL video driver // If we're rendering offscreen, use the "dummy" SDL video driver
if (FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen")) && !getenv("SDL_VIDEODRIVER")) if (FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen")) && !getenv("SDL_VIDEODRIVER"))
{ {
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp --- Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp.orig 2026-04-12 22:58:34.538334467 -0400
--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Source/Runtime/ApplicationCore/Private/Linux/LinuxWindow.cpp 2026-04-12 22:48:15.848291098 -0400
+++ /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp 2026-03-02 15:37:24.464156072 -0500 @@ -235,7 +235,26 @@
// The SDL window doesn't need to be reshaped.
// the size of the window you input is the sizeof the client.
+
+ // Under XWayland, non-override-redirect popup windows don't receive input
+ // events from the compositor. SDL already sets override_redirect for
+ // tooltips and popup menus, but other borderless child windows (like
+ // notification popups and dialogs) also need it. We temporarily enable
+ // the SDL hint to force override_redirect for these windows.
+ bool bForceOverrideRedirect = !Definition->HasOSWindowBorder
+ && InParent.IsValid()
+ && !(WindowStyle & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU));
+ if (bForceOverrideRedirect)
+ {
+ SDL_SetHint(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, "1");
+ }
+
HWnd = SDL_CreateWindow( TCHAR_TO_ANSI( *Definition->Title ), X, Y, ClientWidth, ClientHeight, WindowStyle );
+
+ if (bForceOverrideRedirect)
+ {
+ SDL_SetHint(SDL_HINT_X11_FORCE_OVERRIDE_REDIRECT, "0");
+ }
// produce a helpful message for common driver errors
if (HWnd == nullptr)
{
--- Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp.orig 2026-04-12 22:58:34.349328955 -0400
+++ Engine/Source/Runtime/Core/Private/Unix/UnixPlatformStackWalk.cpp 2026-04-12 23:05:56.395187515 -0400
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
#include "HAL/ExceptionHandling.h" #include "HAL/ExceptionHandling.h"
#include "HAL/PlatformProcess.h" #include "HAL/PlatformProcess.h"
@@ -151,9 +175,8 @@ diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--
+ +
+ return false; + return false;
+} +}
diff -u --recursive '--exclude=*.o' '--exclude=*.d' '--exclude=Intermediate' '--exclude=Binaries' '--exclude=Saved' '--exclude=DerivedDataCache' '--exclude=*.pyc' '--exclude=__pycache__' /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h --- Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h.orig 2026-04-12 22:58:34.451331930 -0400
--- /home/jyelon/integration.UE.orig/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h 2025-03-11 10:33:14.000000000 -0400 +++ Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h 2026-04-12 23:06:10.273590986 -0400
+++ /home/jyelon/integration.UE/Engine/Source/Runtime/Core/Public/Unix/UnixPlatformStackWalk.h 2026-03-02 15:36:22.690627000 -0500
@@ -24,6 +24,8 @@ @@ -24,6 +24,8 @@
static CORE_API void ThreadStackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, int32 IgnoreCount, uint32 ThreadId); static CORE_API void ThreadStackWalkAndDump(ANSICHAR* HumanReadableString, SIZE_T HumanReadableStringSize, int32 IgnoreCount, uint32 ThreadId);
static CORE_API int32 GetProcessModuleCount(); static CORE_API int32 GetProcessModuleCount();

View File

@@ -222,7 +222,7 @@ def unzip_unreal_engine_and_apply_patch():
with JZipFile(zipfn) as zf: with JZipFile(zipfn) as zf:
zf.extractall(INTEGRATION) zf.extractall(INTEGRATION)
patchfile = f"{INTEGRATION}/EnginePatches/EnginePatch" patchfile = f"{INTEGRATION}/EnginePatches/EnginePatch"
shell(extracted, f"patch -p1 < {patchfile}") shell(extracted, f"patch -p0 < {patchfile}")
Path(extracted).rename(UNREALENGINE) Path(extracted).rename(UNREALENGINE)
Path(touchfile).touch() Path(touchfile).touch()