Browse Source

Support for replacement of individual entries in json lists

It is now possible for mods to modify json lists precisely, without full
replacement. Supported options:
- `append`: appends a single item to end of list
- `appendItems`: appends multiple items to end of list
- `insert@NUM`: inserts a single item *before* item NUM (item counting
is 0-based)
- `modify@NUM`: allows editing of a single item NUM (item counting is 0-
based)

Example - addition of a new item into town hall slots:
```json
"hallSlots":
{
	"modify@4" : {
		"append" : [ "dwellingLvl7B", "dwellingUpLvl7B" ]
	}
},
```
This would modify 4th element (last row) by appending new entry to the
end of last row

```json
{
	"modify@4" : {
		"insert@1" : [ "dwellingLvl5B", "dwellingUpLvl5B" ]
	}
},
```
This would add new slot not in the end of last row, but before 1st item
(between 5th and 6th dwellings)
Ivan Savenko 7 months ago
parent
commit
aeb6c0be26
1 changed files with 58 additions and 3 deletions
  1. 58 3
      lib/json/JsonUtils.cpp

+ 58 - 3
lib/json/JsonUtils.cpp

@@ -209,9 +209,64 @@ void JsonUtils::merge(JsonNode & dest, JsonNode & source, bool ignoreOverride, b
 				if (copyMeta)
 					dest.setModScope(source.getModScope(), false);
 
-				//recursively merge all entries from struct
-				for(auto & node : source.Struct())
-					merge(dest[node.first], node.second, ignoreOverride);
+				if (dest.isStruct())
+				{
+					//recursively merge all entries from struct
+					for(auto & node : source.Struct())
+						merge(dest[node.first], node.second, ignoreOverride);
+					break;
+				}
+				if (dest.isVector())
+				{
+					auto getIndexSafe = [&dest](const std::string & keyName) -> std::optional<int>
+					{
+						try {
+							int index = std::stoi(keyName);
+							if (index < 0 || index > dest.Vector().size())
+								throw std::out_of_range("dummy");
+							return index;
+						}
+						catch(const std::invalid_argument &)
+						{
+							logMod->warn("Failed to interpret key '%s' when replacing individual items in array. Expected 'appendItem', 'appendItems', 'modify@NUM' or 'insert@NUM", keyName);
+							return std::nullopt;
+						}
+						catch(const std::out_of_range & )
+						{
+							logMod->warn("Failed to replace index when replacing individual items in array. Value '%s' does not exists in targeted array", keyName);
+							return std::nullopt;
+						}
+					};
+
+					for(auto & node : source.Struct())
+					{
+						if (node.first == "append")
+						{
+							dest.Vector().push_back(std::move(node.second));
+						}
+						else if (node.first == "appendItems")
+						{
+							assert(node.second.isVector());
+							std::move(dest.Vector().begin(), dest.Vector().end(), std::back_inserter(dest.Vector()));
+						}
+						else if (boost::algorithm::starts_with(node.first, "insert@"))
+						{
+							constexpr int numberPosition = std::char_traits<char>::length("insert@");
+							auto index = getIndexSafe(node.first.substr(numberPosition));
+							if (index)
+								dest.Vector().insert(dest.Vector().begin() + index.value(), std::move(node.second));
+						}
+						else if (boost::algorithm::starts_with(node.first, "modify@"))
+						{
+							constexpr int numberPosition = std::char_traits<char>::length("modify@");
+							auto index = getIndexSafe(node.first.substr(numberPosition));
+							if (index)
+								merge(dest.Vector().at(index.value()), node.second, ignoreOverride);
+						}
+					}
+					break;
+				}
+				assert(false);
 			}
 		}
 	}