Skip to content

Commit 9ac1863

Browse files
committed
feat(core/rdr3): block level loading
Currently, RedM will run through the entire gameSkeleton::RunInitFunctions (up to INIT_SESSION). This leads to several subsystems in RDR3 (such as CTrain, CNetwork, etc) being initalized before a user connects to a server. This leads to servers not being able to override these components. Rendering rdr3.meta changes pointless. The early initialization of CNetwork also leads to some minor issues with OneSync mainly effecting the future object length extension This patch matches FiveM's behaviour by preventing the game from loading past INIT_CORE until a connection is successfully made to a server where loading resumes. This changeset also introduces support for servers to properly override the games RDR3.meta while cleaning up citizen-level-loader-rdr3 to share the same code as five's implementation
1 parent 13ef3ca commit 9ac1863

File tree

8 files changed

+407
-447
lines changed

8 files changed

+407
-447
lines changed

code/components/citizen-level-loader-five/src/LevelLoader.cpp

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,41 @@ void AddDataFileToLoadList(const std::string& type, const std::string& path);
3636
#include "Hooking.h"
3737
#include "Hooking.Stubs.h"
3838

39+
#ifdef GTA_FIVE
40+
#define LEVEL_NAME "gta5"
41+
#else
42+
#define LEVEL_NAME "rdr3"
43+
#endif
44+
3945
static std::string g_overrideNextLoadedLevel;
4046
static std::string g_nextLevelPath;
4147

4248
static bool g_wasLastLevelCustom;
4349
static bool g_gameUnloaded = false;
4450

4551
static void(*g_origLoadLevelByIndex)(int);
46-
static void(*g_loadLevel)(const char* levelPath);
52+
#ifdef IS_RDR3
53+
static int* g_levelIndex;
54+
#endif
55+
static void (*g_loadLevel)(const char* levelPath);
4756

4857
enum NativeIdentifiers : uint64_t
4958
{
59+
#ifdef GTA_FIVE
5060
PLAYER_PED_ID = 0xD80958FC74E988A6,
5161
SET_ENTITY_COORDS = 0x621873ECE1178967,
5262
LOAD_SCENE = 0x4448EB75B4904BDB,
5363
SHUTDOWN_LOADING_SCREEN = 0x078EBE9809CCD637,
54-
DO_SCREEN_FADE_IN = 0xD4E8E24955024033
64+
DO_SCREEN_FADE_IN = 0xD4E8E24955024033,
65+
FREEZE_ENTITY_POSITION = 0x428CA6DBD1094446,
66+
ACTIVATE_ROCKSTAR_EDITOR = 0x49DA8145672B2725
67+
#else
68+
PLAYER_PED_ID = 0x096275889B8E0EE0,
69+
SET_ENTITY_COORDS = 0x06843DA7060A026B,
70+
SHUTDOWN_LOADING_SCREEN = 0xFC179D7E8886DADF,
71+
DO_SCREEN_FADE_IN = 0x6A053CF596F67DF7,
72+
FREEZE_ENTITY_POSITION = 0x7D9EFB7AD6B19754
73+
#endif
5574
};
5675

5776
class SpawnThread : public CfxThread
@@ -79,14 +98,23 @@ class SpawnThread : public CfxThread
7998
NativeInvoke::Invoke<SHUTDOWN_LOADING_SCREEN, int>();
8099
NativeInvoke::Invoke<DO_SCREEN_FADE_IN, int>(0);
81100

82-
NativeInvoke::Invoke<SET_ENTITY_COORDS, int>(playerPedId, 293.089f, 180.466f, 104.301f);
83-
NativeInvoke::Invoke<0x428CA6DBD1094446, int>(NativeInvoke::Invoke<0xD80958FC74E988A6, int>(), false);
84-
101+
NativeInvoke::Invoke<SET_ENTITY_COORDS, int>(playerPedId,
102+
#ifdef GTA_FIVE
103+
293.089f, 180.466f, 104.301f
104+
#else
105+
35.0f, 35.0f, 102.0f
106+
#endif
107+
);
108+
109+
NativeInvoke::Invoke<FREEZE_ENTITY_POSITION, int>(NativeInvoke::Invoke<PLAYER_PED_ID, int>(), false);
110+
111+
#ifdef GTA_FIVE
85112
if (Instance<ICoreGameInit>::Get()->HasVariable("editorMode"))
86113
{
87-
NativeInvoke::Invoke<0x49DA8145672B2725, int>();
114+
NativeInvoke::Invoke<ACTIVATE_ROCKSTAR_EDITOR, int>();
88115
Instance<ICoreGameInit>::Get()->ClearVariable("editorMode");
89116
}
117+
#endif
90118

91119
m_doInityThings = false;
92120
}
@@ -158,14 +186,18 @@ static void DoLoadLevel(int index)
158186
}
159187

