Skip to content

Commit

Permalink
random_float, minor bugfixes/improvements, debug/release build comman…
Browse files Browse the repository at this point in the history
…ds, todo

* Modified token_gen.nvgt to use bitwise flags instead of a rigid enum.

* settings.nvgt can now be more easily extended as the list of available formats is no longer hardcoded. Adds test for settings include, though should be made to execute faster.

* The test runner now allows test cases to safely change the current working directory.

* Fix a couple of const qualifiers in ini.nvgt.

* It is now possible to specify prebuild and postbuild commands based on debug/release builds.

* Add random_float function.

* Update Todo (nonconclusive).
  • Loading branch information
samtupy committed Nov 28, 2024
1 parent 9f8c05b commit 17fd98c
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 50 deletions.
8 changes: 7 additions & 1 deletion doc/src/appendix/!To do.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ Especially since Survive the Wild has implemented voice chat support, people rig
NVGT allows a scripter to execute Angelscript code from within their Angelscript code, such as the python eval function. The user is given control of what builtin NVGT functions and classes these subscripts have access to, but it's still a bit rough. Basically Angelscript provides us with this 32 bit DWORD where we can map certain registered functions to bitflags and restrict access to them if a calling module's access bitmask doesn't include a flag the functions were registered with. However this means that we have 32 systems or switches to choose from, so either we need to assign builtin systems to them in a better way, or investigate this feature Angelscript has which is known as config groups and see if we can use them for permission control. C++ plugins in particular complicate this issue.

### Provide user facing pack file encryption
Currently pack file encryption uses internal methods requiring a user to rebuild NVGT to change the encryption routines, but there is full intention of also adding a `pack.set_encryption` so that users of NVGT can also manage how their packs are encrypted.
Currently pack file encryption uses internal methods requiring a user to rebuild NVGT to change the encryption routines, but there is full intention of also adding a `pack.set_encryption` so that users of NVGT can also manage how their packs are encrypted. A secondary SQL based pack file object has been contributed to the project which does include this functionality though we still intend to make it a part of the smaller, primary pack object.

### Integrate with Github releases
Currently, NVGT is downloaded from nvgt.gg/downloads instead of from Github releases. While it is likely that the mirror at nvgt.gg/downloads will remain in place, we plan to update our CI and properly use Github tags/releases.

### Angelscript version checks
One issue we run into relatively often is that someone will try building NVGT from source and then will report bytecode load errors to us because they're running from an outdated stub or something similar. We should encode the Angelscript version string into the bytecode payload and verify it upon application load. This way if bytecode gets attached to the wrong stub, we can catch it with a graceful error message instead of showing some random bytecode load aerror.

### get_last_error()
One area of NVGT that still needs heavy improvement is error handling. Some things use exceptions, some libraries have a get_error function, some things may use the backwards compatibility function get_last_error() etc. We need to find a way to unify this as much as possible into one system.
Expand Down
2 changes: 2 additions & 0 deletions doc/src/manual/-Toolkit Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,10 @@ This section contains options that are directly related to the compiling/bundlin
* output_basename = string default set from input filename: the output file or directory name of the final compiled package without an extension
* precommand = string: a custom system command that will be executed before the build begins if no platform specific command is set
* `precommand_<platform>` = string: allows the execution of custom prebuild commands on a per-platform basis
* `precommand_<platform>_<debug or release>` = string: allows the execution of custom prebuild commands on a per-platform basis with the condition of only exicuting on debug or release builds
* postcommand = string: a custom system command that will be executed after the build completes but before the success message
* `postcommand_<platform>` = string: allows the execution of custom postbuild commands on a per-platform basis
* `postcommand_<platform>_<debug or release>` = string: allows the execution of custom postbuild commands on a per-platform basis with the condition of only exicuting on debug or release builds
* product_identifier=string default com.NVGTUser.InputBasenameSlug: the reverse domain bundle identifier for your application (highly recommended to customize for mobile platforms, see compiling for distribution tutorial)
* product_identifier_domain = string defaults to com.NVGTUser: everything accept the final chunk of a reverse domain identifier (used only if build.product_identifier is default)
* product_name=string defaults to input file basename: human friendly display name of your application
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# token_gen_flag
This enum holds various constants that can be passed to the mode parameter in order to change how tokens are generated in the `generate_token` function.
This enum holds various constants that can be passed to the mode parameter of the generate_token function in order to control what characters appear in results.

