Jump to content

Highlander Mods: Modding Native Classes and Core Packages (XComGame.upk, etc)


abeclancy

Recommended Posts

Important Notice!

Most mods do not need to know any of this! They can function without modding core packages at all, and should not do so! Do not modify the core packages in your mod unless you HAVE to!

If you're looking for a "My First Weapon Mod" tutorial, look at the pinned Mod Resources thread by davidallen. This post contains information for what you might consider "advanced" modding.

A normal mod only has a single package, say YourMod.u. Combined with the INI files for the mod, this could do anything, like add in a new weapon, new enemy types, what-have-you. But eventually, some hardcore modders will want to do more. Change the way the game functions at a core level. Add multiplayer co-op support. Things that are drastic. For those modders, they may need to modify core game packages, like Core.upk, XComGame.upk, etc.

Why would they need to do this? Because any class in the game that contains a "native" class declaration cannot be extended using XCOM 2's built-in mod support, and must be modified as part of a Highlander mod (as far as I know). The problem is there can only be one such mod installed by the game at any one time (hence, Highlander). So two mods that both modify the core packages cannot be used together.

As such, your mod will be more usable if it doesn't modify these core packages! Try and build your mod without editing core packages. But, if you need to, read this for steps and information regarding the building and cooking process of core packages.

 

[/line]

Notices

Core mods are currently only supported via manual install. You cannot get a core mod distributed through the Steam Workshop! FxsRMcFall explained it to me as thus: The Steam API loads after most of the game engine has been loaded into memory, so it's too late to override core classes.

Intro

I've been doing a lot of experiments, fixes, and tests on trying to be able to mod MORE of the game. XComGame, core packages, cooking everything so they're small and light. This is going to be my little instruction set and notes for building as much of the game as I know how.

Big thanks to FxsRMcFall for his assistance in figuring out the exact cooking arguments needed to get this working!

By the end of this instruction/note sheet, you should be able to compile XComGame, Core, or any of the other base UPK packages that ship with the game, without requiring the game to be set up to run debug builds, and without the game needing to have the -NOSEEKFREELOADING argument set. HOWEVER, please note that only a single mod can override a default package. So if your mod doesn't require it, don't do it! If you are making a new weapon, class, mission type, you should be able to do everything you want without modifying one of these base packages.

Additionally, be VERY careful about what you change! If you limit yourself to modifying functions, you shouldn't run in to data compatibility issues, but even then any modifications that you make might have unintended consequences if another mod is running at the same time. Here's what FxsRMcFall has to say about it:

Mods that replace XComGame are the highlander of mods, there can only be one. But it lets you do pretty much whatever you want with the script source - don't need to use class replacements, screen listeners, et. al. Also, regular mods that just add new script packages can still work with an XComGame replacement mod - but they would be effectively modding a mod so it could get weird unless they use your mod's source code as a base. "Weird" could include crashes if you start renaming or deleting variables or functions in the base classes that another mod's complied script code is using.

Lastly, be very careful about making changes to base game classes that are part of the cooked data. One of the reasons that cooked data loading is so much faster is that it doesn't do any data type checking / version checking / tagged serialization / etc. So if you modify the data layout of a cooked object with script changes and don't update the cooked data then the object will serialize in using the old data layout. This causes random memory overwrites, crashes. So best to leave actors in the cooked data ( ie. stuff like XComLevelActor ) alone, but if you do make changes stick to just function changes and transient variables if you add variables to the class.

 

[/line]

Standard Building

Download the XCOM 2 Development Tools (XCOM 2 SDK) from Steam. It's about a 65GB download. I would also recommend making a copy of the XCOM 2 SDK directory (and even the XCOM 2 directory as well) because the building and debugging processes CAN make modifications to these installation directories, and it's a good idea before you release your final mod version, to restore your XCOM 2 directory from the backup and try to download your mod from the Steam Workshop (or install it manually as via Nexus), just to make sure that everything works correctly.

(Note however, that because of the instructions from FxsRMcFall, the building and cooking processes shouldn't modify any XCOM 2 vanilla files anymore in these instructions, so it isn't really necessary to have copies of these directories anymore. It could still be useful to have however, especially if you are testing building and cooking code.)

