spacebox/src/Node.cpp
Cocktail Frank 29a694200d Add sb::progress for save files, stats, and achievements
sb::progress encapsulates save progress objects, stats, and
achievements. sb::progress::Progress objects track progress using stats,
achievements, and any arbitrary user created game data. The data can be
saved and loaded through JSON serialization.

The sb::progress::Progress class is also used to unlock achievements and
update stats. They can optionally be synced directly with an app's
corresponding Stat and Achievement objects on Steam through the progress
object.

The sb::progress::Stat and sb::progress::Achievement classes represent
user defined stats and achievements. They have corresponding objects
that parse and load stats and achievements from a config file.

Other changes include:

JSON/Configuration:

- Upgrade nlohmann::json to v3.11.3

- Use nlohmann::json::merge_patch to merge JSON into configuration, which
  provides recursive merge

- Move json_from_file from Configuration to sb:: since it was already
  static and provides general access to a JSON object

- Move Configuration into sb:: namespace

- Move Configuration::access into sb::json_access to share with Progress
  class

Steam API:

- Automatically request stats when Steam API is initialized

- Add responses for events to Steam API event loop: UserStatsReceived,
  UserStatsStored, UserAchievementStored

- Add call for storing stats on Steam's servers

- Add publicly accessible boolean that controls whether or not sb::init
  tries to load Steam

Testing:

- Capture log messages with stdout in the test program before logging is
  initialized in the Game object

- In test cases that run a game, record start time within the mainloop
  to prevent the start time from lagging when the game takes a while to
  start running

- New tests: sb::progress, sb::json_access, setting stats and
  achievements with the Steam API

- Add another Dockerized Ubuntu build launching that launches the test
  on the container after it is built

Bug fix:

- Remove PostMix processor when Recorder object is destroyed to prevent
  the processor function (which doesn't exist anymore) from being called

Linting:

- superxbr.cpp, Delegate.hpp
2024-10-05 20:43:48 -04:00

114 lines
2.5 KiB
C++

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+-------------*/
#include "Game.hpp"
#include "Node.hpp"
Node::Node() : Node(nullptr) {}
Node::Node(Node* parent) : parent(parent) {}
void Node::set_parent(Node* other)
{
parent = other;
}
bool Node::is_active() const
{
return active;
}
const sb::Configuration& Node::configuration() const
{
return get_root()->configuration();
}
sb::Configuration& Node::configuration()
{
return get_root()->configuration();
}
const sb::Delegate& Node::delegate() const
{
return get_root()->delegate();
}
sb::Delegate& Node::delegate()
{
return get_root()->delegate();
}
sb::Delegate& Node::get_delegate()
{
return delegate();
}
const sb::Display& Node::get_display() const
{
return get_root()->display;
}
const std::shared_ptr<SDL_Window> Node::window() const
{
return get_root()->window();
}
std::shared_ptr<SDL_Window> Node::window()
{
return get_root()->window();
}
const Game* Node::get_root() const
{
const Node* r = this;
while (r->parent != NULL)
{
r = r->parent;
}
return dynamic_cast<const Game*>(r);
}
/* Get the window dimensions in pixels as a box object. If invert_y is set, the bottom left will be (0, 0).
* Otherwise, it the top left will be (0, 0). */
Box Node::window_box(bool invert_y)
{
return get_display().window_box(invert_y);
}
const std::string Node::get_branch_as_string() const
{
const Node* current = this;
std::stringstream branch;
while (current != nullptr)
{
branch << current->class_name() << " @ " << current;
if (current->parent != nullptr)
{
branch << " -> ";
}
current = current->parent;
}
return branch.str();
}
Node::~Node()
{
/* This logging call will segfault because it requires access to the configuration which may be deleted already:
*
* SDL_Log("Destroying Node %s", get_branch_as_string().c_str());
*/
/* This will cause problems for Delegates being destroyed but calling unsubscribe, among other undefined behavior
* issues (?)
*
* get_delegate().unsubscribe(this);
*/
}