From e9e5ad88a1d90280bec419d9fa2c9307c1083dc4 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Sun, 31 Dec 2023 14:25:56 +0100 Subject: [PATCH] more sync code --- plugins/plugin_crdtnotes.cpp | 16 ++- plugins/plugin_crdtnotes_imgui.cpp | 10 +- plugins/plugin_crdtnotes_toxsync.cpp | 9 +- src/solanaceae/crdtnotes/crdtnotes.cpp | 12 -- src/solanaceae/crdtnotes/crdtnotes.hpp | 8 -- src/solanaceae/crdtnotes/crdtnotes_sync.cpp | 130 ++++++++++++++++++ src/solanaceae/crdtnotes/crdtnotes_sync.hpp | 38 +++++ .../crdtnotes_imgui/crdtnotes_imgui.cpp | 39 +++--- .../crdtnotes_imgui/crdtnotes_imgui.hpp | 6 +- .../crdtnotes_toxsync/crdtnotes_toxsync.cpp | 3 +- .../crdtnotes_toxsync/crdtnotes_toxsync.hpp | 2 - 11 files changed, 208 insertions(+), 65 deletions(-) diff --git a/plugins/plugin_crdtnotes.cpp b/plugins/plugin_crdtnotes.cpp index 3da0ded..32fc48d 100644 --- a/plugins/plugin_crdtnotes.cpp +++ b/plugins/plugin_crdtnotes.cpp @@ -31,24 +31,29 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) } //ConfigModelI* conf = nullptr; + Contact3Registry* cr = nullptr; { // make sure required types are loaded //conf = RESOLVE_INSTANCE(ConfigModelI); + cr = RESOLVE_INSTANCE(Contact3Registry); //if (conf == nullptr) { //std::cerr << "PLUGIN CRDTN missing ConfigModelI\n"; //return 2; //} + + if (cr == nullptr) { + std::cerr << "PLUGIN CRDTNTS missing Contact3Registry\n"; + return 2; + } } // static store, could be anywhere tho // construct with fetched dependencies - g_crdtn = std::make_unique(/**conf*/); - g_crdtns = std::make_unique(/**conf*/); + g_crdtn = std::make_unique(); + g_crdtns = std::make_unique(*g_crdtn, *cr); // register types - PROVIDE_INSTANCE(CRDTNotes, "CRDTNotes", g_crdtn.get()); - PROVIDE_INSTANCE(CRDTNotesSync, "CRDTNotes", g_crdtns.get()); PROVIDE_INSTANCE(CRDTNotesEventI, "CRDTNotes", g_crdtns.get()); @@ -59,12 +64,13 @@ SOLANA_PLUGIN_EXPORT void solana_plugin_stop(void) { std::cout << "PLUGIN CRDTN STOP()\n"; g_crdtn.reset(); + g_crdtns.reset(); } SOLANA_PLUGIN_EXPORT void solana_plugin_tick(float delta) { (void)delta; //std::cout << "PLUGIN CRDTN TICK()\n"; - //g_crdtn->iterate(); + g_crdtns->iterate(delta); } } // extern C diff --git a/plugins/plugin_crdtnotes_imgui.cpp b/plugins/plugin_crdtnotes_imgui.cpp index 7337e4b..ec54ac3 100644 --- a/plugins/plugin_crdtnotes_imgui.cpp +++ b/plugins/plugin_crdtnotes_imgui.cpp @@ -30,13 +30,13 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) } //ConfigModelI* conf = nullptr; - CRDTNotes* crdtn = nullptr; + CRDTNotesSync* crdtns = nullptr; Contact3Registry* cr = nullptr; ImGuiContext* imguic = nullptr; { // make sure required types are loaded //conf = RESOLVE_INSTANCE(ConfigModelI); - crdtn = RESOLVE_INSTANCE(CRDTNotes); + crdtns = RESOLVE_INSTANCE(CRDTNotesSync); cr = RESOLVE_INSTANCE(Contact3Registry); imguic = RESOLVE_INSTANCE(ImGuiContext); @@ -45,8 +45,8 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) //return 2; //} - if (crdtn == nullptr) { - std::cerr << "PLUGIN CRDTNIMGUI missing CRDTNotes\n"; + if (crdtns == nullptr) { + std::cerr << "PLUGIN CRDTNIMGUI missing CRDTNotesSync\n"; return 2; } @@ -65,7 +65,7 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) // static store, could be anywhere tho // construct with fetched dependencies - g_crdtn_imgui = std::make_unique(*crdtn, *cr); + g_crdtn_imgui = std::make_unique(*crdtns, *cr); // register types PROVIDE_INSTANCE(CRDTNotesImGui, "CRDTNotesImGui", g_crdtn_imgui.get()); diff --git a/plugins/plugin_crdtnotes_toxsync.cpp b/plugins/plugin_crdtnotes_toxsync.cpp index e13c711..6f40944 100644 --- a/plugins/plugin_crdtnotes_toxsync.cpp +++ b/plugins/plugin_crdtnotes_toxsync.cpp @@ -35,7 +35,6 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) } //ConfigModelI* conf = nullptr; - CRDTNotes* notes = nullptr; CRDTNotesEventI* notes_sync = nullptr; Contact3Registry* cr = nullptr; ToxI* t = nullptr; @@ -44,7 +43,6 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) { // make sure required types are loaded //conf = RESOLVE_INSTANCE(ConfigModelI); - notes = RESOLVE_INSTANCE(CRDTNotes); notes_sync = RESOLVE_INSTANCE(CRDTNotesEventI); cr = RESOLVE_INSTANCE(Contact3Registry); t = RESOLVE_INSTANCE(ToxI); @@ -56,11 +54,6 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) //return 2; //} - if (notes == nullptr) { - std::cerr << "PLUGIN CRDTNTS missing CRDTNotes\n"; - return 2; - } - if (notes_sync == nullptr) { std::cerr << "PLUGIN CRDTNTS missing CRDTNotesEventI\n"; return 2; @@ -89,7 +82,7 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api) // static store, could be anywhere tho // construct with fetched dependencies - g_crdtn_ts = std::make_unique(*notes, *notes_sync, *cr, *t, *tep, *tcm); + g_crdtn_ts = std::make_unique(*notes_sync, *cr, *t, *tep, *tcm); // register types PROVIDE_INSTANCE(CRDTNotesToxSync, "CRDTNotesToxSync", g_crdtn_ts.get()); diff --git a/src/solanaceae/crdtnotes/crdtnotes.cpp b/src/solanaceae/crdtnotes/crdtnotes.cpp index 5deb1fa..ab6fcd5 100644 --- a/src/solanaceae/crdtnotes/crdtnotes.cpp +++ b/src/solanaceae/crdtnotes/crdtnotes.cpp @@ -1,8 +1,6 @@ #include "./crdtnotes.hpp" CRDTNotes::CRDTNotes(void) { - _rng.seed(std::random_device{}()); - _rng.discard(707); } CRDTNotes::~CRDTNotes(void) { @@ -39,13 +37,3 @@ CRDTNotes::Doc* CRDTNotes::addDoc(const CRDTAgent& self_agent, const DocID& id) return &doc; } -CRDTNotes::Doc* CRDTNotes::addDoc(const CRDTAgent& self_agent) { - DocID new_id; - for (auto& it : new_id) { - // TODO: this discards alot - it = static_cast(_rng()); - } - - return addDoc(self_agent, new_id); -} - diff --git a/src/solanaceae/crdtnotes/crdtnotes.hpp b/src/solanaceae/crdtnotes/crdtnotes.hpp index a423969..249501d 100644 --- a/src/solanaceae/crdtnotes/crdtnotes.hpp +++ b/src/solanaceae/crdtnotes/crdtnotes.hpp @@ -6,10 +6,6 @@ #include #include #include -#include - -// fwd -struct CRDTNotesContactSyncModelI; using ID32 = std::array; @@ -44,7 +40,6 @@ class CRDTNotes { private: // TODO: add metadata to docs std::unordered_map _docs; - std::default_random_engine _rng; public: // config? @@ -57,8 +52,5 @@ class CRDTNotes { Doc* getDoc(const DocID& id); Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc); - - // new doc with random id - Doc* addDoc(const CRDTAgent& self_agent); }; diff --git a/src/solanaceae/crdtnotes/crdtnotes_sync.cpp b/src/solanaceae/crdtnotes/crdtnotes_sync.cpp index 39b5210..ec85429 100644 --- a/src/solanaceae/crdtnotes/crdtnotes_sync.cpp +++ b/src/solanaceae/crdtnotes/crdtnotes_sync.cpp @@ -1,6 +1,136 @@ #include "./crdtnotes_sync.hpp" +#include + +#include +#include +#include + +static ID32 id_from_vec(const std::vector& vec) { + ID32 new_id; + for (size_t i = 0; i < new_id.size() && i < vec.size(); i++) { + new_id.at(i) = vec.at(i); + } + + return new_id; +} + +CRDTNotesSync::CRDTNotesSync(CRDTNotes& notes, Contact3Registry& cr) : _notes(notes), _cr(cr) { + _rng.seed(std::random_device{}()); + _rng.discard(707); +} + +CRDTNotesSync::~CRDTNotesSync(void) { +} + +float CRDTNotesSync::iterate(float time_delta) { + return 1.f; +} + +CRDTNotes::Doc* CRDTNotesSync::getDoc(const CRDTNotes::DocID& doc_id) { + return _notes.getDoc(doc_id); +} + +std::optional CRDTNotesSync::addNewDoc(Contact3Handle c, bool secret) { + if (!static_cast(c)) { + std::cerr << "CRDTNS error: invalid contact\n"; + return std::nullopt; + } + + const auto& self = c.get().self; + assert(_cr.all_of(self)); + const auto& self_id = _cr.get(self); + assert(!self_id.data.empty()); + + CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data); + + CRDTNotes::DocID new_id; + { // generate new random id + for (auto& it : new_id) { + // TODO: this discards alot + it = static_cast(_rng()); + } + } + + const auto* doc_ptr = _notes.addDoc( + // tox id (id from self) + self_agent_id, + new_id // docid + ); + + if (doc_ptr == nullptr) { + return std::nullopt; + } + + if (!secret) { + _docs_contacts[new_id].emplace(c); + } + + return new_id; +} + +bool CRDTNotesSync::addDoc(const CRDTNotes::DocID& doc_id, Contact3Handle c) { + if (!static_cast(c)) { + std::cerr << "CRDTNS error: invalid contact\n"; + return false; + } + + const auto& self = c.get().self; + assert(_cr.all_of(self)); + const auto& self_id = _cr.get(self); + assert(!self_id.data.empty()); + + CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data); + + // preexisting does not overwrite self!!! + const auto* doc_ptr = _notes.addDoc(self_agent_id, doc_id); + + _docs_contacts[doc_id].emplace(c); + + return doc_ptr != nullptr; +} + +std::vector CRDTNotesSync::getDocList(void) { + return _notes.getDocList(); +} + +std::vector CRDTNotesSync::getDocList(Contact3Handle c) { + std::vector list; + + Contact3Handle parent; + if (c.all_of()) { + parent = Contact3Handle{*c.registry(), c.get().parent}; + } + + for (const auto& [k, v] : _docs_contacts) { + if (v.count(c)) { + list.push_back(k); + continue; // avoid dups + } + + if (v.count(parent)) { + list.push_back(k); + } + } + + return list; +} + +void CRDTNotesSync::merge(const CRDTNotes::DocID& doc_id, std::string_view new_text) { + auto* doc_ptr = _notes.getDoc(doc_id); + if (doc_ptr == nullptr) { + std::cerr << "CRDTNS error: tried to merge into unknown doc\n"; + return; + } + + auto op_vec = doc_ptr->merge(new_text); + std::cout << "doc changed " << op_vec.size() << " ops generated\n"; + + // USE OPS +} + void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) { + addDoc(e.doc_id, e.c); } void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e) { diff --git a/src/solanaceae/crdtnotes/crdtnotes_sync.hpp b/src/solanaceae/crdtnotes/crdtnotes_sync.hpp index 030e60f..257c8ee 100644 --- a/src/solanaceae/crdtnotes/crdtnotes_sync.hpp +++ b/src/solanaceae/crdtnotes/crdtnotes_sync.hpp @@ -1,8 +1,15 @@ #pragma once #include "./crdtnotes.hpp" + #include +#include +#include + +// fwd +struct CRDTNotesContactSyncModelI; + namespace Events { // - DocID @@ -67,6 +74,37 @@ struct CRDTNotesEventI { // gets called on incoming packets // calls CRDTNotesContactSyncModelI on contacts class CRDTNotesSync final : public CRDTNotesEventI { + // pull inside???? + CRDTNotes& _notes; + Contact3Registry& _cr; + + std::default_random_engine _rng; + + std::unordered_map> _docs_contacts; + + public: + CRDTNotesSync(CRDTNotes& notes, Contact3Registry& cr); + + ~CRDTNotesSync(void); + + // called from main thread periodically + float iterate(float time_delta); + + public: // CRDTNotes api + CRDTNotes::Doc* getDoc(const CRDTNotes::DocID& doc_id); + + // adds a doc and assosiates contact (and self) + // if secret, only self is added (and thats why contact is needed) + std::optional addNewDoc(Contact3Handle c, bool secret = false); + + // adds a doc by id to a contact + // (for gossip or manual add) + bool addDoc(const CRDTNotes::DocID& doc_id, Contact3Handle c); + + std::vector getDocList(void); + std::vector getDocList(Contact3Handle c); + + void merge(const CRDTNotes::DocID& doc_id, std::string_view new_text); public: void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) override; diff --git a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp index c006fab..9eedd55 100644 --- a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp +++ b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.cpp @@ -47,7 +47,7 @@ namespace detail { } // detail -CRDTNotesImGui::CRDTNotesImGui(CRDTNotes& notes, Contact3Registry& cr) : _notes(notes), _cr(cr) { +CRDTNotesImGui::CRDTNotesImGui(CRDTNotesSync& notes_sync, Contact3Registry& cr) : _notes_sync(notes_sync), _cr(cr) { } float CRDTNotesImGui::render(void) { @@ -60,24 +60,25 @@ float CRDTNotesImGui::render(void) { if (ImGui::BeginPopup("create new doc contact")) { for (const auto& c : _cr.view()) { if (renderContactListContactSmall(c, false)) { - const auto& self = _cr.get(c).self; - assert(_cr.all_of(self)); - const auto& self_id = _cr.get(self); - assert(!self_id.data.empty()); + //const auto& self = _cr.get(c).self; + //assert(_cr.all_of(self)); + //const auto& self_id = _cr.get(self); + //assert(!self_id.data.empty()); - CRDTNotes::CRDTAgent self_agent_id; + //CRDTNotes::CRDTAgent self_agent_id; - // at most agent size, up to self id size - for (size_t i = 0; i < self_agent_id.size() && i < self_id.data.size(); i++) { - self_agent_id.at(i) = self_id.data.at(i); - } + //// at most agent size, up to self id size + //for (size_t i = 0; i < self_agent_id.size() && i < self_id.data.size(); i++) { + //self_agent_id.at(i) = self_id.data.at(i); + //} - _notes.addDoc( - // tox id (id from self) - self_agent_id - ); + //_notes.addDoc( + //// tox id (id from self) + //self_agent_id + //); + _notes_sync.addNewDoc({_cr, c}, false); - // and open the doc + //// and open the doc } } ImGui::EndPopup(); @@ -85,7 +86,7 @@ float CRDTNotesImGui::render(void) { ImGui::SeparatorText("Global list"); - const auto doclist = _notes.getDocList(); + const auto doclist = _notes_sync.getDocList(); for (const auto& docid : doclist) { const auto docid_str = detail::to_hex(docid); //ImGui::TextUnformatted(docid_str.c_str()); @@ -132,16 +133,14 @@ bool CRDTNotesImGui::renderContactListContactSmall(const Contact3 c, const bool } bool CRDTNotesImGui::renderDoc(const CRDTNotes::DocID& doc_id) { - auto* doc = _notes.getDoc(doc_id); + auto* doc = _notes_sync.getDoc(doc_id); if (doc == nullptr) { return false; } auto text = doc->getText(); if (renderDocText(text)) { - auto op_vec = doc->merge(text); - std::cout << "doc changed " << op_vec.size() << " ops generated\n"; - // ... uh? + _notes_sync.merge(doc_id, text); return true; } diff --git a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp index f81884d..9033255 100644 --- a/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp +++ b/src/solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp @@ -1,12 +1,12 @@ #pragma once -#include +#include #include #include class CRDTNotesImGui { - CRDTNotes& _notes; + CRDTNotesSync& _notes_sync; Contact3Registry& _cr; bool _show_global_list {true}; @@ -14,7 +14,7 @@ class CRDTNotesImGui { std::set _open_docs; public: - CRDTNotesImGui(CRDTNotes& notes, Contact3Registry& cr); + CRDTNotesImGui(CRDTNotesSync& notes_sync, Contact3Registry& cr); float render(void); diff --git a/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp b/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp index 520dde9..cde1f38 100644 --- a/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp +++ b/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.cpp @@ -38,13 +38,12 @@ enum class NGCEXT_Event : uint8_t { #define _DATA_HAVE(x, error) if ((data_size - curser) < (x)) { error } CRDTNotesToxSync::CRDTNotesToxSync( - CRDTNotes& notes, CRDTNotesEventI& notes_sync, Contact3Registry& cr, ToxI& t, ToxEventProviderI& tep, ToxContactModel2& tcm -) : _notes(notes), _notes_sync(notes_sync), _cr(cr), _t(t), _tep(tep), _tcm(tcm) { +) : _notes_sync(notes_sync), _cr(cr), _t(t), _tep(tep), _tcm(tcm) { // TODO: non groups // should be called for every peer (except self) diff --git a/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp b/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp index 16330f9..a3880d0 100644 --- a/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp +++ b/src/solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp @@ -13,7 +13,6 @@ struct ToxEventProviderI; // implements CRDTNotesContactSyncModelI and attaches itself to tox contacts class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI { - CRDTNotes& _notes; CRDTNotesEventI& _notes_sync; Contact3Registry& _cr; ToxI& _t; @@ -22,7 +21,6 @@ class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI { public: CRDTNotesToxSync( - CRDTNotes& notes, CRDTNotesEventI& notes_sync, Contact3Registry& cr, ToxI& t,