* token_gen_flag_all: Uses all characters, numbers and symbols, see below.
* token_gen_flag_characters: Uses only characters, a-zA-Z
* token_gen_flag_numbers: Uses only numbers, 0-9
* token_gen_flag_symbols: Uses only symbols, \`\~\!\@\#\$\%\^\&\*\(\)\_\+\=\-\[\]\{\}\/\.\,\;\:\|\?\>\<
* token_gen_flag_numbers_symbols: Uses numbers and symbols.
* token_gen_flag_characters_symbols: Uses characters and symbols.
* TOKEN_CHARACTERS = 1: Allows for characters, a-zA-Z
* TOKEN_NUMBERS = 2: Allows for numbers, 0-9
* TOKEN_SYMBOLS = 4: Uses only symbols, \`\~\!\@\#\$\%\^\&\*\(\)\_\+\=\-\[\]\{\}\/\.\,\;\:\|\?\>\<

## Remarks:
These are flags that should be combined together using the bitwise OR operator. To generate a token containing characters and symbols, for example, you would pass `TOKEN_CHARACTERS | TOKEN_NUMBERS | TOKEN_SYMBOLS` to the mode argument of generate_token. The default flags are TOKEN_CHARACTERS | TOKEN_NUMBERS.
28 changes: 14 additions & 14 deletions release/include/ini.nvgt
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ class ini {
}

// Fetch a string given a section and key. All getters will use this format, and if one returns a default value (blank string, an int that equals 0, a boolean that equals false etc), and if you want to know whether the key actually existed, use the error checking system.
string get_string(const string& in section, const string& in key, string default_value = "") {
string get_string(const string& in section, const string& in key, const string&in default_value = "") {
ini_section@ tmp = this.get_section_verified(section, key, true, "get string");
if (@tmp != null)
return tmp.get_string(key);
Expand Down Expand Up @@ -581,7 +581,7 @@ class ini_section {
}

// Add a raw string value to the section. If the key already exists, overwrite it in it's current place. This is the base function that is used to set all other datatypes, since they all end up as strings. The raw argument controls whether the string should be escaped before adding or whether this is raw data. It is mostly used internally and it is safe to keep it at false (the default) unless there is not a suitable datatype in this class for your situation and you're trying to write your own or something. Regardless only change the raw argument if you know what your doing.
void set_string(const string& in key_name, string value, bool raw = false) {
void set_string(const string& in key_name, const string&in value, bool raw = false) {
if (!this.keys.exists(key_name))
this.key_names.insert_last(key_name);
if (!raw)
Expand Down Expand Up @@ -635,21 +635,21 @@ bool INI_IS_VALID_IDENT_CHAR(const string& in char) {
}

// Escape backslash, cama, colon and semicolon
string INI_ESCAPE_STRING(string value) {
value.replace_this("\\", "\\\\", true);
value.replace_this(",", "\\,", true);
value.replace_this(":", "\\:", true);
value.replace_this(";", "\\;", true);
return value;
string INI_ESCAPE_STRING(const string&in value) {
string result = value.replace("\\", "\\\\", true);
result.replace_this(",", "\\,", true);
result.replace_this(":", "\\:", true);
result.replace_this(";", "\\;", true);
return result;
}

// Unescape backslash, cama, colon and semicolon
string INI_UNESCAPE_STRING(string value) {
value.replace_this("\\\\", "\\", true);
value.replace_this("\\,", ",", true);
value.replace_this("\\:", ":", true);
value.replace_this("\\;", ";", true);
return value;
string INI_UNESCAPE_STRING(const string&in value) {
string result = value.replace("\\\\", "\\", true);
result.replace_this("\\,", ",", true);
result.replace_this("\\:", ":", true);
result.replace_this("\\;", ";", true);
return result;
}

// Verify an identifier to make sure keys and section names with invalid characters aren't permitted. A blank string returned means that the identifier is OK, anything else is error text.
Expand Down
20 changes: 13 additions & 7 deletions release/include/settings.nvgt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/

#include"ini.nvgt"

class settings_helper {
string company_name, product;
private bool can_encrypt_data = false;
Expand Down Expand Up @@ -88,6 +89,8 @@ class settings_helper {
return false;
}
}
funcdef settings_helper@ settings_helper_factory(settings@ parent);

class ini_settings_helper: settings_helper {
ini@config;
ini_settings_helper(settings@parent) {
Expand Down Expand Up @@ -155,6 +158,8 @@ class ini_settings_helper: settings_helper {
return true;
}
}
settings_helper@ ini_settings_factory(settings@ parent) { return ini_settings_helper(parent); }

class json_settings_helper: settings_helper {
json_object@config;
json_settings_helper(settings@parent) {
Expand Down Expand Up @@ -220,6 +225,8 @@ class json_settings_helper: settings_helper {
return true;
}
}
settings_helper@ json_settings_factory(settings@ parent) { return json_settings_helper(parent); }

class nvgt_settings_helper: settings_helper {
dictionary@config;
nvgt_settings_helper(settings@parent) {
Expand Down Expand Up @@ -293,6 +300,10 @@ class nvgt_settings_helper: settings_helper {
return true;
}
}
settings_helper@ nvgt_settings_factory(settings@ parent) { return nvgt_settings_helper(parent); }

dictionary available_settings_formats = {{"ini", @ini_settings_factory}, {"json", @json_settings_factory}, {"nvgt", @nvgt_settings_factory}};

class settings {
string company_name, product;
bool local_path = false;
Expand Down Expand Up @@ -361,19 +372,14 @@ class settings {
this.company_name = company;
this.product = product;
this.local_path = local_path;
settings_helper@temp = find_settings_helper(format, this);
settings_helper_factory@ factory = cast<settings_helper_factory@>(available_settings_formats[format]);
settings_helper@ temp = @factory != null? factory(this) : null;
if (@temp == null) return false;
bool success = temp.setup(company, product, local_path);
if (success) @this.data_helper = temp;
return success;
}
}
settings_helper@find_settings_helper(string&in format, settings@parent) {
if (format == "json") return json_settings_helper(parent);
else if (format == "ini") return ini_settings_helper(parent);
else if (format == "nvgt") return nvgt_settings_helper(parent);
return null;
}
bool ensure_data_path(string&in filename) {
//make sure that all directories within the given path exist.
string[] paths = filename.split("/", false);
Expand Down
31 changes: 12 additions & 19 deletions release/include/token_gen.nvgt
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,24 @@

//Possible token modes.
enum token_gen_flag {
token_gen_flag_all = 0,
token_gen_flag_characters,
token_gen_flag_numbers,
token_gen_flag_symbols,
token_gen_flag_numbers_symbols,
token_gen_flag_characters_symbols
TOKEN_CHARACTERS = 1 << 0,
TOKEN_NUMBERS = 1 << 1,
TOKEN_SYMBOLS = 1 << 2
}

string generate_token(int token_length, int mode = token_gen_flag_all) {
string chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
string numbers = "1234567890";
string symbols = "`~!@#$%^&*()_+=-[]{}/.,;:|?><";
string token_sims = chars + numbers + symbols;
if (mode == token_gen_flag_characters) token_sims = chars;
else if (mode == token_gen_flag_numbers) token_sims = numbers;
else if (mode == token_gen_flag_symbols) token_sims = symbols;
else if (mode == token_gen_flag_numbers_symbols) token_sims = numbers + symbols;
else if (mode == token_gen_flag_characters_symbols) token_sims = chars + symbols;
return generate_custom_token(token_length, token_sims);
string generate_token(int token_length, int mode = TOKEN_CHARACTERS | TOKEN_NUMBERS) {
string chars;
if (mode & TOKEN_CHARACTERS != 0) chars += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (mode & TOKEN_NUMBERS != 0) chars += "1234567890";
if (mode & TOKEN_SYMBOLS != 0) chars += "`~!@#$%^&*()_+=-[]{}/.,;:|?><";
return generate_custom_token(token_length, chars);
}