The mod building process is as follows. Some of these processes could easily be automated, but that is not the purpose of this, so automation will not be discussed! I will also be going in to details as I see fit, so my apologies if this is a bit long.

  • 1. You create a mod in ModBuddy.

The Default Mod example project starts with a copy of all of the XComGame class files, but this is unnecessary. A mod only needs to have the class files that it modifies: every other files is automatically copied to the build directory in its original state, so there is no need to have it in your project if it is unchanged.

  • 2. When you start a build, the existing \XCOM 2\XComGame\Mods\YourMod and \XCOM 2 SDK\XComGame\Mods\YourMod directories are deleted.

This process is flaky. It sometimes will work, and sometimes won't work. So, I just delete these two directories manually before each build, because that's the most reliable. If someone was to modify the build process, it would be smart to put in better deletion steps here.

  • 3. Your project is copied from your project directory to \XCOM 2 SDK\XComGame\Mods\YourMod.

Everything is copied as-is, except for TXT files which are dumped in to your root \Mods\YourMod directory. It's worth noting, that the XCOM2.targets file (there are actually two in \XCOM 2 SDK\Binaries subdirectories) contains the build code that is responsible for this. You might THINK that a simple solution, would be to modify the files to copy the TXT files without flattening them to the root directory. You would be wrong to think this.

 

files:

  • \steamapps\common\XCOM 2 SDK\Binaries\Win32\ModBuddy\XCOM2.targets
  • \steamapps\common\XCOM 2 SDK\Binaries\Win32\ModBuddy\Extensions\Application\XCOM2.targets

from:

<TextFiles Include="@(Content)" Condition="'%(Extension)' == '.txt'">
  <TargetDir>$(OutputDir)</TargetDir>
  <TargetPath>$(OutputDir)%(Filename)%(Extension)</TargetPath>
</TextFiles>

to:

<TextFiles Include="@(Content)" Condition="'%(Extension)' == '.txt'">
  <TargetDir>$(OutputDir)%(RelativeDir)</TargetDir>
  <TargetPath>$(OutputDir)%(Identity)</TargetPath>
</TextFiles>

Should work, but doesn't. Unfortunately whenever I tried to make this modification to prevent the TXT files from being moved around, the build would fail, for an incredibly strange reason: The \XCOM 2 SDK\XComGame\Mods\YourMod\Script folder would be deleted before the newly created packs could be moved in to that folder.

And I really have no idea why this is the case. I checked the decompiled XCOM2.Tasks.dll file to identify the exact code that is responsible for this, and it *should* be working, but for some reason isn't:

base.Log.LogMessage(MessageImportance.High, "\n\nSUCCESS! Copying compiled script binaries...\n", new object[0]);
string text10 = this.OutputDir + "Script\\";
if (Directory.Exists(text10)) {
  Directory.Delete(text10, true);
}
Directory.CreateDirectory(text10);

It deletes the directory then recreates it. But for some strange reason, that XCOM2.targets file modification results in this apparently failing to recreate the directory that it just deletes. If anyone has any idea what's up with this, please let me know, because I'd like for text files to stay put in my projects if at all possible.

  • 4. The \XCOM 2 SDK\Development\SrcOrig directory is copied to \XCOM 2 SDK\Development\Src, overwriting anything that's there.

This is just to refresh the build directory. Anything that shouldn't be there is deleted. Anything that was modified is restored. The Windows copy command xcopy is used here.

  • 5. *.uc files in the \YourProject\Src directory are scanned and copied to \XCOM 2 SDK\Development\Src for compilation.

You can have a ton of *.uc files in \YourProject\Src directory, especially if you use the Default Mod example. Thing is, the build process doesn't copy *every* *.uc file to the build directory. It only copies a file over if it does not have a read-only attribute (like the operating system's read-only attribute). This is why all class files in the Default Mod project are read-only. As soon as you save the file, the next time you build it will be copied.

 

This process of copying files over to the build directory serves another purpose. Any file that is not read-only and copied over "marks" the package the file belongs to as being a part of your mod. So, just having the \XComGame\Classes files in your project won't make a package a part of your final build, but having a modified (non read-only) file will.

 

