Skip to content

Instantly share code, notes, and snippets.

@pardeike
Last active January 1, 2024 09:49
Show Gist options
  • Save pardeike/08ff826bf40ee60452f02d85e59f32ff to your computer and use it in GitHub Desktop.
Save pardeike/08ff826bf40ee60452f02d85e59f32ff to your computer and use it in GitHub Desktop.
How to update your Mod from RimWorld from 1.0 to 1.1 and Harmony 1.2.0.1 to 2.0

Updating RimWorld from 1.0 to 1.1

Introduction

My name is Andreas Pardeike, aka Brrainz, or the creator of Harmony. This guide is my personal attempt to help everyone through the journey of upgrading their mods. I am not affiliated with LUDEON studios or with developing RimWorld. It's just a personal thank you from me to the community.

Scope

This guide will help modders to update their C# mod to RimWorld 1.1. I will tell you how you upgrade to Harmony 2 and why. It will also serve as a guide to common changes that I encountered while I upgraded my own Camera+ mod. During the guide, I will use Visual Studio 2019. It's community edition is free. If you use a different IDE, please adapt your changes.

While I am sure that there will be changes to the xml loading part and the fields and structures of RimWorld, this guide will not cover this. My focus is C# and patching the game with Harmony. For more details, ask your favorite discord admin or visit the Ludeon forums.

New Harmony

The release of RimWorld 1.1 is in sync with the release of Harmony 2. It introduces a few new features and contains all the bug fixes and improvements that have cumulated since v1.2.0.1. I did change the syntax of the API to be more consistent which means some renaming on your side.

🔴 IMPORTANT: Harmony 1.x and 2.x are not compatible with each other. The simplest way to solve this is to not allow Harmony 1.x on RimWorld 1.1. It's a clean cut. Since you need to touch your code and your project anyway, it seems logical to update Harmony too.

This guide will help you to make the transition as smooth as possible. More on that later.

About.xml

If you don't update the About.xml file, RimWorld 1.1 will show your mod in yellow text to indicate that it is not made for the new version:

The About.xml file is shared between RimWorld 1.0 and 1.1. Currently, this results in red (but harmless) error messages in RimWorld 1.0 but works for both versions. Here is what I used:

<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
   <name>Camera+</name>
   <author>Andreas Pardeike</author>
   <supportedVersions>
      <li>1.0</li>
      <li>1.1</li>
   </supportedVersions>
   <packageId>brrainz.cameraplus</packageId>
   <description>...</description>
</ModMetaData>

// Note: the first release of 1.1 had a new tag called supportedGameVersions but it was removed again

The only new thing new is the packageId which identifies your mod uniquely. RimWorld is pretty strict about the format so I chose a simple brrainz.cameraplus.

Folder Structure

RimWorld 1.1 mod folder builds on the previous structure that allowed for multiple versions. It is mostly the same but there are some changes that are specific for v1.1 and newer. There are several ways to configure it but I wanted to keep it simple and satisfy my OCD so I chose to keep my mod folder structure mostly the same, except for the new CameraPlus.dll and 0Harmony.dll that will be specific for RimWorld 1.1:

📦 CameraPlus
 ┃
 ┣ 📂 About					<-- One About folder only
 ┃ ┣ 📜 About.xml
 ┃ ┗ 📜 Preview.png
 ┃
 ┣ 📂 Assemblies				<-- RW1.0 assemblies (copied from old RW1.0 release)
 ┃ ┣ 📜 0Harmony.dll (1.2.0.1)
 ┃ ┗ 📜 CameraPlus.dll (for RW1.0)
 ┃
 ┣ 📂 Common					<-- I don't use the Common folder
 ┃
 ┣ 📂 Languages					<-- RW1.0
 ┣ 📂 Textures					<-- RW1.0
 ┣ 📂 other...					<-- RW1.0
 ┃
 ┣ 📂 v1.1					<-- This is the RW1.1 folder!
 ┃ ┗ 📂 Assemblies				<-- RW1.1 assemblies (compiled by mod project)
 ┃    ┣ 📜 0Harmony.dll (2.0)
 ┃    ┗ 📜 CameraPlus.dll (for RW1.1)
 ┃
 ┗📜 LoadFolders.xml				<-- Defines custom folders (I use it for 'v'1.1)

