Modding Tutorial for Chrono Ark

This tutorial is designed to guide programmers with no prior modding experience through the process of modding the wonderful card game, Chrono Ark. If you're familiar with programming but new to modding, you're in the right place.

Note: The post is polished by ChatGPT.

Modding a General C# (Unity) Game

Patching Code with Harmony

Harmony is a handy tool for modifying code behaviors in a C# program. It's highly recommended to familiarize yourself with its documentation, particularly the Injections section, as understanding the naming rules for patch methods is crucial.

HarmonyX is a derivative of Harmony, specifically tailored for game modding.

Executing the Patch

Once you have Harmony or HarmonyX at your disposal, the next step is to determine how to get the game to execute your patch. There are primarily two scenarios:

  1. The game has explicit mod support provided by the developers, often including Steam workshop support. In this scenario, it's simpler because there's an "official" method to execute your code.
  2. The game does not have mod support. Here, you'll need to utilize injection tools or frameworks like BepInEx to run your patch code.

Reading the Source Code

To decompile DLL files, use dnSpy. IDEs, such as JetBrains's Rider, also have built-in decompilers, enabling you to view decompiled code without additional software. However, dnSpy is still recommended as it can be helpful in learning from mods created by other users.

Submitting Your Mod to the Steam Workshop

Unfortunately, there isn't a straightforward method like a zip uploader button for this. The process varies depending on the game. You may need to conduct web searches or participate in Steam discussions to learn more about the submission process for your specific game.

Modding Guide for Chrono Ark

Unfortunately, there's no official documentation on how to create mods for Chrono Ark. This could be due to the fact that workshop/mod support is currently only available in the beta channel. However, Shaphon has provided an extensive guide on modding, which you can find on Discord. I've included the current version in the appendix of this post to give you a general understanding of what you'll need to create a mod for Chrono Ark.

I've created a mod called NoIdentification, the source code for which is available on GitHub. I can confidently say that it's a minimal mod and a great starting point for beginners. To explore it, open the project in an IDE (I use Rider, but Visual Studio or Visual Studio Code would also work), and ensure that you have a .Net SDK installed.

Once you've opened the project, use the dotnet build command to compile it. To run the mod, navigate to the game folder, then to Chrono Ark\ChronoArk_Data\StreamingAssets\Mod. In this directory, create a new folder named after your mod, and populate it with the following content:

1
2
3
4
5
[mod name]
├── Assemblies
│   ├── 0Harmony.dll
│   ├── ...
└── ChronoArkMod.json

The DLLs in the Assemblies folder can be found in [mod project folder]\bin\Release\net40\publish after running the dotnet publish --configuration Release command. For ChronoArkMod.json, you can use the same JSON found in the project. Once these steps are completed, you can load your mod from the game (note that you need to use the beta channel at the time of writing this guide).

The most crucial DLL for reading the source code is located at Chrono Ark\ChronoArk_Data\Managed\Assembly-CSharp.dll, and Chrono Ark\ChronoArk_Data\StreamingAssets\LangDataDB.csv can provide insights into the naming conventions for items, characters, and skills.

By now, you should have a good understanding of how to develop and test a mod. After creating your own mod, remember to complete the "Uploader" field in ChronoArkMod.json (as well as any other workshop-related fields), and you'll find an upload button in the mod selection screen.

Enjoy both the game and the process of modding!

Appendix

This version is probably outdated at the time you're reading this. You can find the updated version on our Discord server.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
1. The location of the Mod file
When the game starts, mod files are read from two locations,
One is the ...\steamapps\common\Chrono Ark\ChronoArk_Data\StreamingAssets\Mod folder
The second is the ...\steamapps\workshop\content\1188930 folder
The game will go through all subfolders within these two folders and look for the file ChronoArkMod.json in the subfolders
If this file is found, all the content in this subfolder will be recognized as a Mod, and this subfolder will be referred to as the Mod folder hereinafter