However, even though you might not have an XComGame.u package in your build, you will still have all the source files copied over to your mod's build directory for safe-keeping. As such, it is very much recommended that you do not have unused, unmodified source files in your project. All of the game's source files can be found in \XCOM 2 SDK\Development\SrcOrig, so if you need a file you can always import it in to your project from there. But try to keep your project to only necessary, modified files.

 

So, even if you are only modifying a single file from XComGame, you can just have that single file in \YourProject\Src\XComGame\Classes, and your build will work fine, and it will be as lean as possible. It will also be easier to update your code, in the case that the game receives an official update, because you will know that your changes are limited to the files within your project, not potentially every file in the game.

 

There's another aspect to this step that needs discussion. Only *.uc files that are not read-only are moved to the \Development\Src directory for compilation. Unfortunately, this ignores *.uci files, which are valid source code files for the game! For example, the \Development\SrcOrig\Core\Globals.uci file, if modified and included in your project, will be completely ignored by the build process!

I imagine this is not expected behavior, but there is a workaround so that these files are processed by the build process. In order to resolve this, I used a decompiler to modify the XCOM2.Tasks.dll file that ModBuddy uses to build the project. I changed it enough so that it will search for both extensions, *.uc and *.uci files, which should be all of the file types in the \Development\SrcOrig directory. So, with this modification, anyone will be able to have *.uci files in their projects and have them properly compile with everything else.

I'm not sure if posting a link to this custom-modified file (\XCOM 2 SDK\Binaries\Win32\ModBuddy\Extensions\Application\XCOM2.Tasks.dll 18KB) would violate any of Nexus's rules, but I'd consider this more of a bug fix than anything else. As far as I can tell, the build process SHOULD be copying the *.uci files, but it isn't, so your builds will fail if you try to modify *.uci files in your project. If someone gives me the go-ahead, I'd be more than happy to include a link to the file, that people can drop in to replace the existing version, which should fix this little issue.

Now you might also think that it doesn't matter, or that you shouldn't be modifying *.uci files in the first place because those globals could be used by other packages that would need to be recompiled since it's really just a bit list of preprocessor define statements. The fact is though that someone MIGHT want to do this in the future, for a massive mod that touches many parts of the game code. So I'm putting this here to help that person if they need their build process to copy *.uci files. These instructions are for those people who want to modify as much of the game as possible, and I'd like to help make their lives a little easier.

However, there is one point that should be made. Since preprocessor definitions are baked in to any package that references it, an external mod that is compiled using the vanilla proprocessor definitions will not use your modified definitions. This could either matter little, or make the external mod completely incompatible with your new core packages. Just know what you are doing and the potential consequences of your modifications before you start editing things.

  • 6. The \Development\Src directory is compiled, all packages are created and placed in \XCOM 2 SDK\XComGame\Script, and any packages "marked" in step 4 are moved to \XCOM 2 SDK\XComGame\Mods\YourMod\Script.

This is as it sounds, the build is performed, and ONLY those packages that were "marked" (eg have non read-only source files in your project) are copied to your mod's build directory. This is useful as you can have any form of external automation you want detect which packages are included in your project via their presence in that directory.

 

If you are modifying the global proprocessor statements in *.uci or other files, you might want to forcibly make the build process "mark" all of the game's core packages so they are recompiled against your changes and are included in your mod build output. I haven't tested this, but you might be able to do this simply by creating empty *.uci files in your project's \Src directory, one for each of the core packages.

  • 7. If the build is compiled without errors, then \XCOM 2 SDK\XComGame\Mods\YourMod is copied to \XCOM 2\XComGame\Mods\YourMod.

I think it just copies over the entire directory, but everything that was modified or created by the build process is contained within this location. If your build does NOT modify any core packages, then you can just run the game without -NOSEEKFREELOAD and it should run your mod.

 

If your mod contains core packages (XComGame.u, Core.u, etc), then the game will run, but not with these modified core packages loaded. If your mod also contains a non-core package (YourMod.u), then THAT package WILL be loaded by the game, but the game might crash if you've added functions or expected behaviors to the modified core packages that are not present, because of course they didn't load.

 