The LoadFolders.xml looks like this:

<loadFolders>
  <v1.1>
    <li>/</li>
    <li>v1.1</li>
  </v1.1>
</loadFolders>

RimWorld 1.1 per default looks for folders with the same version number (1.1) but I like my folders alphabetically relevant and chose a slightly different name: v1.1. So I use LoadFolders.xml to define a tag <v1.1/> that contains a list of folders to search for. The root <li>/</li> is required and then I add <li>v1.1</li> which points to my custom folder v1.1.

As a result, RimWorld 1.0 uses the root folders and RimWorld 1.1 finds my new Assemblies folder inside v1.1 and prefers it over the root Assemblies folder and re-uses all my different assets from before.

For more configuration possibilities, please have a look at the ModUpdating.txt document that comes with RimWorld.

Project Changes

Since RimWorld uses a new Unity version which comes with .NET 4.7.2. There are other source code changes too but since the .NET version has changed, you need to update your project. Here is how I did it, it depends on your version of Visual Studio project. You need to change your Visual Studio project from .NET 3.5 to .NET 4.7.2:

Old project format

If you are using the old project format, you can select the project in the Solution Explorer and then choose "Properties" from the right-click context menu to get to the project settings:

Change it to .NET Framework 4.7.2 and close/save the dialog.

New project format

For newer project formats, it is easiest to just edit the project file yourself. Right click on the project in the Solution Explorer and choose "Edit Project File" which opens an xml file. There you change the following then close and save the project:

<!--Old: <TargetFramework>net35</TargetFramework>-->
<TargetFramework>net472</TargetFramework>

Output path

You also want to change the output path of your mod dlls. Since I chose to have my 1.0 version fixed in the old Assemblies folder and the new version in v1.1\Assemblies I opened the project settings, clicked on Build and Configuration: All Configurations and changed the output path:

Change it to "..\v1.1\Assemblies\" and close/save the dialog.

References

Next, you will get broken references to all the Unity dlls you used in your project. RimWorld has a slightly different folder structure and the Unity API is spread over many dlls:

To fix this, remove the old references and add the new ones back. Unfortunately, this is harder than it looks because you now have 62 Unity dll's in C:\Program Files (x86)\Steam\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\. You can either hand pick them or add them all to your project. Picking them by hand takes time because you need to know where your API is located:

Unity.TextMeshPro.dll                        UnityEngine.ScreenCaptureModule.dll
UnityEngine.AccessibilityModule.dll          UnityEngine.SharedInternalsModule.dll
UnityEngine.AIModule.dll                     UnityEngine.SpriteMaskModule.dll
UnityEngine.AndroidJNIModule.dll             UnityEngine.SpriteShapeModule.dll
UnityEngine.AnimationModule.dll              UnityEngine.StreamingModule.dll
UnityEngine.ARModule.dll                     UnityEngine.SubstanceModule.dll
UnityEngine.AssetBundleModule.dll            UnityEngine.TerrainModule.dll
UnityEngine.AudioModule.dll                  UnityEngine.TerrainPhysicsModule.dll
UnityEngine.ClothModule.dll                  UnityEngine.TextCoreModule.dll
UnityEngine.ClusterInputModule.dll           UnityEngine.TextRenderingModule.dll
UnityEngine.ClusterRendererModule.dll        UnityEngine.TilemapModule.dll
UnityEngine.CoreModule.dll                   UnityEngine.TLSModule.dll
UnityEngine.CrashReportingModule.dll         UnityEngine.UI.dll
UnityEngine.DirectorModule.dll               UnityEngine.UIElementsModule.dll
UnityEngine.dll                              UnityEngine.UIModule.dll
UnityEngine.DSPGraphModule.dll               UnityEngine.UmbraModule.dll
UnityEngine.FileSystemHttpModule.dll         UnityEngine.UNETModule.dll
UnityEngine.GameCenterModule.dll             UnityEngine.UnityAnalyticsModule.dll
UnityEngine.GridModule.dll                   UnityEngine.UnityConnectModule.dll
UnityEngine.HotReloadModule.dll              UnityEngine.UnityTestProtocolModule.dll
UnityEngine.ImageConversionModule.dll        UnityEngine.UnityWebRequestAssetBundleModule.dll
UnityEngine.IMGUIModule.dll                  UnityEngine.UnityWebRequestAudioModule.dll
UnityEngine.InputLegacyModule.dll            UnityEngine.UnityWebRequestModule.dll
UnityEngine.InputModule.dll                  UnityEngine.UnityWebRequestTextureModule.dll
UnityEngine.JSONSerializeModule.dll          UnityEngine.UnityWebRequestWWWModule.dll
UnityEngine.LocalizationModule.dll           UnityEngine.VehiclesModule.dll
UnityEngine.ParticleSystemModule.dll         UnityEngine.VFXModule.dll
UnityEngine.PerformanceReportingModule.dll   UnityEngine.VideoModule.dll
UnityEngine.Physics2DModule.dll              UnityEngine.VRModule.dll
UnityEngine.PhysicsModule.dll                UnityEngine.WindModule.dll
UnityEngine.ProfilerModule.dll               UnityEngine.XRModule.dll