2. Fill in ChronoArkMod.json
ChronoArkMod.json is the basic information of the Mod, which needs to be filled in as a dictionary.
Fillable information includes the following content, if the dictionary does not contain the following content, it will be regarded as the default value
***** 1) ~ 6) are the basic information of the mod, which will be displayed directly in the mod selection UI******
1) id
String, this is the unique identifier of the mod and cannot be repeated with other mods. In the game code, you can use the ModManager.getModInfo(string id) method to access all the information of this Mod
2) Title
String, this is the title of the mod (supports translation files)
3) Author
String, this is the author name of the mod
4) Description
String, this is the description of the Mod (supports translation files)
5) Version
String, this is the version of the mod
6) Cover
String, this is the cover image of the Mod, which is the path of an image relative to the Mod folder
***** 7) is the configuration of the mod's adjustable parameters, which the mod setting UI will be generated automatically in the mod selection UI *****
7) ModSettingEntries
List, and each item in the list is a dictionary, which contains information about the adjustable parameters of the item
a. Each dictionary always contains the following three items
I. SettingType
String, this is the type of setting UI, including ToggleSetting, SliderSetting, DropdownSetting, InputFieldSetting, InputFieldSetting_Int
II. SettingKey
String, this is the unique identifier of this setting in this Mod, you can use modinfo.GetSetting<T>(string key) to access this setting after accessing ModInfo, T is the setting type
III. DisplayName
String, the name will be displayed in the settings UI (supports translation files)
IV.Description
String, in the settings UI, when the mouse moves over a setting name, this setting description text will be displayed (supports translation files)
b. The dictionary of ToggleSetting also contains
I. InitValue
Boolean value, the default value for the first use of the mod
c. The dictionary of SliderSetting also contains
I. InitValue
Floating point number, the default value for the first use of the mod
II. Max
Floating point number, the maximum value of Slider
III. Min
Floating point number, the minimum value of Slider
IV. StepSize
Floating point number, which is the step size of Slider sliding, if it is 0, it means continuous sliding
d. Dropdown Setting
I. InitValue
Integer, the default value for the first use of the mod
II.Options
List, each item is a string. They will be displayed in the drop-down menu of the setting UI
e.InputFieldSetting
I. InitValue
String, the default value for the first use of the mod
e.InputFieldSetting_Int
I. InitValue
Integer, the default value for the first use of the mod
***** 8) ~ 12) are common configuration options for mods*****
8) DefaultAssetBundlePath
The string is the relative path of an AssetBundle relative to the Mod Assets folder. In Mod gdata, you can directly fill in the path in this AssetBundle.
9) Dependencies
List, each item is the id string of other Mods, indicating that these Mods must be loaded before this Mod (that is, above this Mod in the Mod selection UI)
10) NeedRestartWhenSettingChanged
Boolean value, indicating that after the settings of this mod are changed, clicking the apply button in the mod selection UI will require restarting the game
11) NeedReloadAllWhenEnabled
Boolean value, indicating that when the Apply button is clicked in the Mod selection UI, if the Mod is enabled, all other enabled Mods will be reloaded
12) NeedRestartWhenEnabled
Boolean value, indicating that when the Apply button is clicked in the Mod selection UI, if the Mod is enabled, the game will be required to be restarted
***** 13) ~ 17) are Steam Workshop upload tools *****
13) Uploader
String, fill in the uploader's SteamID or Steam community nickname here. When the game player agrees with it, an upload button will appear in the Mod selection UI
14) TagList
List, each item is the Tag string in Steam Workshop
15) Visibility
String, which is the visibility of the Steam Workshop, there are three options: Public, Private, FriendsOnly
16) ChangeNote
String, which is the update description for this upload
17) WorkShopId
The string is the ID of this mod in Steam Workshop. It does not need to be filled in manually for the first upload, it will be automatically added to the file


3. Mod assembly
Mod assemblies are placed in the Assemblies subfolder in the Mod folder, and all assemblies in the subfolder will be loaded when the Mod is loaded
Please ensure that all assemblies referenced by the assembly (and the assemblies referenced by the referenced assembly) are in the Assemblies folder, especially when HarmonyPatch needs to be written
1) When loading, game will notice three special classes defined in the assembly
I. Classes inheriting ChronoArkPlugin
This class needs to have the Attribute of PluginConfig, and when loading, it will call the Initialize method of this class
In addition, this class has:
- Dispose method (executed when mod is disabled)
- OnModSettingUpdate method (executed when mod settings are changed and applied in the mod selection UI)
- OnModLoaded method (executed when the Mod is loaded)
II. Classes inheriting ChronoArkPluginMonoBehaviour
This class needs to have the Attribute of PluginConfig. When loading, it will be added as a component to the GameObject exclusive to this Mod.
III. Classes inheriting ModDefinition
see gdata section
2) In the assembly, you can use the `public static ModInfo getModInfo(string id)` in the ModManager class to get all the information of the Mod,
The following information can be obtained in the ModInfo class:
I. Use `public T GetSetting<T>(string key)` to get Mod setting information
Where T is the SettingEntry class corresponding to different UIs, you can use the Value property of the return value of this method to obtain or change the value of the setting
It should be noted that if you use the assembly to change the value of the setting, you need to call `public void SaveSetting()` method to save it
II. Use `public ModAssemblyInfo assemblyInfo` to get the assembly information of the Mod, including
- All assemblies inside the Assemblies folder
- All classes that inherit ChronoArkPlugin
- The GameObject of the class that inherits ChronoArkPluginMonoBehaviour in this Mod
- All ModDefinitions
III. Use `public ModAssetInfo assetInfo` to get the Asset information of the Mod
See Mod Asset section
IV. Use `public ModAudioInfo audioinfo` to get the sound effect information of the Mod
See sound effects section
V. Use `public ModGDEInfo gdeinfo` to get all the gdata information of Mod
VI. Use `public ModLocalizationInfo localizationInfo` to get all translation file information of this Mod
See the Translation Files section


