From 3a929569ee90ab47855ec069cdcc0126fc640283 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Tue, 6 May 2025 15:27:18 +0200 Subject: [PATCH] avatar sending, uses self in avatar dir also fix cwd in file selector --- src/CMakeLists.txt | 2 + src/chat_gui/file_selector.cpp | 2 +- src/main_screen.cpp | 4 +- src/main_screen.hpp | 2 + src/tox_avatar_manager.cpp | 20 ++- src/tox_avatar_manager.hpp | 5 +- src/tox_avatar_sender.cpp | 215 +++++++++++++++++++++++++++++++++ src/tox_avatar_sender.hpp | 51 ++++++++ 8 files changed, 296 insertions(+), 5 deletions(-) create mode 100644 src/tox_avatar_sender.cpp create mode 100644 src/tox_avatar_sender.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9dea7d82..86330a2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -69,6 +69,8 @@ target_sources(tomato PUBLIC ./tox_avatar_manager.hpp ./tox_avatar_manager.cpp + ./tox_avatar_sender.hpp + ./tox_avatar_sender.cpp ./media_meta_info_loader.hpp ./media_meta_info_loader.cpp diff --git a/src/chat_gui/file_selector.cpp b/src/chat_gui/file_selector.cpp index b433b2e6..644ecb28 100644 --- a/src/chat_gui/file_selector.cpp +++ b/src/chat_gui/file_selector.cpp @@ -16,7 +16,7 @@ void FileSelector::reset(void) { FileSelector::FileSelector(void) { try { - _current_file_path = std::filesystem::current_path(); + _current_file_path = std::filesystem::current_path() / ""; } catch (std::filesystem::filesystem_error const& ex) { std::cerr << "FS: exception creating _current_file_path: " << ex.what() << "\n"; } diff --git a/src/main_screen.cpp b/src/main_screen.cpp index f7de9e5b..77e316c9 100644 --- a/src/main_screen.cpp +++ b/src/main_screen.cpp @@ -55,7 +55,8 @@ MainScreen::MainScreen(const SimpleConfigModel& conf_, SDL_Renderer* renderer_, #endif theme(theme_), mmil(rmm), - tam(os, cs, conf), + tam(os, cs, conf, tc), + tas(os, cs, rmm), sdlrtu(renderer_), tal(cs, os), contact_tc(tal, sdlrtu), @@ -604,6 +605,7 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { const float fo_interval = tffom.tick(time_delta); tam.iterate(); // compute + tas.iterate(time_delta); const float pm_interval = pm.tick(time_delta); // compute diff --git a/src/main_screen.hpp b/src/main_screen.hpp index 5767e544..19539429 100644 --- a/src/main_screen.hpp +++ b/src/main_screen.hpp @@ -23,6 +23,7 @@ #include "./media_meta_info_loader.hpp" #include "./tox_avatar_manager.hpp" +#include "./tox_avatar_sender.hpp" #include "./sdlrenderer_texture_uploader.hpp" #include "./texture_cache.hpp" @@ -88,6 +89,7 @@ struct MainScreen final : public Screen { MediaMetaInfoLoader mmil; ToxAvatarManager tam; + ToxAvatarSender tas; SDLRendererTextureUploader sdlrtu; //OpenGLTextureUploader ogltu; diff --git a/src/tox_avatar_manager.cpp b/src/tox_avatar_manager.cpp index 74044255..264d0f01 100644 --- a/src/tox_avatar_manager.cpp +++ b/src/tox_avatar_manager.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -33,8 +34,9 @@ namespace Components { ToxAvatarManager::ToxAvatarManager( ObjectStore2& os, ContactStore4I& cs, - ConfigModelI& conf -) : _os(os), _os_sr(_os.newSubRef(this)), _cs(cs), _conf(conf), _sb_tcs(os) { + ConfigModelI& conf, + ToxI& t +) : _os(os), _os_sr(_os.newSubRef(this)), _cs(cs), _conf(conf), _t(t), _sb_tcs(os) { _os_sr .subscribe(ObjectStore_Event::object_construct) .subscribe(ObjectStore_Event::object_update) @@ -126,6 +128,20 @@ void ToxAvatarManager::addAvatarFileToContact(const Contact4 c, const ToxKey& ke std::filesystem::file_size(file_path) ); + { // toxhash for tox file id, so the remote can optimize cached files + auto file = o.get().ptr->file2(o, StorageBackendIFile2::FILE2_READ); + if (file) { + auto file_buf = file->read(o.get().file_size); + // HACK: tox interface needs bytespan + if (file_buf.isOwning()) { + o.emplace_or_replace(_t.toxHash(file_buf._data_owner)); + } else { + // TODO: tox bytespan !! + o.emplace_or_replace(_t.toxHash(std::vector(file_buf.cbegin(), file_buf.cend()))); + } + } + } + _os.throwEventConstruct(o); // avatar file "png" exists diff --git a/src/tox_avatar_manager.hpp b/src/tox_avatar_manager.hpp index 0c131199..975a3dff 100644 --- a/src/tox_avatar_manager.hpp +++ b/src/tox_avatar_manager.hpp @@ -2,6 +2,7 @@ #include #include +#include #include "./backends/std_fs.hpp" @@ -19,6 +20,7 @@ class ToxAvatarManager : public ObjectStoreEventI { ObjectStore2::SubscriptionReference _os_sr; ContactStore4I& _cs; ConfigModelI& _conf; + ToxI& _t; Backends::STDFS _sb_tcs; @@ -32,7 +34,8 @@ class ToxAvatarManager : public ObjectStoreEventI { ToxAvatarManager( ObjectStore2& os, ContactStore4I& cs, - ConfigModelI& conf + ConfigModelI& conf, + ToxI& t ); void iterate(void); diff --git a/src/tox_avatar_sender.cpp b/src/tox_avatar_sender.cpp new file mode 100644 index 00000000..4d73ef55 --- /dev/null +++ b/src/tox_avatar_sender.cpp @@ -0,0 +1,215 @@ +#include "./tox_avatar_sender.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace Components { + struct TagAvatarOffered {}; +} // Components + +bool ToxAvatarSender::checkContact(ContactHandle4 c) { + // check if tox friend and not sent + if (!c.all_of< + Contact::Components::ToxFriendEphemeral, + Contact::Components::ConnectionState + >()) { + return false; + } + if (c.any_of()) { + return false; + } + + // is (not) self + if (c.all_of()) { + return false; + } + + if (!c.all_of()) { + std::cout << "TAS warning: contact has no self\n"; + return false; + } + + auto self_c = _cs.contactHandle(c.get().self); + if (!static_cast(self_c)) { + std::cerr << "TAS error: invalid self\n"; + assert(false); + return false; + } + + // self has avatar (obj) + if (!self_c.all_of()) { + return false; + } + + return true; +} + +bool ToxAvatarSender::inQueue(ContactHandle4 c) const { + return std::find_if(_queue.cbegin(), _queue.cend(), [&c](const Entry& e) { return e.c == c; }) != _queue.cend(); +} + +void ToxAvatarSender::addToQueue(ContactHandle4 c) { + assert(!inQueue(c)); + _queue.push_back({ + c, + c.get().state + }); +} + +void ToxAvatarSender::sendAvatar(ContactHandle4 c) { + std::cout << "TAS: sending self avatar to " << entt::to_integral(c.entity()) << "\n"; + + if (!c.all_of()) { + std::cout << "TAS warning: contact has no self\n"; + return; + } + + auto self_c = _cs.contactHandle(c.get().self); + if (!static_cast(self_c)) { + std::cerr << "TAS error: invalid self\n"; + assert(false); + return; + } + + if (!self_c.all_of()) { + return; + } + + const auto ao = self_c.get().obj; + + // .... ? + // duplicate object? + // a single object can only ever be one transfer (meh) + // TODO: make object multi transfer-able (enforce) + auto self_o = _os.objectHandle(ao); + if (!static_cast(self_o)) { + std::cerr << "TAS error: self avatar obj not real?\n"; + return; + } + // TODO: move to tox avatar specific backend + // HACK: manual clone + // using self_o meta backend newObject() should emplace the file backend too + auto new_o = self_o.get().ptr->newObject(ByteSpan{}, false); + assert(new_o); + + if (self_o.all_of()) { + new_o.emplace_or_replace(self_o.get()); + } + if (self_o.all_of()) { + new_o.emplace_or_replace(self_o.get()); + } + if (self_o.all_of()) { + new_o.emplace_or_replace(self_o.get()); + } + if (self_o.all_of()) { + new_o.emplace_or_replace(); + } + + // tox avatar + new_o.emplace_or_replace(uint64_t(1)); + + _os.throwEventConstruct(new_o); // ? + + if (!_rmm.sendFileObj(c, new_o)) { + std::cerr << "TAS error: failed to send avatar file obj\n"; + _os.throwEventDestroy(new_o); + new_o.destroy(); + } + + c.emplace_or_replace(); +} + +ToxAvatarSender::ToxAvatarSender(ObjectStore2& os, ContactStore4I& cs, RegistryMessageModelI& rmm) : + _os(os), + _cs(cs), + _cs_sr(cs.newSubRef(this)), + _rmm(rmm), + _rmm_sr(_rmm.newSubRef(this)) +{ + _cs_sr + .subscribe(ContactStore4_Event::contact_construct) + .subscribe(ContactStore4_Event::contact_update) + .subscribe(ContactStore4_Event::contact_destroy) + ; + + _rmm_sr + .subscribe(RegistryMessageModel_Event::send_file_obj) + ; +} + +void ToxAvatarSender::iterate(float delta) { + for (auto it = _queue.begin(); it != _queue.end();) { + it->timer -= delta; + if (it->timer <= 0.f) { + sendAvatar(it->c); + it = _queue.erase(it); + } else { + it++; + } + } +} + +bool ToxAvatarSender::onEvent(const ContactStore::Events::Contact4Construct& e) { + if (!static_cast(e.e)) { + assert(false); + return false; + } + + if (!checkContact(e.e)) { + return false; + } + + if (e.e.get().state == Contact::Components::ConnectionState::disconnected) { + return false; + } + + addToQueue(e.e); + + return false; +} + +bool ToxAvatarSender::onEvent(const ContactStore::Events::Contact4Update& e) { + if (!static_cast(e.e)) { + assert(false); + return false; + } + + if (!checkContact(e.e)) { + return false; + } + + auto it = std::find_if(_queue.begin(), _queue.end(), [c = e.e](const Entry& en) { return en.c == c; }); + if (it != _queue.end()) { + // check if connections state changed and reset timer + const auto current_state = e.e.get().state; + if (current_state == Contact::Components::ConnectionState::disconnected) { + _queue.erase(it); + } else if (it->ls != current_state) { + // state changed, reset timer + it->timer = 21.33f; + } + } else if (e.e.get().state != Contact::Components::ConnectionState::disconnected) { + addToQueue(e.e); + } + + return false; +} + +bool ToxAvatarSender::onEvent(const ContactStore::Events::Contact4Destory& e) { + // remove from queue + // queue is assumed to be short + auto it = std::find_if(_queue.begin(), _queue.end(), [c = e.e](const Entry& en) { return en.c == c; }); + if (it != _queue.end()) { + _queue.erase(it); + } + + return false; +} + diff --git a/src/tox_avatar_sender.hpp b/src/tox_avatar_sender.hpp new file mode 100644 index 00000000..7d0027ed --- /dev/null +++ b/src/tox_avatar_sender.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "solanaceae/contact/fwd.hpp" +#include +#include +#include + +#include + +#include +#include + +#include +#include + + +class ToxAvatarSender : public ContactStore4EventI, public RegistryMessageModelEventI { + ObjectStore2& _os; + ContactStore4I& _cs; + ContactStore4I::SubscriptionReference _cs_sr; + RegistryMessageModelI& _rmm; + RegistryMessageModelI::SubscriptionReference _rmm_sr; + + struct Entry { + ContactHandle4 c; + Contact::Components::ConnectionState::State ls; + float timer {23.122f}; + }; + std::vector _queue; + + bool checkContact(ContactHandle4 c); + bool inQueue(ContactHandle4 c) const; + void addToQueue(ContactHandle4 c); + + void sendAvatar(ContactHandle4 c); + + public: + ToxAvatarSender( + ObjectStore2& os, + ContactStore4I& cs, + RegistryMessageModelI& rmm + ); + + void iterate(float delta); + + protected: + bool onEvent(const ContactStore::Events::Contact4Construct&) override; + bool onEvent(const ContactStore::Events::Contact4Update&) override; + bool onEvent(const ContactStore::Events::Contact4Destory&) override; +}; +