You can get your modified core packages to load in to the game right now for testing purposes quite easily. But, before you publish your mod, you will need to do a proper cook of these modified core packages (discussed later).

 

To get these packages loaded for testing right now, start by making sure that the following four symlinks are created. Just open a Windows CMD prompt as an administrator, and run the following four commands, making sure to change the directories appropriately.

  • mklink /J "E:\SteamGames\steamapps\common\XCOM 2\XComGame\Content" "E:\SteamGames\steamapps\common\XCOM 2 SDK\XComGame\Content"

  • mklink /J "E:\SteamGames\steamapps\common\XCOM 2\XComGame\Script" "E:\SteamGames\steamapps\common\XCOM 2 SDK\XComGame\Script"

  • mklink /J "E:\SteamGames\steamapps\common\XCOM 2\Engine\Content" "E:\SteamGames\steamapps\common\XCOM 2 SDK\Engine\Content"

  • mklink /J "E:\SteamGames\steamapps\common\XCOM 2\Engine\EditorResources" "E:\SteamGames\steamapps\common\XCOM 2 SDK\Engine\EditorResources"

Then, just make sure that you launch the game with the additional launch argument "-NOSEEKFREELOADING" (no quotes) specified. You can do this through Steam by right clicking XCOM 2, selecting Properties, then selecting Set Launch Options. I like launching the game through Steam, because if you use ModBuddy to "Start Debugging" and launch XCOM, it will require that it rebuild your mod, even if it was already built. Launching through Steam means that you can launch it whenever the heck you want.

 

But, to be thorough, if you want to launch through ModBuddy's Start Debugging menu option, to get XCOM 2 to launch with the "-NOSEEKFREELOADING" argument, you'll have to make a quick modification. Just edit the file \XCOM 2\Binaries\Win64\Launcher\StartDebugging.bat, and add the argument to the end of the first line in the file. (More on this later too.)

 

At this point, you should be able to launch XCOM 2, and the game should load both your custom non-core packages AND custom core packages. You can test this easily by putting in log statements in the XComGame classes in places, and view the main log file to make sure it's working. The Launch.log file is located at: %USERPROFILE%\Documents\My Games\XCOM2\XComGame\Logs. %USERPROFILE% is a Windows directory shortcut to C:\Users\UserName.

 

(Note that log messages might not appear in the Launch.log file when you eventually do a final release version of your mod. There's a section on logging in this document with more information.)

One final thing to note: The StartDebugging.bat file that I mentioned earlier, about adding the "-NOSEEKFREELOADING" argument to, actually adds this argument on its own, but only if you build using the Debug configuration that we just created, and launch the game via Start Debugging in ModBuddy. Whenever you run a build in Default, that argument is removed from the BAT file. You can use this to your advantage by performing your normal testing in the Debug configuration, and just switch to the Default configuration to test the cooked final version of your mod (more on cooking below). This way, you never have to worry about setting this argument yourself, because it's handled automatically! (You'll still have to add the argument yourself if you launch through Steam, this only affects the Start Debugging option in ModBuddy.)

But at the very least, just keep this in mind. If your mod isn't working, or the game is crashing while you are testing it, or you can't figure out what's wrong, just CHECK to see whether this argument is set. You can check via the Launch.log file. There's a line early in the file that tells your the arguments used to run the game. For example: "Init: Command line: -FROMLAUNCHER -noRedScreens -review -LANGUAGE=INT". Before you release your mod to the public, MAKE SURE that it runs without this argument and is properly cooked!

