Compare commits
12 Commits
31c3ed4688
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98af0c288f | ||
|
|
f41bfeaa65 | ||
|
|
31f5adfcc0 | ||
|
|
95f3c821a5 | ||
|
|
9cab041af4 | ||
|
|
396a03c229 | ||
|
|
66b5fdecfb | ||
|
|
b6a68c830e | ||
|
|
3bf46dcaba | ||
|
|
e7096ee20f | ||
|
|
c731d51a46 | ||
|
|
bb45379199 |
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Erik Scholz
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
2
external/CMakeLists.txt
vendored
2
external/CMakeLists.txt
vendored
@@ -13,7 +13,7 @@ endif()
|
|||||||
if (NOT TARGET imgui)
|
if (NOT TARGET imgui)
|
||||||
FetchContent_Declare(imgui
|
FetchContent_Declare(imgui
|
||||||
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
GIT_REPOSITORY https://github.com/ocornut/imgui.git
|
||||||
GIT_TAG 5c1d2d1e4c562a2ed3efbc64476e703a655b45fd # v1.91.7
|
GIT_TAG bf75bfec48fc00f532af8926130b70c0e26eb099 # v1.92.3
|
||||||
EXCLUDE_FROM_ALL
|
EXCLUDE_FROM_ALL
|
||||||
CONFIGURE_COMMAND "" # remove?
|
CONFIGURE_COMMAND "" # remove?
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#include <solanaceae/plugin/solana_plugin_v1.h>
|
#include <solanaceae/plugin/solana_plugin_v1.h>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
|
|
||||||
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
||||||
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
||||||
|
|
||||||
@@ -32,14 +34,15 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api)
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
auto* cr = PLUG_RESOLVE_INSTANCE_VERSIONED(Contact3Registry, "1");
|
auto* cs = PLUG_RESOLVE_INSTANCE(ContactStore4I);
|
||||||
|
|
||||||
// static store, could be anywhere tho
|
// static store, could be anywhere tho
|
||||||
// construct with fetched dependencies
|
// construct with fetched dependencies
|
||||||
g_crdtn = std::make_unique<CRDTNotes>();
|
g_crdtn = std::make_unique<CRDTNotes>();
|
||||||
g_crdtns = std::make_unique<CRDTNotesSync>(*g_crdtn, *cr);
|
g_crdtns = std::make_unique<CRDTNotesSync>(*g_crdtn, *cs);
|
||||||
|
|
||||||
// register types
|
// register types
|
||||||
|
PLUG_PROVIDE_INSTANCE(CRDTNotes, plugin_name, g_crdtn.get());
|
||||||
PLUG_PROVIDE_INSTANCE(CRDTNotesSync, plugin_name, g_crdtns.get());
|
PLUG_PROVIDE_INSTANCE(CRDTNotesSync, plugin_name, g_crdtns.get());
|
||||||
PLUG_PROVIDE_INSTANCE(CRDTNotesEventI, plugin_name, g_crdtns.get());
|
PLUG_PROVIDE_INSTANCE(CRDTNotesEventI, plugin_name, g_crdtns.get());
|
||||||
} catch (const ResolveException& e) {
|
} catch (const ResolveException& e) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include <solanaceae/plugin/solana_plugin_v1.h>
|
#include <solanaceae/plugin/solana_plugin_v1.h>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
|
|
||||||
#include <solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp>
|
#include <solanaceae/crdtnotes_imgui/crdtnotes_imgui.hpp>
|
||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
#include <entt/entt.hpp>
|
#include <entt/entt.hpp>
|
||||||
@@ -32,8 +35,9 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api)
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
auto* crdtn = PLUG_RESOLVE_INSTANCE(CRDTNotes);
|
||||||
auto* crdtns = PLUG_RESOLVE_INSTANCE(CRDTNotesSync);
|
auto* crdtns = PLUG_RESOLVE_INSTANCE(CRDTNotesSync);
|
||||||
auto* cr = PLUG_RESOLVE_INSTANCE_VERSIONED(Contact3Registry, "1");
|
auto* cs = PLUG_RESOLVE_INSTANCE(ContactStore4I);
|
||||||
auto* imguic = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiContext, ImGui::GetVersion());
|
auto* imguic = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiContext, ImGui::GetVersion());
|
||||||
auto* imguimemaf = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiMemAllocFunc, ImGui::GetVersion());
|
auto* imguimemaf = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiMemAllocFunc, ImGui::GetVersion());
|
||||||
auto* imguimemff = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiMemFreeFunc, ImGui::GetVersion());
|
auto* imguimemff = PLUG_RESOLVE_INSTANCE_VERSIONED(ImGuiMemFreeFunc, ImGui::GetVersion());
|
||||||
@@ -45,7 +49,7 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api)
|
|||||||
|
|
||||||
// static store, could be anywhere tho
|
// static store, could be anywhere tho
|
||||||
// construct with fetched dependencies
|
// construct with fetched dependencies
|
||||||
g_crdtn_imgui = std::make_unique<CRDTNotesImGui>(*crdtns, *cr);
|
g_crdtn_imgui = std::make_unique<CRDTNotesImGui>(*crdtn, *crdtns, *cs);
|
||||||
|
|
||||||
// register types
|
// register types
|
||||||
PLUG_PROVIDE_INSTANCE(CRDTNotesImGui, plugin_name, g_crdtn_imgui.get());
|
PLUG_PROVIDE_INSTANCE(CRDTNotesImGui, plugin_name, g_crdtn_imgui.get());
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#include <solanaceae/plugin/solana_plugin_v1.h>
|
#include <solanaceae/plugin/solana_plugin_v1.h>
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
|
|
||||||
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
||||||
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
||||||
#include <solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp>
|
#include <solanaceae/crdtnotes_toxsync/crdtnotes_toxsync.hpp>
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
|
||||||
#include <solanaceae/toxcore/tox_interface.hpp>
|
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||||
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||||
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||||
@@ -37,14 +38,14 @@ SOLANA_PLUGIN_EXPORT uint32_t solana_plugin_start(struct SolanaAPI* solana_api)
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
auto* notes_sync = PLUG_RESOLVE_INSTANCE(CRDTNotesEventI);
|
auto* notes_sync = PLUG_RESOLVE_INSTANCE(CRDTNotesEventI);
|
||||||
auto* cr = PLUG_RESOLVE_INSTANCE_VERSIONED(Contact3Registry, "1");
|
auto* cs = PLUG_RESOLVE_INSTANCE(ContactStore4I);
|
||||||
auto* t = PLUG_RESOLVE_INSTANCE(ToxI);
|
auto* t = PLUG_RESOLVE_INSTANCE(ToxI);
|
||||||
auto* tep = PLUG_RESOLVE_INSTANCE(ToxEventProviderI);
|
auto* tep = PLUG_RESOLVE_INSTANCE(ToxEventProviderI);
|
||||||
auto* tcm = PLUG_RESOLVE_INSTANCE(ToxContactModel2);
|
auto* tcm = PLUG_RESOLVE_INSTANCE(ToxContactModel2);
|
||||||
|
|
||||||
// static store, could be anywhere tho
|
// static store, could be anywhere tho
|
||||||
// construct with fetched dependencies
|
// construct with fetched dependencies
|
||||||
g_crdtn_ts = std::make_unique<CRDTNotesToxSync>(*notes_sync, *cr, *t, *tep, *tcm);
|
g_crdtn_ts = std::make_unique<CRDTNotesToxSync>(*notes_sync, *cs, *t, *tep, *tcm);
|
||||||
|
|
||||||
// register types
|
// register types
|
||||||
PLUG_PROVIDE_INSTANCE(CRDTNotesToxSync, plugin_name, g_crdtn_ts.get());
|
PLUG_PROVIDE_INSTANCE(CRDTNotesToxSync, plugin_name, g_crdtn_ts.get());
|
||||||
|
|||||||
@@ -37,3 +37,20 @@ CRDTNotes::Doc* CRDTNotes::addDoc(const CRDTAgent& self_agent, const DocID& id)
|
|||||||
return &doc;
|
return &doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRDTNotes::writeLockRelease(const DocID& id) {
|
||||||
|
assert(_doc_write_locks.count(id) > 0);
|
||||||
|
_doc_write_locks.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRDTNotes::isWriteLocked(const DocID& id) const {
|
||||||
|
return _doc_write_locks.count(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<CRDTNotes::DocWriteLock> CRDTNotes::writeLockAquire(const DocID& id) {
|
||||||
|
if (_doc_write_locks.count(id)) {
|
||||||
|
return std::nullopt; // replace with exception instead?
|
||||||
|
}
|
||||||
|
|
||||||
|
_doc_write_locks.emplace(id);
|
||||||
|
return DocWriteLock{*this, id};
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
using ID32 = std::array<uint8_t, 32>;
|
using ID32 = std::array<uint8_t, 32>;
|
||||||
|
|
||||||
@@ -35,11 +37,26 @@ class CRDTNotes {
|
|||||||
struct Frontier { // newest known seq for given agent
|
struct Frontier { // newest known seq for given agent
|
||||||
CRDTAgent agent;
|
CRDTAgent agent;
|
||||||
uint64_t seq{0};
|
uint64_t seq{0};
|
||||||
|
uint64_t del_num{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
// RAII lock wrapper
|
||||||
|
struct DocWriteLock {
|
||||||
|
CRDTNotes* notes;
|
||||||
|
DocID id;
|
||||||
|
|
||||||
|
// ctr assumes lock
|
||||||
|
DocWriteLock(CRDTNotes& notes, const DocID& id) : notes(¬es), id(id) {}
|
||||||
|
DocWriteLock(const DocWriteLock&) = delete;
|
||||||
|
DocWriteLock(DocWriteLock&& other) : notes(other.notes), id(other.id) { other.notes = nullptr; }
|
||||||
|
~DocWriteLock(void) { if (notes) { notes->writeLockRelease(id); } }
|
||||||
|
bool operator==(const DocWriteLock& other) const { return id == other.id; }
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: add metadata to docs
|
// TODO: add metadata to docs
|
||||||
std::unordered_map<DocID, Doc> _docs;
|
std::unordered_map<DocID, Doc> _docs;
|
||||||
|
std::unordered_set<DocID> _doc_write_locks;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// config?
|
// config?
|
||||||
@@ -52,5 +69,16 @@ class CRDTNotes {
|
|||||||
Doc* getDoc(const DocID& id);
|
Doc* getDoc(const DocID& id);
|
||||||
|
|
||||||
Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc);
|
Doc* addDoc(const CRDTAgent& self_agent, const DocID& doc);
|
||||||
|
|
||||||
|
void writeLockRelease(const DocID& id);
|
||||||
|
bool isWriteLocked(const DocID& id) const;
|
||||||
|
std::optional<DocWriteLock> writeLockAquire(const DocID& id);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct std::hash<CRDTNotes::DocWriteLock> {
|
||||||
|
std::uint64_t operator()(const CRDTNotes::DocWriteLock& s) const noexcept {
|
||||||
|
return std::hash<ID32>{}(s.id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#include "./crdtnotes.hpp"
|
#include "./crdtnotes.hpp"
|
||||||
|
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
#include <solanaceae/contact/fwd.hpp>
|
||||||
|
|
||||||
// send api
|
// send api
|
||||||
struct CRDTNotesContactSyncModelI {
|
struct CRDTNotesContactSyncModelI {
|
||||||
@@ -12,36 +12,42 @@ struct CRDTNotesContactSyncModelI {
|
|||||||
public:
|
public:
|
||||||
// notify of doc existing
|
// notify of doc existing
|
||||||
virtual void SendGossip(
|
virtual void SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
virtual void SendGossip(
|
virtual void SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
const std::vector<CRDTNotes::Frontier>& frontier
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
// fetch
|
// fetch
|
||||||
public:
|
public:
|
||||||
// causes the other peer to send gossip with all known frontiers (on cool down)
|
// causes the other peer to send gossip with all known frontiers (on cool down)
|
||||||
virtual void SendFetchCompleteFrontier(
|
virtual void SendFetchCompleteFrontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
// action range request
|
// action range request
|
||||||
virtual void SendFetchOps(
|
virtual void SendFetchAddRange(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const CRDTNotes::CRDTAgent& agent,
|
const CRDTNotes::CRDTAgent& agent,
|
||||||
const uint64_t seq_from,
|
const uint64_t seq_from,
|
||||||
const uint64_t seq_to
|
const uint64_t seq_to
|
||||||
) = 0;
|
) = 0;
|
||||||
|
|
||||||
|
virtual void SendFetchDel(
|
||||||
|
ContactHandle4 c,
|
||||||
|
const CRDTNotes::DocID& doc_id,
|
||||||
|
const CRDTNotes::CRDTAgent& agent
|
||||||
|
) = 0;
|
||||||
|
|
||||||
public: // ops response
|
public: // ops response
|
||||||
virtual void SendOps(
|
virtual void SendOps(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
// TODO: optimize this
|
// TODO: optimize this
|
||||||
const std::vector<CRDTNotes::Doc::Op>&
|
const std::vector<CRDTNotes::Doc::Op>&
|
||||||
|
|||||||
@@ -1,13 +1,27 @@
|
|||||||
#include "./crdtnotes_sync.hpp"
|
#include "./crdtnotes_sync.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
#include <solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp>
|
||||||
|
|
||||||
#include <solanaceae/contact/components.hpp>
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
|
#include <entt/container/dense_set.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace Components {
|
||||||
|
|
||||||
|
// attached to contact
|
||||||
|
struct OpSendQueue {
|
||||||
|
std::map<CRDTNotes::DocID, std::vector<CRDTNotes::Doc::Op>> ops;
|
||||||
|
// HACK: limit to 5 ops per packet for now
|
||||||
|
// TODO: ft based alternative for >5 ops
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Components
|
||||||
|
|
||||||
static ID32 id_from_vec(const std::vector<uint8_t>& vec) {
|
static ID32 id_from_vec(const std::vector<uint8_t>& vec) {
|
||||||
ID32 new_id;
|
ID32 new_id;
|
||||||
for (size_t i = 0; i < new_id.size() && i < vec.size(); i++) {
|
for (size_t i = 0; i < new_id.size() && i < vec.size(); i++) {
|
||||||
@@ -17,7 +31,7 @@ static ID32 id_from_vec(const std::vector<uint8_t>& vec) {
|
|||||||
return new_id;
|
return new_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRDTNotesSync::CRDTNotesSync(CRDTNotes& notes, Contact3Registry& cr) : _notes(notes), _cr(cr) {
|
CRDTNotesSync::CRDTNotesSync(CRDTNotes& notes, ContactStore4I& cs) : _notes(notes), _cs(cs) {
|
||||||
_rng.seed(std::random_device{}());
|
_rng.seed(std::random_device{}());
|
||||||
_rng.discard(707);
|
_rng.discard(707);
|
||||||
}
|
}
|
||||||
@@ -26,6 +40,25 @@ CRDTNotesSync::~CRDTNotesSync(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float CRDTNotesSync::iterate(float time_delta) {
|
float CRDTNotesSync::iterate(float time_delta) {
|
||||||
|
for (auto doc_it = _docs_incoming_ops.begin(); doc_it != _docs_incoming_ops.end();) {
|
||||||
|
if (_notes.isWriteLocked(doc_it->first)) {
|
||||||
|
doc_it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto lock_opt = _notes.writeLockAquire(doc_it->first);
|
||||||
|
assert(lock_opt);
|
||||||
|
|
||||||
|
auto* doc_ptr = getDoc(doc_it->first);
|
||||||
|
// TODO: record every applied op and throw event, so eg gui can react better
|
||||||
|
// , or better yet, edit events in string space (imgui can consume them)
|
||||||
|
doc_ptr->apply(doc_it->second);
|
||||||
|
|
||||||
|
std::cout << "CRDTNotesSync: applied " << doc_it->second.size() << " ops\n";
|
||||||
|
|
||||||
|
doc_it = _docs_incoming_ops.erase(doc_it);
|
||||||
|
}
|
||||||
|
|
||||||
if (!_gossip_queue.empty()) {
|
if (!_gossip_queue.empty()) {
|
||||||
// TODO: set is sorted by id, not by order added
|
// TODO: set is sorted by id, not by order added
|
||||||
// only one per iterate *should* be enough
|
// only one per iterate *should* be enough
|
||||||
@@ -37,7 +70,11 @@ float CRDTNotesSync::iterate(float time_delta) {
|
|||||||
// TODO: this is a fallback, remove
|
// TODO: this is a fallback, remove
|
||||||
if (c.all_of<Contact::Components::ParentOf>()) {
|
if (c.all_of<Contact::Components::ParentOf>()) {
|
||||||
for (const auto child : c.get<Contact::Components::ParentOf>().subs) {
|
for (const auto child : c.get<Contact::Components::ParentOf>().subs) {
|
||||||
|
if (c.registry()->all_of<Contact::Components::TagSelfStrong>(child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!c.registry()->all_of<CRDTNotesContactSyncModelI*>(child)) {
|
if (!c.registry()->all_of<CRDTNotesContactSyncModelI*>(child)) {
|
||||||
|
std::cerr << "CRDTNotesSync: error, fallback failed\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +101,11 @@ float CRDTNotesSync::iterate(float time_delta) {
|
|||||||
// TODO: this is a fallback, remove
|
// TODO: this is a fallback, remove
|
||||||
if (c.all_of<Contact::Components::ParentOf>()) {
|
if (c.all_of<Contact::Components::ParentOf>()) {
|
||||||
for (const auto child : c.get<Contact::Components::ParentOf>().subs) {
|
for (const auto child : c.get<Contact::Components::ParentOf>().subs) {
|
||||||
|
if (c.registry()->all_of<Contact::Components::TagSelfStrong>(child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!c.registry()->all_of<CRDTNotesContactSyncModelI*>(child)) {
|
if (!c.registry()->all_of<CRDTNotesContactSyncModelI*>(child)) {
|
||||||
|
std::cerr << "CRDTNotesSync: error, fallback failed\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,22 +121,57 @@ float CRDTNotesSync::iterate(float time_delta) {
|
|||||||
_fetch_frontier_queue.erase(it);
|
_fetch_frontier_queue.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.f;
|
bool sending_ops {false};
|
||||||
|
{ // send ops in queue
|
||||||
|
std::vector<Contact4> empty_queue;
|
||||||
|
for (const auto& [c, op_comp, sync_model] : _cs.registry().view<Components::OpSendQueue, CRDTNotesContactSyncModelI*>().each()) {
|
||||||
|
// HACK: one pkg with up to 5 ops per tick per peer
|
||||||
|
//for (const auto& [doc_id, op_vec] : op_comp.ops) {
|
||||||
|
for (auto it = op_comp.ops.begin(); it != op_comp.ops.end();) {
|
||||||
|
if (it->second.empty()) {
|
||||||
|
it = op_comp.ops.erase(it);
|
||||||
|
continue;
|
||||||
|
} else if (it->second.size() <= 5) {
|
||||||
|
std::cout << "sending " << it->second.size() << " ops\n";
|
||||||
|
sync_model->SendOps(_cs.contactHandle(c), it->first, it->second);
|
||||||
|
it = op_comp.ops.erase(it);
|
||||||
|
//sending_ops = true;
|
||||||
|
} else {
|
||||||
|
std::vector<CRDTNotes::Doc::Op> tmp_ops {it->second.cbegin(), it->second.cbegin()+5};
|
||||||
|
assert(tmp_ops.size() == 5);
|
||||||
|
sync_model->SendOps(_cs.contactHandle(c), it->first, tmp_ops);
|
||||||
|
it->second.erase(it->second.cbegin(), it->second.cbegin()+5);
|
||||||
|
sending_ops = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break; // single update only
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op_comp.ops.empty()) {
|
||||||
|
empty_queue.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_cs.registry().remove<Components::OpSendQueue>(empty_queue.cbegin(), empty_queue.cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sending_ops ? 0.05f : 2.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
CRDTNotes::Doc* CRDTNotesSync::getDoc(const CRDTNotes::DocID& doc_id) {
|
CRDTNotes::Doc* CRDTNotesSync::getDoc(const CRDTNotes::DocID& doc_id) {
|
||||||
return _notes.getDoc(doc_id);
|
return _notes.getDoc(doc_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<CRDTNotes::DocID> CRDTNotesSync::addNewDoc(Contact3Handle c, bool secret) {
|
std::optional<CRDTNotes::DocID> CRDTNotesSync::addNewDoc(ContactHandle4 c, bool secret) {
|
||||||
if (!static_cast<bool>(c)) {
|
if (!static_cast<bool>(c)) {
|
||||||
std::cerr << "CRDTNS error: invalid contact\n";
|
std::cerr << "CRDTNS error: invalid contact\n";
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& self = c.get<Contact::Components::Self>().self;
|
const auto& cr = _cs.registry();
|
||||||
assert(_cr.all_of<Contact::Components::ID>(self));
|
|
||||||
const auto& self_id = _cr.get<Contact::Components::ID>(self);
|
const auto self = c.get<Contact::Components::Self>().self;
|
||||||
|
assert(cr.all_of<Contact::Components::ID>(self));
|
||||||
|
const auto& self_id = cr.get<Contact::Components::ID>(self);
|
||||||
assert(!self_id.data.empty());
|
assert(!self_id.data.empty());
|
||||||
|
|
||||||
CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data);
|
CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data);
|
||||||
@@ -127,15 +203,17 @@ std::optional<CRDTNotes::DocID> CRDTNotesSync::addNewDoc(Contact3Handle c, bool
|
|||||||
return new_id;
|
return new_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesSync::addDoc(const CRDTNotes::DocID& doc_id, Contact3Handle c) {
|
bool CRDTNotesSync::addDoc(const CRDTNotes::DocID& doc_id, ContactHandle4 c) {
|
||||||
if (!static_cast<bool>(c)) {
|
if (!static_cast<bool>(c)) {
|
||||||
std::cerr << "CRDTNS error: invalid contact\n";
|
std::cerr << "CRDTNS error: invalid contact\n";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto& cr = _cs.registry();
|
||||||
|
|
||||||
const auto& self = c.get<Contact::Components::Self>().self;
|
const auto& self = c.get<Contact::Components::Self>().self;
|
||||||
assert(_cr.all_of<Contact::Components::ID>(self));
|
assert(cr.all_of<Contact::Components::ID>(self));
|
||||||
const auto& self_id = _cr.get<Contact::Components::ID>(self);
|
const auto& self_id = cr.get<Contact::Components::ID>(self);
|
||||||
assert(!self_id.data.empty());
|
assert(!self_id.data.empty());
|
||||||
|
|
||||||
CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data);
|
CRDTNotes::CRDTAgent self_agent_id = id_from_vec(self_id.data);
|
||||||
@@ -158,12 +236,12 @@ std::vector<CRDTNotes::DocID> CRDTNotesSync::getDocList(void) {
|
|||||||
return _notes.getDocList();
|
return _notes.getDocList();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CRDTNotes::DocID> CRDTNotesSync::getDocList(Contact3Handle c) {
|
std::vector<CRDTNotes::DocID> CRDTNotesSync::getDocList(ContactHandle4 c) {
|
||||||
std::vector<CRDTNotes::DocID> list;
|
std::vector<CRDTNotes::DocID> list;
|
||||||
|
|
||||||
Contact3Handle parent;
|
ContactHandle4 parent;
|
||||||
if (c.all_of<Contact::Components::Parent>()) {
|
if (c.all_of<Contact::Components::Parent>()) {
|
||||||
parent = Contact3Handle{*c.registry(), c.get<Contact::Components::Parent>().parent};
|
parent = ContactHandle4{*c.registry(), c.get<Contact::Components::Parent>().parent};
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [k, v] : _docs_contacts) {
|
for (const auto& [k, v] : _docs_contacts) {
|
||||||
@@ -190,7 +268,50 @@ void CRDTNotesSync::merge(const CRDTNotes::DocID& doc_id, std::string_view new_t
|
|||||||
auto op_vec = doc_ptr->merge(new_text);
|
auto op_vec = doc_ptr->merge(new_text);
|
||||||
std::cout << "doc changed " << op_vec.size() << " ops generated\n";
|
std::cout << "doc changed " << op_vec.size() << " ops generated\n";
|
||||||
|
|
||||||
// USE OPS
|
// attach OpSendQueue to every contact
|
||||||
|
// needs to be placed at the contact with the sync model
|
||||||
|
entt::dense_set<Contact4> handled_contacts;
|
||||||
|
for (auto c : _docs_contacts.at(doc_id)) {
|
||||||
|
if (handled_contacts.contains(c)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!c.all_of<CRDTNotesContactSyncModelI*>()) {
|
||||||
|
|
||||||
|
// TODO: this is a fallback, remove
|
||||||
|
if (c.all_of<Contact::Components::ParentOf>()) {
|
||||||
|
for (const auto child : c.get<Contact::Components::ParentOf>().subs) {
|
||||||
|
if (handled_contacts.contains(child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (c.registry()->all_of<Contact::Components::TagSelfStrong>(child)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!c.registry()->all_of<CRDTNotesContactSyncModelI*>(child)) {
|
||||||
|
std::cerr << "CRDTNotesSync error: fallback failed\n";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& op_queue = c.registry()->get_or_emplace<Components::OpSendQueue>(child).ops[doc_id];
|
||||||
|
if (op_queue.empty()) {
|
||||||
|
op_queue = op_vec;
|
||||||
|
} else {
|
||||||
|
op_queue.insert(op_queue.cend(), op_vec.cbegin(), op_vec.cend());
|
||||||
|
}
|
||||||
|
handled_contacts.emplace(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue; // skip, not impl
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& op_queue = c.get_or_emplace<Components::OpSendQueue>().ops[doc_id];
|
||||||
|
if (op_queue.empty()) {
|
||||||
|
op_queue = op_vec;
|
||||||
|
} else {
|
||||||
|
op_queue.insert(op_queue.cend(), op_vec.cbegin(), op_vec.cend());
|
||||||
|
}
|
||||||
|
handled_contacts.emplace(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) {
|
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) {
|
||||||
@@ -203,9 +324,38 @@ void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e)
|
|||||||
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) {
|
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_op_range&& e) {
|
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_add_range&& e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_del&& e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) {
|
void CRDTNotesSync::onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) {
|
||||||
|
addDoc(e.doc_id, e.c);
|
||||||
|
|
||||||
|
if (e.ops.empty()) {
|
||||||
|
std::cerr << "CRDTNotesSync warning: got empty ops event/pkg\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: deduplicate ops ?
|
||||||
|
auto lock_opt = _notes.writeLockAquire(e.doc_id);
|
||||||
|
if (lock_opt) {
|
||||||
|
// TODO: perms n stuff
|
||||||
|
// TODO: check if seq missing
|
||||||
|
auto* doc_ptr = getDoc(e.doc_id);
|
||||||
|
// TODO: record every applied op and throw event, so eg gui can react better
|
||||||
|
// , or better yet, edit events in string space (imgui can consume them)
|
||||||
|
doc_ptr->apply(e.ops);
|
||||||
|
|
||||||
|
// TODO: check if new frontier
|
||||||
|
} else {
|
||||||
|
auto& op_in_vec = _docs_incoming_ops[e.doc_id];
|
||||||
|
if (op_in_vec.empty()) {
|
||||||
|
op_in_vec = e.ops;
|
||||||
|
} else {
|
||||||
|
op_in_vec.insert(op_in_vec.cend(), e.ops.cbegin(), e.ops.cend());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,10 +2,15 @@
|
|||||||
|
|
||||||
#include "./crdtnotes.hpp"
|
#include "./crdtnotes.hpp"
|
||||||
|
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
#include <solanaceae/contact/fwd.hpp>
|
||||||
|
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
#include <entt/entity/handle.hpp>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
// fwd
|
// fwd
|
||||||
struct CRDTNotesContactSyncModelI;
|
struct CRDTNotesContactSyncModelI;
|
||||||
@@ -14,7 +19,7 @@ namespace Events {
|
|||||||
|
|
||||||
// - DocID
|
// - DocID
|
||||||
struct NGCEXT_crdtns_gossip {
|
struct NGCEXT_crdtns_gossip {
|
||||||
Contact3Handle c;
|
ContactHandle4 c;
|
||||||
CRDTNotes::DocID doc_id;
|
CRDTNotes::DocID doc_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,16 +27,17 @@ namespace Events {
|
|||||||
// - array [
|
// - array [
|
||||||
// - AgentID
|
// - AgentID
|
||||||
// - seq (frontier)
|
// - seq (frontier)
|
||||||
|
// - del_num
|
||||||
// - ]
|
// - ]
|
||||||
struct NGCEXT_crdtns_gossip_frontier {
|
struct NGCEXT_crdtns_gossip_frontier {
|
||||||
Contact3Handle c;
|
ContactHandle4 c;
|
||||||
CRDTNotes::DocID doc_id;
|
CRDTNotes::DocID doc_id;
|
||||||
std::vector<CRDTNotes::Frontier> selected_frontier;
|
std::vector<CRDTNotes::Frontier> frontier;
|
||||||
};
|
};
|
||||||
|
|
||||||
// - DocID
|
// - DocID
|
||||||
struct NGCEXT_crdtns_fetch_complete_frontier {
|
struct NGCEXT_crdtns_fetch_complete_frontier {
|
||||||
Contact3Handle c;
|
ContactHandle4 c;
|
||||||
CRDTNotes::DocID doc_id;
|
CRDTNotes::DocID doc_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -39,24 +45,37 @@ namespace Events {
|
|||||||
// - AgentID
|
// - AgentID
|
||||||
// - seq_from
|
// - seq_from
|
||||||
// - seq_to
|
// - seq_to
|
||||||
struct NGCEXT_crdtns_fetch_op_range {
|
struct NGCEXT_crdtns_fetch_add_range {
|
||||||
Contact3Handle c;
|
ContactHandle4 c;
|
||||||
CRDTNotes::DocID doc_id;
|
CRDTNotes::DocID doc_id;
|
||||||
CRDTNotes::CRDTAgent agent;
|
CRDTNotes::CRDTAgent agent;
|
||||||
uint64_t seq_from;
|
uint64_t seq_from;
|
||||||
uint64_t seq_to;
|
uint64_t seq_to;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// - DocID
|
||||||
|
// - AgentID
|
||||||
|
struct NGCEXT_crdtns_fetch_del {
|
||||||
|
ContactHandle4 c;
|
||||||
|
CRDTNotes::DocID doc_id;
|
||||||
|
CRDTNotes::CRDTAgent agent;
|
||||||
|
};
|
||||||
|
|
||||||
// - DocID
|
// - DocID
|
||||||
// - array [
|
// - array [
|
||||||
// - op
|
// - op
|
||||||
// - ]
|
// - ]
|
||||||
struct NGCEXT_crdtns_ops {
|
struct NGCEXT_crdtns_ops {
|
||||||
Contact3Handle c;
|
ContactHandle4 c;
|
||||||
CRDTNotes::DocID doc_id;
|
CRDTNotes::DocID doc_id;
|
||||||
std::vector<CRDTNotes::Doc::Op> ops;
|
std::vector<CRDTNotes::Doc::Op> ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: curser
|
||||||
|
// - DocID
|
||||||
|
// - AgentID
|
||||||
|
// - CRDTNotes::Doc::ListType::ListID parent_left
|
||||||
|
|
||||||
} // Events
|
} // Events
|
||||||
|
|
||||||
// this is different than other "i might not handle this" event interfaces
|
// this is different than other "i might not handle this" event interfaces
|
||||||
@@ -66,7 +85,8 @@ struct CRDTNotesEventI {
|
|||||||
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) = 0;
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) = 0;
|
||||||
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e) = 0;
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e) = 0;
|
||||||
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) = 0;
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) = 0;
|
||||||
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_op_range&& e) = 0;
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_add_range&& e) = 0;
|
||||||
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_del&& e) = 0;
|
||||||
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) = 0;
|
virtual void onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,18 +96,27 @@ struct CRDTNotesEventI {
|
|||||||
class CRDTNotesSync final : public CRDTNotesEventI {
|
class CRDTNotesSync final : public CRDTNotesEventI {
|
||||||
// pull inside????
|
// pull inside????
|
||||||
CRDTNotes& _notes;
|
CRDTNotes& _notes;
|
||||||
Contact3Registry& _cr;
|
ContactStore4I& _cs;
|
||||||
|
|
||||||
std::default_random_engine _rng;
|
std::default_random_engine _rng;
|
||||||
|
|
||||||
std::unordered_map<CRDTNotes::DocID, std::set<Contact3Handle>> _docs_contacts;
|
std::unordered_map<CRDTNotes::DocID, std::set<ContactHandle4>> _docs_contacts;
|
||||||
|
struct Peer {
|
||||||
|
// global frontier
|
||||||
|
// what we know the peer knows(/gossiped) about
|
||||||
|
std::unordered_map<decltype(CRDTNotes::Frontier::agent), decltype(CRDTNotes::Frontier::seq)> other_frontier;
|
||||||
|
};
|
||||||
|
std::unordered_map<CRDTNotes::DocID, std::map<ContactHandle4, Peer>> _docs_peers;
|
||||||
|
|
||||||
|
// queue of unapplied ops, kept here until write lock can be aquired
|
||||||
|
std::unordered_map<CRDTNotes::DocID, std::vector<CRDTNotes::Doc::Op>> _docs_incoming_ops;
|
||||||
|
|
||||||
// if a doc is eg new, it is added here
|
// if a doc is eg new, it is added here
|
||||||
std::set<CRDTNotes::DocID> _gossip_queue;
|
std::set<CRDTNotes::DocID> _gossip_queue; // TODO: no
|
||||||
std::set<CRDTNotes::DocID> _fetch_frontier_queue;
|
std::set<CRDTNotes::DocID> _fetch_frontier_queue;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CRDTNotesSync(CRDTNotes& notes, Contact3Registry& cr);
|
CRDTNotesSync(CRDTNotes& notes, ContactStore4I& cs);
|
||||||
|
|
||||||
~CRDTNotesSync(void);
|
~CRDTNotesSync(void);
|
||||||
|
|
||||||
@@ -99,14 +128,14 @@ class CRDTNotesSync final : public CRDTNotesEventI {
|
|||||||
|
|
||||||
// adds a doc and assosiates contact (and self)
|
// adds a doc and assosiates contact (and self)
|
||||||
// if secret, only self is added (and thats why contact is needed)
|
// if secret, only self is added (and thats why contact is needed)
|
||||||
std::optional<CRDTNotes::DocID> addNewDoc(Contact3Handle c, bool secret = false);
|
std::optional<CRDTNotes::DocID> addNewDoc(ContactHandle4 c, bool secret = false);
|
||||||
|
|
||||||
// adds a doc by id to a contact
|
// adds a doc by id to a contact
|
||||||
// (for gossip or manual add)
|
// (for gossip or manual add)
|
||||||
bool addDoc(const CRDTNotes::DocID& doc_id, Contact3Handle c);
|
bool addDoc(const CRDTNotes::DocID& doc_id, ContactHandle4 c);
|
||||||
|
|
||||||
std::vector<CRDTNotes::DocID> getDocList(void);
|
std::vector<CRDTNotes::DocID> getDocList(void);
|
||||||
std::vector<CRDTNotes::DocID> getDocList(Contact3Handle c);
|
std::vector<CRDTNotes::DocID> getDocList(ContactHandle4 c);
|
||||||
|
|
||||||
void merge(const CRDTNotes::DocID& doc_id, std::string_view new_text);
|
void merge(const CRDTNotes::DocID& doc_id, std::string_view new_text);
|
||||||
|
|
||||||
@@ -114,7 +143,8 @@ class CRDTNotesSync final : public CRDTNotesEventI {
|
|||||||
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) override;
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip&& e) override;
|
||||||
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e) override;
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_gossip_frontier&& e) override;
|
||||||
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) override;
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_complete_frontier&& e) override;
|
||||||
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_op_range&& e) override;
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_add_range&& e) override;
|
||||||
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_fetch_del&& e) override;
|
||||||
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) override;
|
void onCRDTNSyncEvent(Events::NGCEXT_crdtns_ops&& e) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "./crdtnotes_imgui.hpp"
|
#include "./crdtnotes_imgui.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
#include <solanaceae/contact/components.hpp>
|
#include <solanaceae/contact/components.hpp>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -8,7 +9,6 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <misc/cpp/imgui_stdlib.h>
|
#include <misc/cpp/imgui_stdlib.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
@@ -47,7 +47,13 @@ namespace detail {
|
|||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
|
|
||||||
CRDTNotesImGui::CRDTNotesImGui(CRDTNotesSync& notes_sync, Contact3Registry& cr) : _notes_sync(notes_sync), _cr(cr) {
|
std::unordered_set<CRDTNotes::DocWriteLock>::iterator CRDTNotesImGui::findLock(const CRDTNotes::DocID& doc_id) {
|
||||||
|
auto it = _held_locks.begin();
|
||||||
|
for (; it != _held_locks.end() && it->id != doc_id; it++) {}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRDTNotesImGui::CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs) : _notes(notes), _notes_sync(notes_sync), _cs(cs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
float CRDTNotesImGui::render(void) {
|
float CRDTNotesImGui::render(void) {
|
||||||
@@ -58,7 +64,7 @@ float CRDTNotesImGui::render(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginPopup("create new doc contact")) {
|
if (ImGui::BeginPopup("create new doc contact")) {
|
||||||
for (const auto& c : _cr.view<Contact::Components::TagBig>()) {
|
for (const auto& c : _cs.registry().view<Contact::Components::TagBig>()) {
|
||||||
if (renderContactListContactSmall(c, false)) {
|
if (renderContactListContactSmall(c, false)) {
|
||||||
//const auto& self = _cr.get<Contact::Components::Self>(c).self;
|
//const auto& self = _cr.get<Contact::Components::Self>(c).self;
|
||||||
//assert(_cr.all_of<Contact::Components::ID>(self));
|
//assert(_cr.all_of<Contact::Components::ID>(self));
|
||||||
@@ -76,7 +82,7 @@ float CRDTNotesImGui::render(void) {
|
|||||||
//// tox id (id from self)
|
//// tox id (id from self)
|
||||||
//self_agent_id
|
//self_agent_id
|
||||||
//);
|
//);
|
||||||
_notes_sync.addNewDoc({_cr, c}, false);
|
_notes_sync.addNewDoc(_cs.contactHandle(c), false);
|
||||||
|
|
||||||
//// and open the doc
|
//// and open the doc
|
||||||
}
|
}
|
||||||
@@ -105,6 +111,7 @@ float CRDTNotesImGui::render(void) {
|
|||||||
const std::string docid_str = "Doc " + detail::to_hex(docid);
|
const std::string docid_str = "Doc " + detail::to_hex(docid);
|
||||||
bool open = true;
|
bool open = true;
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSize({200, 200}, ImGuiCond_Appearing);
|
||||||
if (ImGui::Begin(docid_str.c_str(), &open)) {
|
if (ImGui::Begin(docid_str.c_str(), &open)) {
|
||||||
renderDoc(docid);
|
renderDoc(docid);
|
||||||
}
|
}
|
||||||
@@ -119,13 +126,15 @@ float CRDTNotesImGui::render(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.f;
|
return 2.f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesImGui::renderContactListContactSmall(const Contact3 c, const bool selected) const {
|
bool CRDTNotesImGui::renderContactListContactSmall(const Contact4 c, const bool selected) const {
|
||||||
std::string label;
|
std::string label;
|
||||||
|
|
||||||
label += (_cr.all_of<Contact::Components::Name>(c) ? _cr.get<Contact::Components::Name>(c).name.c_str() : "<unk>");
|
const auto& cr = _cs.registry();
|
||||||
|
|
||||||
|
label += (cr.all_of<Contact::Components::Name>(c) ? cr.get<Contact::Components::Name>(c).name.c_str() : "<unk>");
|
||||||
label += "###";
|
label += "###";
|
||||||
label += std::to_string(entt::to_integral(c));
|
label += std::to_string(entt::to_integral(c));
|
||||||
|
|
||||||
@@ -138,17 +147,36 @@ bool CRDTNotesImGui::renderDoc(const CRDTNotes::DocID& doc_id) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto lock_it = findLock(doc_id);
|
||||||
|
bool self_held = lock_it != _held_locks.end();
|
||||||
|
const bool foreign_held = !self_held && _notes.isWriteLocked(doc_id);
|
||||||
|
|
||||||
auto text = doc->getText();
|
auto text = doc->getText();
|
||||||
if (renderDocText(text)) {
|
ImGui::InputTextMultiline(
|
||||||
|
"##doc",
|
||||||
|
&text,
|
||||||
|
{-1,-1},
|
||||||
|
ImGuiInputTextFlags_AllowTabInput |
|
||||||
|
(foreign_held ? ImGuiInputTextFlags_ReadOnly : ImGuiInputTextFlags_None) |
|
||||||
|
ImGuiInputTextFlags_CallbackAlways
|
||||||
|
//cb,
|
||||||
|
//&text
|
||||||
|
);
|
||||||
|
if (!foreign_held && !self_held && (ImGui::IsItemActive() || ImGui::IsItemEdited())) {
|
||||||
|
// TODO: check
|
||||||
|
_held_locks.emplace(_notes.writeLockAquire(doc_id).value());
|
||||||
|
self_held = true;
|
||||||
|
//std::cout << "!!!! imgui lock aquired\n";
|
||||||
|
} else if (!foreign_held && self_held && !(ImGui::IsItemActive() || ImGui::IsItemEdited())) {
|
||||||
|
// release lock
|
||||||
|
_held_locks.erase(lock_it);
|
||||||
|
//std::cout << "!!!! imgui lock released\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self_held && ImGui::IsItemEdited()) {
|
||||||
_notes_sync.merge(doc_id, text);
|
_notes_sync.merge(doc_id, text);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesImGui::renderDocText(std::string& text) const {
|
|
||||||
// TODO: replace with text editor (zep) or visualize stuff??
|
|
||||||
return ImGui::InputTextMultiline("##doc", &text, {-1,-1}, ImGuiInputTextFlags_AllowTabInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
#include <solanaceae/contact/fwd.hpp>
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
class CRDTNotesImGui {
|
class CRDTNotesImGui {
|
||||||
|
CRDTNotes& _notes;
|
||||||
CRDTNotesSync& _notes_sync;
|
CRDTNotesSync& _notes_sync;
|
||||||
Contact3Registry& _cr;
|
ContactStore4I& _cs;
|
||||||
|
|
||||||
bool _show_global_list {true};
|
bool _show_global_list {true};
|
||||||
|
|
||||||
std::set<CRDTNotes::DocID> _open_docs;
|
std::set<CRDTNotes::DocID> _open_docs;
|
||||||
|
std::unordered_set<CRDTNotes::DocWriteLock> _held_locks;
|
||||||
|
|
||||||
|
std::unordered_set<CRDTNotes::DocWriteLock>::iterator findLock(const CRDTNotes::DocID& doc_id);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CRDTNotesImGui(CRDTNotesSync& notes_sync, Contact3Registry& cr);
|
CRDTNotesImGui(CRDTNotes& notes, CRDTNotesSync& notes_sync, ContactStore4I& cs);
|
||||||
|
|
||||||
float render(void);
|
float render(void);
|
||||||
|
|
||||||
bool renderContactListContactSmall(const Contact3 c, const bool selected) const;
|
bool renderContactListContactSmall(const Contact4 c, const bool selected) const;
|
||||||
|
|
||||||
bool renderDoc(const CRDTNotes::DocID& doc_id);
|
bool renderDoc(const CRDTNotes::DocID& doc_id);
|
||||||
bool renderDocText(std::string& text) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "./crdtnotes_toxsync.hpp"
|
#include "./crdtnotes_toxsync.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_store_i.hpp>
|
||||||
#include <solanaceae/toxcore/tox_interface.hpp>
|
#include <solanaceae/toxcore/tox_interface.hpp>
|
||||||
|
|
||||||
#include <solanaceae/tox_contacts/components.hpp>
|
#include <solanaceae/tox_contacts/components.hpp>
|
||||||
@@ -15,6 +16,7 @@ enum class NGCEXT_Event : uint8_t {
|
|||||||
// - array [
|
// - array [
|
||||||
// - AgentID
|
// - AgentID
|
||||||
// - seq (frontier)
|
// - seq (frontier)
|
||||||
|
// - del_num
|
||||||
// - ]
|
// - ]
|
||||||
CRDTN_GOSSIP_FRONTIER,
|
CRDTN_GOSSIP_FRONTIER,
|
||||||
|
|
||||||
@@ -25,7 +27,11 @@ enum class NGCEXT_Event : uint8_t {
|
|||||||
// - AgentID
|
// - AgentID
|
||||||
// - seq_from
|
// - seq_from
|
||||||
// - seq_to
|
// - seq_to
|
||||||
CRDTN_FETCH_OP_RANGE,
|
CRDTN_FETCH_ADD_RANGE,
|
||||||
|
|
||||||
|
// - DocID
|
||||||
|
// - AgentID
|
||||||
|
CRDTN_FETCH_DEL,
|
||||||
|
|
||||||
// - DocID
|
// - DocID
|
||||||
// - array [
|
// - array [
|
||||||
@@ -39,11 +45,11 @@ enum class NGCEXT_Event : uint8_t {
|
|||||||
|
|
||||||
CRDTNotesToxSync::CRDTNotesToxSync(
|
CRDTNotesToxSync::CRDTNotesToxSync(
|
||||||
CRDTNotesEventI& notes_sync,
|
CRDTNotesEventI& notes_sync,
|
||||||
Contact3Registry& cr,
|
ContactStore4I& cs,
|
||||||
ToxI& t,
|
ToxI& t,
|
||||||
ToxEventProviderI& tep,
|
ToxEventProviderI& tep,
|
||||||
ToxContactModel2& tcm
|
ToxContactModel2& tcm
|
||||||
) : _notes_sync(notes_sync), _cr(cr), _t(t), _tep_sr(tep.newSubRef(this)), _tcm(tcm) {
|
) : _notes_sync(notes_sync), _cs(cs), _t(t), _tep_sr(tep.newSubRef(this)), _tcm(tcm) {
|
||||||
// TODO: non groups
|
// TODO: non groups
|
||||||
|
|
||||||
// should be called for every peer (except self)
|
// should be called for every peer (except self)
|
||||||
@@ -58,13 +64,13 @@ CRDTNotesToxSync::CRDTNotesToxSync(
|
|||||||
|
|
||||||
CRDTNotesToxSync::~CRDTNotesToxSync(void) {
|
CRDTNotesToxSync::~CRDTNotesToxSync(void) {
|
||||||
// TODO: find a better way to remove dangling pointers
|
// TODO: find a better way to remove dangling pointers
|
||||||
std::vector<Contact3> to_remove_self;
|
std::vector<Contact4> to_remove_self;
|
||||||
_cr.view<CRDTNotesContactSyncModelI*>().each([&to_remove_self, this](Contact3 c, const auto* csm) {
|
_cs.registry().view<CRDTNotesContactSyncModelI*>().each([&to_remove_self, this](Contact4 c, const auto* csm) {
|
||||||
if (this == csm) {
|
if (this == csm) {
|
||||||
to_remove_self.push_back(c);
|
to_remove_self.push_back(c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
_cr.remove<CRDTNotesContactSyncModelI*>(to_remove_self.cbegin(), to_remove_self.cend());
|
_cs.registry().remove<CRDTNotesContactSyncModelI*>(to_remove_self.cbegin(), to_remove_self.cend());
|
||||||
}
|
}
|
||||||
|
|
||||||
float CRDTNotesToxSync::iterate(float time_delta) {
|
float CRDTNotesToxSync::iterate(float time_delta) {
|
||||||
@@ -73,7 +79,7 @@ float CRDTNotesToxSync::iterate(float time_delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesToxSync::SendGossip(
|
void CRDTNotesToxSync::SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) {
|
) {
|
||||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||||
@@ -98,9 +104,9 @@ void CRDTNotesToxSync::SendGossip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesToxSync::SendGossip(
|
void CRDTNotesToxSync::SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
const std::vector<CRDTNotes::Frontier>& frontier
|
||||||
) {
|
) {
|
||||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||||
return;
|
return;
|
||||||
@@ -116,7 +122,7 @@ void CRDTNotesToxSync::SendGossip(
|
|||||||
}
|
}
|
||||||
// +32
|
// +32
|
||||||
|
|
||||||
for (const auto& [f_id, f_seq] : selected_frontier) {
|
for (const auto& [f_id, f_seq, del_num] : frontier) {
|
||||||
for (const uint8_t v : f_id) {
|
for (const uint8_t v : f_id) {
|
||||||
pkg.push_back(v);
|
pkg.push_back(v);
|
||||||
}
|
}
|
||||||
@@ -126,8 +132,13 @@ void CRDTNotesToxSync::SendGossip(
|
|||||||
pkg.push_back((f_seq >> i*8) & 0xff);
|
pkg.push_back((f_seq >> i*8) & 0xff);
|
||||||
}
|
}
|
||||||
// +8
|
// +8
|
||||||
|
|
||||||
|
for (size_t i = 0; i < sizeof(del_num); i++) {
|
||||||
|
pkg.push_back((del_num >> i*8) & 0xff);
|
||||||
|
}
|
||||||
|
// +8
|
||||||
}
|
}
|
||||||
// +40
|
// +48
|
||||||
|
|
||||||
// send
|
// send
|
||||||
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||||
@@ -139,7 +150,7 @@ void CRDTNotesToxSync::SendGossip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesToxSync::SendFetchCompleteFrontier(
|
void CRDTNotesToxSync::SendFetchCompleteFrontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) {
|
) {
|
||||||
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||||
@@ -163,8 +174,8 @@ void CRDTNotesToxSync::SendFetchCompleteFrontier(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CRDTNotesToxSync::SendFetchOps(
|
void CRDTNotesToxSync::SendFetchAddRange(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const CRDTNotes::CRDTAgent& agent,
|
const CRDTNotes::CRDTAgent& agent,
|
||||||
const uint64_t seq_from,
|
const uint64_t seq_from,
|
||||||
@@ -176,7 +187,7 @@ void CRDTNotesToxSync::SendFetchOps(
|
|||||||
|
|
||||||
std::vector<uint8_t> pkg;
|
std::vector<uint8_t> pkg;
|
||||||
|
|
||||||
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_FETCH_OP_RANGE));
|
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_FETCH_ADD_RANGE));
|
||||||
|
|
||||||
for (const uint8_t v : doc_id) {
|
for (const uint8_t v : doc_id) {
|
||||||
pkg.push_back(v);
|
pkg.push_back(v);
|
||||||
@@ -206,8 +217,37 @@ void CRDTNotesToxSync::SendFetchOps(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CRDTNotesToxSync::SendFetchDel(
|
||||||
|
ContactHandle4 c,
|
||||||
|
const CRDTNotes::DocID& doc_id,
|
||||||
|
const CRDTNotes::CRDTAgent& agent
|
||||||
|
) {
|
||||||
|
if (!c.all_of<Contact::Components::ToxGroupPeerEphemeral>()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> pkg;
|
||||||
|
|
||||||
|
pkg.push_back(static_cast<uint8_t>(NGCEXT_Event::CRDTN_FETCH_DEL));
|
||||||
|
|
||||||
|
for (const uint8_t v : doc_id) {
|
||||||
|
pkg.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const uint8_t v : agent) {
|
||||||
|
pkg.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& gp = c.get<Contact::Components::ToxGroupPeerEphemeral>();
|
||||||
|
_t.toxGroupSendCustomPrivatePacket(
|
||||||
|
gp.group_number, gp.peer_number,
|
||||||
|
true,
|
||||||
|
pkg
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void CRDTNotesToxSync::SendOps(
|
void CRDTNotesToxSync::SendOps(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const std::vector<CRDTNotes::Doc::Op>& ops
|
const std::vector<CRDTNotes::Doc::Op>& ops
|
||||||
) {
|
) {
|
||||||
@@ -227,6 +267,7 @@ void CRDTNotesToxSync::SendOps(
|
|||||||
|
|
||||||
// this is very inefficent
|
// this is very inefficent
|
||||||
// a full add op is 124bytes like this
|
// a full add op is 124bytes like this
|
||||||
|
// a full del op is 41bytes
|
||||||
for (const auto& op : ops) {
|
for (const auto& op : ops) {
|
||||||
if(std::holds_alternative<CRDTNotes::Doc::OpAdd>(op)) {
|
if(std::holds_alternative<CRDTNotes::Doc::OpAdd>(op)) {
|
||||||
const auto& add_op = std::get<CRDTNotes::Doc::OpAdd>(op);
|
const auto& add_op = std::get<CRDTNotes::Doc::OpAdd>(op);
|
||||||
@@ -288,7 +329,7 @@ void CRDTNotesToxSync::SendOps(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesToxSync::parse_crdtn_gossip(
|
bool CRDTNotesToxSync::parse_crdtn_gossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool // dont care private
|
bool // dont care private
|
||||||
) {
|
) {
|
||||||
@@ -313,7 +354,7 @@ bool CRDTNotesToxSync::parse_crdtn_gossip(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesToxSync::parse_crdtn_gossip_frontier(
|
bool CRDTNotesToxSync::parse_crdtn_gossip_frontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool // dont care private
|
bool // dont care private
|
||||||
) {
|
) {
|
||||||
@@ -331,7 +372,7 @@ bool CRDTNotesToxSync::parse_crdtn_gossip_frontier(
|
|||||||
|
|
||||||
while (curser < data_size) {
|
while (curser < data_size) {
|
||||||
CRDTNotes::Frontier new_f;
|
CRDTNotes::Frontier new_f;
|
||||||
_DATA_HAVE(new_f.agent.size() * sizeof(CRDTNotes::CRDTAgent::value_type) + sizeof(new_f.seq), std::cerr << "NGCEXT: packet malformed, not enough data for forntier\n"; return false;)
|
_DATA_HAVE(new_f.agent.size() * sizeof(CRDTNotes::CRDTAgent::value_type) + sizeof(new_f.seq) + sizeof(new_f.del_num), std::cerr << "NGCEXT: packet malformed, not enough data for frontier\n"; return false;)
|
||||||
|
|
||||||
for (size_t i = 0; i < new_f.agent.size(); i++, curser++) {
|
for (size_t i = 0; i < new_f.agent.size(); i++, curser++) {
|
||||||
new_f.agent[i] = data[curser];
|
new_f.agent[i] = data[curser];
|
||||||
@@ -342,7 +383,12 @@ bool CRDTNotesToxSync::parse_crdtn_gossip_frontier(
|
|||||||
new_f.seq |= uint64_t(data[curser]) << i*8;
|
new_f.seq |= uint64_t(data[curser]) << i*8;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.selected_frontier.emplace_back(std::move(new_f));
|
new_f.del_num = 0;
|
||||||
|
for (size_t i = 0; i < sizeof(new_f.del_num); i++, curser++) {
|
||||||
|
new_f.del_num |= uint64_t(data[curser]) << i*8;
|
||||||
|
}
|
||||||
|
|
||||||
|
e.frontier.emplace_back(std::move(new_f));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "CRDTN gossip_frontier parsed\n";
|
std::cout << "CRDTN gossip_frontier parsed\n";
|
||||||
@@ -351,7 +397,7 @@ bool CRDTNotesToxSync::parse_crdtn_gossip_frontier(
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesToxSync::parse_crdtn_fetch_complete_frontier(
|
bool CRDTNotesToxSync::parse_crdtn_fetch_complete_frontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool // dont care private
|
bool // dont care private
|
||||||
) {
|
) {
|
||||||
@@ -370,12 +416,12 @@ bool CRDTNotesToxSync::parse_crdtn_fetch_complete_frontier(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesToxSync::parse_crdtn_fetch_op_range(
|
bool CRDTNotesToxSync::parse_crdtn_fetch_add_range(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool // dont care private
|
bool // dont care private
|
||||||
) {
|
) {
|
||||||
Events::NGCEXT_crdtns_fetch_op_range e;
|
Events::NGCEXT_crdtns_fetch_add_range e;
|
||||||
e.c = c;
|
e.c = c;
|
||||||
|
|
||||||
size_t curser = 0;
|
size_t curser = 0;
|
||||||
@@ -402,13 +448,38 @@ bool CRDTNotesToxSync::parse_crdtn_fetch_op_range(
|
|||||||
e.seq_to |= uint64_t(data[curser]) << i*8;
|
e.seq_to |= uint64_t(data[curser]) << i*8;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "CRDTN fetch_op_range parsed\n";
|
std::cout << "CRDTN fetch_add_range parsed\n";
|
||||||
|
_notes_sync.onCRDTNSyncEvent(std::move(e));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CRDTNotesToxSync::parse_crdtn_fetch_del(
|
||||||
|
ContactHandle4 c,
|
||||||
|
const uint8_t* data, size_t data_size,
|
||||||
|
bool // dont care private
|
||||||
|
) {
|
||||||
|
Events::NGCEXT_crdtns_fetch_del e;
|
||||||
|
e.c = c;
|
||||||
|
|
||||||
|
size_t curser = 0;
|
||||||
|
|
||||||
|
_DATA_HAVE(e.doc_id.size() * sizeof(decltype(e.doc_id)::value_type), std::cerr << "NGCEXT: packet too small, missing doc_id\n"; return false;)
|
||||||
|
for (size_t i = 0; i < e.doc_id.size(); i++, curser++) {
|
||||||
|
e.doc_id[i] = data[curser];
|
||||||
|
}
|
||||||
|
|
||||||
|
_DATA_HAVE(e.agent.size() * sizeof(decltype(e.agent)::value_type), std::cerr << "NGCEXT: packet too small, missing agent\n"; return false;)
|
||||||
|
for (size_t i = 0; i < e.agent.size(); i++, curser++) {
|
||||||
|
e.agent[i] = data[curser];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "CRDTN fetch_del parsed\n";
|
||||||
_notes_sync.onCRDTNSyncEvent(std::move(e));
|
_notes_sync.onCRDTNSyncEvent(std::move(e));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CRDTNotesToxSync::parse_crdtn_ops(
|
bool CRDTNotesToxSync::parse_crdtn_ops(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool // dont care private
|
bool // dont care private
|
||||||
) {
|
) {
|
||||||
@@ -530,8 +601,10 @@ bool CRDTNotesToxSync::handlePacket(
|
|||||||
return parse_crdtn_gossip_frontier(c, data+1, data_size-1, _private);
|
return parse_crdtn_gossip_frontier(c, data+1, data_size-1, _private);
|
||||||
case NGCEXT_Event::CRDTN_FETCH_COMPLETE_FRONTIER:
|
case NGCEXT_Event::CRDTN_FETCH_COMPLETE_FRONTIER:
|
||||||
return parse_crdtn_fetch_complete_frontier(c, data+1, data_size-1, _private);
|
return parse_crdtn_fetch_complete_frontier(c, data+1, data_size-1, _private);
|
||||||
case NGCEXT_Event::CRDTN_FETCH_OP_RANGE:
|
case NGCEXT_Event::CRDTN_FETCH_ADD_RANGE:
|
||||||
return parse_crdtn_fetch_op_range(c, data+1, data_size-1, _private);
|
return parse_crdtn_fetch_add_range(c, data+1, data_size-1, _private);
|
||||||
|
case NGCEXT_Event::CRDTN_FETCH_DEL:
|
||||||
|
return parse_crdtn_fetch_del(c, data+1, data_size-1, _private);
|
||||||
case NGCEXT_Event::CRDTN_OPS:
|
case NGCEXT_Event::CRDTN_OPS:
|
||||||
return parse_crdtn_ops(c, data+1, data_size-1, _private);
|
return parse_crdtn_ops(c, data+1, data_size-1, _private);
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "solanaceae/crdtnotes/crdtnotes_sync.hpp"
|
#include <solanaceae/crdtnotes/crdtnotes_sync.hpp>
|
||||||
#include <solanaceae/crdtnotes/crdtnotes.hpp>
|
|
||||||
#include <solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp>
|
#include <solanaceae/crdtnotes/crdtnotes_contact_sync_model.hpp>
|
||||||
#include <solanaceae/contact/contact_model3.hpp>
|
#include <solanaceae/contact/fwd.hpp>
|
||||||
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
#include <solanaceae/toxcore/tox_event_interface.hpp>
|
||||||
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
|
||||||
|
|
||||||
@@ -14,7 +13,7 @@ struct ToxEventProviderI;
|
|||||||
// implements CRDTNotesContactSyncModelI and attaches itself to tox contacts
|
// implements CRDTNotesContactSyncModelI and attaches itself to tox contacts
|
||||||
class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI {
|
class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI {
|
||||||
CRDTNotesEventI& _notes_sync;
|
CRDTNotesEventI& _notes_sync;
|
||||||
Contact3Registry& _cr;
|
ContactStore4I& _cs;
|
||||||
ToxI& _t;
|
ToxI& _t;
|
||||||
ToxEventProviderI::SubscriptionReference _tep_sr;
|
ToxEventProviderI::SubscriptionReference _tep_sr;
|
||||||
ToxContactModel2& _tcm;
|
ToxContactModel2& _tcm;
|
||||||
@@ -22,7 +21,7 @@ class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI {
|
|||||||
public:
|
public:
|
||||||
CRDTNotesToxSync(
|
CRDTNotesToxSync(
|
||||||
CRDTNotesEventI& notes_sync,
|
CRDTNotesEventI& notes_sync,
|
||||||
Contact3Registry& cr,
|
ContactStore4I& cs,
|
||||||
ToxI& t,
|
ToxI& t,
|
||||||
ToxEventProviderI& tep,
|
ToxEventProviderI& tep,
|
||||||
ToxContactModel2& tcm
|
ToxContactModel2& tcm
|
||||||
@@ -33,58 +32,69 @@ class CRDTNotesToxSync : public CRDTNotesContactSyncModelI, public ToxEventI {
|
|||||||
|
|
||||||
public: // sync api
|
public: // sync api
|
||||||
void SendGossip(
|
void SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
void SendGossip(
|
void SendGossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const std::vector<CRDTNotes::Frontier>& selected_frontier
|
const std::vector<CRDTNotes::Frontier>& frontier
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
void SendFetchCompleteFrontier(
|
void SendFetchCompleteFrontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id
|
const CRDTNotes::DocID& doc_id
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
void SendFetchOps(
|
void SendFetchAddRange(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const CRDTNotes::CRDTAgent& agent,
|
const CRDTNotes::CRDTAgent& agent,
|
||||||
const uint64_t seq_from,
|
const uint64_t seq_from,
|
||||||
const uint64_t seq_to
|
const uint64_t seq_to
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
|
void SendFetchDel(
|
||||||
|
ContactHandle4 c,
|
||||||
|
const CRDTNotes::DocID& doc_id,
|
||||||
|
const CRDTNotes::CRDTAgent& agent
|
||||||
|
) override;
|
||||||
|
|
||||||
void SendOps(
|
void SendOps(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const CRDTNotes::DocID& doc_id,
|
const CRDTNotes::DocID& doc_id,
|
||||||
const std::vector<CRDTNotes::Doc::Op>&
|
const std::vector<CRDTNotes::Doc::Op>&
|
||||||
) override;
|
) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool parse_crdtn_gossip(
|
bool parse_crdtn_gossip(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool _private
|
bool _private
|
||||||
);
|
);
|
||||||
bool parse_crdtn_gossip_frontier(
|
bool parse_crdtn_gossip_frontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool _private
|
bool _private
|
||||||
);
|
);
|
||||||
bool parse_crdtn_fetch_complete_frontier(
|
bool parse_crdtn_fetch_complete_frontier(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool _private
|
bool _private
|
||||||
);
|
);
|
||||||
bool parse_crdtn_fetch_op_range(
|
bool parse_crdtn_fetch_add_range(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
|
const uint8_t* data, size_t data_size,
|
||||||
|
bool _private
|
||||||
|
);
|
||||||
|
bool parse_crdtn_fetch_del(
|
||||||
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool _private
|
bool _private
|
||||||
);
|
);
|
||||||
bool parse_crdtn_ops(
|
bool parse_crdtn_ops(
|
||||||
Contact3Handle c,
|
ContactHandle4 c,
|
||||||
const uint8_t* data, size_t data_size,
|
const uint8_t* data, size_t data_size,
|
||||||
bool _private
|
bool _private
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user