Hand picked dll's - for the pedantic

If you only want to include the dlls you really need, you can use dnSpy to open all the Unity dll's and then search for a given API. Don't forget that Unity now has a bunch of extension methods that are more often than not defined in their own extension dll. This step took me the longest time of all because it was sort of new and cumbersome but I learned a lot of how the API is structured. It will also help you when the method you did use no longer exists in the new Unity version. The decompiler is as usually your friend.

Updating Harmomy

Most likely, you are using Harmony via NuGet. If not, I recommend doing so. To update the reference from 1.2.0.1 to 2.0 you can simply to into the NuGet Manager for the solution

and there, choose Updates [1], select the checkbox at the Lib.Harmony row and choose Update:

If you have not used NuGet yet, Simply choose Browse and search for Lib.Harmony, select it, choose your project and then the Harmony 2.0 version and install it.

Harmony 1 => 2 Changes

Note: Official Harmony 2 documentation: https://harmony.pardeike.net

A few things in Harmony were renamed to make things more consistent. The main instance is now called Harmony instead of HarmonyInstance which requires that the namespace changes from Harmony to HarmonyLib. Also, methods that work globally are now static instead of instance methods:

Most common changes

// using Harmony;
using HarmonyLib;

// var harmony = HarmonyInstance.Create("net.pardeike.test");
var harmony = new Harmony("net.pardeike.test");

// HarmonyInstance.DEBUG = true;
Harmony.DEBUG = true;
// or better use the new annotation instead:
[HarmonyDebug]

// public DynamicMethod Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null)
public MethodInfo Patch(MethodBase original, HarmonyMethod prefix = null, HarmonyMethod postfix = null, HarmonyMethod transpiler = null, HarmonyMethod finalizer = null) {}

// public bool HasAnyPatches(string harmonyID)
public static bool HasAnyPatches(string harmonyID) {}

// public Patches GetPatchInfo(MethodBase method)
public static Patches GetPatchInfo(MethodBase method) {}

// public Dictionary<string, Version> VersionInfo(out Version currentVersion)
public static Dictionary<string, Version> VersionInfo(out Version currentVersion) {}

// public CodeInstruction Clone(OpCode opcode, object operand)
public CodeInstruction Clone(object operand) {}

// public static IEnumerable<T> Add<T>(this IEnumerable<T> sequence, T item)
public static IEnumerable<T> AddItem<T>(this IEnumerable<T> sequence, T item) {}

New API (partial)

// new annotation on classes or methods to debug those
[HarmonyDebug]

// reverse patching attribute
[HarmonyReversePatch]

// manual patching helper
public HarmonyMethod(MethodInfo method, int priority = -1, string[] before = null, string[] after = null, bool? debug = null) {}

// reverse patching an original onto your own stub method
public static MethodInfo ReversePatch(MethodBase original, HarmonyMethod standin, MethodInfo transpiler = null) {}

// manual patching but keeping the class structure
public PatchClassProcessor ProcessorForAnnotatedClass(Type type) {}

// simple transpiler helper
public static IEnumerable<CodeInstruction> Transpilers.Manipulator(this IEnumerable<CodeInstruction> instructions, Func<CodeInstruction, bool> predicate, Action<CodeInstruction> action) {}

AccessTools

// binding flags for declared members only:
allDeclared;

// getting all types from an assembly
public static Type[] GetTypesFromAssembly(Assembly assembly) {}