And in fact, before you release your mod to the public, make sure to also remove those symlinks that we created earlier (if you used them). You can just use Windows Explorer, go to the directory, and delete the folders (they'll have a little arrow/shortcut icon on them). You're not deleting the folders, just deleting the symlink, and you can always recreate it with the commands previously listed. Do all your final testing in as vanilla an XCOM 2 environment as possible.

[/line]

Debugging

If you want to properly debug the Unreal Script code (all the *.uc files), you can. First, in ModBuddy open the Configuration Manager (either the Default or XCOM 2 dropdowns at the top will lead you there). In the Manager, select Active Solution Configuration > New, copy from Default, and call it Debug. Now, you can switch between Default (which performs Release building) and Debug (which performs Debug building). If you ever want to debug with the debugger, you HAVE to build using the Debug option. But otherwise, there is no other change to the project you need to worry about.

 

The second thing you need to do, is download the debugger itself. The one recommended in the development docs in the XCOM 2 SDK is the link below.

 

https://code.google.com/archive/p/unreal-debugger/downloads

 

Download the latest 64-bit build, unzip it, and copy all the DLLs contained into the \XCOM 2\Binaries\Win64 directory. The next time you build a Debug version of your mod and run XCOM, the debugger should just pop up on its own. The debugger automatically pauses program execution when it is loaded, doing so in the function XComGameViewportClient.Init(). You will probably have to manually locate the \XCOM 2 SDK\Development\Src directory in the debugger, but you should only have to do it once.

 

A couple of notes regarding debugging.

 

If an object instance cannot be expanded in the variables panels, try turning it in to a watch list variable. For example, if you have an array of objects, each element of the array won't be expandable, but if you convert each element in the variables panel to a watch list variable, then it can be expanded from there.

 

Interface instances cannot be expanded like objects can. In the code, you'll have to convert it to an actual object instance variable in order to expand it in the debugger.

[/line]

Cooking Final Version

Now, let's say you are making a BIG mod for XCOM 2. You've modified XComGame.u, added a ton of content, whatever. You want it to work easily, to either download via the Steam Workshop or unzip to the \XCOM 2\XComGame\Mods directory and have it Just Work. There IS a way to do so. It involves creating a "final release" build of any core packages, and cooking them.

The process to do this is actually relatively straightforward, but until recently was beyond even my immense reach (/s). But big thanks you to FxsRMcFall again for providing me with the key missing elements from the building and cooking processes that lets this work.

The process for building and cooking a "final release" version of the core packages that Just Works as a standalone mod are as follows.

  • 1. Make a Default build of your mod.

This part should be obvious, you need something to work with. But this also is important in putting all of your custom source code into the \XCOM 2 SDK\Development\Src directory, which even after your mod is built, will be used to re-build the core packages that NEED to be cooked. Your non-core packages don't need to be cooked.

  • 2. Run the following command:

  • "E:\SteamGames\steamapps\common\XCOM 2 SDK\Binaries\Win64\XComGame.exe" make -final_release -full

This takes all of the \Src code, and builds it as a final release build. If you are given any prompts indicated that certain files may need to be updated, just accept them.

 

When the build process says that the commandlet has finished, just CTRL-C to close the window. ESC, space, anykey, doesn't work to close the window after the build, so I just have to close it a little bit more forcibly. Not really sure how you would go about automating this part of the build process, but whatever, not my job.

 

What you should end up with, is a new directory \XCOM 2 SDK\XComGame\ScriptFinalRelease. This contains all the core packages in uncooked, *.u formats.

  • 3. Run the following command:

  • "E:\SteamGames\steamapps\common\XCOM 2 SDK\Binaries\Win64\XComGame.exe" CookPackages -platform=pcconsole -final_release -quickanddirty -modcook -sha -multilanguagecook=INT+FRA+ITA+DEU+RUS+POL+KOR+ESN -singlethread

This takes these uncooked packages, and cooks them in a format that the game will be able to load naturally. This also strips out source code from the packages, and optimizes them for the game.

 

This creates output in the directory \XCOM 2 SDK\XComGame\Published\CookedPCConsole. There will be *.upk and *.upk.uncompressed_size files for each of the game's core packages, and a smattering of other files.

  • 4. Move the cooked packages that you need, put them in a new \CookedPCConsole directory in your mod output folder, and delete the corresponding \Script\*.u files.

This one might need a slight bit of explaining and an example (XComGame). If you have custom code in the \XComGame\Classes source in your project, then at this point in the build-and-cook process, take the resulting XComGame.upk and upk.uncompressed_size files from the \XCOM 2 SDK\XComGame\Published\CookedPCConsole directory, and put them in the directory \XCOM 2\XComGame\Mods\YourMod\CookedPCConsole. This \Mods\YourMod folder was created as part of the build process, and houses your current build. Delete the XComGame.u file from \Mods\YourMod\Script, since we're replacing it with the cooked one.

 

You have now created a final, cooked package that the game should run! Make sure that no symlinks exists, no "-NOSEEKFREELOADING" argument is specified when you run the game. This is where you can test your mod in a pure vanilla XCOM 2 installation, so verify your files in Steam, restore from a backup copy, whatever you want.

 

HOWEVER... Does the game crash when you try to start it up? Likely that's normal, and something we only have to fix once. The Launch.log file should indicate something like the following (exact numbers, files, error codes will vary):

[0015.50] Warning: Warning, Detected data corruption [header] trying to read 1048576 bytes at offset 10129142 from '..\..\XComGame\CookedPCConsole\World-a.tfc'. Please delete file and recook.
[0015.50] Critical: appError called: Assertion failed: appErrorf [File:G:\BuildAgent\work\a78b974cadaf0436\branches\Shipping\XCOM2\Development\Src\Core\Src\UnVcWin32.cpp] [Line: 150]
I/O failure operating on '..\..\XComGame\CookedPCConsole\World-a.tfc'
Stack: Address = 0x4029dad5 (filename not found) [in E:\SteamGames\steamapps\common\XCOM 2\Binaries\Win64\XCom2.exe]

When we cooked the \ScriptFinalRelease packages, we didn't prepare some needed information, so the cooking process didn't know what was in these files (the *.tfc ones). We can fix this by copying the following files (if they exist) from \XCOM 2\XComGame\CookedPCConsole into \XCOM 2 SDK\XComGame\Published\CookedPCConsole:

  • GuidCache.upk

  • GlobalPersistentCookerData.upk

  • PersistentCookerShaderData.bin

  • *.tfc

If you can't find some of these files, it's fine. Copy what's there, then just go back to step (3) above. You should be able to do the process again, and get a working build that doesn't crash the game.

 

And that should be it! You should have custom core packages, running naturally in a vanilla installation of XCOM 2, without symlinks or launcher arguments. If you packages it all up, you should be able to distribute on Steam and Nexus.

 

A few notes: 1) If you package to Steam via ModBuddy, I'm not sure right now where it pulls the files from when it uploads, so just make sure that your final final build is located in both \XCOM 2\XComGame\Mods and \XCOM 2 SDK\XComGame\Mods. 2) Source code is, by default, uploaded with your package. If you don't want source code released, just delete it from the \Mods\YourMod directories. Source code is included not only in the \Src directory, but also all the \Script\*.u files have source code embedded within them. If you don't want it there, cook it out.

 

