-
Notifications
You must be signed in to change notification settings - Fork 44
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
[WIP] Replay system #365
[WIP] Replay system #365
Conversation
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.
Preferences need to be recorded and retrieved by the replay system, or else the replays won't be deterministic in a lot of very unpredictable ways.
On Mac, a snazzy system library is used to manage preferences. But I think we should change things so that Mac handles preferences the same as Windows and Linux, because that way recording and replay can be deterministic on Mac 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.
I'd prefer not to change how the Mac stores preferences, and I don't understand how the Mac's preferences system gets in the way of what you want to do.
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.
So, looking through the code it seems like a Mac library handles all of the preference syncing behind the scenes, and I couldn't find an API function that would let me iterate through and serialize all of the preferences. It looked like the Mac API provides a dictionary object where you have to know a key to get it's value.
Which is really, really, weird. So maybe I just missed something.
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.
https://github.com/calref/cboe/blob/master/src/tools/prefs.mac.mm#L73
Under the hood, MacOS is using this NSUserDefaults object to store preferences: https://developer.apple.com/documentation/foundation/nsuserdefaults
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.
Oh. This might be what I was looking for and couldn't find: https://developer.apple.com/documentation/foundation/nsuserdefaults/1415919-dictionaryrepresentation
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.
So maybe I can use that function to write Mac-specific logic that serializes and deserializes the NSUserDefaults, but that seems weird to me, when we could run the same code on all 3 platforms instead.
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 also don't know the programming language that prefs.mac.mm
is written in, which makes things awkward. Is it Objective-C?
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.
Yes and no. It's Objective-C++ – an unholy fusion of C++ and Objective-C.
You should probably be aware that things other than the core preferences are stored in the MacOS preferences plist. For example, it'll remember the last folder you opened a saved game from. Such things likely shouldn't be persisted in the replay, as they are platform-dependent.
Theoretically, the scenario and character editors could also have replay systems
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.
Looks decent enough as a starting point. The goal of recording the entire application (as opposed to just a game session) seems slightly absurd to me, but I guess we'll see.
} | ||
|
||
void record_action(std::string action_type, std::string inner_text) { | ||
Element* root = log_document.FirstChildElement(); |
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 isn't really an issue, but I'm noticing you didn't use the XML printer class that the scenario editor uses to write scenario files. It might not actually be appropriate here, I'm not sure; but in case you simply didn't know about it, I thought I'd mention it.
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 situation with this? Is it appropriate to use my XML printer class here or not?
src/tools/replay.cpp
Outdated
@@ -83,8 +83,7 @@ std::unique_ptr<Element> pop_next_action(std::string expected_action_type) { | |||
Element* clone = next_action->Clone()->ToElement(); | |||
root->RemoveChild(next_action); | |||
|
|||
std::unique_ptr<Element> ptr { clone }; | |||
return ptr; | |||
return *clone; |
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.
If this is what you're doing, do you really need to clone? Couldn't you just replace the clone line by Element clone = *next_action;
?
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.
Calling RemoveChild() on next_action was freeing the data. Cloning is necessary to keep it around without leaving it in the document object
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.
Are you saying that Element clone = *next_action
does not copy it?
But as it is now, isn't the clone still being leaked?
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.
But as it is now, isn't the clone still being leaked?
when a block does
{
Element next = pop_next_action()
}
I thought the block is taking ownership of the data, then when the scope ends, it gets deleted.
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.
Are you saying that Element clone = *next_action does not copy it?
Maybe it would.
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.
For it to not crash, Clone() is required.
Element clone = *(next_action->Clone()->ToElement());
This works. Does that avoid a memory leak?
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 think calling Clone()
in and of itself might be a memory leak, but I'd have to see the internals of Clone()
be sure. The typical internals of a function called clone is essentially return new ClassName(*this)
, and if that's what it's doing, then this line is certainly a memory leak. But maybe Clone()
is actually doing something else. I'm not sure.
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 looked into Clone() and it's definitely calling new
. Which brings me full-circle to what I was trying to accomplish by returning a unique_ptr
: I thought wrapping the pointer returned by Clone() in unique_ptr would make the unique_ptr
manage that memory, so when the pointer went out of scope in the calling context of pop_next_action()
, it would delete the clone.
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 think the document object might still manage the memory of the pointer returned by Clone()
though. Because I get a segfault when unique_ptr
manages the pointer and frees it.
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 looked into Clone() and it's definitely calling
new
. Which brings me full-circle to what I was trying to accomplish by returning aunique_ptr
: I thought wrapping the pointer returned by Clone() in unique_ptr would make theunique_ptr
manage that memory, so when the pointer went out of scope in the calling context ofpop_next_action()
, it would delete the clone.
This is a more or less correct line of reasoning. The only problem is, it's not clear whether deleting the clone is the correct way to dispose of it.
* DRY, standardized window top offset * handle_splash_events() handle multiple events per frame * accurate windows menubar height for multiple rows * Windows filter a resize event triggered by the menubar * windows expand small window to fit menubar * splash screens draw in view rect, not window rect
That's annoying -- I wanted to rebase this onto master and it ended up adding commits from master into the PR EDIT: trying again. |
bf9900f
to
43ef19c
Compare
Kinda looks like you rebased it backwards? If you're on the replay branch, you want to rebase it onto master with |
This is the big one.