Compare commits
	
		
			10 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b56d581e4b | |||
| aa661aaaa7 | |||
| cc3f430bab | |||
| 139db5b03b | |||
| 7d0e5c80bd | |||
| f716ad9dd1 | |||
| 671772a20e | |||
| b0173f6d68 | |||
| 3da5872df8 | |||
| 3deb6e8469 | 
							
								
								
									
										2
									
								
								external/solanaceae_contact
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								external/solanaceae_contact
									
									
									
									
										vendored
									
									
								
							 Submodule external/solanaceae_contact updated: 2d73c7272c...9ca6adee4f
									
								
							
							
								
								
									
										2
									
								
								external/solanaceae_message3
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								external/solanaceae_message3
									
									
									
									
										vendored
									
									
								
							 Submodule external/solanaceae_message3 updated: ab282235b5...a1f5add8d3
									
								
							
							
								
								
									
										2
									
								
								external/solanaceae_plugin
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								external/solanaceae_plugin
									
									
									
									
										vendored
									
									
								
							 Submodule external/solanaceae_plugin updated: 17ffaee013...82cfb6d492
									
								
							
							
								
								
									
										2
									
								
								external/solanaceae_util
									
									
									
									
										vendored
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										2
									
								
								external/solanaceae_util
									
									
									
									
										vendored
									
									
								
							 Submodule external/solanaceae_util updated: 390b123fb7...d304d719e9
									
								
							| @@ -37,7 +37,7 @@ namespace Components { | ||||
|  | ||||
| } // Components | ||||
|  | ||||
| static float lerp(float a, float b, float t) { | ||||
| static constexpr float lerp(float a, float b, float t) { | ||||
| 	return a + t * (b - a); | ||||
| } | ||||
|  | ||||
| @@ -121,8 +121,10 @@ ChatGui4::ChatGui4( | ||||
| 	ConfigModelI& conf, | ||||
| 	RegistryMessageModel& rmm, | ||||
| 	Contact3Registry& cr, | ||||
| 	TextureUploaderI& tu | ||||
| ) : _conf(conf), _rmm(rmm), _cr(cr), _tal(_cr), _contact_tc(_tal, tu), _msg_tc(_mil, tu), _sip(tu) { | ||||
| 	TextureUploaderI& tu, | ||||
| 	ContactTextureCache& contact_tc, | ||||
| 	MessageTextureCache& msg_tc | ||||
| ) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _sip(tu) { | ||||
| } | ||||
|  | ||||
| ChatGui4::~ChatGui4(void) { | ||||
| @@ -136,20 +138,7 @@ ChatGui4::~ChatGui4(void) { | ||||
| 	//} | ||||
| } | ||||
|  | ||||
| void ChatGui4::render(float time_delta) { | ||||
| 	if (!_cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars | ||||
| 		std::vector<Contact3> to_purge; | ||||
| 		_cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) { | ||||
| 			to_purge.push_back(c); | ||||
| 		}); | ||||
| 		_cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend()); | ||||
| 		_contact_tc.invalidate(to_purge); | ||||
| 	} | ||||
| 	// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE | ||||
| 	// it might unload textures, so it needs to be done before rendering | ||||
| 	_contact_tc.update(); | ||||
| 	_msg_tc.update(); | ||||
|  | ||||
| float ChatGui4::render(float time_delta) { | ||||
| 	_fss.render(); | ||||
| 	_sip.render(time_delta); | ||||
|  | ||||
| @@ -627,8 +616,7 @@ void ChatGui4::render(float time_delta) { | ||||
| 	} | ||||
| 	ImGui::End(); | ||||
|  | ||||
| 	_contact_tc.workLoadQueue(); | ||||
| 	_msg_tc.workLoadQueue(); | ||||
| 	return 1000.f; // TODO: higher min fps? | ||||
| } | ||||
|  | ||||
| void ChatGui4::sendFilePath(const char* file_path) { | ||||
|   | ||||
| @@ -9,7 +9,8 @@ | ||||
| #include "./message_image_loader.hpp" | ||||
| #include "./file_selector.hpp" | ||||
| #include "./send_image_popup.hpp" | ||||
| #include "entt/container/dense_map.hpp" | ||||
|  | ||||
| #include <entt/container/dense_map.hpp> | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <vector> | ||||
| @@ -17,15 +18,16 @@ | ||||
| #include <mutex> | ||||
| #include <memory> | ||||
|  | ||||
| using ContactTextureCache = TextureCache<void*, Contact3, ToxAvatarLoader>; | ||||
| using MessageTextureCache = TextureCache<void*, Message3Handle, MessageImageLoader>; | ||||
|  | ||||
| class ChatGui4 { | ||||
| 	ConfigModelI& _conf; | ||||
| 	RegistryMessageModel& _rmm; | ||||
| 	Contact3Registry& _cr; | ||||
|  | ||||
| 	ToxAvatarLoader _tal; | ||||
| 	TextureCache<void*, Contact3, ToxAvatarLoader> _contact_tc; | ||||
| 	MessageImageLoader _mil; | ||||
| 	TextureCache<void*, Message3Handle, MessageImageLoader> _msg_tc; | ||||
| 	ContactTextureCache& _contact_tc; | ||||
| 	MessageTextureCache& _msg_tc; | ||||
|  | ||||
| 	FileSelector _fss; | ||||
| 	SendImagePopup _sip; | ||||
| @@ -52,12 +54,14 @@ class ChatGui4 { | ||||
| 			ConfigModelI& conf, | ||||
| 			RegistryMessageModel& rmm, | ||||
| 			Contact3Registry& cr, | ||||
| 			TextureUploaderI& tu | ||||
| 			TextureUploaderI& tu, | ||||
| 			ContactTextureCache& contact_tc, | ||||
| 			MessageTextureCache& msg_tc | ||||
| 		); | ||||
| 		~ChatGui4(void); | ||||
|  | ||||
| 	public: | ||||
| 		void render(float time_delta); | ||||
| 		float render(float time_delta); | ||||
|  | ||||
| 	public: | ||||
| 		bool any_unread {false}; | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/main.cpp
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								src/main.cpp
									
									
									
									
									
								
							| @@ -178,9 +178,13 @@ int main(int argc, char** argv) { | ||||
| 				//) | ||||
| 			//)); | ||||
|  | ||||
| 			const float min_delay = std::min<float>( | ||||
| 					screen->nextTick() - time_delta_tick, | ||||
| 					screen->nextRender() - time_delta_render | ||||
| 			const float min_delay = | ||||
| 				std::min<float>( | ||||
| 					std::min<float>( | ||||
| 						screen->nextTick() - time_delta_tick, | ||||
| 						screen->nextRender() - time_delta_render | ||||
| 					), | ||||
| 					0.25f // dont sleep too long | ||||
| 				) * 1000.f; | ||||
|  | ||||
| 			if (min_delay > 0.f) { | ||||
|   | ||||
| @@ -1,10 +1,13 @@ | ||||
| #include "./main_screen.hpp" | ||||
|  | ||||
| #include <solanaceae/contact/components.hpp> | ||||
|  | ||||
| #include <imgui/imgui.h> | ||||
|  | ||||
| #include <SDL3/SDL.h> | ||||
|  | ||||
| #include <memory> | ||||
| #include <cmath> | ||||
|  | ||||
| MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::string save_password, std::vector<std::string> plugins) : | ||||
| 	renderer(renderer_), | ||||
| @@ -20,7 +23,11 @@ MainScreen::MainScreen(SDL_Renderer* renderer_, std::string save_path, std::stri | ||||
| 	mmil(rmm), | ||||
| 	tam(rmm, cr, conf), | ||||
| 	sdlrtu(renderer_), | ||||
| 	cg(conf, rmm, cr, sdlrtu), | ||||
| 	tal(cr), | ||||
| 	contact_tc(tal, sdlrtu), | ||||
| 	mil(), | ||||
| 	msg_tc(mil, sdlrtu), | ||||
| 	cg(conf, rmm, cr, sdlrtu, contact_tc, msg_tc), | ||||
| 	sw(conf), | ||||
| 	tuiu(tc, conf), | ||||
| 	tdch(tpi) | ||||
| @@ -167,7 +174,22 @@ Screen* MainScreen::render(float time_delta, bool&) { | ||||
|  | ||||
| 	const float pm_interval = pm.render(time_delta); // render | ||||
|  | ||||
| 	cg.render(time_delta); // render | ||||
| 	// TODO: move this somewhere else!!! | ||||
| 	// needs both tal and tc <.< | ||||
| 	if (!cr.storage<Contact::Components::TagAvatarInvalidate>().empty()) { // handle force-reloads for avatars | ||||
| 		std::vector<Contact3> to_purge; | ||||
| 		cr.view<Contact::Components::TagAvatarInvalidate>().each([&to_purge](const Contact3 c) { | ||||
| 			to_purge.push_back(c); | ||||
| 		}); | ||||
| 		cr.remove<Contact::Components::TagAvatarInvalidate>(to_purge.cbegin(), to_purge.cend()); | ||||
| 		contact_tc.invalidate(to_purge); | ||||
| 	} | ||||
| 	// ACTUALLY NOT IF RENDERED, MOVED LOGIC TO ABOVE | ||||
| 	// it might unload textures, so it needs to be done before rendering | ||||
| 	const float ctc_interval = contact_tc.update(); | ||||
| 	const float msgtc_interval = msg_tc.update(); | ||||
|  | ||||
| 	const float cg_interval = cg.render(time_delta); // render | ||||
| 	sw.render(); // render | ||||
| 	tuiu.render(); // render | ||||
| 	tdch.render(); // render | ||||
| @@ -216,20 +238,136 @@ Screen* MainScreen::render(float time_delta, bool&) { | ||||
| 		ImGui::ShowDemoWindow(); | ||||
| 	} | ||||
|  | ||||
| 	if ( | ||||
| 		_fps_perf_mode > 1 // TODO: magic | ||||
| 	) { | ||||
| 		// powersave forces 250ms | ||||
| 		_render_interval = 1.f/4.f; | ||||
| 	} else if ( | ||||
| 		_time_since_event > 1.f && ( // 1sec cool down | ||||
| 			_fps_perf_mode == 1 || // TODO: magic | ||||
| 			_window_hidden | ||||
| 	float tc_unfinished_queue_interval; | ||||
| 	{ // load rendered but not loaded textures | ||||
| 		bool unfinished_work_queue = contact_tc.workLoadQueue(); | ||||
| 		unfinished_work_queue = unfinished_work_queue || msg_tc.workLoadQueue(); | ||||
|  | ||||
| 		if (unfinished_work_queue) { | ||||
| 			tc_unfinished_queue_interval = 0.1f; // so we can get images loaded faster | ||||
| 		} else { | ||||
| 			tc_unfinished_queue_interval = 1.f; // TODO: higher min fps? | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// calculate interval for next frame | ||||
| 	// normal: | ||||
| 	//  - if < 1.5sec since last event | ||||
| 	//    - min all and clamp(1/60, 1/1) | ||||
| 	//  - if < 30sec since last event | ||||
| 	//    - min all (anim + everything else) clamp(1/60, 1/1) (maybe less?) | ||||
| 	//  - else | ||||
| 	//    - min without anim and clamp(1/60, 1/1) (maybe more?) | ||||
| 	// reduced: | ||||
| 	//  - if < 1sec since last event | ||||
| 	//    - min all and clamp(1/60, 1/1) | ||||
| 	//  - if < 10sec since last event | ||||
| 	//    - min all (anim + everything else) clamp(1/10, 1/1) | ||||
| 	//  - else | ||||
| 	//    - min without anim and max clamp(1/10, 1/1) | ||||
| 	// powersave: | ||||
| 	//  - if < 0sec since last event | ||||
| 	//    - (ignored) | ||||
| 	//  - if < 1sec since last event | ||||
| 	//    - min all (anim + everything else) clamp(1/8, 1/1) | ||||
| 	//  - else | ||||
| 	//    - min without anim and clamp(1/1, 1/1) | ||||
| 	struct PerfProfileRender { | ||||
| 		float low_delay_window {1.5f}; | ||||
| 		float low_delay_min {1.f/60.f}; | ||||
| 		float low_delay_max {1.f/60.f}; | ||||
|  | ||||
| 		float mid_delay_window {30.f}; | ||||
| 		float mid_delay_min {1.f/60.f}; | ||||
| 		float mid_delay_max {1.f/2.f}; | ||||
|  | ||||
| 		// also when main window hidden | ||||
| 		float else_delay_min {1.f/60.f}; | ||||
| 		float else_delay_max {1.f/2.f}; | ||||
| 	}; | ||||
|  | ||||
| 	const static PerfProfileRender normalPerfProfile{ | ||||
| 		//1.5f,		// low_delay_window | ||||
| 		//1.f/60.f,	// low_delay_min | ||||
| 		//1.f/60.f,	// low_delay_max | ||||
|  | ||||
| 		//30.f,		// mid_delay_window | ||||
| 		//1.f/60.f,	// mid_delay_min | ||||
| 		//1.f/2.f,	// mid_delay_max | ||||
|  | ||||
| 		//1.f/60.f,	// else_delay_min | ||||
| 		//1.f/2.f,	// else_delay_max | ||||
| 	}; | ||||
| 	const static PerfProfileRender reducedPerfProfile{ | ||||
| 		1.f,		// low_delay_window | ||||
| 		1.f/60.f,	// low_delay_min | ||||
| 		1.f/30.f,	// low_delay_max | ||||
|  | ||||
| 		10.f,		// mid_delay_window | ||||
| 		1.f/10.f,	// mid_delay_min | ||||
| 		1.f/4.f,	// mid_delay_max | ||||
|  | ||||
| 		1.f/10.f,	// else_delay_min | ||||
| 		1.f,		// else_delay_max | ||||
| 	}; | ||||
| 	// TODO: fix powersave by adjusting it in the events handler (make ppr member) | ||||
| 	const static PerfProfileRender powersavePerfProfile{ | ||||
| 		// no window -> ignore first case | ||||
| 		0.f,		// low_delay_window | ||||
| 		1.f,		// low_delay_min | ||||
| 		1.f,		// low_delay_max | ||||
|  | ||||
| 		1.f,		// mid_delay_window | ||||
| 		1.f/8.f,	// mid_delay_min | ||||
| 		1.f/4.f,	// mid_delay_max | ||||
|  | ||||
| 		1.f,		// else_delay_min | ||||
| 		1.f,		// else_delay_max | ||||
| 	}; | ||||
|  | ||||
| 	const PerfProfileRender& curr_profile = | ||||
| 		// TODO: magic | ||||
| 		_fps_perf_mode > 1 | ||||
| 		? powersavePerfProfile | ||||
| 		: ( | ||||
| 			_fps_perf_mode == 1 | ||||
| 			? reducedPerfProfile | ||||
| 			: normalPerfProfile | ||||
| 		) | ||||
| 	) { | ||||
| 		_render_interval = std::min<float>(1.f/4.f, pm_interval); | ||||
| 	; | ||||
|  | ||||
| 	// min over non animations in all cases | ||||
| 	_render_interval = std::min<float>(pm_interval, cg_interval); | ||||
| 	_render_interval = std::min<float>(_render_interval, tc_unfinished_queue_interval); | ||||
|  | ||||
| 	// low delay time window | ||||
| 	if (!_window_hidden && _time_since_event < curr_profile.low_delay_window) { | ||||
| 		_render_interval = std::min<float>(_render_interval, ctc_interval); | ||||
| 		_render_interval = std::min<float>(_render_interval, msgtc_interval); | ||||
|  | ||||
| 		_render_interval = std::clamp( | ||||
| 			_render_interval, | ||||
| 			curr_profile.low_delay_min, | ||||
| 			curr_profile.low_delay_max | ||||
| 		); | ||||
| 	// mid delay time window | ||||
| 	} else if (!_window_hidden && _time_since_event < curr_profile.mid_delay_window) { | ||||
| 		_render_interval = std::min<float>(_render_interval, ctc_interval); | ||||
| 		_render_interval = std::min<float>(_render_interval, msgtc_interval); | ||||
|  | ||||
| 		_render_interval = std::clamp( | ||||
| 			_render_interval, | ||||
| 			curr_profile.mid_delay_min, | ||||
| 			curr_profile.mid_delay_max | ||||
| 		); | ||||
| 	// timed out or window hidden | ||||
| 	} else { | ||||
| 		_render_interval = std::min<float>(1.f/60.f, pm_interval); | ||||
| 		// no animation timing here | ||||
| 		_render_interval = std::clamp( | ||||
| 			_render_interval, | ||||
| 			curr_profile.else_delay_min, | ||||
| 			curr_profile.else_delay_max | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	_time_since_event += time_delta; | ||||
| @@ -253,7 +391,9 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { | ||||
| 	mts.iterate(); // compute | ||||
|  | ||||
| 	_min_tick_interval = std::min<float>( | ||||
| 		tc.toxIterationInterval()/1000.f, | ||||
| 		// HACK: pow by 1.6 to increase 50 -> ~500 (~522) | ||||
| 		// and it does not change 1 | ||||
| 		std::pow(tc.toxIterationInterval(), 1.6f)/1000.f, | ||||
| 		pm_interval | ||||
| 	); | ||||
| 	_min_tick_interval = std::min<float>( | ||||
| @@ -261,6 +401,8 @@ Screen* MainScreen::tick(float time_delta, bool& quit) { | ||||
| 		fo_interval | ||||
| 	); | ||||
|  | ||||
| 	//std::cout << "MS: min tick interval: " << _min_tick_interval << "\n"; | ||||
|  | ||||
| 	switch (_compute_perf_mode) { | ||||
| 		// normal 1ms lower bound | ||||
| 		case 0: _min_tick_interval = std::max<float>(_min_tick_interval, 0.001f); break; | ||||
|   | ||||
| @@ -21,6 +21,10 @@ | ||||
| #include "./tox_avatar_manager.hpp" | ||||
|  | ||||
| #include "./sdlrenderer_texture_uploader.hpp" | ||||
| #include "./texture_cache.hpp" | ||||
| #include "./tox_avatar_loader.hpp" | ||||
| #include "./message_image_loader.hpp" | ||||
|  | ||||
| #include "./chat_gui4.hpp" | ||||
| #include "./settings_window.hpp" | ||||
| #include "./tox_ui_utils.hpp" | ||||
| @@ -61,6 +65,11 @@ struct MainScreen final : public Screen { | ||||
| 	SDLRendererTextureUploader sdlrtu; | ||||
| 	//OpenGLTextureUploader ogltu; | ||||
|  | ||||
| 	ToxAvatarLoader tal; | ||||
| 	TextureCache<void*, Contact3, ToxAvatarLoader> contact_tc; | ||||
| 	MessageImageLoader mil; | ||||
| 	TextureCache<void*, Message3Handle, MessageImageLoader> msg_tc; | ||||
|  | ||||
| 	ChatGui4 cg; | ||||
| 	SettingsWindow sw; | ||||
| 	ToxUIUtils tuiu; | ||||
| @@ -69,7 +78,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); | ||||
| @@ -85,7 +94,7 @@ struct MainScreen final : public Screen { | ||||
| 	// 0 - normal | ||||
| 	// 1 - reduced | ||||
| 	// 2 - power save | ||||
| 	int _fps_perf_mode {1}; | ||||
| 	int _fps_perf_mode {0}; | ||||
| 	// 0 - normal | ||||
| 	// 1 - power save | ||||
| 	int _compute_perf_mode {0}; | ||||
|   | ||||
| @@ -32,7 +32,7 @@ struct SendImagePopup { | ||||
| 	Rect crop_rect; | ||||
| 	Rect crop_before_drag; | ||||
|  | ||||
| 	bool cropping {false}; | ||||
| 	bool cropping {true}; | ||||
| 	bool dragging_last_frame_ul {false}; | ||||
| 	bool dragging_last_frame_lr {false}; | ||||
|  | ||||
|   | ||||
| @@ -2,8 +2,9 @@ | ||||
|  | ||||
| #include <chrono> | ||||
| #include <array> | ||||
| #include <limits> | ||||
|  | ||||
| void TextureEntry::doAnimation(const int64_t ts_now) { | ||||
| int64_t TextureEntry::doAnimation(const int64_t ts_now) { | ||||
| 	if (frame_duration.size() > 1) { // is animation | ||||
| 		do { // why is this loop so ugly | ||||
| 			const int64_t duration = getDuration(); | ||||
| @@ -11,11 +12,13 @@ void TextureEntry::doAnimation(const int64_t ts_now) { | ||||
| 				timestamp_last_rendered += duration; | ||||
| 				next(); | ||||
| 			} else { | ||||
| 				break; | ||||
| 				// return ts for next frame | ||||
| 				return timestamp_last_rendered + duration; | ||||
| 			} | ||||
| 		} while(true); | ||||
| 		} while (true); | ||||
| 	} else { | ||||
| 		timestamp_last_rendered = ts_now; | ||||
| 		return std::numeric_limits<int64_t>::max(); // static image | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -50,7 +50,8 @@ struct TextureEntry { | ||||
| 		current_texture = (current_texture + 1) % frame_duration.size(); | ||||
| 	} | ||||
|  | ||||
| 	void doAnimation(const int64_t ts_now); | ||||
| 	// returns ts for next frame | ||||
| 	int64_t doAnimation(const int64_t ts_now); | ||||
|  | ||||
| 	template<typename TextureType> | ||||
| 	TextureType getID(void) { | ||||
| @@ -133,14 +134,16 @@ struct TextureCache { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void update(void) { | ||||
| 	float update(void) { | ||||
| 		const uint64_t ts_now = Message::getTimeMS(); | ||||
| 		uint64_t ts_min_next = ts_now + ms_before_purge; | ||||
|  | ||||
| 		std::vector<KeyType> to_purge; | ||||
| 		for (auto&& [key, te] : _cache) { | ||||
| 			if (te.rendered_this_frame) { | ||||
| 				te.doAnimation(ts_now); | ||||
| 				const uint64_t ts_next = te.doAnimation(ts_now); | ||||
| 				te.rendered_this_frame = false; | ||||
| 				ts_min_next = std::min(ts_min_next, ts_next); | ||||
| 			} else if (_cache.size() > min_count_before_purge && ts_now - te.timestamp_last_rendered >= ms_before_purge) { | ||||
| 				to_purge.push_back(key); | ||||
| 			} | ||||
| @@ -148,7 +151,10 @@ struct TextureCache { | ||||
|  | ||||
| 		invalidate(to_purge); | ||||
|  | ||||
| 		// we ignore the default texture ts :) | ||||
| 		_default_texture.doAnimation(ts_now); | ||||
|  | ||||
| 		return (ts_min_next - ts_now) / 1000.f; | ||||
| 	} | ||||
|  | ||||
| 	void invalidate(const std::vector<KeyType>& to_purge) { | ||||
| @@ -162,16 +168,22 @@ struct TextureCache { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void workLoadQueue(void) { | ||||
| 		for (auto it = _to_load.begin(); it != _to_load.end(); it++) { | ||||
| 	// returns true if there is still work queued up | ||||
| 	bool workLoadQueue(void) { | ||||
| 		auto it = _to_load.begin(); | ||||
| 		for (; it != _to_load.end(); it++) { | ||||
| 			auto new_entry_opt = _l.load(_tu, *it); | ||||
| 			if (new_entry_opt.has_value()) { | ||||
| 				_cache.emplace(*it, new_entry_opt.value()); | ||||
| 				_to_load.erase(it); | ||||
| 				// TODO: not a good idea | ||||
| 				it = _to_load.erase(it); | ||||
|  | ||||
| 				// TODO: not a good idea? | ||||
| 				break; // end load from queue/onlyload 1 per update | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// peak | ||||
| 		return it != _to_load.end(); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,8 @@ | ||||
| #include <limits> | ||||
| #include <cstdint> | ||||
|  | ||||
| //#include <iostream> | ||||
|  | ||||
| namespace Message::Components { | ||||
| 	struct LastSendAttempt { | ||||
| 		uint64_t ts {0}; | ||||
| @@ -29,15 +31,17 @@ ToxFriendFauxOfflineMessaging::ToxFriendFauxOfflineMessaging( | ||||
| 	ToxI& t, | ||||
| 	ToxEventProviderI& tep | ||||
| ) : _cr(cr), _rmm(rmm), _tcm(tcm), _t(t), _tep(tep) { | ||||
| 	_tep.subscribe(this, Tox_Event_Type::TOX_EVENT_FRIEND_CONNECTION_STATUS); | ||||
| } | ||||
|  | ||||
| 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 -= time_delta; | ||||
| 	if (_interval_timer > 0.f) { | ||||
| 		return std::max(_interval_timer, 0.001f); // TODO: min next timer | ||||
| 	} | ||||
| 	_interval_timer = 0.f; | ||||
| 	// interval ~ once per minute | ||||
| 	_interval_timer = 60.f; | ||||
|  | ||||
|  | ||||
| 	const uint64_t ts_now = Message::getTimeMS(); | ||||
|  | ||||
| @@ -50,37 +54,47 @@ float ToxFriendFauxOfflineMessaging::tick(float time_delta) { | ||||
| 			// cleanup | ||||
| 			if (_cr.all_of<Contact::Components::NextSendAttempt>(c)) { | ||||
| 				_cr.remove<Contact::Components::NextSendAttempt>(c); | ||||
| 				auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c); | ||||
| 				if (mr != nullptr) { | ||||
| 					mr->storage<Message::Components::LastSendAttempt>().clear(); | ||||
| 				} | ||||
| 			} | ||||
| 		} 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); | ||||
| 				if (false) { // has unsent messages | ||||
| 					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 ret = doFriendMessageCheck(c, tfe); | ||||
| 				if (ret == dfmc_Ret::SENT_THIS_TICK) { | ||||
| 					const auto ts = _cr.get<Contact::Components::NextSendAttempt>(c).ts = ts_now + uint64_t(_delay_inbetween*1000); | ||||
| 					min_next_attempt_ts = std::min(min_next_attempt_ts, ts); | ||||
| 				} else if (ret == dfmc_Ret::TOO_SOON) { | ||||
| 					// TODO: set to _delay_inbetween? prob expensive for no good reason | ||||
| 					min_next_attempt_ts = std::min(min_next_attempt_ts, _cr.get<Contact::Components::NextSendAttempt>(c).ts); | ||||
| 				} else { | ||||
| 					_cr.remove<Contact::Components::NextSendAttempt>(c); | ||||
| 				} | ||||
|  | ||||
| 				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; | ||||
| 		_interval_timer = 0.1f; // TODO: ugly magic | ||||
| 	} 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); | ||||
| 		_interval_timer = std::min(_interval_timer, (min_next_attempt_ts - ts_now) / 1000.f); | ||||
| 	} | ||||
|  | ||||
| 	//std::cout << "TFFOM: iterate (i:" << _interval_timer << ")\n"; | ||||
|  | ||||
| 	return _interval_timer; | ||||
| } | ||||
|  | ||||
| bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const Contact::Components::ToxFriendEphemeral& tfe) { | ||||
| ToxFriendFauxOfflineMessaging::dfmc_Ret 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) | ||||
| @@ -88,7 +102,7 @@ bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const | ||||
| 	auto* mr = static_cast<const RegistryMessageModel&>(_rmm).get(c); | ||||
| 	if (mr == nullptr) { | ||||
| 		// no messages | ||||
| 		return false; | ||||
| 		return dfmc_Ret::NO_MSG; | ||||
| 	} | ||||
|  | ||||
| 	const uint64_t ts_now = Message::getTimeMS(); | ||||
| @@ -98,6 +112,7 @@ bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const | ||||
| 	// we assume sorted | ||||
| 	// ("reverse" iteration <.<) | ||||
| 	auto msg_view = mr->view<Message::Components::Timestamp>(); | ||||
| 	bool valid_unsent {false}; | ||||
| 	// 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; | ||||
| @@ -119,6 +134,12 @@ bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const | ||||
| 			continue; // skip | ||||
| 		} | ||||
|  | ||||
| 		if (mr->get<Message::Components::ContactTo>(msg).c != c) { | ||||
| 			continue; // not outbound (in private) | ||||
| 		} | ||||
|  | ||||
| 		valid_unsent = true; | ||||
|  | ||||
| 		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; | ||||
| @@ -155,12 +176,17 @@ bool ToxFriendFauxOfflineMessaging::doFriendMessageCheck(const Contact3 c, const | ||||
| 		} // else error | ||||
|  | ||||
| 		// we sent our message, no point further iterating | ||||
| 		return true; | ||||
| 		return dfmc_Ret::SENT_THIS_TICK; | ||||
| 	} | ||||
|  | ||||
| 	// TODO: somehow cleanup lsa | ||||
| 	if (!valid_unsent) { | ||||
| 		// somehow cleanup lsa | ||||
| 		mr->storage<Message::Components::LastSendAttempt>().clear(); | ||||
| 		//std::cout << "TFFOM: all sent, deleting lsa\n"; | ||||
| 		return dfmc_Ret::NO_MSG; | ||||
| 	} | ||||
|  | ||||
| 	return false; | ||||
| 	return dfmc_Ret::TOO_SOON; | ||||
| } | ||||
|  | ||||
| bool ToxFriendFauxOfflineMessaging::onToxEvent(const Tox_Event_Friend_Connection_Status* e) { | ||||
| @@ -180,8 +206,7 @@ bool ToxFriendFauxOfflineMessaging::onToxEvent(const Tox_Event_Friend_Connection | ||||
|  | ||||
| 	_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; | ||||
| 	_interval_timer = 0.f; | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -39,10 +39,15 @@ class ToxFriendFauxOfflineMessaging : public ToxEventI { | ||||
| 		float tick(float time_delta); | ||||
|  | ||||
| 	private: | ||||
| 		enum class dfmc_Ret { | ||||
| 			TOO_SOON, | ||||
| 			SENT_THIS_TICK, | ||||
| 			NO_MSG, | ||||
| 		}; | ||||
| 		// 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); | ||||
| 		dfmc_Ret 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