3) I haven't tested doing final-release cooking of your own non-core mod packages, but theoretically it should be very easy to do. Since your non-core packages are, well, non-core, you should just be able to cook them however you want and they should be able to load. Likely, running something similar to the following command should work:

 

XComGame.exe CookPackages YourMod.u -platform=pc -usermode -singlethread

 

I'm not an Unreal guy, so frankly I have no idea how to optimize this cook statement to work better. So take note of a couple things regarding this cook command. 1) It will take a long time, and copy about 4GB of unnecessary files to the output directory \XCOM 2 SDK\XComGame\CookedPCConsole. It almost assuredly could be optimized to cook JUST the package you want, but I don't know how right now. 2) Since it outputs stuff to this directory, MAKE SURE that it isn't symlinked to the \XCOM 2\ main directory, otherwise you will likely overwrite vanilla content. Instead, just rename the symlink directory, perform the manual cook command, then rename it back and just take the one resulting file you need.

 

If you know how to make a proper cook statement, or how to include YourMod.u to be built as a final release version and cooked, let me know and I'll update this section.

 

[/line]

 

Logging

 

So, normally you would add log statements to UC code by using `log("..."); or `warn("...");, which are preprocessor macros. Either will work to put a log message to the Launch.log file that the game uses, but if you are making a final-release build version of the core package (or even a final-release build version of a mod package), then both of these macros will be removed from the final code output.

 

