Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 3deb6e8469 | |||
| 0c674e0137 | 
@@ -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
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -67,7 +69,7 @@ struct MainScreen final : public Screen {
 | 
			
		||||
	bool _show_tool_style_editor {false};
 | 
			
		||||
 | 
			
		||||
	bool _window_hidden {false};
 | 
			
		||||
	bool _window_hidden_ts {0};
 | 
			
		||||
	uint64_t _window_hidden_ts {0};
 | 
			
		||||
	float _time_since_event {0.f};
 | 
			
		||||
 | 
			
		||||
	MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										188
									
								
								src/tox_friend_faux_offline_messaging.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/tox_friend_faux_offline_messaging.cpp
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								src/tox_friend_faux_offline_messaging.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/tox_friend_faux_offline_messaging.hpp
									
									
									
									
									
										Normal 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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user