-
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
Replay system part 1 #379
Merged
Merged
Replay system part 1 #379
Changes from 5 commits
Commits
Show all changes
41 commits
Select commit
Hold shift + click to select a range
c324647
record and replay srand() in an action log
NQNStudios 5374a20
pre-declare action log vars/functions for global use
NQNStudios 8ae1585
windows and linux record preferences at startup
NQNStudios a9f098d
don't save preferences when replaying
NQNStudios 75bd1e9
allow providing base filename for log recording
NQNStudios aee9d8f
Reorganize global replay code
NQNStudios fead544
don't double-compile ticpp.cpp
NQNStudios 77b73a9
all scons platforms add src include directories
NQNStudios 8c63c4f
include string and sstream directly
NQNStudios d692f70
visual studio projects add new files
NQNStudios f50bfe4
try adding replay.cpp to xcode projects
NQNStudios c29931e
refactor without raw pointers
NQNStudios f508235
add cppcodec header library for base64
NQNStudios 4349fb6
action log has_next_action()
NQNStudios 8cee7a4
WIP encode and decode save files in action logs
NQNStudios 63e7a07
put decoded replay save in tempDir
NQNStudios da76e16
finish_load_party()
NQNStudios f9a7615
add cppcodec to visual studio include paths
NQNStudios 36a1e1d
xcode add cppcodec to search paths
NQNStudios aee878e
add src/tools to xcode header search path
NQNStudios edf7ca3
fix command-line arg docs
NQNStudios 93fffe3
allow parsing locations
NQNStudios 3eaa3fe
record movement actions
NQNStudios 9961a09
no spaces after ifs
NQNStudios 78b615d
only record movement in recording mode
NQNStudios b3cb33f
replay movement actions
NQNStudios 3a79aac
allow recording action with multiple parameters
NQNStudios fd785fb
record clicks on dialog controls
NQNStudios e307bc8
allow peeking the type of next replay action
NQNStudios 3d61a67
parse std::map of replay action info elements
NQNStudios eade4fd
replay control clicks in open dialogs
NQNStudios 9d36086
pop_next_action() iterate through without actually removing
NQNStudios 391fb3b
record and replay control focus events
NQNStudios 2b68e13
record and replay startup button clicks
NQNStudios aab34a7
when replaying, don't open file browsers
NQNStudios 5fec4e9
use nullptr
NQNStudios ded0a2f
use specific stringstream types
NQNStudios b4c300c
put extern forward-declarations first
NQNStudios 43f1482
make pop_next_action() return a reference
NQNStudios cea3edd
throw error when checking next_action_type() of null
NQNStudios c935d55
throw std::strings
NQNStudios File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,10 +2,15 @@ | |
#include "boe.global.hpp" | ||
#include "universe/universe.hpp" | ||
|
||
#include <boost/algorithm/string/predicate.hpp> | ||
#include <boost/filesystem/operations.hpp> | ||
#include <boost/lexical_cast.hpp> | ||
#include <unordered_map> | ||
#include <string> | ||
#include <memory> | ||
#include <iostream> | ||
#include <ctime> | ||
#include <sstream> | ||
#include "boe.graphics.hpp" | ||
#include "boe.newgraph.hpp" | ||
#include "boe.fileio.hpp" | ||
|
@@ -215,12 +220,95 @@ static void init_ui() { | |
init_buttons(); | ||
} | ||
|
||
// Input recording system | ||
|
||
#include <ctime> | ||
#include <iomanip> | ||
|
||
bool recording = false; | ||
bool replaying = false; | ||
|
||
using namespace ticpp; | ||
Document log_document; | ||
std::string log_file; | ||
|
||
bool init_action_log(std::string command, std::string file) { | ||
if (command == "record") { | ||
// If a filename is given, use it as a base, but insert a timestamp for uniqueness. | ||
if (file.empty()) | ||
file = "BoE"; | ||
|
||
if (boost::ends_with(file, ".xml")) | ||
file = file.substr(0, file.length() - 4); | ||
|
||
// Get a time stamp | ||
std::time_t t = time(nullptr); | ||
auto tm = *std::localtime(&t); | ||
std::ostringstream stream; | ||
stream << file << std::put_time(&tm, "_%d-%m-%Y_%H-%M-%S") << ".xml"; | ||
log_file = stream.str(); | ||
|
||
try { | ||
Element root_element("actions"); | ||
log_document.InsertEndChild(root_element); | ||
log_document.SaveFile(log_file); | ||
recording = true; | ||
std::cout << "Recording this session: " << log_file << std::endl; | ||
} catch(...) { | ||
std::cout << "Failed to write to file " << log_file << std::endl; | ||
} | ||
return true; | ||
} | ||
else if (command == "replay") { | ||
try { | ||
log_document.LoadFile(file); | ||
replaying = true; | ||
} catch(...) { | ||
std::cout << "Failed to load file " << file << std::endl; | ||
} | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
void record_action(std::string action_type, std::string inner_text) { | ||
Element* root = log_document.FirstChildElement(); | ||
Element next_action(action_type); | ||
Text action_text(inner_text); | ||
next_action.InsertEndChild(action_text); | ||
root->InsertEndChild(next_action); | ||
log_document.SaveFile(log_file); | ||
} | ||
|
||
Element* pop_next_action(std::string expected_action_type) { | ||
Element* root = log_document.FirstChildElement(); | ||
Element* next_action = root->FirstChildElement(); | ||
|
||
if (expected_action_type != "" && next_action->Value() != expected_action_type) { | ||
std::ostringstream stream; | ||
stream << "Replay error! Expected '" << expected_action_type << "' action next"; | ||
throw stream.str(); | ||
} | ||
|
||
Element* clone = next_action->Clone()->ToElement(); | ||
root->RemoveChild(next_action); | ||
return clone; | ||
} | ||
|
||
void process_args(int argc, char* argv[]) { | ||
// Command line usage: | ||
// "Blades of Exile" # basic launch | ||
// "Blades of Exile" <save file> # launch and load save file | ||
|
||
// "Blades of Exile" record <optional file> # record this session in a time-stamped xml file | ||
// "Blades of Exile" play <file> # replay a session from an xml file | ||
if (argc > 1) { | ||
std::string file = ""; | ||
if (argc > 2) { | ||
file = argv[2]; | ||
} | ||
if (init_action_log(argv[1], file)) | ||
return; | ||
|
||
if(!load_party(argv[1], univ)) { | ||
std::cout << "Failed to load save file: " << argv[1] << std::endl; | ||
return; | ||
|
@@ -245,21 +333,38 @@ void init_boe(int argc, char* argv[]) { | |
#ifdef __APPLE__ | ||
init_menubar(); // Do this first of all because otherwise a default File and Window menu will be seen | ||
#endif | ||
|
||
// TODO preferences need to be recorded in the action log for deterministic replay | ||
sync_prefs(); | ||
init_shaders(); | ||
init_tiling(); | ||
init_snd_tool(); | ||
|
||
init_ui(); | ||
|
||
adjust_window_mode(); | ||
// If we don't do this now it'll flash white to start with | ||
mainPtr.clear(sf::Color::Black); | ||
mainPtr.display(); | ||
|
||
set_cursor(watch_curs); | ||
init_buf(); | ||
srand(time(nullptr)); | ||
|
||
// Seed the RNG | ||
if (replaying) { | ||
auto srand_element = pop_next_action("srand"); | ||
|
||
std::string ts(srand_element->GetText()); | ||
srand(atoi(ts.c_str())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seeing this made me realize that we may want to consider moving away from |
||
} else { | ||
auto t = time(nullptr); | ||
if (recording) { | ||
std::string ts = boost::lexical_cast<std::string>(t); | ||
record_action("srand", ts); | ||
} | ||
srand(t); | ||
} | ||
std::cout << rand() << std::endl; | ||
init_screen_locs(); | ||
init_startup(); | ||
flushingInput = true; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 Boost.Algorithm is a new dependency, right? I don't mind adding it, but it needs to be mentioned in the README.
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.
It didn't need to be added to the setup of the CI for any of the platforms--it must be a dependency of one of the packages we already install.
The readme says
So I think it could fall under that umbrella.
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.
Nice to see that the readme is contradicting itself slightly – the vcpkg instructions do enumerate every necessary Boost library, after all. (Though I'm not confident there are no others missing.)