string generate_custom_token(int token_length, string characters) {
string generate_custom_token(int token_length, const string&in characters) {
if (characters.empty() || token_length <= 0) return "";
string final_token;
final_token.resize(token_length);
for (uint i = 0; i < token_length; i++)
final_token += characters[random(0, characters.length() - 1)];
final_token[i] = characters[random(0, characters.length() - 1)];
return final_token;
}
4 changes: 2 additions & 2 deletions src/bundling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class nvgt_compilation_output_impl : public virtual nvgt_compilation_output {
stubpath = format("%snvgt_%s%s.bin", stubpath.toString(), platform, (stub != "" ? string("_") + stub : ""));
outpath = config.getString("build.output_basename", Path(input_file).setExtension("").toString());
alter_output_path(outpath);
string precommand = config.getString("build.precommand_" + g_platform, config.getString("build.precommand", ""));
string precommand = config.getString("build.precommand_" + g_platform + "_"s + (g_debug? "debug" : "release"), config.getString("build.precommand" + g_platform, config.getString("build.precommand", "")));
if (!precommand.empty()) {
set_status("executing prebuild command...");
if (!user_command(precommand)) throw Exception("prebuild command failed");
Expand Down Expand Up @@ -183,7 +183,7 @@ class nvgt_compilation_output_impl : public virtual nvgt_compilation_output {
fs.close();
finalize_product(outpath);
output_file = outpath.toString();
string postcommand = config.getString("build.postcommand_" + g_platform, config.getString("build.postcommand", ""));
string postcommand = config.getString("build.postcommand_" + g_platform + "_"s + (g_debug? "debug" : "release"), config.getString("build.postcommand" + g_platform, config.getString("build.postcommand", "")));
if (!postcommand.empty()) {
set_status("executing postbuild command...");
if (!user_command(postcommand)) throw Exception("postbuild command failed");
Expand Down
4 changes: 4 additions & 0 deletions src/random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ std::string random_get_state() {
int random(int min, int max) {
return rnd_pcg_range(&rng, min, max);
}
float random_float() {
return rnd_pcg_nextf(&rng);
}
bool random_bool(int percent) {
if (percent < 1) return false;
if (percent >= 100) return true;
Expand Down Expand Up @@ -109,6 +112,7 @@ void RegisterScriptRandom(asIScriptEngine* engine) {
engine->RegisterGlobalFunction(_O("uint random_seed()"), asFUNCTION(random_seed), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("uint64 random_seed64()"), asFUNCTION(random_seed64), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("int random(int, int)"), WRAP_FN_PR(random, (int, int), int), asCALL_GENERIC);
engine->RegisterGlobalFunction(_O("float random_float()"), asFUNCTION(random_float), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("bool random_bool(int = 50)"), asFUNCTION(random_bool), asCALL_CDECL);
engine->RegisterGlobalFunction(_O("string random_character(const string& in, const string& in)"), asFUNCTION(random_character), asCALL_CDECL);
engine->RegisterObjectMethod(_O("array<T>"), _O("const T& random() const"), asFUNCTION(random_choice), asCALL_CDECL_OBJFIRST);
Expand Down
39 changes: 39 additions & 0 deletions test/case/settings_include.nvgt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "settings.nvgt"
#include "token_gen.nvgt"

// We run this test multiple times, once per format.
void evaluate_format(string format, settings@ s = null) {
if (@s == null) @s = settings(); // Allow already existing objects to test whether a settings object can close one format and open another.
assert(s.setup("NVGTTesting", "testing_framework", true, format));
uint key_count = 10;
string[] strings(key_count);
double[] numbers(key_count);
for (uint i = 0; i < key_count; i++) {
strings[i] = generate_token(i * 10);
assert(s.write_string("s" + i, strings[i]));
assert(s.read_string("s" + i) == strings[i]);
numbers[i] = random(i * 2, i * 10) + random_float();
assert(s.write_number("n" + i, numbers[i]));
assert(s.read_number("n" + i) == numbers[i]);
}
assert(s.close());
assert(s.setup("NVGTTesting", "testing_framework", true, format));
for (uint i = 0; i < key_count; i++) {
assert(s.read_string("s" + i) == strings[i]);
assert(s.read_number("n" + i) == numbers[i]);
}
assert(s.remove_product());
}

void test_settings_include() {
directory_delete("NVGTTesting"); // Start from scratch.
settings s;
assert(s.setup("NVGTTesting", "testing_framework", true));
assert(!s.has_other_products());
evaluate_format("ini");
evaluate_format("ini", s);
evaluate_format("json");
evaluate_format("json", s);
evaluate_format("nvgt");
evaluate_format("nvgt", s);
}
3 changes: 3 additions & 0 deletions test/tests.nvgt
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,11 @@ void test(const string&in testcase) {
}
string[] errors;
if (verbose) outp("Testing %0... ".format(testcase));
// Give test cases the freedom to safely manipulate the current working directory.
string current_dir = cwdir();
timer test_clock(0, 1);
func({}, errors);
chdir(current_dir);
if (errors.length() < 1) {
if (verbose) outln("OK (" + (test_clock.elapsed / double(MILLISECONDS)) + "ms)");
tests_passed++;
Expand Down

0 comments on commit 17fd98c

Please sign in to comment.