4. gdata
The original gdata of the game can be found in ...\steamapps\common\Chrono Ark\ChronoArk_Data\StreamingAssets\gdata.json
There are two ways to add/modify the gdata of the game, and they can be used at the same time
1) Json way
In the gdata subfolder of the Mod folder, gdata can be filled in Json
The format of gdata is the same as the original gdata.json, which is a dictionary whose value is a dictionary. The elements of the outermost dictionary are called entries, and the keys of the inner dictionary are called fields below
5 kinds of changes to the game's original gdata are supported. Put the json file into the following subfolders in the gdata folder to adopt the corresponding modification method
I. Add
Add the gdata in this folder to the original gdata of the game
The structure of the json file here is the same as that of the original gdata, but it can be simplified as follows
- Fields starting with _gdeType can be omitted
- The fields at the beginning of _gdeIsList can be omitted
- If the value of this field is the same as the default value, it can be omitted. You can refer to the entry starting with _gdeSchema_ in the original gdata to get the default value
II. Replace
Replace the value of the corresponding field under the corresponding entry in the original gdata
- _gdeSchema field is required
- The remaining fields that do not need to be replaced do not need to be filled in
III. Remove
Delete the corresponding entry in the original gdata
- The file still needs to maintain the format of the dictionary, but only the Key of the outermost dictionary will be read
IV. AddItemToList
It only takes effect when the field of the entry is a list
You can fill in the content in this folder to add elements under the original list instead of directly deleting the original list and replacing it with your own
- _gdeSchema field is required
V. RemoveItemFromList
It only takes effect when the field of the entry is a list
You can fill in the content in this folder to delete elements under the original list instead of directly deleting the original list and replacing it with your own
- _gdeSchema field is required
There are two field types that need to be emphasized
I. the fields representing the name of a class within an assembly
When filling in, you need to fill in the namespace. When the game reads, it will go through all the assemblies in the Assembies folder
II. the fields representing the paths of assets
See Mod Asset section
2) Assembly way
Define a class inherited from ModDefinition in any assembly, and you can write codes to change the game gdata
At this point, each class inherited from CustomGDE defined in the assembly is equivalent to a Json entry
In actual use, templates such as CustomSkillGDE<Mod> should be inherited, where Mod is the class just defined and inherited from ModDefinition
All CustomGDE classes such as CustomSkillGDE<Mod> have the following methods that can be overloaded
I. public virtual string Key()
Overloading this method can change the Key of this entry. If the Key is the same as the Key of an entry in Json, it will directly modifies the corresponding dictionary in Json.
II. public abstract ModGDEInfo.LoadingType GetLoadingType();
That is, the five modification methods of the above Json method
III. public abstract void SetValue();
It is the core, where you can directly assign values to the corresponding fields to fill in Mod gdata
If the value is not assigned, the value filled in by the Json method, or the default value will be used
The ModDefinition class also provides the following methods that can be overloaded
I. public virtual IEnumerable<CustomGDE> SetLoadingTargets()
Changing the output of this method will change the entry that is finally loaded into the game
If some entries need to be written in the assembly, but you do not want them to be loaded , you can change this method
II. public virtual string ModKey<GDE>() where GDE is the CustomGDE class such as CustomSkillGDE<Mod>
Changing this method can modify the Keys of all loaded entries in batches
Its priority is lower than directly overloading the `public virtual string Key()` method in CustomGDE
In addition, the CustomGDE class also provides some methods to quickly access Mod information, as well as some string generators commonly used when filling in gdata
I. `public ModDefinition myMod` can directly get the instance of the ModDefinition it is in
II. `public ModInfo modInfo` can directly access all the information of the mod
III. `public ModAssetInfo assetInfo` can directly access the Mod Asset information of the Mod where it is located
IV. There are three methods to quickly generate Asset addresses, see the Mod Asset chapter for specific meanings
- public string RegisterObjectFromAssetBundle<T>(string AssetBundlePath, string FilePath) where T : Object
- public string RegisterSpriteFromAssetBundle(string AssetBundlePath, string FilePath)
- public string RegisterSpriteFromImageFile(string FilePath)


