… but not mentioned anywhere on wiki. Probably I mistaken it for some of vcmi config files…
One format is easier to code. So this:
"cost" : 200
will be replaced with this:
"cost": { "gold" : 200}
As for merging - that depends on what keys will be merged. It can be done for each resource separately (so mercury will remain) or for a whole “cost” structure (only gold will remain). This can be changed anytime but right now merging “cost” seems more logical for me.
One format will also solve “how this will be merged” issue.
For now I am more interested in general algorithm structure (how to convert txt’s to json, load vcmi configs and merge all of this). I’ll post something when I’ll start thinking about new format - right now I’m using same formats as our current config files.
Warmonger, I think you understood my post wrong. With key or name given by the modder, I meant
"abilities" :
{
"ChargeImmune" : {}
^ this is the key/name
}
It is only ever used by the merging system. You don’t need C++ code to remove the bonus because the bonus was never even seen by the bonus system. The unique (for one creature) name makes saying which bonus should be removed easy.
The loading would work like this:
1. get list of mods to load
2. create temporary storage for data
3. for each mod
a. get list of configs to load
b. load configs into temporary storage, merging with data already present
4. create real creatures from temporary data
This means that a bonus that the original creature had and was removed by a mod never reaches step 4. The bonus system only sees things that reach step 4. Using this system means that the translators json->CCreature,… don’t need to know about the moddin system. The whole modding system acts as a preprocessor for the data.
This system also makes supporting the old formats easy. If the old files exist, load them first into the temporary storage.
then it becomes easier. I think merging each resource separately makes sense then. Also this would mean that gold does not have to be hard-coded into the game.
This means only that we will need yet another new, immediate storage class (what class? how would you implement it?) between not yet existing Json parser and not yet functional modding system. You multiplicate troubles instead of solving them.
Parsers and config files always had and always will be limited only to fixed and predictable cases. If you want to have unlimited possibilities, you need to do real coding.
I’ve been thinking about merging: It looks that we actually can make universal json merging.
Json structures/records - entries with same name will override each other. No possible issues here.
Json vectors/lists - append to end. In creature config we have such vectors:
“damage” : [0, 0] - replace with {“min” : 0, “max” : 0}
“advMapAmount” : [0, 0] - min/max as well
“upgrades” : ] a) keep it as it, removing upgrades will be done by scripting or some special format; b) turn it into structure:
"upgrades" : {"halbedier" : true }
this will also allow removing upgrades by overriding (as long as ID is known) - just change true to false or (probably better) to null
"abilities" : {} - same as upgrades
Don’t like embedding into code but separate file will definitely work. Right now defaultSettings.json is used to validate settings.json (which can be edited by user)
This is good. I just looked at the format and the only problems with general merging were damage, amm (I see you gave this a better name :)), upgrades and abilities. If we use min/max for damage and amm they are not a problem. Making upgrades a structure is also ok (I prefer false, then the only allowed values are booleans). This leaves abilities. This one we have to think about more anyway.
I changed damage, advMapAmount and upgrades in the wiki.
EDIT: It is funny how the format moved away from the one in my first post but has now moved back :).
My suggestion for abilities:
"abilities" :
{
"ArbitraryName" : // modder given name for ability, never seen by code, used only for merging
{
// bonus format here, can change existing values with the same name
},
"AnotherName" : null
}
I added a json schema to the wiki page. I assume you are using json-schema.org/ for the schema. Probably not all of the schema syntax is implemented. This would have to be done before using the schema in the code.
Actually we’re not using any specific format - I haven’t found something simple and usable for us when I implemented our validator.
Your example looks good though and quite similar to ours - switching to this schema format should be OK.
Implemented config in latest revision. However, it will need some more changes:
-Sounds are stored in SoundHandler on Client, which is completely inacessible for ModHandler.
-When generating creature from config, it’s id is needed for VLC->creh->idToProjectile. However, in this case numeric id must remain unchanged for all time, which doesn’t allow to turn mods on and off.
A simple solution would be to move ALL creature-related info to CCreature, so it could be managed as a whole, without need to update or configure things in different parts of engine.
I think first making the system load a given set of mods is a good first step. When that works, turning mods off can be implemented.
I think turning mods off can only be implemented by reloading all mods because mods can and do overwrite the data from other mods. This means there is after loading no way to say which mod a setting came from.
Looks like the right way to me. I still would rather move all client-only data somewhere else but it definitely should be one common location instead of spreading it to different handlers.
Aren’t we able to control what is overridden and by which mod?
Reconfiguring filesystem from scratch may take ~50ms top - several times less compared to full initialization.
Warmonger,
I am not sure that placing all loading into modhandler itself is a good idea. Right now I have TownHandler with 300 lines only for loading. I expect this to be ~500 lines for complete config implementation.
Add to this number loading of creatures, heroes, artifacts, adventure map objects - placing all this code into modhandler doesn’t sounds like a good idea.
What do you think about doing this in such way:
ModHandler reads all mods and their configuration into list of Json’s (done once on startup)
ModHandler merges configurations of all active mods into one Json structure (will be one-liner) and validates it (one liner as well), any broken data removed.
ModHandler sends this structure to loaders in CreatureHandler, TownHandler, etc.
Handers clean any existing data (for reconfiguration) and loads received data into C++ classes.
In this way it will be possible to have mods with incomplete data - one building to town, one ability to creature, etc.
This still won’t solve “How pass data to client” issue though.
I think it is better to have everything in one place. The overhead from the server knowing the names of the sound files is extremely small. The server would of course never actually load any sound files (or check that they exist, good for lightweight servers).
I meant the json configs, not the art assets. Sounds and graphics should not be a problem.
I think the easiest to implement would be to throw away everything and reload all configuration if the user changes the list of mods. I don’t think this would take more than a few seconds even on old hardware. The profit from doing a complex implementation that remembers all the json configs is very small.
Of course this could easily be changed later because it requires no change to any mod formats. No one would notice (except better speed?).
I think dividing up the code into smaller parts that only deal with one type of config is a good idea. For each type of object three thing must be implemented: validate a json config before loading (use schema), validate the loaded and merged config (are all required properties present?), translate json into C++ class (CCreature).
All of the general code could be in ModHandler (merging of configs, …).
I am not quite sure what you mean with passing data. What data? I think it would be easiest to have the client reload all the configs itself. Also, I thought that the client starts the server and not the other way around.
You mean validating each mod separately? Won’t work - if mod adds one ability to existing creature it won’t have complete data for it - only updated fields.
Complete reloading will take same time as starting (1 second on my system). However some steps like data directory parsing can be done only once on startup. Storing parsed json configs from all mods may decrease reload time even further. But all game content should be replaced from scratch.
I mean passing all gui-specific information from loaders in library to client. Storing it in the library is not a best solution. And you simply can’t write something like this:
if (client)
client->loadImages();
There is no way to make this compile without hacks.
For example mod that adds descriptions to creatures. Mod can be completely client-side but due to fact that loading handled exclusively by library you will need to modify loading (read: library) as well. This quite notably decreases functionality of GUI-only mods.
Probably something like this will work:
ModHandler::loadCreatures(JsonNode creatures)
{
// Json config is already merged and validated. Final step - create C++ classes from it
foreach (creatureJson, creatures)
{
// load creature into creature handler
creatureHandler->load(creatureJson);
// parent is some interface implemented separately by client and server
// all data specific to client (or server) will be loaded in it.
parent->onCreatureLoaded(creatureHandler->creatures.back(), creatureJson);
}
}
Acceptable for the time being. CCreature already has lots of mechanics-irrelevant data anyway. It may be a good to put it in a nested class that is populated by separate function, so detachment in the future will be easier.
Does registering a creature-consuming callback by providing an interface* or std::function<> is a hack?
Client, before calling init, registers as creature consumer. Library can register itself when requested for init.
That won’t be a problem, if client receives a JsonNode and takes the needed info from it by itself.
Generally, does the mod handler actually need to know what it is actually loading? It could just output JsonNode (containing loaded, merged and valdiated property tree) — and
a) it would be a responsibility of caller (creature handler? lib? client?) to consume it properly.
b) or just have it some kind of enum EDataType {CREATURE, TOWN, …} and mantain map<EDataType, CFunctionList<void(JsonNode)> for registered callbacks.
But anyway… we either have all creature data in place (that’ll be lib) or we have it splitted into multiple locations (mechanic relevant in lib, GUI in client) and the lib is not bothered with GUI-only changes. It’s a trade-off, both options have their qualities. Right now we have both splitted data in multiple places and some GUI info in lib, so whatever will be done, it’ll be improvement. I guess it’s easier for now to put data in one place, and then we can consider, if it’s actually worth of splitting.
Hack is calling client-specific handlers from library code. Callback or interface is OK. Interface sounds better for me - I don’t like large number of callbacks.
But the main problem is lack of any client-side location that can store all this data. Simple storage of strings URI’s won’t work - data is too complex.
So right now we don’t have anything that can be used as this callback or interface.
I think it worth but definitely not needed immediately. Nested structure for GUI will work fine.
I didnt read all what u wrote, but i have proposition.
First config should be load from folder config, and next should be load from Mods, and script check whether new .json are some differences.
For example i made mod: “new units”, and in folder is creatures.json which have changed creatures, and only them and next when i launch VCMI, it load creatures.json from config and load changed creatures from mods.
Why you need to change creatures if you made mod with new creatures?
Anyway it should be possible to load completely new creatures from mods without any modification of existing files. This includes replacing icons so you won’t need to bother with incompatibility between mods if they modify same .def files.
Editing existing creatures from original game for some kind of rebalance mod will be possible as well.
Ok, in r2891 I added very simple possibility to add new creature(s). The creature config needs to be named mod.json and placed in Mods\modname folder. Also, creatures need to be listed in a form: