add tox friend faux offline message (still wonky) + small file copy error handling

This commit is contained in:
Green Sky 2024-02-02 20:26:50 +01:00
parent 7948d820c3
commit 0c674e0137
No known key found for this signature in database
6 changed files with 261 additions and 3 deletions

View File

@ -61,6 +61,9 @@ add_executable(tomato
./tox_dht_cap_histo.hpp
./tox_dht_cap_histo.cpp
./tox_friend_faux_offline_messaging.hpp
./tox_friend_faux_offline_messaging.cpp
./chat_gui4.hpp
./chat_gui4.cpp
)

View File

@ -96,13 +96,21 @@ const void* clipboard_callback(void* userdata, const char* mime_type, size_t* si
}
void ChatGui4::setClipboardData(std::vector<std::string> mime_types, std::shared_ptr<std::vector<uint8_t>>&& data) {
std::vector<const char*> tmp_mimetype_list;
for (const auto& mime_type : mime_types) {
tmp_mimetype_list.push_back(mime_type.data());
if (!static_cast<bool>(data)) {
std::cerr << "CG error: tried to set clipboard with empty shp\n";
return;
}
if (data->empty()) {
std::cerr << "CG error: tried to set clipboard with empty data\n";
return;
}
std::vector<const char*> tmp_mimetype_list;
std::lock_guard lg{_set_clipboard_data_mutex};
for (const auto& mime_type : mime_types) {
tmp_mimetype_list.push_back(mime_type.data());
_set_clipboard_data[mime_type] = data;
}

View File

@ -16,6 +16,7 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri
tcm(cr, tc, tc),
tmm(rmm, cr, tcm, tc, tc),
ttm(rmm, cr, tcm, tc, tc),
tffom(cr, rmm, tcm, tc, tc),
mmil(rmm),
tam(rmm, cr, conf),
sdlrtu(renderer_),
@ -241,6 +242,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
tcm.iterate(time_delta); // compute
const float fo_interval = tffom.tick(time_delta);
tam.iterate(); // compute
const float pm_interval = pm.tick(time_delta); // compute
@ -253,6 +256,10 @@ Screen* MainScreen::tick(float time_delta, bool& quit) {
tc.toxIterationInterval()/1000.f,
pm_interval
);
_min_tick_interval = std::min<float>(
_min_tick_interval,
fo_interval
);
switch (_compute_perf_mode) {
// normal 1ms lower bound

View File

@ -25,6 +25,7 @@
#include "./settings_window.hpp"
#include "./tox_ui_utils.hpp"
#include "./tox_dht_cap_histo.hpp"
#include "./tox_friend_faux_offline_messaging.hpp"
#include <string>
#include <iostream>
@ -52,6 +53,7 @@ struct MainScreen final : public Screen {
ToxContactModel2 tcm;
ToxMessageManager tmm;
ToxTransferManager ttm;
ToxFriendFauxOfflineMessaging tffom;
MediaMetaInfoLoader mmil;
ToxAvatarManager tam;

View File

@ -0,0 +1,188 @@
#include "./tox_friend_faux_offline_messaging.hpp"
#include <solanaceae/toxcore/tox_interface.hpp>
#include <solanaceae/contact/components.hpp>
#include <solanaceae/tox_contacts/components.hpp>
#include <solanaceae/message3/components.hpp>
#include <solanaceae/tox_messages/components.hpp>
#include <limits>
#include <cstdint>
namespace Message::Components {
struct LastSendAttempt {
uint64_t ts {0};
};
} // Message::Components
namespace Contact::Components {
struct NextSendAttempt {
uint64_t ts {0};
};
} // Contact::Components
ToxFriendFauxOfflineMessaging::ToxFriendFauxOfflineMessaging(
Contact3Registry& cr,
RegistryMessageModel& rmm,
ToxContactModel2& tcm,
ToxI& t,
ToxEventProviderI& tep
) : _cr(cr), _rmm(rmm), _tcm(tcm), _t(t), _tep(tep) {
}
float ToxFriendFauxOfflineMessaging::tick(float time_delta) {
// hard limit interval to once per minute
_interval_timer += time_delta;
if (_interval_timer < 1.f * 60.f) {
return std::max(60.f - _interval_timer, 0.001f); // TODO: min next timer
}
_interval_timer = 0.f;
const uint64_t ts_now = Message::getTimeMS();
// check ALL
// for each online tox friend
uint64_t min_next_attempt_ts {std::numeric_limits<uint64_t>::max()};
_cr.view<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()
.each([this, &min_next_attempt_ts, ts_now](const Contact3 c, const auto& tfe, const auto& cs) {
if (cs.state == Contact::Components::ConnectionState::disconnected) {
// cleanup
if (_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
_cr.remove<Contact::Components::NextSendAttempt>(c);
}
} else {
if (!_cr.all_of<Contact::Components::NextSendAttempt>(c)) {
const auto& nsa = _cr.emplace<Contact::Components::NextSendAttempt>(c, ts_now + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
min_next_attempt_ts = std::min(min_next_attempt_ts, nsa.ts);
} else {
auto& next_attempt = _cr.get<Contact::Components::NextSendAttempt>(c).ts;
if (doFriendMessageCheck(c, tfe)) {
next_attempt = ts_now + uint64_t(_delay_inbetween*1000);
}
min_next_attempt_ts = std::min(min_next_attempt_ts, next_attempt);
}
}
});
if (min_next_attempt_ts <= ts_now) {
// we (probably) sent this iterate
_interval_timer = 60.f - 0.1f; // TODO: ugly magic
return 0.1f;
} else if (min_next_attempt_ts == std::numeric_limits<uint64_t>::max()) {
// nothing to sync or all offline that need syncing
return 60.f; // TODO: ugly magic
} else {
// TODO: ugly magic
return _interval_timer = 60.f - std::min(60.f, (min_next_attempt_ts - ts_now) / 1000.f);
}
}
bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe) {
// walk all messages and check if
// unacked message
// timeouts for exising unacked messages expired (send)
auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c);
if (mr == nullptr) {
// no messages
return false;
}
const uint64_t ts_now = Message::getTimeMS();
// filter for unconfirmed messages
// we assume sorted
// ("reverse" iteration <.<)
auto msg_view = mr->view<Message::Components::Timestamp>();
// we search for the oldest, not too recently sent, unconfirmed message
for (auto it = msg_view.rbegin(), view_end = msg_view.rend(); it != view_end; it++) {
const Message3 msg = *it;
// require
if (!mr->all_of<
Message::Components::MessageText, // text only for now
Message::Components::ContactTo
>(msg)
) {
continue; // skip
}
// exclude
if (mr->any_of<
Message::Components::Remote::TimestampReceived // this acts like a tag, which is wrong in groups
>(msg)
) {
continue; // skip
}
uint64_t msg_ts = msg_view.get<Message::Components::Timestamp>(msg).ts;
if (mr->all_of<Message::Components::TimestampWritten>(msg)) {
msg_ts = mr->get<Message::Components::TimestampWritten>(msg).ts;
}
if (mr->all_of<Message::Components::LastSendAttempt>(msg)) {
const auto lsa = mr->get<Message::Components::LastSendAttempt>(msg).ts;
if (lsa > msg_ts) {
msg_ts = lsa;
}
}
if (ts_now < (msg_ts + uint64_t(_delay_retry * 1000))) {
// not time yet
continue;
}
// it is time
const auto [msg_id, _] = _t.toxFriendSendMessage(
tfe.friend_number,
(
mr->all_of<Message::Components::TagMessageIsAction>(msg)
? Tox_Message_Type::TOX_MESSAGE_TYPE_ACTION
: Tox_Message_Type::TOX_MESSAGE_TYPE_NORMAL
),
mr->get<Message::Components::MessageText>(msg).text
);
// TODO: this is ugly
mr->emplace_or_replace<Message::Components::LastSendAttempt>(msg, ts_now);
if (msg_id.has_value()) {
// tmm will pick this up for us
mr->emplace_or_replace<Message::Components::ToxFriendMessageID>(msg, msg_id.value());
} // else error
// we sent our message, no point further iterating
return true;
}
// TODO: somehow cleanup lsa
return false;
}
bool ToxFriendFauxOfflineMessaging::onToxEvent(const Tox_Event_Friend_Connection_Status* e) {
const auto friend_number = tox_event_friend_connection_status_get_friend_number(e);
const auto friend_status = tox_event_friend_connection_status_get_connection_status(e);
if (friend_status == Tox_Connection::TOX_CONNECTION_NONE) {
return false; // skip
// maybe cleanup?
}
auto c = _tcm.getContactFriend(friend_number);
if (!static_cast<bool>(c) || !c.all_of<Contact::Components::ToxFriendEphemeral, Contact::Components::ConnectionState>()) {
// UH error??
return false;
}
_cr.emplace_or_replace<Contact::Components::NextSendAttempt>(c, Message::getTimeMS() + uint64_t(_delay_after_cc*1000)); // wait before first message is sent
// TODO: ugly magic
_interval_timer = 60.f - 0.1f;
return false;
}

View File

@ -0,0 +1,50 @@
#pragma once
#include <solanaceae/toxcore/tox_event_interface.hpp>
#include <solanaceae/tox_contacts/tox_contact_model2.hpp>
#include <solanaceae/contact/contact_model3.hpp>
#include <solanaceae/message3/registry_message_model.hpp>
// fwd
struct ToxI;
namespace Contact::Components {
struct ToxFriendEphemeral;
}
// resends unconfirmed messages.
// timers get reset on connection changes, and send order is preserved.
class ToxFriendFauxOfflineMessaging : public ToxEventI {
Contact3Registry& _cr;
RegistryMessageModel& _rmm;
ToxContactModel2& _tcm;
ToxI& _t;
ToxEventProviderI& _tep;
float _interval_timer{0.f};
// TODO: increase timer?
const float _delay_after_cc {4.5f};
const float _delay_inbetween {0.3f};
const float _delay_retry {10.f}; // retry sending after 10s
public:
ToxFriendFauxOfflineMessaging(
Contact3Registry& cr,
RegistryMessageModel& rmm,
ToxContactModel2& tcm,
ToxI& t,
ToxEventProviderI& tep
);
float tick(float time_delta);
private:
// only called for online friends
// returns true if a message was sent
// dont call this too often
bool doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe);
protected:
bool onToxEvent(const Tox_Event_Friend_Connection_Status* e) override;
};