From 922e5f778d6a62648c982fe715be8e508c36dac3 Mon Sep 17 00:00:00 2001 From: mono Date: Mon, 6 Jun 2022 17:37:28 +0200 Subject: [PATCH] Implemented auto ping functionality. --- system/data/data_singleton.gd | 4 + .../message_data/message_data_singleton.gd | 2 + .../miscellaneous_data_singleton.gd | 53 ++++---- .../data/topic_data/topic_data_singleton.gd | 2 + system/ping_system/ping_system_singleton.gd | 126 ++++++++++++++++-- system/settings/settings_singleton.gd | 10 ++ system/time/time_singleton.gd | 86 +++++++++++- user_interface/chat_panel/message_box.gd | 2 +- user_interface/chat_panel/message_editable.gd | 9 +- user_interface/chat_panel/message_feed.gd | 2 +- .../chat_panel/message_group_container.gd | 35 +---- user_interface/main.gd | 2 + .../debug_settings/debug_settings.tscn | 30 +++-- .../debug_settings/time_offset.gd | 10 ++ user_interface/settings_menu/settings_menu.gd | 3 + .../utility_controls/content_fit_text_edit.gd | 3 + .../utility_controls/editable_text_block.gd | 8 +- 17 files changed, 300 insertions(+), 87 deletions(-) diff --git a/system/data/data_singleton.gd b/system/data/data_singleton.gd index 3dfb9e6..39160a6 100644 --- a/system/data/data_singleton.gd +++ b/system/data/data_singleton.gd @@ -1,4 +1,5 @@ extends Node +# This is not for configuration data. See settings_singleton.gd. signal loaded() @@ -26,6 +27,9 @@ func setup(): miscellaneous = MiscellaneousData.new() yield(get_tree(), "idle_frame") _load_data() + PingSystem.reschedule_pings() + yield(get_tree(), "idle_frame") + PingSystem.reevaluate_unreceived_pings() func _load_data(): diff --git a/system/data/message_data/message_data_singleton.gd b/system/data/message_data/message_data_singleton.gd index 05b4ce4..0787794 100644 --- a/system/data/message_data/message_data_singleton.gd +++ b/system/data/message_data/message_data_singleton.gd @@ -60,6 +60,8 @@ func get_last_message_of_user(user: User) -> Message: func load_data(): var file = File.new() + if not file.file_exists("user://" + FILE_NAME): + return file.open("user://" + FILE_NAME, File.READ) var json_string = file.get_as_text() if validate_json(json_string): diff --git a/system/data/miscellaneous_data/miscellaneous_data_singleton.gd b/system/data/miscellaneous_data/miscellaneous_data_singleton.gd index f3b8ff4..5ec60dd 100644 --- a/system/data/miscellaneous_data/miscellaneous_data_singleton.gd +++ b/system/data/miscellaneous_data/miscellaneous_data_singleton.gd @@ -3,38 +3,33 @@ class_name MiscellaneousData const FILE_NAME = "miscellaneous.dat" -var close_time_stamp +var dirty_flag = false +var lkp_time_stamp = 0 # last_key_press_time_stamp func load_data(): - pass -# var file = File.new() -# file.open("user://" + FILE_NAME, File.READ) -# var json_string = file.get_as_text() -# if validate_json(json_string): -# printerr("\"" + FILE_NAME + "\" was found, but is corrupted.") -# return -# var message_data_list = parse_json(json_string) -# for message_data in message_data_list: -# var user = Users.get_primary() if message_data["user"] else Users.get_helper() + var file = File.new() + if not file.file_exists("user://" + FILE_NAME): + return + file.open("user://" + FILE_NAME, File.READ) + var json_string = file.get_as_text() + if validate_json(json_string): + printerr("\"" + FILE_NAME + "\" was found, but is corrupted.") + return + var data = parse_json(json_string) + if data.has("lkpts"): + lkp_time_stamp = data["lkpts"] func save_data(): - # We do not apply a dirty flag here, because close_time_stamp needs to be - # saved anyway. - pass -# var message_data_list = [] -# for message_group in loaded_message_groups: -# var user = message_group.associated_user.is_primary() -# for message in message_group.messages: -# var message_data = { -# "user" : user, -# "time_stamp" : message.time_stamp, -# "content" : message.content -# } -# message_data_list.append(message_data) -# var json_string = to_json(message_data_list) -# var file = File.new() -# file.open("user://" + FILE_NAME, File.WRITE) -# file.store_string(json_string) -# file.close() + if dirty_flag: + var data = { + "lkpts" : lkp_time_stamp + } + var json_string = to_json(data) + var file = File.new() + file.open("user://" + FILE_NAME, File.WRITE) + file.store_string(json_string) + file.close() + # dirty_flag needs to be set to false after successful save! + dirty_flag = false diff --git a/system/data/topic_data/topic_data_singleton.gd b/system/data/topic_data/topic_data_singleton.gd index 63c50cd..9e2d510 100644 --- a/system/data/topic_data/topic_data_singleton.gd +++ b/system/data/topic_data/topic_data_singleton.gd @@ -37,6 +37,8 @@ func _compare_topic_index(a, b): func load_data(): var file = File.new() + if not file.file_exists("user://" + FILE_NAME): + return file.open("user://" + FILE_NAME, File.READ) var json_string = file.get_as_text() if validate_json(json_string): diff --git a/system/ping_system/ping_system_singleton.gd b/system/ping_system/ping_system_singleton.gd index d37cbba..5246b50 100644 --- a/system/ping_system/ping_system_singleton.gd +++ b/system/ping_system/ping_system_singleton.gd @@ -1,30 +1,140 @@ extends Node # warning-ignore:unused_signal -signal ping_handled +signal ping_handled(ping) +const AUTO_PING_ENABLED = true +const MAX_PINGS_ALLOWED = 8 onready var ping_sound = get_node("PingSound") -var unhandled_ping_count = 0 +var unhandled_ping_list = [] +var _unhandled_ping_count = 0 var total_ping_count = 0 var current_ping_id = 0 +var scheduled_pings = [] + + +func _ready(): + if AUTO_PING_ENABLED: + var autoping_timer = Timer.new() + autoping_timer.wait_time = 1.0 # Every second. + autoping_timer.autostart = true + autoping_timer.connect("timeout", self, "reevaluate_unreceived_pings") + add_child(autoping_timer) + func _input(event): if event is InputEventKey: if event.scancode == KEY_P and event.control and event.alt: if event.pressed and !event.is_echo(): - send_ping() + send_ping(Time.get_current_time()) -func send_ping(): - if unhandled_ping_count < 15: - Data.messages.add_message("", Time.get_current_time(), Users.get_helper()) +func send_ping(time_stamp: int): + if unhandled_ping_list.size() < MAX_PINGS_ALLOWED: + Data.messages.add_message("", time_stamp, Users.get_helper()) ping_sound.play() OS.request_attention() -func _on_ping_handled(): - unhandled_ping_count -= 1 +func reschedule_pings(): + scheduled_pings.clear() + + var lmbp_time_stamp = _get_lmbp_time_stamp() + + var lkp_time_stamp = Data.miscellaneous.lkp_time_stamp # last_key_press_time_stamp + + var last_t_s_of_interaction = max(lmbp_time_stamp, lkp_time_stamp) + + seed(last_t_s_of_interaction) + + var minimum_t_s_for_next_ping + minimum_t_s_for_next_ping = max( + lmbp_time_stamp + _get_time_until_next_ping(), + lkp_time_stamp + 30 + ) + + var previous_time_stamp = minimum_t_s_for_next_ping + for _i in range(8): + if not _is_time_stamp_during_active_phase(previous_time_stamp): + previous_time_stamp = Time.get_current_phase_skipped( + previous_time_stamp + ) + _random_addition([-300, 600]) + var keep_pinging = true + while keep_pinging: + scheduled_pings.append(previous_time_stamp) + keep_pinging = randi() % 100 < 30 # 30% chance. + if keep_pinging: + previous_time_stamp += randi() % 6 + previous_time_stamp += _get_time_until_next_ping() + + +func _get_lmbp_time_stamp(): + var lmbp # last_message_by_primary + lmbp = Data.messages.get_last_message_of_user( + Users.get_primary() + ) + if lmbp: + return lmbp.time_stamp + else: + return Time.get_current_time() # Just act as if just sent. + + +func _is_time_stamp_during_active_phase(time_stamp: int) -> bool: + if Time.is_during_night_time(time_stamp): + return false + elif Time.is_during_inactive_time(time_stamp): + return Time.is_during_inactive_time(_get_lmbp_time_stamp()) + elif Time.is_during_active_time(time_stamp): + return true + else: + return Time.is_during_calm_down_time(_get_lmbp_time_stamp()) + + +func _get_time_until_next_ping() -> int: + var time_addition_for_next_ping = 30 + var average_in_seconds = Settings.average_minutes_until_ping * 60 + + var rare_time_addition = [ + int(average_in_seconds * 0.05), # Minimum. + int(average_in_seconds * 0.35) # Span. + ] + + var average_time_addition = [ + int(average_in_seconds * 0.9), # Minimum. + int(average_in_seconds * 0.2) # Span. + ] + + var uncommon_time_addition = [ + int(average_in_seconds * 0.4), # Minimum. + int(average_in_seconds * 1.2) # Span. + ] + + var random_number = randi() % 1000 + + if random_number < 35: + time_addition_for_next_ping += _random_addition(rare_time_addition) + elif random_number < 665: + time_addition_for_next_ping += _random_addition(average_time_addition) + else: + time_addition_for_next_ping += _random_addition(uncommon_time_addition) + + return time_addition_for_next_ping + + +func _random_addition(span: Array) -> int: + return span[0] + int(span[1] * randf()) + + +func reevaluate_unreceived_pings(): + for scheduled_ping_t_s in scheduled_pings: + if scheduled_ping_t_s < Time.get_current_time(): + if not scheduled_ping_t_s in unhandled_ping_list: + send_ping(scheduled_ping_t_s) + + +func _on_ping_handled(ping: Message): + unhandled_ping_list.erase(ping.time_stamp) current_ping_id += 1 diff --git a/system/settings/settings_singleton.gd b/system/settings/settings_singleton.gd index f70a1a5..26178e8 100644 --- a/system/settings/settings_singleton.gd +++ b/system/settings/settings_singleton.gd @@ -2,3 +2,13 @@ extends Node # warning-ignore:unused_signal signal settings_menu_requested + +var menu_open = false + + +# Ping System behavior. +var inactive_time = 7 +var active_time = 15 +var calm_down_time = 19 +var night_time = 22 +var average_minutes_until_ping = 50 diff --git a/system/time/time_singleton.gd b/system/time/time_singleton.gd index 05f28a0..efc433d 100644 --- a/system/time/time_singleton.gd +++ b/system/time/time_singleton.gd @@ -1,8 +1,92 @@ extends Node +var locational_time_offset: int = 7200 var offset: int = 0 # This is for debug purposes. (Doing sudden time jumps) func get_current_time() -> int: - return OS.get_unix_time() + offset + return OS.get_unix_time() + locational_time_offset + offset + + +func get_time_string(time_stamp: int) -> String: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var hour = datetime["hour"] + var minute = datetime["minute"] + var am_pm + + var offset_wrapped_hour = wrapi(hour - 1, 0, 24) + var processed_hour = offset_wrapped_hour + if offset_wrapped_hour >= 12: + processed_hour -= 12 + + if hour >= 12: + am_pm = " PM" + else: + am_pm = " AM" + processed_hour += 1 + + var hour_string = str(processed_hour) + var minute_string = "%02d" % minute + + return hour_string + ":" + minute_string + am_pm + + +func get_date_string(time_stamp: int) -> String: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var month_string = "%02d" % datetime["month"] + var day_string = "%02d" % datetime["day"] + var year_string = str(datetime["year"]) + return month_string + "/" + day_string + "/" + year_string + + +func is_during_night_time(time_stamp: int) -> bool: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var hour = datetime["hour"] + return hour >= Settings.night_time or hour < Settings.inactive_time + + +func is_during_inactive_time(time_stamp: int) -> bool: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var hour = datetime["hour"] + return hour >= Settings.inactive_time and hour < Settings.active_time + + +func is_during_active_time(time_stamp: int) -> bool: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var hour = datetime["hour"] + return hour >= Settings.active_time and hour < Settings.calm_down_time + + +func is_during_calm_down_time(time_stamp: int) -> bool: + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + + var hour = datetime["hour"] + return hour >= Settings.calm_down_time and hour < Settings.night_time + + +func get_current_phase_skipped(time_stamp: int) -> int: + var DAY_IN_SECONDS = 86400 + var datetime: Dictionary = OS.get_datetime_from_unix_time(time_stamp) + var hour = datetime["hour"] + + if hour < Settings.inactive_time: + datetime["hour"] = Settings.inactive_time + elif hour >= Settings.inactive_time and hour < Settings.active_time: + datetime["hour"] = Settings.active_time + elif hour >= Settings.active_time and hour < Settings.calm_down_time: + datetime["hour"] = Settings.calm_down_time + elif hour >= Settings.calm_down_time and hour < Settings.night_time: + datetime["hour"] = Settings.night_time + else: + datetime = OS.get_datetime_from_unix_time(time_stamp + DAY_IN_SECONDS) + datetime["hour"] = Settings.inactive_time + + datetime["minute"] = 0 + datetime["second"] = 0 + return OS.get_unix_time_from_datetime(datetime) diff --git a/user_interface/chat_panel/message_box.gd b/user_interface/chat_panel/message_box.gd index 46dbd32..88c2763 100644 --- a/user_interface/chat_panel/message_box.gd +++ b/user_interface/chat_panel/message_box.gd @@ -21,7 +21,7 @@ func _input(event): func _on_message_box_confirmed(content): - if PingSystem.unhandled_ping_count == 0 and not content.empty(): + if PingSystem.unhandled_ping_list.size() == 0 and not content.empty(): text_edit.text = "" text_edit._on_text_changed() # To go back to minimum height. else: diff --git a/user_interface/chat_panel/message_editable.gd b/user_interface/chat_panel/message_editable.gd index 386eb03..7c95baf 100644 --- a/user_interface/chat_panel/message_editable.gd +++ b/user_interface/chat_panel/message_editable.gd @@ -40,8 +40,11 @@ func _setup_insert_mode(): # warning-ignore:return_value_discarded PingSystem.connect("ping_handled", self, "_on_ping_handled") ping_id = PingSystem.total_ping_count - PingSystem.unhandled_ping_count += 1 + PingSystem.unhandled_ping_list.append( + associated_message.get_ref().time_stamp + ) PingSystem.total_ping_count += 1 + auto_focus = false # warning-ignore:return_value_discarded _check_if_next_ping() @@ -127,7 +130,7 @@ func _disable_insert_mode(): text_edit.min_width = 0 text_edit.min_height = 0 PingSystem.disconnect("ping_handled", self, "_on_ping_handled") - PingSystem.emit_signal("ping_handled") + PingSystem.emit_signal("ping_handled", associated_message.get_ref()) message_inserted_sound.play() @@ -155,6 +158,6 @@ func _on_text_edit_confirmed(confirmed_content): # Override _disable_edit_mode() -func _on_ping_handled(): +func _on_ping_handled(_ping): if _check_if_next_ping(): text_edit.grab_focus() diff --git a/user_interface/chat_panel/message_feed.gd b/user_interface/chat_panel/message_feed.gd index e85cc55..be88133 100644 --- a/user_interface/chat_panel/message_feed.gd +++ b/user_interface/chat_panel/message_feed.gd @@ -23,7 +23,7 @@ func scroll_to_newest(): func _on_message_box_confirmed(content): - if PingSystem.unhandled_ping_count == 0 and not content.empty(): + if PingSystem.unhandled_ping_list.size() == 0 and not content.empty(): Data.messages.add_message( content, Time.get_current_time(), Users.get_current() ) diff --git a/user_interface/chat_panel/message_group_container.gd b/user_interface/chat_panel/message_group_container.gd index 6d56a96..6a0caa6 100644 --- a/user_interface/chat_panel/message_group_container.gd +++ b/user_interface/chat_panel/message_group_container.gd @@ -36,45 +36,18 @@ func _setup(): # Define date. var time_stamp = associated_message_group.get_ref().time_stamp var storaged_date = OS.get_datetime_from_unix_time(time_stamp) - var os_time = OS.get_datetime() + var os_time = OS.get_datetime_from_unix_time(Time.get_current_time()) if storaged_date.day == os_time.day: - date.text = "Today at " + _get_time_string(storaged_date) + date.text = "Today at " + Time.get_time_string(time_stamp) elif storaged_date.day == os_time.day - 1: - date.text = "Yesterday at " + _get_time_string(storaged_date) + date.text = "Yesterday at " + Time.get_time_string(time_stamp) else: - date.text = _get_date_string(storaged_date) + date.text = Time.get_date_string(time_stamp) _adjust_layout_to_user() -func _get_time_string(datetime: Dictionary) -> String: - var hour = datetime.hour - var minute = datetime.minute - var am_pm - - var offset_wrapped_hour = wrapi(hour - 1, 0, 24) - var processed_hour = offset_wrapped_hour - if offset_wrapped_hour >= 12: - am_pm = " PM" - processed_hour -= 12 - else: - am_pm = " AM" - processed_hour += 1 - - var hour_string = str(processed_hour) - var minute_string = "%02d" % minute - - return hour_string + ":" + minute_string + am_pm - - -func _get_date_string(datetime: Dictionary) -> String: - var month_string = "%02d" % datetime.month - var day_string = "%02d" % datetime.day - var year_string = str(datetime.year) - return month_string + "/" + day_string + "/" + year_string - - func _adjust_layout_to_user(): if get_user().is_current(): move_child(profile_image_rect, 1) diff --git a/user_interface/main.gd b/user_interface/main.gd index fc16847..793a40b 100644 --- a/user_interface/main.gd +++ b/user_interface/main.gd @@ -10,6 +10,8 @@ func _ready(): # warning-ignore:return_value_discarded Users.connect("switched", self, "_on_user_switched") Users.emit_signal("switched") + + print(OS.get_datetime_from_unix_time(0)) func _enter_tree(): diff --git a/user_interface/settings_menu/debug_settings/debug_settings.tscn b/user_interface/settings_menu/debug_settings/debug_settings.tscn index f48feef..60db496 100644 --- a/user_interface/settings_menu/debug_settings/debug_settings.tscn +++ b/user_interface/settings_menu/debug_settings/debug_settings.tscn @@ -20,8 +20,8 @@ tag = "Debug Settings" [node name="TimeOffset" type="Control" parent="."] margin_top = 19.0 margin_right = 1024.0 -margin_bottom = 83.0 -rect_min_size = Vector2( 0, 64 ) +margin_bottom = 99.0 +rect_min_size = Vector2( 0, 80 ) script = ExtResource( 2 ) [node name="LabelTimeOffset" type="Label" parent="TimeOffset"] @@ -45,8 +45,8 @@ margin_left = 86.0 margin_top = 28.0 margin_right = 160.0 margin_bottom = 52.0 -min_value = -48.0 -max_value = 48.0 +min_value = -480.0 +max_value = 480.0 __meta__ = { "_edit_use_anchors_": false } @@ -83,19 +83,29 @@ __meta__ = { } [node name="LabelTO" type="Label" parent="TimeOffset"] -margin_left = 509.0 -margin_top = 32.0 -margin_right = 549.0 -margin_bottom = 46.0 +margin_left = 68.0 +margin_top = 58.0 +margin_right = 108.0 +margin_bottom = 72.0 +text = "to:" +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="LabelTimeString" type="Label" parent="TimeOffset"] +margin_left = 311.0 +margin_top = 58.0 +margin_right = 351.0 +margin_bottom = 72.0 text = "to:" __meta__ = { "_edit_use_anchors_": false } [node name="Restart" type="Control" parent="."] -margin_top = 87.0 +margin_top = 103.0 margin_right = 1024.0 -margin_bottom = 151.0 +margin_bottom = 167.0 rect_min_size = Vector2( 0, 64 ) script = ExtResource( 3 ) diff --git a/user_interface/settings_menu/debug_settings/time_offset.gd b/user_interface/settings_menu/debug_settings/time_offset.gd index f595fed..2a6b458 100644 --- a/user_interface/settings_menu/debug_settings/time_offset.gd +++ b/user_interface/settings_menu/debug_settings/time_offset.gd @@ -1,6 +1,7 @@ extends Control onready var label_t_o = get_node("LabelTO") +onready var label_time_string = get_node("LabelTimeString") onready var spin_box_hours = get_node("SpinBoxHours") onready var spin_box_minutes = get_node("SpinBoxMinutes") @@ -18,6 +19,15 @@ func _ready(): _display_applied_time_offset() +func _process(_delta): + if not Settings.menu_open: + return + label_time_string.text = Time.get_time_string( + Time.get_current_time() + ) + " " + Time.get_date_string( + Time.get_current_time()) + + func _update_unapplied_time_offset(_value): var offset_hours = spin_box_hours.value * 3600 var offset_minutes = spin_box_minutes.value * 60 diff --git a/user_interface/settings_menu/settings_menu.gd b/user_interface/settings_menu/settings_menu.gd index 6e0d859..e456568 100644 --- a/user_interface/settings_menu/settings_menu.gd +++ b/user_interface/settings_menu/settings_menu.gd @@ -8,13 +8,16 @@ func _ready(): func _on_requested(): visible = true + Settings.menu_open = true func _on_ColorRect_gui_input(event): if event is InputEventMouseButton: if event.button_index == BUTTON_LEFT and event.is_pressed(): visible = false + Settings.menu_open = false func _on_CloseButton_pressed(): visible = false + Settings.menu_open = false diff --git a/user_interface/utility_controls/content_fit_text_edit.gd b/user_interface/utility_controls/content_fit_text_edit.gd index d895e93..76d2bc3 100644 --- a/user_interface/utility_controls/content_fit_text_edit.gd +++ b/user_interface/utility_controls/content_fit_text_edit.gd @@ -66,6 +66,9 @@ func _on_text_changed(): if expand_width: # I honestly don't know why, but the second call helps with refreshing. adjust_size() + Data.miscellaneous.lkp_time_stamp = Time.get_current_time() + Data.miscellaneous.dirty_flag = true + PingSystem.reschedule_pings() func _on_resized(): diff --git a/user_interface/utility_controls/editable_text_block.gd b/user_interface/utility_controls/editable_text_block.gd index 5c1d123..9a85bcb 100644 --- a/user_interface/utility_controls/editable_text_block.gd +++ b/user_interface/utility_controls/editable_text_block.gd @@ -13,6 +13,7 @@ onready var text_edit = get_node("VBoxContainer/MarginContainer/ContentFitTextEd onready var popup_menu = get_node("PopupMenu") +var auto_focus: bool = true var edit_mode: bool = false @@ -132,9 +133,10 @@ func _enable_edit_mode(): text_edit.adjust_size() - text_edit.grab_focus() - yield(get_tree(), "idle_frame") - text_edit.select_all() + if auto_focus: + text_edit.grab_focus() + yield(get_tree(), "idle_frame") + text_edit.select_all() func _post_enable_edit_mode(): # Virtual.