2026-02-23 18:38:03 -06:00
|
|
|
extends Node
|
|
|
|
|
|
2026-03-08 13:19:39 -05:00
|
|
|
#region Signals
|
|
|
|
|
signal live_streamers_updating
|
|
|
|
|
signal live_streamers_updated
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Public Variables
|
|
|
|
|
var twitcher: TwitcherExtended:
|
|
|
|
|
set(val):
|
|
|
|
|
twitcher = val
|
|
|
|
|
twitcher.streamer_token_validated.connect(_setup_live_stream_timer)
|
|
|
|
|
|
2026-02-23 18:38:03 -06:00
|
|
|
var context: OverlayContext
|
|
|
|
|
var settings: OverlaySettings
|
2026-03-02 02:06:17 -06:00
|
|
|
var main_win: OverlayWindow
|
2026-03-08 13:19:39 -05:00
|
|
|
var live_streamers: Dictionary[String, TwitchStream] = {}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Private Variables
|
2026-02-24 03:15:31 -06:00
|
|
|
var _pt_except: Array[Control] = []
|
|
|
|
|
var _pt_mouse: bool = false
|
|
|
|
|
var _current_pt_mask: PackedVector2Array = []
|
2026-02-26 14:36:35 -06:00
|
|
|
var _hull_points: PackedVector2Array = []
|
|
|
|
|
var _debug_draw: DebugDraw
|
2026-03-08 13:19:39 -05:00
|
|
|
var _tmr_live_stream: Timer
|
|
|
|
|
#endregion
|
2026-02-23 18:38:03 -06:00
|
|
|
|
|
|
|
|
# Called when the node enters the scene tree for the first time.
|
|
|
|
|
func _ready() -> void:
|
|
|
|
|
context = OverlayContext.new()
|
|
|
|
|
context.setup()
|
|
|
|
|
|
|
|
|
|
context.open_db("user://overlay.db")
|
2026-03-06 22:44:23 -06:00
|
|
|
# Method one, just create all tables, prevents alterations to existing tables.
|
|
|
|
|
#context.ensure_tables()
|
|
|
|
|
# Method two, use migrations to handle setting up database tables, ensuring that
|
|
|
|
|
# tables can be modified in the future
|
|
|
|
|
context.run_migrations()
|
2026-02-23 18:38:03 -06:00
|
|
|
if FileAccess.file_exists("user://settings.tres"):
|
|
|
|
|
settings = load("user://settings.tres")
|
|
|
|
|
else:
|
|
|
|
|
settings = OverlaySettings.new()
|
|
|
|
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
|
|
|
save_settings()
|
|
|
|
|
|
2026-02-26 14:36:35 -06:00
|
|
|
func _input(_event: InputEvent) -> void:
|
|
|
|
|
if Input.is_action_just_pressed("debug_draw"):
|
|
|
|
|
if _debug_draw:
|
|
|
|
|
_debug_draw.queue_free()
|
|
|
|
|
_debug_draw = null
|
|
|
|
|
else:
|
|
|
|
|
_debug_draw = DebugDraw.new()
|
|
|
|
|
add_child(_debug_draw)
|
|
|
|
|
|
2026-03-08 13:19:39 -05:00
|
|
|
func _setup_live_stream_timer() -> void:
|
|
|
|
|
if _tmr_live_stream:
|
|
|
|
|
return
|
|
|
|
|
_tmr_live_stream = Timer.new()
|
|
|
|
|
_tmr_live_stream.name = "TimerLiveStreamCheck"
|
|
|
|
|
_tmr_live_stream.wait_time = 240
|
|
|
|
|
_tmr_live_stream.one_shot = false
|
|
|
|
|
_tmr_live_stream.timeout.connect(_run_live_streamer_update)
|
|
|
|
|
add_child(_tmr_live_stream)
|
|
|
|
|
|
|
|
|
|
_run_live_streamer_update()
|
|
|
|
|
|
|
|
|
|
_tmr_live_stream.start()
|
2026-03-08 16:19:22 -05:00
|
|
|
get_indie_game_devs_members()
|
2026-03-08 13:19:39 -05:00
|
|
|
|
|
|
|
|
func _run_live_streamer_update() -> void:
|
|
|
|
|
live_streamers_updating.emit()
|
|
|
|
|
live_streamers = await twitcher.get_live_streamers_data()
|
|
|
|
|
live_streamers_updated.emit()
|
|
|
|
|
|
2026-03-08 19:28:41 -05:00
|
|
|
func refresh_live_streamer() -> void:
|
|
|
|
|
if _tmr_live_stream: _tmr_live_stream.stop()
|
|
|
|
|
_run_live_streamer_update()
|
|
|
|
|
if _tmr_live_stream: _tmr_live_stream.start(240)
|
|
|
|
|
|
2026-02-23 18:38:03 -06:00
|
|
|
func save_settings() -> void:
|
|
|
|
|
ResourceSaver.save(settings, "user://settings.tres")
|
2026-02-24 03:15:31 -06:00
|
|
|
|
|
|
|
|
func clear_mouse_passthrough() -> void:
|
|
|
|
|
DisplayServer.window_set_mouse_passthrough([])
|
|
|
|
|
|
|
|
|
|
func _update_mouse_passthrough() -> void:
|
2026-02-27 16:15:46 -06:00
|
|
|
if not _pt_mouse: return
|
2026-02-24 03:15:31 -06:00
|
|
|
var _pt_mask: PackedVector2Array = []
|
|
|
|
|
for node in _pt_except:
|
|
|
|
|
var rect := node.get_global_rect()
|
|
|
|
|
_pt_mask.append_array([
|
|
|
|
|
rect.position,
|
|
|
|
|
Vector2(rect.position.x + rect.size.x, rect.position.y),
|
|
|
|
|
rect.position + rect.size,
|
|
|
|
|
Vector2(rect.position.x, rect.position.y + rect.size.y)
|
|
|
|
|
])
|
|
|
|
|
|
|
|
|
|
_current_pt_mask = _pt_mask
|
2026-02-26 14:36:35 -06:00
|
|
|
var hull_points := Geometry2D.convex_hull(_pt_mask)
|
|
|
|
|
_hull_points = hull_points
|
|
|
|
|
DisplayServer.window_set_mouse_passthrough(hull_points)
|
|
|
|
|
|
|
|
|
|
func _process(_d: float) -> void:
|
|
|
|
|
if _pt_except.size() == 0: return
|
2026-02-27 16:15:46 -06:00
|
|
|
if not _pt_mouse: return
|
2026-02-26 14:36:35 -06:00
|
|
|
_update_mouse_passthrough()
|
2026-02-24 03:15:31 -06:00
|
|
|
|
|
|
|
|
func add_to_passthrough_exception(control: Control) -> void:
|
|
|
|
|
if not _pt_except.has(control):
|
|
|
|
|
_pt_except.append(control)
|
|
|
|
|
control.item_rect_changed.connect(_update_mouse_passthrough)
|
|
|
|
|
|
2026-02-26 14:36:35 -06:00
|
|
|
func add_children_to_passthrough_exception(control: Control, ignore: Array[Control] = []) -> void:
|
|
|
|
|
if not _pt_except.has(control):
|
|
|
|
|
_pt_except.append(control)
|
|
|
|
|
for child in control.get_children():
|
|
|
|
|
if child in ignore: continue
|
|
|
|
|
if child.get_child_count() > 0:
|
|
|
|
|
add_children_to_passthrough_exception(child, ignore)
|
|
|
|
|
else:
|
|
|
|
|
_pt_except.append(child)
|
|
|
|
|
child.item_rect_changed.connect(_update_mouse_passthrough)
|
|
|
|
|
|
2026-02-24 03:15:31 -06:00
|
|
|
func remove_from_passthrough_exceptioon(control: Control) -> void:
|
|
|
|
|
if _pt_except.has(control):
|
|
|
|
|
_pt_except.erase(control)
|
|
|
|
|
control.item_rect_changed.disconnect(_update_mouse_passthrough)
|
|
|
|
|
|
|
|
|
|
func enable_mouse_passthrough() -> void:
|
|
|
|
|
if _pt_mouse:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
_pt_mouse = true
|
|
|
|
|
_update_mouse_passthrough()
|
|
|
|
|
|
|
|
|
|
func disable_mouse_passthrough() -> void:
|
|
|
|
|
if not _pt_mouse:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
_pt_mouse = false
|
|
|
|
|
clear_mouse_passthrough()
|
2026-02-26 14:36:35 -06:00
|
|
|
|
|
|
|
|
func get_exception_points() -> PackedVector2Array:
|
|
|
|
|
return _current_pt_mask
|
|
|
|
|
|
|
|
|
|
func get_polygon_points() -> PackedVector2Array:
|
|
|
|
|
return _hull_points
|
2026-03-08 16:19:22 -05:00
|
|
|
|
|
|
|
|
func get_indie_game_devs_members() -> void:
|
|
|
|
|
var team = await twitcher.get_team_info("indiegamedevs")
|
|
|
|
|
var new_users: Array[TwitchUser]
|
|
|
|
|
if team:
|
|
|
|
|
var chatters: Array[Chatter] = []
|
|
|
|
|
chatters.assign(context.chatters.all())
|
|
|
|
|
var new_members: Array[TwitchTeam.Users] = []
|
|
|
|
|
print("Got team: ", team.team_name)
|
|
|
|
|
print("Member Count: ", team.users.size())
|
|
|
|
|
for team_member: TwitchTeam.Users in team.users:
|
|
|
|
|
if not chatters.any(func(x: Chatter): return x.twitch_id == team_member.user_id):
|
|
|
|
|
new_members.append(team_member)
|
|
|
|
|
|
|
|
|
|
if new_members.size() > 0:
|
|
|
|
|
var iter = 0
|
|
|
|
|
const MAX_ITER = 100
|
|
|
|
|
while not new_members.is_empty():
|
|
|
|
|
iter += 1
|
|
|
|
|
if iter > MAX_ITER:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var new_batch: Array[String] = []
|
|
|
|
|
new_batch.assign(new_members.slice(0,99).map(func(x): return x.user_id))
|
|
|
|
|
new_members.assign(new_members.slice(99))
|
|
|
|
|
var res = await twitcher.get_users_by_id(new_batch)
|
|
|
|
|
new_users.append_array(res)
|
|
|
|
|
print("Found: %d new members not in database" % new_users.size())
|
|
|
|
|
for user: TwitchUser in new_users:
|
|
|
|
|
var chatter = Chatter.new()
|
|
|
|
|
chatter.twitch_id = user.id
|
|
|
|
|
chatter.is_streamer = true
|
|
|
|
|
chatter.is_indie_game_dev = true
|
|
|
|
|
chatter.user = user
|
|
|
|
|
chatter.first_added = Time.get_unix_time_from_system()
|
|
|
|
|
chatter.first_seen = -1
|
|
|
|
|
chatter.last_seen = -1
|
|
|
|
|
context.chatters.append(chatter)
|