However, one way that I've found that allows you to write messages to the log files regardless of build release, is to use a particular native function that allows you to write to a filtered log file directly, using C++ instead of the Unreal scripting:

`CHEATMGR.WriteToFilteredLogFile("To Hit: " $ ToHitValue, 'XCom_HitRolls');

Unfortunately, this is extremely unreliable, as there appears to be no way to modify the logging filters (editing the filters in the DefaultEngine.ini file don't seem to work). And some of the existing filters (such as XCom_CombatLog) only will write to the log file during a tactical mission!

 

The best way in practice that I have found to do logging, is actually to remove the `{prevent_direct_calls} definitions in the LogInternal function in Object.uc in the Core package (WarnInternal doesn't seem to work). By calling class'Object'.static.LogInternal() directly, log messages appear in the log files correctly. This is basically the only way that I have found that reliably and predictably gives me log messages.

 

However, do note that using this to do logging means that log messages are not disabled in release builds! Logging always has a performance impact, which is why `log() and `warn() are removed from final release builds in the first place! Each of your log messages, if present during a final release build, will have a minor performance impact on the running game, so keep logging to an absolute minimum on release builds! You can always place some preprocessor conditionals around your log statements so that they can be removed during final release builds.

`if(`isdefined(FINAL_RELEASE))
  class'Object'.static.LogInternal(GeneratePerformanceHeavyLoggingString());
`endif

[/line]

 

Localization/Localizing Text

 

Localizing text is done by creating a file in the Localization/ directory of your mod project, with the name "XComPackage.int". "Package" is the name of your mod package, and "int" is both the default localized text language and the default english text language. You can create multiple files with different extensions to support different languages. So, for example, if your mod package is YourProject, your localization filenames would be "XComYourProject.int". Then, just place your class names in the [section] declarations, and assign the strings.

 

Note that the localization file is just "XComGame.int" for the main core package. All other mod packages that are not a core package will be prepended with "XCom".

[ACustomClass]
ALocalizedStringVariable=The text to display
ALocalizedStringVariableQuoted="some additional text to display"

Note that the file format for these localization files should be, as far as I know, "UCS-2 Little Endian".

 

Your class can declare that variables are localized with the "localized" keyword.

class ACustomClass extends UIScreenListener config(YourProject);
var config bool EnableCustomClassMenu;
var localized string ALocalizedStringVariable;
var localized string ALocalizedStringVariableQuoted;

The thing that will probably kick you in the ass and make you pull out your hair in frustration and "WTF THIS SHOULD BE WORKING!!??!?!", is that in order for localized text to be loaded, the class must also load something from a configuration file. You can do this simply by adding a single config variable to the class and load it via the config file. I don't know why this works, but as soon as I added a single config variable to the class the localized text started to load properly. So, in XComYourProject.ini, just add some configuration option like:

[YourProject.ACustomClass]
EnableCustomClassMenu=true

If anyone knows of a way to disassociate this seeming requirement of having a config option in order to have localized text, let me know. But until then, my rule of thumb is to load a config variable in any class that has localized text. It appears to not even be enough to just have a config variable be declared in the class, there MUST be at least one variable loaded from a config file in order for ANY localized text to be loaded. I haven't seen this mentioned anywhere, so I thought I would include this here in case anyone has issues and reads it.

 

[/line]

 

Configuration Files

 

So I'll be blunt: I might be an idiot and not know how to properly use configuration files in UnrealScript. But I do have a semi-solution that I use that seems to work, so here it is.

 

In the below text:

Mod-C = Mod-Configuration, either the class, directory, or file, depending on context (XCOM 2/XComGame/Mods/YourProject/Config/).

User-C = User-Configuration, either the class, directory, or file, depending on context (User/My Documents/My Games/XCOM2/XComGame/Config/).

 

There seems to be issues with loading default configuration options from Mod-C, and saving them to User-C. Here is a little bit of testing that I did. A class tries to load configuration from Mod-C (by not having a configuration file present in User-C), and call SaveConfig(), with the goal of writing a new configuration file in User-C. The idea is to load reasonable defaults from a configuration file, while also saving actual, current, runtime configuration to the user's directory so it is saved over time. The only difference between these tests, is whether there was a configuration file present in Mod-C, User-C, both, or neither.

Files Present        Reads From   Writes To
Mod-Config/*.ini      Mod-C        nothing
User-Config/*.ini     User-C       User-C
Neither               nothing      User-C
Both                  Mod-C        nothing

So there actually doesn't seem to be a simple way of loading from Mod-C and saving from User-C from a single class instance! Weird!

 

I haven't looked in to StaticSaveConfig() all that much, because I don't really plan on needing to write to the mod's Config/ directory when the user's Config/ directory is available. It's honestly been a little while since I ran those tests, but I don't remember the StaticSaveConfig() even being able to write anything at all.

 

So, this has put me in a weird position where I honestly couldn't figure out how to make a single class read from Mod-C and write to User-C.

 

What I ended up doing was just create a configuration base class, and extend it twice, once for Mod-Config loading and once for User-Config saving:

class BaseConfig extends Object config(DoesntMatter);
var config bool IS_LOADED;
var config bool SOME_OPTION;


class ModConfig extends BaseConfig config(ActualConfigFile_Default);


class UserConfig extends BaseConfig config(ActualConfigFile);


function BaseConfig LoadConfig() {
  ModC = new class'ModConfig';
  UserC = new class'UserConfig';
  if (!UserC.IS_LOADED) {
    if (!ModC.IS_LOADED) {
      `log("No configuration available!");
      // could load reasonable defaults instead of failing
      //UserC.LoadReasonableDefaults();
      //UserC.SaveConfig();
      return None;
    } else {
      UserC.LoadFrom(ModC);
      UserC.SaveConfig();
    }
  }
  return UserC;
}