// fetching declared members
public static FieldInfo DeclaredField(Type type, string name) {}
public static FieldInfo DeclaredField(Type type, int idx) {}
public static bool IsDeclaredMember<T>(this T member) where T : MemberInfo {}
public static T GetDeclaredMember<T>(this T member) where T : MemberInfo {}

// restrict search to static constructor
public static ConstructorInfo Constructor(Type type, Type[] parameters = null, bool searchForStatic = false) {}
public static List<ConstructorInfo> GetDeclaredConstructors(Type type, bool? searchForStatic = null) {}

// FieldRef can take a FieldInfo
public static FieldRef<T, F> FieldRefAccess<T, F>(FieldInfo fieldInfo) {}

// FieldRef can now be used on statics
public delegate ref F FieldRef<T, F>(T obj = default);
public delegate ref F FieldRef<F>();
public static ref F StaticFieldRefAccess<T, F>(string fieldName) {}
public static FieldRef<F> StaticFieldRefAccess<F>(FieldInfo fieldInfo) {}

// new test for nullable
public static bool IsOfNullableType<T>(T instance) {}

// create hash from multiple objects
public static int CombinedHashCode(IEnumerable<object> objects) {}

Instead of listing all changes, I rather want users to go to https://harmony.pardeike.net and read the documentation I put up. Combine that with discovering the API by using your IDE's auto-completion and you will find that not much has changed. Harmony 2 is an evolution of Harmony 1 and does not touch basics.

Regarding refactoring an existing mod, you can assume that most of the remaining API has stayed the same: annotations, helper methods from Harmony 1 and manual patching still exist. New API has been added to a lot of places: AccessTools, Traverse and other classes have better separation between members that are inherited and those that are declared. New patch types have been added: Finalizers to wrap original methods into try/catch logic and Reverse Patches to reuse all or part of original methods in your own code.

RimWorld changes

The following are changes I discovered while refactoring my mods. It's not a complete list but I hope it helps and saves time for someone:

// CellRect.GetIterator()
CellRect.GetEnumerable()

// Toils_LayDown.GroundRestEffectiveness
StatDefOf.BedRestEffectiveness.valueIfMissing

// Verse.ContentSource.LocalFolder
Verse.ContentSource.ModsFolder

// ModContentPack.Identifier
ModContentPack.PackageId

// SoundDefOf.RadioButtonClicked
SoundDefOf.Checkbox_TurnedOff;
SoundDefOf.Checkbox_TurnedOn;
SoundDefOf.Click;

// CameraDriver.GUI
CameraDriver.CameraDriverOnGUI

// RenderPawnAt has an extra bool argument
[HarmonyPatch(typeof(PawnRenderer), "RenderPawnAt")]
[HarmonyPatch(new Type[] { typeof(Vector3), typeof(RotDrawMode), typeof(bool), typeof(bool) })]

