Files
aya/engine/app/src/DataModel/ChangeHistory.hpp
2025-12-17 16:47:48 +00:00

167 lines
5.1 KiB
C++

#pragma once
#include "Tree/Service.hpp"
#include "Utility/RunStateOwner.hpp"
#include "Voxel/Cell.hpp"
#include "Voxel/CellChangeListener.hpp"
#include "Utility/SpatialRegion.hpp"
#include <queue>
#include "Voxel2/GridListener.hpp"
namespace Aya
{
class DataModel;
class MegaClusterInstance;
extern const char* const sChangeHistoryService;
class ChangeHistoryService
: public DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL>
, public Service
, public Voxel::CellChangeListener
, public Voxel2::GridListener
{
private:
typedef DescribedCreatable<ChangeHistoryService, Instance, sChangeHistoryService, Reflection::ClassDescriptor::INTERNAL> Super;
DataModel* dataModel;
class Item;
class Waypoint;
shared_ptr<MegaClusterInstance> megaClusterInstance;
Waypoint* recording;
typedef std::list<Waypoint*> Waypoints; // must be a list and not a vector because we maintain iterators after reallocs
Waypoints waypoints;
Waypoints::iterator playWaypoint; // playing nextWaypoint is a "redo"
Waypoints::iterator unplayWaypoint; // unplaying unplayWaypoint is an "undo"
Waypoints::iterator runStartWaypoint; // unplaying up to runStartWaypoint is a "reset"
bool playing;
bool enabled;
int dataSize;
shared_ptr<RunService> runService;
shared_ptr<Instance> statsItem;
Aya::signals::scoped_connection itemAddedConnection;
Aya::signals::scoped_connection itemRemovedConnection;
Aya::signals::scoped_connection itemChangedConnection;
Aya::signals::scoped_connection runTransitionConnection;
public:
static const int minWaypoints = 3;
static const int maxWaypoints = 250;
static const int maxMemoryUsage = 250 * 1024 * 1024;
typedef enum
{
Aggregate, // Don't allow waypoints while running. All changes are recorded and made part of the reset waypoint
Snapshot, // When recording waypoints while running, take a snapshot of positions and velocities
Hybrid // When recording waypoints while running, ignore unrelated position and velocity changes
} RuntimeUndoBehavior;
static RuntimeUndoBehavior runtimeUndoBehavior;
ChangeHistoryService();
~ChangeHistoryService();
static void requestWaypoint(const char* name, const Instance* context);
void resetBaseWaypoint();
void clearWaypoints();
void setEnabled(bool state);
bool isEnabled() const
{
return enabled;
}
void requestWaypoint(const char* name);
void requestWaypoint2(std::string name)
{
requestWaypoint(name.c_str());
}
// TODO: rename play-->redo unplay-->undo???
bool getPlayWaypoint(std::string& name, int steps = 0) const;
bool getUnplayWaypoint(std::string& name, int steps = 0) const;
bool canPlay() const
{
std::string name;
return getPlayWaypoint(name);
}
bool canUnplay() const
{
std::string name;
return getUnplayWaypoint(name);
}
// returns a tuple: bool state, [string name]
shared_ptr<const Reflection::Tuple> canUnplay2();
shared_ptr<const Reflection::Tuple> canPlay2();
// std::string getPlayName() const { std::string name; getPlayWaypoint(name); return name; }
// std::string getUnplayName() const { std::string name; getUnplayWaypoint(name); return name; }
void play();
void playLua();
void unplay();
void unplayLua();
// to show progress dialog in studio
boost::function<void(boost::function<void()>, std::string)> withLongRunningOperation;
bool isResetEnabled() const;
void reset();
int getWaypointDataSize() const
{
return dataSize;
}
int getWaypointCount() const
{
return waypoints.size();
}
Aya::signal<void()> waypointChangedSignal;
Aya::signal<void(std::string)> undoSignal;
Aya::signal<void(std::string)> redoSignal;
MegaClusterInstance* getTerrain() const
{
return megaClusterInstance.get();
}
protected:
virtual void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider);
private:
void attach();
void dettach();
void trimWaypoints();
void mergeFirstTwoWaypoints();
void computeDataSize();
virtual void onItemAdded(shared_ptr<Aya::Instance> item);
virtual void onItemRemoved(shared_ptr<Aya::Instance> item);
virtual void onItemChanged(shared_ptr<Aya::Instance> item, const Reflection::PropertyDescriptor* descriptor);
bool isRecordable(Instance* instance);
/*override*/ virtual void terrainCellChanged(const Voxel::CellChangeInfo& cell);
/*override*/ virtual void onTerrainRegionChanged(const Voxel2::Region& region);
void setCell(const SpatialRegion::Id& chunkPos, const Vector3int16& cellInChunk, Voxel::Cell detail, Voxel::CellMaterial material);
typedef enum
{
Accept,
Reject
} CheckResult;
CheckResult checkSettingWaypoint();
void onRunTransition(RunTransition event);
void setWaypoint(const char* name);
void setRunWaypoint();
void reportMissedPhysicsChanges(shared_ptr<Aya::Instance> instance);
};
} // namespace Aya