-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add extension to write to cba_settings.sqf file #1333
base: master
Are you sure you want to change the base?
Conversation
while (file.good()) { | ||
getline(file, line); | ||
if (file.good() || line.length() != 0) // Don't explode file by appending duplicate additional newlines. | ||
result = result + trim(line) + "\n"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
result = result + trim(line) + "\n"; | |
result.append(trim(line) + "\n"); |
} | ||
|
||
string load(int strlen) { | ||
string result = ""; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
reserve.
result.reserve(std::filesystem::size(SETTINGS_FILE));
Otherwise you resize/reallocate/copy almost every time you append a line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, didn't know filestream::size
is a thing. Though, is it reasonable to allocate ~10k characters (bytes?) at once?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well more reasonable than first allocating 16, then deleting and reallocating 32, then deleting and reallocating 64, then deleting and reallocating 128, then deleting and reallocating 512, then deleting and reallocating 2048, then deleting and reallocating 8192, then...
return "true"; | ||
} | ||
|
||
string load(int strlen) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strlen is the name of a function, please don't use that as variable name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Haha. Oops.
line = trim_left(line); | ||
|
||
// Ignore commented out lines. | ||
if (line.substr(0, 2) == "//") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This copies the string every time. Would be WAY more efficient to use a string_view here, as that is all that you need. For trim_left too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also why trim left and right seperately? why not immediately trim both sides?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not immediately trim both sides?
force force setting = 123;
is something one could write. If I process this, I need multiple trims, but not on both sides all the time.
return str.substr(0, last + 1); | ||
} | ||
|
||
string trim(string str) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Example for a more efficient trim that doesn't copy the string
https://github.com/dedmen/armake/blob/cpp/src/utils.cpp#L240
if (argsCnt >= 3) | ||
force = min(stoi(args[2]), 2); | ||
|
||
if (_stricmp(function, "version") == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why even check the whole string?
c
l
p
r
a
the first character would be sufficient for what you need
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True I guess, but what does it matter? This is not something that needs to be particularly fast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't really matter, just my "every nanosecond counts" bullshit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand.
string result = "false"; | ||
|
||
ofstream temp; | ||
temp.open(string(TEMP_FILE).c_str(), ios::out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are reloading, reparsing, rewriting the whole file for every setting property write?
You could just parse it once, and store it in memory. Your extension won't get unloaded
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
store it in memory
How?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment below, just a global variable. Array or map or some container
|
||
// Count force tags. | ||
int force = 0; | ||
while (line.substr(0, 6) == "force ") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are copying the substring on every iteration here. If you use the string_view trim, it will only move a pointer around
I would struct settingEntry { std::unordered_map<std::string, settingEntry> settings; on parse then clear map and parse entries into it. write(setValue) is settings[...] = ... + write also then no need for append vs write, as duplicate entries don't make sense anyway?
is it? I thought callbacks were unlimited |
It only gave me that long a string (17 something k characters) when I tried how large a settings file I can get. |
Co-authored-by: Dedmen Miller <[email protected]>
But what if I edit the file while the game is running? |
store file change date of last parse/write. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some comments.
strncpy_s(output, outputSize, result.c_str(), _TRUNCATE);\ | ||
return (val) | ||
|
||
#define SYNTAX_ERROR_WRONG_PARAMS_SIZE 101 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since dedmen is suggesting c++17 features (string_view, filesystem) it is also advisable to use for numerical data the following:
#define SYNTAX_ERROR_WRONG_PARAMS_SIZE 101 | |
static constexpr int32_t SYNTAX_ERROR_WRONG_PARAMS_SIZE = 101; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the benefit over
const int SYNTAX_ERROR_WRONG_PARAMS_SIZE = 101;
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
constexpr is evaluated at compile time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which won't make any difference here as the end result.
#define DELIM ":" | ||
#define MAX_CALLBACK_LENGTH 10000 | ||
|
||
using namespace std; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not recommended in general and it is rather dangerous.
|
||
file.close(); | ||
|
||
if (strlen * MAX_CALLBACK_LENGTH > result.length()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not writing { } after if/while etc is not recommended at all. Consider the following
if (strlen * MAX_CALLBACK_LENGTH > result.length())
do_stuf()
do_other_stuff
and then one decides to comment do_stuff
if (strlen * MAX_CALLBACK_LENGTH > result.length())
// do_stuf()
do_other_stuff
resulting in do_other_stuff being inside the if
context breaking a lot of stuff
return result; | ||
} | ||
|
||
string append(string setting, string value, int force) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passing by reference is advisable with classes. It avoids copying the whole class. The same can be applied to the other functions.
string append(string setting, string value, int force) { | |
string append(const string &setting, const string &value, int force) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool. Didn't know it copies, and that this is how one prevents it.
|
||
string line = ""; | ||
while (force-- > 0) | ||
line = line + "force "; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
line = line + "force "; | |
line.append("force "); |
When merged this pull request will:
It would be super convenient if settings could be written by the game to the settings files directly. So I wanted to have an extension for that. Pardon my poor C++, I have no idea what's going on, lol
READ
-> reads setting value & force lvlWRITE
-> overwrites setting with given value & force lvlCLEAR
-> empties cba_settings.sqfPARSE
-> gives aparseSimpleArray
'able super array of all settings, value, force lvl triplets. Since extension callback is limited by like 17k chars, this comes in batches of 10k. The idea is to append the 10k batches in SQF before using PSA.Currently only works for the main game userconfig, not for missions (need to figure out the system path).