160188
// mark the level as being custom
161-
g_wasLastLevelCustom = (g_overrideNextLoadedLevel != "gta5" && g_overrideNextLoadedLevel.find("/gta5") == std::string::npos);
189+
g_wasLastLevelCustom = (g_overrideNextLoadedLevel != LEVEL_NAME && g_overrideNextLoadedLevel.find("/" LEVEL_NAME) == std::string::npos);
162190

163191
// clear the 'next' level
164192
g_overrideNextLoadedLevel.clear();
165193

166194
// save globally to prevent va() reuse messing up
167195
g_nextLevelPath = levelPath;
168196

197+
#ifdef IS_RDR3
198+
// Manually set the level index.
199+
*g_levelIndex = index;
200+
#endif
169201
// load the level
170202
g_loadLevel(g_nextLevelPath.c_str());
171203
}
@@ -191,6 +223,7 @@ static bool DoesLevelHashMatch(void* evaluator, uint32_t* hash)
191223
return (!g_wasLastLevelCustom);
192224
}
193225

226+
#ifdef GTA_FIVE
194227
struct CDataFileMgr__ContentChangeSet
195228
{
196229
// technically, some atString?
@@ -215,9 +248,11 @@ static void CFileLoader__BuildContentChangeSetActionList_Hook(const CDataFileMgr
215248

216249
return g_orig_CFileLoader__BuildContentChangeSetActionList(changeset, outArray, a3, a4, a5);
217250
}
251+
#endif
218252

219253
static HookFunction hookFunction([] ()
220254
{
255+
#ifdef GTA_FIVE
221256
char* levelCaller = xbr::IsGameBuildOrGreater<2060>() ? hook::pattern("33 D0 81 E2 FF 00 FF 00 33 D1 48").count(1).get(0).get<char>(0x33) : hook::pattern("0F 94 C2 C1 C1 10 33 CB 03 D3 89 0D").count(1).get(0).get<char>(46);
222257
char* levelByIndex = hook::get_call(levelCaller);
223258

@@ -237,6 +272,26 @@ static HookFunction hookFunction([] ()
237272

238273
hook::jump(location + *(int32_t*)location + 4, DoesLevelHashMatch);
239274
}
275+
#else
276+
char* levelCaller = hook::get_pattern<char>("F3 0F 10 0D ? ? ? ? 48 8D 0D ? ? ? ? 83 25", 61);
277+
char* levelByIndex = hook::get_call(levelCaller);
278+
279+
hook::set_call(&g_origLoadLevelByIndex, levelCaller);
280+
hook::call(levelCaller, DoLoadLevel);
281+
282+
g_loadLevel = (decltype(g_loadLevel))hook::get_pattern("B9 ? ? ? ? E8 ? ? ? ? 40 88 7C 24 ? 48 8D 0D", -0x3B);
283+
284+
// change set condition evaluator's $level variable comparer
285+
{
286+
char* location = hook::pattern("48 8D 4C 24 ? 48 8B DA E8 ? ? ? ? 8B 0B").count(1).get(0).get<char>(-5);
287+
hook::trampoline(location, DoesLevelHashMatch);
288+
}
289+
290+
// change set applicability
291+
hook::jump(hook::pattern("4C 8B F1 B3 ? 76").count(1).get(0).get<void>(-0x21), IsLevelApplicable);
292+
293+
g_levelIndex = hook::get_address<int*>(hook::get_pattern<int>("8B 15 ? ? ? ? 0F 29 45 ? E8", 2));
294+
#endif
240295
});
241296

242297
enum class Mode : uint8_t
@@ -394,15 +449,17 @@ static InitFunction initFunction([] ()
394449
LoadLevel(level.c_str(), Mode::LEVEL_LOAD);
395450
});
396451

452+
#ifdef GTA_FIVE
397453
static ConsoleCommand storyModeyCommand("storymode", []()
398454
{
399-
LoadLevel("gta5", Mode::STORY_MODE);
455+
LoadLevel(LEVEL_NAME, Mode::STORY_MODE);
400456
});
401457

402458
static ConsoleCommand editorModeCommand("replayEditor", []()
403459
{
404-
LoadLevel("gta5", Mode::EDITOR_MODE);
460+
LoadLevel(LEVEL_NAME, Mode::EDITOR_MODE);
405461
});
462+
#endif
406463

407464
static ConsoleCommand localGameCommand("localGame", [](const std::string& resourceDir)
408465
{
@@ -491,7 +548,7 @@ static InitFunction initFunction([] ()
491548
res->Start();
492549
});
493550

494-
LoadLevel("gta5", Mode::LOCAL_MODE);
551+
LoadLevel(LEVEL_NAME, Mode::LOCAL_MODE);
495552
});
496553

497554
static ConsoleCommand loadLevelCommand2("invoke-levelload", [](const std::string& level)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
return function()
2+
filter {}
3+
4+
files {
5+
'components/citizen-level-loader-five/src/LevelLoader.cpp',
6+
}
7+
end

0 commit comments

Comments
 (0)