Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 49 additions & 17 deletions sqf/CFG_To_JSON.sqf
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// DESCRIPTION: This script converts the contents of a key cfg config to a JSON array, which can then be copied from the clipboard and used to populate JSON files, databases or anything else that is required.
// AUTHOR: Morgan Davies
// USAGE:
// 1) Alter the _CONFIGS hashmap variable to include the config classes you want to add to the json array. Beware of the 10,000,000 limit on array sizes however. You might need to run the config extraction one at a time. See _exampleCONFIGS below for the currently supported configs.
// 1) Alter the _CONFIGS hashmap variable to include the config classes you want to add to the json array. Beware of the 10,000,000 limit on array sizes however. You might need to run the config extraction one at a time. If you do run it one at a time, ensure you don't include a comma at the the end of the config entry. See _exampleCONFIGS below for the currently supported configs.
// 2) Run the script in a debug console on arma 3. It may take a few seconds to complete depending on your system specs.
// 3) You should now have a json array on your clipboard. Don't copy it from the debug console output as it will include extra double quotes to escape the existing ones.
// 4) It is worth mentioning that the last JSON element in the array will include a trailing comma, which may make the array invalid (depending on the parser type). Due to the difficulty in extracting said comma out, I haven't bothered fixing it.
Expand All @@ -18,7 +18,7 @@
// ["glasses", "getNumber (_x >> 'scope') isEqualTo 2" configClasses (configFile >> "CfgGlasses")]
// ];
private _CONFIGS = createHashMapFromArray [
["vehicles", "getNumber (_x >> 'scope') isEqualTo 2" configClasses (configFile >> "CfgVehicles")]
["glasses", "getNumber (_x >> 'scope') isEqualTo 2" configClasses (configFile >> "CfgGlasses")]
];
forceunicode 0;
private _jsonClasses = [];
Expand Down Expand Up @@ -225,50 +225,82 @@ getModName = { params["_NAME", "_DLC", "_AUTHOR"];
_modName = "immersioncigs";
_found = true;
};
if ((["Enoch", _DLC] call BIS_fnc_inString || ["Contact", _DLC] call BIS_fnc_inString) && _found == false) {
if (["Adacas", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "militarygearpack";
_found = true;
};
if (["Rainman", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "rmswatuniform";
_found = true;
};
if (["Exocet", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "solidcoloruniforms";
_found = true;
};
if (["Exocet", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "solidcoloruniforms";
_found = true;
};
if (["Drongo", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "drongosartillery";
_found = true;
};
if (["camel", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "projectbjc";
_found = true;
};
if (["kat_", _NAME] call BIS_fnc_inString && _found == false) then {
_modName = "katadvancedmedical";
_found = true;
};
if (["SKEENBREEN", _AUTHOR] call BIS_fnc_inString && _found == false) then {
_modName = "sbsvsmretexture";
_found = true;
};
if ((["Enoch", _DLC] call BIS_fnc_inString || ["Contact", _DLC] call BIS_fnc_inString) && _found == false) then {
_modName = "contact";
_found = true;
};
if (["Kart", _DLC] call BIS_fnc_inString && _found == false) {
if (["Kart", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "karts";
_found = true;
};
if (["Mark", _DLC] call BIS_fnc_inString && _found == false) {
if (["Mark", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "marksman";
_found = true;
};
if (["Tank", _DLC] call BIS_fnc_inString && _found == false) {
if (["Tank", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "tanks";
_found = true;
};
if (["Expansion", _DLC] call BIS_fnc_inString && _found == false) {
if (["Expansion", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "apex";
_found = true;
};
if (["AoW", _DLC] call BIS_fnc_inString && _found == false) {
if (["AoW", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "artofwar";
_found = true;
};
if (["Heli", _DLC] call BIS_fnc_inString && _found == false) {
if (["Heli", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "helicopters";
_found = true;
};
if (["Orange", _DLC] call BIS_fnc_inString && _found == false) {
if (["Orange", _DLC] call BIS_fnc_inString && _found == false) then {
_modName = "lawsofwar";
_found = true;
};
if ((["Bravo Zero One Studios", _AUTHOR] call BIS_fnc_inString || ["Jets", _DLC] call BIS_fnc_inString) && _found == false) {
if ((["Bravo Zero One Studios", _AUTHOR] call BIS_fnc_inString || ["Jets", _DLC] call BIS_fnc_inString) && _found == false) then {
_modname = "jets";
_found = true;
}
};
if ((["Bohemia Interactive", _AUTHOR] call BIS_fnc_inString || _NAME == "None") && _found == false) then {
_modName = "vanilla";
_found = true;
};

// Not found, either object is a placeholder null class, author is not provided or is not supported yet
if (_found == false) then {
throw format ["[ERROR] Unknown mod author for class = %1", _NAME];
_modName = "[UNKNOWN]";
};

_modName
Expand All @@ -290,7 +322,7 @@ getModName = { params["_NAME", "_DLC", "_AUTHOR"];
switch (_CONFIGTYPE) do {
case "weapons": {
format [
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "image":"%8",%9 "magazines":%10,%11 "type":"%12",%13 "subtype":"%14",%15 "mod":"%16",%17 "weight":%18,%19 "magwell":%20%21 },%22',
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "imagePath":"%8",%9 "magazines":%10,%11 "type":"%12",%13 "subtype":"%14",%15 "mod":"%16",%17 "weight":%18,%19 "magwell":%20%21 },%22',
endl,
_CONFIGNAME,
endl,
Expand Down Expand Up @@ -318,7 +350,7 @@ getModName = { params["_NAME", "_DLC", "_AUTHOR"];

case "magazines": {
format [
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "image":"%8",%9 "ammo":"%10",%11 "count":%12,%13 "mod":"%14",%15 "weight":%16,%17 "type":"%18",%19 "subtype":"%20"%21 },%22',
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "imagePath":"%8",%9 "ammo":"%10",%11 "count":%12,%13 "mod":"%14",%15 "weight":%16,%17 "type":"%18",%19 "subtype":"%20"%21 },%22',
endl,
_CONFIGNAME,
endl,
Expand Down Expand Up @@ -346,7 +378,7 @@ getModName = { params["_NAME", "_DLC", "_AUTHOR"];

case "vehicles": {
format [
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "image":"%8",%9 "mod":"%10",%11 "weight":%12,%13 "type":"%14",%15 "subtype":"%16"%17 },%18',
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "imagePath":"%8",%9 "mod":"%10",%11 "weight":%12,%13 "type":"%14",%15 "subtype":"%16"%17 },%18',
endl,
_CONFIGNAME,
endl,
Expand All @@ -370,7 +402,7 @@ getModName = { params["_NAME", "_DLC", "_AUTHOR"];

case "glasses": {
format [
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "image":"%8",%9 "mod":"%10",%11 "weight":%12,%13 "type":"%14",%15 "subtype":"%16"%17 },%18',
' {%1 "class":"%2",%3 "name":"%4",%5 "description":"%6",%7 "imagePath":"%8",%9 "mod":"%10",%11 "weight":%12,%13 "type":"%14",%15 "subtype":"%16"%17 },%18',
endl,
_CONFIGNAME,
endl,
Expand Down
18 changes: 16 additions & 2 deletions src/main/java/com/api/main/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
@Configuration
public class Config {

@Value("${config.HOST:http://localhost:8080}")
private String host;

@Value("${config.MONGO_URI:mongodb://localhost:27017}")
private String mongoUri;

Expand All @@ -26,6 +29,9 @@ public class Config {
@Value("${config.SUPPORTED_MODS:vanilla,ace,3cb,rhs,niarms,tacvests,tryk,vsm,rksl,acre,projectopfor,immersioncigs}")
private String supportedMods;

@Value("${config.ARMA_PATH}")
private String armaPath;

private static final ArrayList<String> TYPES = new ArrayList<String>(Arrays.asList(
"Primaries", "Secondaries", "Launchers", "Throwables", "Explosives", "Muzzles",
"Pointers", "Optics", "Bipods", "Tools", "Terminals", "Maps", "GPSs", "Radios",
Expand All @@ -35,6 +41,10 @@ public class Config {

private final static ArrayList<Character> SPECIAL_MONGO_CHARS = new ArrayList<Character>(Arrays.asList('\'', '\"', '\\', ';', '{', '}', '$'));

public String getHost() {
return host;
}

public MongoClientURI getMongoUri() {
return new MongoClientURI(mongoUri);
}
Expand All @@ -51,11 +61,15 @@ public ArrayList<String> getMods() {
return new ArrayList<String>(Arrays.asList(supportedMods.split(",")));
}

public static ArrayList<String> getTypes() {
public String getArmaPath() {
return armaPath;
}

public ArrayList<String> getTypes() {
return TYPES;
}

public static ArrayList<Character> getSpecialChars() {
public ArrayList<Character> getSpecialChars() {
return SPECIAL_MONGO_CHARS;
}
}
118 changes: 83 additions & 35 deletions src/main/java/com/api/main/Executer.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public static void main(String[] args) throws FileNotFoundException, IOException
// Run the updater if requested
if (Arrays.asList(args).contains("--updater")) {
try {
if (new Updater().update(MONGO_CLIENT, DATABASE) == true) {
if (new Updater().update(MONGO_CLIENT, DATABASE, config) == true) {
LOGGER.log(Level.INFO, "[SUCCESS] Updater has successfully backed up and updated all collections in the database!");
} else {
throw new Exception("[ERROR] Updater failed to backup and/or update all collections in the database. See log for more details...");
Expand Down Expand Up @@ -105,25 +105,20 @@ public String classes (
final String filteredType = type == "" || type == null ? "" : escapeUserInput(type);

// Verify params
if (!config.getMods().contains(filteredMod) && filteredMod != "") {
throw new Exception(String.format("Unidentified mod (%s). Available values are %s", filteredMod, config.getMods().toString()));
}
if (!Config.getTypes().contains(filteredType) && filteredType != "") {
throw new Exception(String.format("Unidentified object type (%s). Available values are %s", filteredType, Config.getTypes().toString()));
}
verifyParams(filteredMod, filteredType);

// Filter db by keywords or return all
ArrayList<Document> dbContents = new ArrayList<Document>();
for (String collectionName : DATABASE.listCollectionNames()) {
MongoCollection<Document> modContents = retrieveCollection(collectionName);

if (collectionName.equals("data." + filteredMod)) {
dbContents.addAll(filterByType(modContents, filteredType));
if (!filteredMod.isEmpty() && collectionName.equals("data." + filteredMod)) {
dbContents.addAll(filterResults(modContents, filteredType, ""));

// Break so we only return a singular mod spec
break;
} else if (filteredMod.isEmpty()) {
dbContents.addAll(filterByType(modContents, filteredType));
dbContents.addAll(filterResults(modContents, filteredType, ""));
} else {
// We haven't reached the requested mod yet
continue;
Expand Down Expand Up @@ -159,6 +154,8 @@ public String classes (
/**
* A search route for the class list, this will return all the classes in the database that match a user provided search term (after escaping mongo chars).
* @param term The search term. It can be a classname, a config key or a config value
* @param mod A mod to filter for (e.g. ace, vanilla, 3cb)
* @param type A type to filter the results by (e.g. weapon, vest, headgear)
* @param page Pagination page number
* @param size Pagination page size (max number of items in the json array on each page)
* @return A string representation of JSON list containing the filtered configs
Expand All @@ -167,31 +164,35 @@ public String classes (
@GetMapping(value = {"/classes/search/{term}"})
public String search (
@PathVariable(required = true, value = "term") String term,
@RequestParam(required = false, value = "mod") String mod,
@RequestParam(required = false, value = "type") String type,
@RequestParam(required = false, value = "page", defaultValue = "0") Integer page,
@RequestParam(required = false, value = "size", defaultValue = "-1") Integer size
) throws Exception {
LOGGER.log(Level.INFO, String.format("Executing /classes/search endpoint with parameters %s (term) and %s (page) and %s (size)", term, page, size));
LOGGER.log(Level.INFO, String.format("Executing /classes/search endpoint with parameters %s (term) %s (mod) %s (type) and %s (page) and %s (size)", term, mod, type, page, size));

// Escape user input
// Check user input
final String filteredMod = mod == "" || mod == null ? "" : escapeUserInput(mod);
final String filteredType = type == "" || type == null ? "" : escapeUserInput(type);
final String filteredTerm = escapeUserInput(term);

// Verify params
verifyParams(filteredMod, filteredType);

// Match using Bson filter
ArrayList<Document> matchedClasses = new ArrayList<Document>();
for (String modName : DATABASE.listCollectionNames()) {
ArrayList<Document> filteredContents = new ArrayList<Document>();
try {
// TODO: This will need updating as we add more numeric fields
retrieveCollection(modName).find(Filters.or(
Filters.eq("count", Long.parseLong(filteredTerm)),
Filters.eq("weight", Long.parseLong(filteredTerm))
)).into(filteredContents);
} catch (NumberFormatException e) {
retrieveCollection(modName).find(Filters.text(filteredTerm)).into(filteredContents);
}
// Filter by a user provided mod if one is given
if (!filteredMod.isEmpty() && modName.equals("data." + filteredMod)) {
matchedClasses.addAll(filterResults(retrieveCollection(modName), filteredType, filteredTerm));

// Add to final doc if match is found
if (filteredContents != null) {
matchedClasses.addAll(filteredContents);
// Break so we only return a singular mod spec
break;
} else if (filteredMod.isEmpty()) {
matchedClasses.addAll(filterResults(retrieveCollection(modName), filteredType, filteredTerm));
} else {
// We haven't reached the requested mod yet
continue;
}
}

Expand Down Expand Up @@ -247,27 +248,74 @@ private String escapeUserInput(String unfilteredInput) {
String filteredInput = "";
for (int i = 0; i < unfilteredInput.length(); i++) {
char currentCharecter = unfilteredInput.charAt(i);
if (!Config.getSpecialChars().contains(currentCharecter)) {
if (!config.getSpecialChars().contains(currentCharecter)) {
filteredInput += currentCharecter;
}
}
return filteredInput;
}

/**
* Filters a given collection by a user requested type or retrieves all the types if user input is not given.
* Checks the safe user input parameters against stored static config values
* @param filteredMod A mod to filter for (e.g. ace, vanilla, 3cb)
* @param filteredType The search term. It can be a classname, a config key or a config value
* @return True if all tests passed or an exception otherwise
* @throws Exception If a param failed to verify
*/
private Boolean verifyParams(String filteredMod, String filteredType) throws Exception {
if (!config.getMods().contains(filteredMod) && filteredMod != "") {
throw new Exception(String.format("Unidentified mod (%s). Available values are %s", filteredMod, config.getMods().toString()));
}
if (!config.getTypes().contains(filteredType) && filteredType != "") {
throw new Exception(String.format("Unidentified object type (%s). Available values are %s", filteredType, config.getTypes().toString()));
}
return true;
}

/**
* Filters a given collection by terminology or retrieves all the types if user input is not given.
* @param collection The collection to search in
* @param type The config type to filter the collection by
* @return A list of all the documents in the collection that matches the type (or all if no type was given)
* @param type The type to filter the collection by
* @param filteredTerm Search term to filter the results by
* @return A list of all the documents in the collection that fits the requested spec
*/
private ArrayList<Document> filterByType(MongoCollection<Document> collection, String type) {
private ArrayList<Document> filterResults(MongoCollection<Document> collection, String type, String filteredTerm) {
ArrayList<Document> filteredContents = new ArrayList<Document>();
if (type.isEmpty()) {
// Get all types
collection.find().into(filteredContents);
// Are we searching by a user provided term?
if (!filteredTerm.isEmpty()) {
try {
// Filter all types by term
if (type.isEmpty()) {
collection.find(Filters.or(
Filters.eq("count", Long.parseLong(filteredTerm)),
Filters.eq("weight", Long.parseLong(filteredTerm))
)).into(filteredContents);
} else {
// Filter by type and term
collection.find(Filters.and(
Filters.eq("type", type),
Filters.or(
Filters.eq("count", Long.parseLong(filteredTerm)),
Filters.eq("weight", Long.parseLong(filteredTerm))
)
)).into(filteredContents);
}
} catch (NumberFormatException e) {
// Couldn't parse a suspected long as a long, search as text instead
if (type.isEmpty()) {
collection.find(Filters.text(filteredTerm)).into(filteredContents);
} else {
collection.find(Filters.and(Filters.eq("type", type), Filters.text(filteredTerm))).into(filteredContents);
}
}
} else {
// Filter by type
collection.find(Filters.eq("type", type)).into(filteredContents);
if (type.isEmpty()) {
// Get all types
collection.find().into(filteredContents);
} else {
// Filter by type
collection.find(Filters.eq("type", type)).into(filteredContents);
}
}
return filteredContents;
}
Expand Down
Loading