If the Mod-C configuration file is pre-created and called XComGameActualConfigFile_Default.ini, then the User-C configuration file (called XComGameActualConfigFile.ini) will load from itself if it exists, or manually load from the Mod-C if the User-C does not exist. Just make sure that IS_LOADED=true, which is the main flag that indicates that the configuration was loaded from the particular directory successfully.

 

This feels like a bit of a hacky solution, like I am missing something in UnrealScript that isn't properly documented, or maybe some of the C++ code would shed light on all this.

 

[/line]

 

The End

 

And that should be it! Should be all the information I've gathered regarding building and cooking the core packages. I'm not sure there's any mods out there doing this yet, but again, users can only have a single mod installed at a time that modifies core packages. Take note that you inform users of this fact when you make and upload your mod!

 

And once again, be careful with creating a mod that modifies core packages! Your chances could break other mods if they try to run with your mod installed. With great power comes great responsibility, and it is your responsibility to understand how your changes to the game code will affect players and other mods.

Edited by abeclancy
Link to comment
Share on other sites

This thread is also listed in the pinned guide to guides. If you feel the guide to the guides could be organized differently or better, please PM me your thoughts.

David, didn't realize you had updated that thread with all of this. Good to know. Last time I checked yours, there were six tutorials. Thanks for keeping it up to date.

Link to comment
Share on other sites

  • 1 month later...

I've been fooling around the thing for a week now, I was finally settled to try this but after modding the native classes I ran into this error:

Error, Unable to create object of type 'XComGameState_Unit': native class layout differs from script layout.  Please build C++ before continuing to avoid data corruption.

Any clues how I could do that? Do I need to compile some DLL's with Visual Studio (only remote reference to this issue I could find was from Civ 5 where they had to compile DLL's) or am I just blind?

Link to comment
Share on other sites

Ah, it appears that the reason is quite simple. I had garbage from previous run in E:\SteamLibrary\steamapps\common\XCOM 2 SDK\Development\Src

 

Removing the folder seems to have resolved the issue as it gets recreated with all the contents.

 

It's good that I didn't spend hours on this and then figure it out right after posting a post about it /s.

 

EDIT: I've been a gigantic moron. Obviously DefaultMod has release sources, you have to use latest patch source code if you're going to go native. You can find those from \SteamLibrary\steamapps\common\XCOM 2 SDK\Development\SrcOrig

Edited by Mythrell
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...