Started work on new undo system

This commit is contained in:
Yousef Khadadeh 2026-01-02 17:11:32 +03:00
parent da3a764ae9
commit be2ba1e285
8 changed files with 182 additions and 4 deletions

View file

@ -49,6 +49,7 @@ set(sources "src/main.cpp"
"src/FileHelpers.cpp"
"src/VersionConstants.cpp"
"src/ScaleUpCanvas.cpp"
"src/WorldUndoManager.cpp"
"src/Bookmarks/BookmarkListItem.cpp"
"src/Bookmarks/BookmarkManager.cpp"
"src/ResourceDisplay/ResourceDisplay.cpp"

View file

@ -43,6 +43,12 @@ namespace NetworkingObjects {
bool NetObjManager::is_connected() const {
return (server && !server->is_disconnected()) || (client && !client->is_disconnected());
}
void NetObjManager::set_netid_reassign_callback(const std::function<void(const NetworkingObjects::NetObjID& oldID, const NetworkingObjects::NetObjID& newID)>& newNetIDReassignCallback) {
netIDReassignCallback = newNetIDReassignCallback;
}
void NetObjManager::set_netobj_destroy_callback(const std::function<void(const NetworkingObjects::NetObjID& netID)>& newDestroyCallback) {
destroyCallback = newDestroyCallback;
}
void NetObjManager::send_multi_update_messsage(std::function<void()> captureSendBlock, SendUpdateType updateType, const std::shared_ptr<NetServer::ClientData>& specificClient) {
if(is_connected()) {
std::shared_ptr<MultiUpdateData> d = std::make_shared<MultiUpdateData>();

View file

@ -27,6 +27,8 @@ namespace NetworkingObjects {
void disconnect();
bool is_server() const;
bool is_connected() const;
void set_netid_reassign_callback(const std::function<void(const NetworkingObjects::NetObjID& oldID, const NetworkingObjects::NetObjID& newID)>& newNetIDReassignCallback);
void set_netobj_destroy_callback(const std::function<void(const NetworkingObjects::NetObjID& netID)>& newDestroyCallback);
template <typename T> NetObjOwnerPtr<T> read_create_message(cereal::PortableBinaryInputArchive& a, const std::shared_ptr<NetServer::ClientData>& clientReceivedFrom) {
NetObjID id;
a(id);
@ -173,5 +175,7 @@ namespace NetworkingObjects {
std::unordered_map<NetObjID, SingleObjectData> objectData;
NetObjManagerTypeList typeList;
NetTypeIDType nextTypeID;
std::function<void(const NetworkingObjects::NetObjID& oldID, const NetworkingObjects::NetObjID& newID)> netIDReassignCallback;
std::function<void(const NetworkingObjects::NetObjID& netID)> destroyCallback;
};
}

View file

@ -34,6 +34,8 @@ namespace NetworkingObjects {
template <typename T> NetObjOwnerPtr<T>::~NetObjOwnerPtr() {
if(objMan && rawPtr) {
if(objMan->destroyCallback)
objMan->destroyCallback(id);
auto it = objMan->objectData.find(id);
delete static_cast<T*>(it->second.p);
objMan->objectData.erase(it);
@ -49,10 +51,13 @@ namespace NetworkingObjects {
NetObjManager::SingleObjectData objData = it->second;
objData.p = rawPtr;
objMan->objectData.erase(it);
NetworkingObjects::NetObjID oldID = id;
id = NetObjID::random_gen();
objMan->objectData.emplace(id, objData);
if constexpr(hasReassignNetObjIds)
rawPtr->reassign_netobj_ids_call();
if(objMan->netIDReassignCallback)
objMan->netIDReassignCallback(oldID, id);
}
}

View file

@ -38,6 +38,7 @@
World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
netObjMan(!worldInfo.isClient),
main(initMain),
undo(*this),
rMan(*this),
drawProg(*this),
bMan(*this),
@ -45,6 +46,12 @@ World::World(MainProgram& initMain, OpenWorldInfo& worldInfo):
canvasTheme(*this)
{
init_net_obj_type_list();
netObjMan.set_netobj_destroy_callback([&undo = undo](const NetworkingObjects::NetObjID& idToDestroy) {
undo.remove_by_netid(idToDestroy);
});
netObjMan.set_netid_reassign_callback([&undo = undo](const NetworkingObjects::NetObjID& oldID, const NetworkingObjects::NetObjID& newID) {
undo.reassign_netid(oldID, newID);
});
netSource = worldInfo.netSource;
@ -474,8 +481,7 @@ void World::scale_up(const WorldScalar& scaleUpAmount) {
// This means that the scale up message must be send BEFORE the World::scale_up function is called on our end
// if this client is the one responsible for the scale up
drawProg.scale_up(scaleUpAmount);
undo.clear(); // Do last to make sure that any undo generated while scaling up is removed, as it contains outdated scale info
undo.scale_up(scaleUpAmount);
}
void World::draw(SkCanvas* canvas) {

View file

@ -2,7 +2,7 @@
#include <Helpers/NetworkingObjects/DelayUpdateSerializedClassManager.hpp>
#include <Helpers/NetworkingObjects/NetObjOrderedList.hpp>
#include "Helpers/NetworkingObjects/NetObjUnorderedSet.hpp"
#include "UndoManager.hpp"
#include "WorldUndoManager.hpp"
#include "Bookmarks/BookmarkManager.hpp"
#include "ResourceManager.hpp"
#include "DrawingProgram/DrawingProgram.hpp"
@ -42,7 +42,7 @@ class World {
MainProgram& main;
DrawData drawData;
UndoManager undo;
WorldUndoManager undo;
ResourceManager rMan;
DrawingProgram drawProg;
BookmarkManager bMan;

107
src/WorldUndoManager.cpp Normal file
View file

@ -0,0 +1,107 @@
#include "WorldUndoManager.hpp"
void WorldUndoAction::scale_up(const WorldScalar& scaleAmount) {}
WorldUndoAction::~WorldUndoAction() {}
WorldUndoManager::WorldUndoManager(World& initWorld):
world(initWorld)
{}
bool WorldUndoManager::can_undo() {
return !undoQueue.empty();
}
bool WorldUndoManager::can_redo() {
return !redoQueue.empty();
}
void WorldUndoManager::push(std::unique_ptr<WorldUndoAction> undoAction) {
push_undo(std::move(undoAction));
redoQueue.clear();
}
void WorldUndoManager::push_undo(std::unique_ptr<WorldUndoAction> undoAction) {
if(undoQueue.size() == UNDO_QUEUE_LIMIT)
undoQueue.pop_front();
undoQueue.emplace_back(std::move(undoAction));
}
void WorldUndoManager::push_redo(std::unique_ptr<WorldUndoAction> undoAction) {
if(redoQueue.size() == UNDO_QUEUE_LIMIT)
redoQueue.pop_front();
redoQueue.emplace_back(std::move(undoAction));
}
void WorldUndoManager::undo() {
if(undoQueue.empty())
return;
if(!undoQueue.back()->undo(*this))
clear();
else {
push_redo(std::move(undoQueue.back()));
undoQueue.pop_back();
}
}
void WorldUndoManager::redo() {
if(redoQueue.empty())
return;
if(!redoQueue.back()->redo(*this))
clear();
else {
push_undo(std::move(redoQueue.back()));
redoQueue.pop_back();
}
}
void WorldUndoManager::clear() {
redoQueue.clear();
undoQueue.clear();
}
void WorldUndoManager::scale_up(const WorldScalar& scaleAmount) {
for(auto& u : undoQueue)
u->scale_up(scaleAmount);
for(auto& r : redoQueue)
r->scale_up(scaleAmount);
}
void WorldUndoManager::reassign_netid(const NetworkingObjects::NetObjID& oldNetObjID, const NetworkingObjects::NetObjID& newNetObjID) {
auto netIDtoUndoIDIterator = netIDToUndoID.find(oldNetObjID);
if(netIDtoUndoIDIterator != netIDToUndoID.end()) {
UndoObjectID undoID = netIDtoUndoIDIterator->second;
netIDToUndoID.erase(netIDtoUndoIDIterator);
netIDToUndoID.emplace(newNetObjID, undoID);
undoIDToNetID[undoID] = newNetObjID;
}
}
void WorldUndoManager::remove_by_netid(const NetworkingObjects::NetObjID& netObjID) {
auto netIDtoUndoIDIterator = netIDToUndoID.find(netObjID);
if(netIDtoUndoIDIterator != netIDToUndoID.end()) {
undoIDToNetID.erase(netIDtoUndoIDIterator->second);
netIDToUndoID.erase(netIDtoUndoIDIterator);
}
}
void WorldUndoManager::remove_by_undoid(UndoObjectID undoID) {
auto undoIDtoNetIDIterator = undoIDToNetID.find(undoID);
if(undoIDtoNetIDIterator != undoIDToNetID.end()) {
netIDToUndoID.erase(undoIDtoNetIDIterator->second);
undoIDToNetID.erase(undoID);
}
}
WorldUndoManager::UndoObjectID WorldUndoManager::register_new_netobj(const NetworkingObjects::NetObjID& netObjID) {
++lastUndoObjectID;
undoIDToNetID[lastUndoObjectID] = netObjID;
netIDToUndoID[netObjID] = lastUndoObjectID;
return lastUndoObjectID;
}
std::optional<NetworkingObjects::NetObjID> WorldUndoManager::get_netid_from_undoid(UndoObjectID undoID) {
auto it = undoIDToNetID.find(undoID);
if(it == undoIDToNetID.end())
return std::nullopt;
return it->second;
}

49
src/WorldUndoManager.hpp Normal file
View file

@ -0,0 +1,49 @@
#pragma once
#include <memory>
#include <Helpers/NetworkingObjects/NetObjID.hpp>
#include <unordered_map>
#include <deque>
class World;
class WorldUndoManager;
class WorldUndoAction {
public:
virtual bool undo(WorldUndoManager& undoMan) = 0;
virtual bool redo(WorldUndoManager& undoMan) = 0;
virtual void scale_up(const WorldScalar& scaleAmount);
virtual ~WorldUndoAction();
};
class WorldUndoManager {
public:
typedef uint64_t UndoObjectID;
WorldUndoManager(World& initWorld);
void push(std::unique_ptr<WorldUndoAction> undoAction);
void undo();
void redo();
void clear();
bool can_undo();
bool can_redo();
void scale_up(const WorldScalar& scaleAmount);
void reassign_netid(const NetworkingObjects::NetObjID& oldNetObjID, const NetworkingObjects::NetObjID& newNetObjID);
void remove_by_netid(const NetworkingObjects::NetObjID& netObjID);
void remove_by_undoid(UndoObjectID undoID);
UndoObjectID register_new_netobj(const NetworkingObjects::NetObjID& netObjID);
std::optional<NetworkingObjects::NetObjID> get_netid_from_undoid(UndoObjectID undoID);
World& world;
private:
void push_undo(std::unique_ptr<WorldUndoAction> undoAction);
void push_redo(std::unique_ptr<WorldUndoAction> undoAction);
constexpr static size_t UNDO_QUEUE_LIMIT = 500;
std::unordered_map<UndoObjectID, NetworkingObjects::NetObjID> undoIDToNetID;
std::unordered_map<NetworkingObjects::NetObjID, UndoObjectID> netIDToUndoID;
std::deque<std::unique_ptr<WorldUndoAction>> undoQueue;
std::deque<std::unique_ptr<WorldUndoAction>> redoQueue;
UndoObjectID lastUndoObjectID = 0;
};