5. Mod Assets
The game uses Addressable as the asset management system, so the core of this part is the method to generate the addresses
1) Automatically generated addresses
When the game reads gdata content, it regards the string ending in .png written in gdata as a picture and translates it into an image address, and translates the string ending in .prefab into a GameObject address
For a string ending in .png, it will first check whether the string is the path of an image relative to the Mod Assets folder, otherwise it will be regarded as the path in the DefaultAssetBundle
2) Use the code to get the address
In the ModInfo of a Mod, use `public ModAssetInfo assetInfo` to get the Asset information of the Mod
Call the following methods of ModAssetInfo to register and get the address string
for pictures
I. public string ImageFromFile(string path)
path is the path relative to the Mod Assets folder
II. public string ImageFromAsset(string AssetBundlePath, string FilePath)
Read pictures from AssetBundle, both Texture2D and Sprite, AssetBundlePath is the path relative to the Mod folder
III. public string ConstructImageByCode(Sprite sprite)
Use the code to build Sprite and register to get the address
IV. public string ChangeToSkillImage(string OldID)
After using other methods to obtain the picture address, if you need to change the picture to the 440*280 format of the skill picture, you can call this method to generate a new address
For other assets
I. public string ObjectFromAsset<T>(string AssetBundlePath, string FilePath) where T : Object
Read from AssetBundle, AssetBundlePath is the path relative to the Mod folder
II. public string ConstructObjectByCode<T>(T gameObject) where T : Object
Use code to build Object. It should be noted that you should remember to call the Object.DontDestroyOnLoad(...) method to prevent the built Object from being destroyed when the scene is loaded.
III. The construction method of two commonly used GameObjects. These can be used when making character mods but do not want to use the Unity editor to generate AssetBundles
- public string FaceGameObject(string ImageKey, Vector2 offsetMax, Vector2 offsetMin, Vector2 pivot)
- public string BattleCharGameObject(string ImageKey, Vector2 offsetMax, Vector2 offsetMin, Vector2 pivot)


6. Translation documents
Two kinds of translation files are provided. They need to be placed in the Localization subfolder in the Mod folder
The internal format of the file is the same as the file with the same name in ...\steamapps\common\Chrono Ark\ChronoArk_Data\StreamingAssets
1) LangDataDB.csv
It is used with Mod gdata, when reading gdata, it will replace the content in your gdata that needs to be translated with the content of this file
2) LangSystemDB.csv
You can use codes to get the translated strings.
First, in the ModInfo of a Mod, use `public ModLocalizationInfo localizationInfo` to get all translation file information of the Mod
In ModLocalizationInfo, you can call `public string SyetemLocalizationUpdate(string key)` to get the translation information in LangSystemDB.csv
If not obtained,it will return the input string key
In addition, for the content marked in ChronoArkMod.json that supports translation files, the content filled in json will be automatically used as an input parameter, and SyetemLocalizationUpdate will be called once


7. Sound effects
The in-game sound effects are managed using DarkTonic.MasterAudio, so the core of this section is to register the required sound effects in it
It should be noted that the string corresponding to each sound effect is unique, so try your best to ensure that it does not conflict with other mods
To register each Mod sound effect, you need to fill in a json file in the Audio subfolder in the Mod folder
Each json file is a dictionary containing the following
1) Name
String, which is the unique string corresponding to each sound effect mentioned in the above text
2) AssetBundlePath
String, the AssetBundle where the sound effect is located, you need to fill in its relative path in the Mod Assets folder
If not filled or empty, it will be considered that the sound effect does not use AssetBundle
3) path
String, the path of the sound effect in the AssetBundle, or the relative path in the Mod Assets folder
4) Loop
Boolean value, whether to loop the sound effect
5) MixerType
String, which means this sound effect is played based on which audio mixer (to control its volume, etc.). There are four options SFX, BGM, FieldBGM, BattleBGM
6) Bus
String, whitch is the sound effect group name of DarkTonic.MasterAudio. The default is SE. If you need to make Boss battle BGM, use BGM. If you need to make regular battle BGM, use BattleBGM