// RenderPawnInternal has an extra bool argument
[HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal")]
[HarmonyPatch(new Type[] { typeof(Vector3), typeof(float), typeof(bool), typeof(Rot4), typeof(Rot4), typeof(RotDrawMode), typeof(bool), typeof(bool), typeof(bool) })]

// Verb.TryStartCastOn now has two overloads:
public bool TryStartCastOn(LocalTargetInfo, bool, bool)
public bool TryStartCastOn(LocalTargetInfo, LocalTargetInfo, bool, bool)

// EventType.repaint
EventType.Repaint

// TerrainDef.acceptTerrainSourceFilth
TerrainDef.filthAcceptanceMask

// FilthMaker.MakeFilth
FilthMaker.TryMakeFilth

// PawnDestinationReservationManager.RegisterFaction(newFaction);
PawnDestinationReservationManager.GetPawnDestinationSetFor(newFaction);

// string Alert.GetExplanation()
TaggedString Alert.GetExplanation()

// WorkGiver_Scanner.LocalRegionsToScanFirst
WorkGiver_Scanner.MaxRegionsToScanBeforeGlobalSearch

// pawn.story.WorkTagIsDisabled(worktag)
pawn.WorkTagIsDisabled(worktag)

// HasDebugOutputAttribute
// CategoryAttribute
// ModeRestrictionPlay
DebugOutputAttribute

// SculptingSpeed
// SmeltingSpeed
// SmithingSpeed
// TailorSpeed
GeneralLaborSpeed

// PartyUtility
GatheringsUtility

// JoyGiver.TryGiveJobInPartyArea
JoyGiver.TryGiveJobInGatheringArea

// JobGiver_EatInPartyArea
JobGiver_EatInGatheringArea

// ThinkNode_ConditionalInPartyArea
ThinkNode_ConditionalInGatheringArea

// JobGiver_GetJoyInPartyArea
JobGiver_GetJoyInGatheringArea

// JobGiver_WanderInPartyArea
JobGiver_WanderInGatheringArea

// FactionDef.backstoryCategories
FactionDef.backstoryFilters

// PawnKindDef.backstoryCategories
PawnKindDef.backstoryFilters

// PoisonSpreader
Defoliator

// PoisonSpreaderShipPart
DefoliatorShipPart

// new Job
JobMaker.MakeJob()

Thank You

If you made it this far, bravo! I have a little extra for you. I have that little build script that deploys the mod from scratch every time I build the project. It deploys to two locations at the same time: Steam/RW1.1 and Standalone/RW1.0 so I can test on both at the same time. Since it does not contain any configuration you can reuse it without modification in all your mod projects.

Just read the instructions in it and copy it into your project. Enjoy and happy modding!

/Brrainz
Andreas Pardeike
🐤 @pardeike
📣 Join my Discord
🎁 https://patreon.com/pardeike


Mod install script (Windows)

REM ################ Mod build and install script (c) Andreas Pardeike 2020 ################
REM
REM Call this script from Visual Studio's Build Events post-build event command line box:
REM "$(ProjectDir)Install.bat" $(ConfigurationName) "$(ProjectDir)" "$(ProjectName)" "About Assemblies Languages Textures v1.1" "LoadFolders.xml"
REM
REM The project structure should look like this:
REM
REM Modname
REM +- .git
REM +- .vs
REM +- About
REM |  +- About.xml
REM |  +- Preview.png
REM |  +- PublishedFileId.txt
REM +- Assemblies                      <----- this is for RW1.0 + Harmony 1
REM |  +- 0Harmony.dll
REM |  +- 0Harmony.dll.mbd
REM |  +- 0Harmony.pdb
REM |  +- Modname.dll
REM |  +- Modname.dll.mbd
REM |  +- Modname.pdb
REM +- Languages
REM +- packages
REM |  +- Lib.Harmony.2.x.x
REM +- Source
REM |  +- .vs
REM |  +- obj
REM |     +- Debug
REM |     +- Release
REM |  +- Properties
REM |  +- Modname.csproj
REM |  +- Modname.csproj.user
REM |  +- packages.config
REM |  +- Install.bat                  <----- this script
REM +- Textures
REM +- v1.1
REM |  +- Assemblies                   <----- this is for RW1.1 + Harmony 2
REM |     +- 0Harmony.dll
REM |     +- 0Harmony.dll.mbd
REM |     +- 0Harmony.pdb
REM |     +- Modname.dll
REM |     +- Modname.dll.mbd
REM |     +- Modname.pdb
REM +- .gitattributes
REM +- .gitignore
REM +- LICENSE
REM +- LoadFolders.xml
REM +- README.md
REM +- Modname.sln
REM
REM Also needed are the following environment variables in the system settings (example values):
REM
REM MONO_EXE = C:\Program Files\Mono\bin\mono.exe
REM PDB2MDB_PATH = C:\Program Files\Mono\lib\mono\4.5\pdb2mdb.exe
REM RIMWORLD_DIR_STEAM = C:\Program Files (x86)\Steam\steamapps\common\RimWorld
REM RIMWORLD_DIR_STANDALONE = %USERPROFILE%\RimWorld1-0-2408Win64
REM RIMWORLD_MOD_DEBUG = --debugger-agent=transport=dt_socket,address=127.0.0.1:56000,server=y
REM
REM Finally, configure Visual Studio's Debug configuration with the rimworld exe as an external
REM program and set the working directory to the directory containing the exe.
REM
REM To debug, build the project (this script will install the mod), then run "Debug" (F5) which
REM will start RimWorld in paused state. Finally, choose "Debug -> Attach Unity Debugger" and
REM press "Input IP" and accept the default 127.0.0.1 : 56000

@ECHO ON
SETLOCAL ENABLEDELAYEDEXPANSION

SET SOLUTION_DIR=%~2
SET SOLUTION_DIR=%SOLUTION_DIR:~0,-7%
SET TARGET_DIR=%RIMWORLD_DIR_STEAM%\Mods\%~3
SET TARGET_DEBUG_DIR=%RIMWORLD_DIR_STANDALONE%\Mods\%~3
SET ZIP_EXE="C:\Program Files\7-Zip\7z.exe"

SET HARMONY_PATH=%SOLUTION_DIR%Assemblies\0Harmony.dll
SET MOD_DLL_PATH=%SOLUTION_DIR%Assemblies\%~3.dll

IF %1==Debug (
	IF EXIST "%HARMONY_PATH:~0,-4%.pdb" (
		ECHO "Creating mdb at %HARMONY_PATH%"
		"%MONO_EXE%" "%PDB2MDB_PATH%" "%HARMONY_PATH%" 1>NUL
	)
	IF EXIST "%MOD_DLL_PATH:~0,-4%.pdb" (
		ECHO "Creating mdb at %MOD_DLL_PATH%"
		"%MONO_EXE%" "%PDB2MDB_PATH%" "%MOD_DLL_PATH%" 1>NUL
	)
)

IF %1==Release (
	IF EXIST "%HARMONY_PATH%.mdb" (
		ECHO "Deleting %HARMONY_PATH%.mdb"
		DEL "%HARMONY_PATH%.mdb" 1>NUL
	)
	IF EXIST "%MOD_DLL_PATH%.mdb" (
		ECHO "Deleting %MOD_DLL_PATH%.mdb"
		DEL "%MOD_DLL_PATH%.mdb" 1>NUL
	)
)

IF EXIST "%RIMWORLD_DIR_STANDALONE%" (
	ECHO "Copying to %TARGET_DEBUG_DIR%"
	IF NOT EXIST "%TARGET_DEBUG_DIR%" MKDIR "%TARGET_DEBUG_DIR%" 1>NUL
	FOR %%D IN (%~4) DO (
		XCOPY /I /Y /E "%SOLUTION_DIR%%%D" "%TARGET_DEBUG_DIR%\%%D" 1>NUL
	)
	FOR %%D IN (%~5) DO (
		XCOPY /Y "%SOLUTION_DIR%%%D" "%TARGET_DEBUG_DIR%\*" 1>NUL
	)
)

IF EXIST "%RIMWORLD_DIR_STEAM%" (
	ECHO "Copying to %TARGET_DIR%"
	IF NOT EXIST "%TARGET_DIR%" MKDIR "%TARGET_DIR%"
	FOR %%D IN (%~4) DO (
		XCOPY /I /Y /E "%SOLUTION_DIR%%%D" "%TARGET_DIR%\%%D" 1>NUL
	)
	FOR %%D IN (%~5) DO (
		XCOPY /Y "%SOLUTION_DIR%%%D" "%TARGET_DIR%\*" 1>NUL
	)
	%ZIP_EXE% a "%TARGET_DIR%.zip" "%TARGET_DIR%" 1>NUL
)

When you run it and look at the Output Build log in Visual Studio, you can see it working:

1>------ Rebuild All started: Project: CameraPlus, Configuration: Release Any CPU ------
1>  CameraPlus -> C:\Users\andre\Source\ModRepos\CameraPlus\v1.1\Assemblies\CameraPlus.dll
1>  "Copying to C:\Users\andre\RimWorld1-0-2408Win64\Mods\CameraPlus"
1>  "Copying to C:\Program Files (x86)\Steam\steamapps\common\RimWorld\Mods\CameraPlus"
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
@FluffierThanThou
Copy link

FluffierThanThou commented Feb 17, 2020

keep in mind that the 'default' build will only be 1.0 until 1.1 becomes the stable. It'd be better to put both the 1.0 and 1.1 libs in their own specific folders.

Otherwise, thanks for the write-up!

(update: apparently there's some odd magical version selecting that means this won't actually be a problem, as a possible future release would try to use 1.1, and not fall back on the 'default' 1.0. Super weird, but ok).

@Ataman
Copy link

Ataman commented Feb 17, 2020

Do not put the 1.0 assemblies/defs into a seperate 1.0 folder as that will break backwards compatibility.

@paulo27ms
Copy link

Do not put the 1.0 assemblies/defs into a seperate 1.0 folder as that will break backwards compatibility.

Does 1.0 not read a "1.0" folder? I believe I have some mods set up that way and they seem to work. I will double check this later.

@Ataman
Copy link

Ataman commented Feb 17, 2020

@paulo27ms
According to ModUpdating.txt it does not. At the moment there are other issues as well if you want dual compatibility. Tynan needs to change a few things first in my opinion. Perhaps he will implement a fix into 1.0 that supports a 1.0 folder...

@jptrrs
Copy link

jptrrs commented Feb 18, 2020

This is so handy! Thanks Brrainz!!

@jptrrs
Copy link

jptrrs commented Feb 18, 2020

@paulo27ms
According to ModUpdating.txt it does not. At the moment there are other issues as well if you want dual compatibility. Tynan needs to change a few things first in my opinion. Perhaps he will implement a fix into 1.0 that supports a 1.0 folder...

Which is funny, since last year's update note says otherwise:

Version-specific content should go in a folder named after the version being targeted. For example, if you make a folder called “1.0/Defs”, version 1.0 of the game will load Defs only from that folder, while other versions of the game will load from “/Defs”. (Note that if you add a version-specific folder like “1.0/Defs”, the default “/Defs” folder will be ignored when playing on 1.0.)

@FluffierThanThou
Copy link

As @jptrrs points out as well, I distinctly remember the multi-version targetting already being a thing when the 0.19 => 1.0 transition happened, so if it is indeed true that a 1.0 folder now doesn't work at all, they would have to have deliberately broken it?

@paulo27ms
Copy link

Alright so the mods I have that throw things in a 1.0 folder are:
Vanilla Furniture Expanded - Security (which only stores Assemblies/Defs/Patches in there, Sounds, etc are in the root folder)
Vanilla Furniture Expanded - Production (same as above)
Turret Extensions
No Friendly Fire
Haven't had any issues with these mods.

@Ataman
Copy link

Ataman commented Feb 18, 2020

@jptrrs, @paulo27ms, @FluffierThanThou
According to staff on Discord it only works for some things. (Which is why Sounds are still stored inside the root folder).
I assume they didn't want to confuse people too much by stating that only specific folders can go to 1.0 so they scrapped the whole thing and wrote to keep everything for 1.0 in the root.

@jptrrs
Copy link

jptrrs commented Feb 18, 2020 via email

@FluffierThanThou
Copy link

that makes more sense. The assumption is that resources (Textures, Sounds) don't change across game versions. That's obviously not entirely true, as mod versions may advance independently of game version, but it's a decent start to work with.

@FluffierThanThou
Copy link

I've just had a chat with developers, and here's what I can gather;

In the 1.0 version of the game, only Defs, Patches and Assemblies will be loaded from a 1.0 folder. Textures and Sounds are assumed to be in locations relative to the root.

In versions >= 1.1, all resources can be loaded from any location, defined in a special loadFolders.xml file. This can be the root folder, 1.x folders, or any other folder of your choosing. You can, for example, have multiple versions load from the same folder, and load a few extra resources for one version from a separate folder.

@I-Knight-I
Copy link

This could've been a change in a build of 1.0 but just in case:
I noticed that the PlaceWorker method "DrawGhost" now takes an additional parameter "Thing thing".
public virtual void DrawGhost(ThingDef def, IntVec3 center, Rot4 rot, Color ghostCol, Thing thing = null)

It used to just be:
public virtual void DrawGhost(ThingDef def, IntVec3 center, Rot4 rot, Color ghostCol)

Fortunately 'thing' doesn't need to be defined, so not a massive change.

@almera-vs
Copy link

Thanks!

@jptrrs
Copy link

jptrrs commented Feb 28, 2020

This could've been a change in a build of 1.0 but just in case:
I noticed that the PlaceWorker method "DrawGhost" now takes an additional parameter "Thing thing".
public virtual void DrawGhost(ThingDef def, IntVec3 center, Rot4 rot, Color ghostCol, Thing thing = null)

It used to just be:
public virtual void DrawGhost(ThingDef def, IntVec3 center, Rot4 rot, Color ghostCol)

Fortunately 'thing' doesn't need to be defined, so not a massive change.

Very useful info, Thanks!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment