diff --git a/hamncheese/Art/chat-circle-dots-fill.svg b/hamncheese/Art/chat-circle-dots-fill.svg
new file mode 100644
index 0000000..6a2da9a
--- /dev/null
+++ b/hamncheese/Art/chat-circle-dots-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/hamncheese/Art/chat-circle-dots-fill.svg.import b/hamncheese/Art/chat-circle-dots-fill.svg.import
new file mode 100644
index 0000000..39fbeb6
--- /dev/null
+++ b/hamncheese/Art/chat-circle-dots-fill.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ddaddue43x40h"
+path="res://.godot/imported/chat-circle-dots-fill.svg-0f6d8e1393ef6af67f741a103fadd3b1.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://Art/chat-circle-dots-fill.svg"
+dest_files=["res://.godot/imported/chat-circle-dots-fill.svg-0f6d8e1393ef6af67f741a103fadd3b1.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1
+svg/scale=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/hamncheese/Scenes/about.tscn b/hamncheese/Scenes/about.tscn
index 80f6565..7cb0322 100644
--- a/hamncheese/Scenes/about.tscn
+++ b/hamncheese/Scenes/about.tscn
@@ -16,7 +16,6 @@ unique_name_in_owner = true
title = "About"
initial_position = 1
size = Vector2i(200, 200)
-visible = false
unresizable = true
[connection signal="close_requested" from="AboutWindow" to="." method="_on_about_window_close_requested"]
diff --git a/hamncheese/Scenes/chat.tscn b/hamncheese/Scenes/chat.tscn
new file mode 100644
index 0000000..034e98a
--- /dev/null
+++ b/hamncheese/Scenes/chat.tscn
@@ -0,0 +1,66 @@
+[gd_scene load_steps=3 format=3 uid="uid://75hpb05uvo2f"]
+
+[ext_resource type="Script" path="res://Scripts/chat.gd" id="1_naye8"]
+
+[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_w4twb"]
+bg_color = Color(0, 0, 0, 1)
+corner_detail = 1
+
+[node name="ChatWindowControl" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_naye8")
+
+[node name="ChatWindow" type="Window" parent="."]
+unique_name_in_owner = true
+title = "Chat"
+position = Vector2i(0, 36)
+size = Vector2i(600, 400)
+
+[node name="VBoxContainer" type="VBoxContainer" parent="ChatWindow"]
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="HSplitContainer" type="HSplitContainer" parent="ChatWindow/VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+split_offset = 100
+
+[node name="ChannelScrollContainer" type="ScrollContainer" parent="ChatWindow/VBoxContainer/HSplitContainer"]
+layout_mode = 2
+
+[node name="ChannelItemList" type="ItemList" parent="ChatWindow/VBoxContainer/HSplitContainer/ChannelScrollContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+
+[node name="TextScrollContainer" type="ScrollContainer" parent="ChatWindow/VBoxContainer/HSplitContainer"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="ChatRichTextLabel" type="RichTextLabel" parent="ChatWindow/VBoxContainer/HSplitContainer/TextScrollContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+size_flags_horizontal = 3
+size_flags_vertical = 3
+theme_override_styles/normal = SubResource("StyleBoxFlat_w4twb")
+bbcode_enabled = true
+fit_content = true
+scroll_following = true
+
+[node name="InputLineEdit" type="LineEdit" parent="ChatWindow/VBoxContainer"]
+unique_name_in_owner = true
+layout_mode = 2
+
+[connection signal="close_requested" from="ChatWindow" to="." method="_on_chat_window_close_requested"]
+[connection signal="text_submitted" from="ChatWindow/VBoxContainer/InputLineEdit" to="." method="_on_input_line_edit_text_submitted"]
diff --git a/hamncheese/Scenes/main.tscn b/hamncheese/Scenes/main.tscn
index 4e51dcb..84cd5fd 100644
--- a/hamncheese/Scenes/main.tscn
+++ b/hamncheese/Scenes/main.tscn
@@ -75,3 +75,4 @@ hide_root = true
[connection signal="id_pressed" from="VBoxContainer/HBoxContainer/MenuBar/File" to="." method="_on_file_id_pressed"]
[connection signal="id_pressed" from="VBoxContainer/HBoxContainer/MenuBar/Help" to="." method="_on_help_id_pressed"]
[connection signal="toggled" from="VBoxContainer/HBoxContainer/OnlineCheckButton" to="." method="_on_online_check_button_toggled"]
+[connection signal="button_clicked" from="VBoxContainer/MarginContainer/PeersTree" to="." method="_on_peers_tree_button_clicked"]
diff --git a/hamncheese/Scenes/manual.tscn b/hamncheese/Scenes/manual.tscn
index 6135017..4b1cbbd 100644
--- a/hamncheese/Scenes/manual.tscn
+++ b/hamncheese/Scenes/manual.tscn
@@ -16,6 +16,5 @@ unique_name_in_owner = true
title = "Manual"
initial_position = 1
size = Vector2i(400, 400)
-visible = false
[connection signal="close_requested" from="ManualWindow" to="." method="_on_manual_window_close_requested"]
diff --git a/hamncheese/Scripts/chat.gd b/hamncheese/Scripts/chat.gd
new file mode 100644
index 0000000..c0d2a3d
--- /dev/null
+++ b/hamncheese/Scripts/chat.gd
@@ -0,0 +1,73 @@
+#
+# Ham'n'Cheese
+# Copyright (C) 2023-2024 Scott Duensing
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see
+#
+
+
+extends Control
+
+
+@onready var chat_window = %ChatWindow
+@onready var channel_item_list = %ChannelItemList
+@onready var chat_rich_text_label = %ChatRichTextLabel
+@onready var input_line_edit = %InputLineEdit
+
+
+func _add_channel(channel_name):
+ channel_item_list.select(channel_item_list.add_item(channel_name))
+
+
+func append_chat_line(username, message):
+ chat_rich_text_label.append_text("%s: [color=green]%s[/color]\n" % [_escape_bbcode(username), _escape_bbcode(message)])
+
+
+func _chat_received(peer, payload):
+ pass
+
+
+func _clear_channels():
+ channel_item_list.clear()
+
+
+func _debug():
+ # Fake a chat window.
+ pass
+
+
+func _escape_bbcode(bbcode_text):
+ return bbcode_text.replace("[", "[lb]")
+
+
+func _on_chat_window_close_requested():
+ chat_window.visible = false
+
+
+func _on_input_line_edit_text_submitted(new_text):
+ input_line_edit.text = ""
+ append_chat_line(Settings.user_name_line_edit.text, new_text)
+
+
+func open(peer):
+ chat_window.visible = true
+ if peer == null:
+ _debug()
+
+
+func startup():
+ chat_window.visible = false
+ _clear_channels()
+ _add_channel("[Everyone]")
+ Peers.subscribe("chat", _chat_received)
diff --git a/hamncheese/Scripts/edge.gd b/hamncheese/Scripts/edge.gd
index 9be247f..76b75fb 100644
--- a/hamncheese/Scripts/edge.gd
+++ b/hamncheese/Scripts/edge.gd
@@ -128,7 +128,7 @@ func start_edge():
_timer.autostart = true
_timer.timeout.connect(_on_timer_timeout)
var binary = OS.get_user_data_dir() + "/edge"
- var arguments = ["--management-password", _password, "-c", _community, "-k", _key, "-l", _supernode + ":" + str(_super_port), "-p"]
+ var arguments = ["-t", _management_port, "--management-password", _password, "-c", _community, "-k", _key, "-l", _supernode + ":" + str(_super_port), "-p"]
if _local_ip != "":
arguments.append(_local_ip + ":" + str(_local_port))
else:
diff --git a/hamncheese/Scripts/main.gd b/hamncheese/Scripts/main.gd
index dfbf558..342f445 100644
--- a/hamncheese/Scripts/main.gd
+++ b/hamncheese/Scripts/main.gd
@@ -41,6 +41,8 @@ const FILE_ID_EXIT = 2
const HELP_ID_ABOUT = 0
const HELP_ID_MANUAL = 1
+const BUTTON_CHAT = 1
+
@onready var online_check_button = %OnlineCheckButton
@onready var my_ip_label = %MyIPLabel
@@ -73,8 +75,9 @@ func _data_recieved(type, data):
func _draw_tree():
# Redraw peer tree.
+ var icon_chat = load("res://Art/chat-circle-dots-fill.svg")
peers_tree.clear()
- #***TODO**** We should really be displaying things in columns.
+ peers_tree.columns = 1
var root = peers_tree.create_item()
for peer in Peers.peerArray:
if peer["online"] or true:
@@ -94,7 +97,9 @@ func _draw_tree():
_:
debug = debug + "Unknown"
var child = peers_tree.create_item(root)
+ child.set_meta("peer", peer["uuid"])
child.set_text(0, peer["user"] + " (" + peer["ip"] + ") " + debug)
+ child.add_button(0, icon_chat, BUTTON_CHAT)
func _go_offline():
@@ -140,7 +145,7 @@ func _on_help_id_pressed(id):
func _on_online_check_button_toggled(toggled_on):
if toggled_on:
- Edge.bind("" if Settings.vpn_bind_check_button.is_pressed else Settings.vpn_bind_line_edit, Settings.listening_port_spin_box.value)
+ Edge.bind("" if Settings.vpn_bind_check_button.is_pressed else Settings.vpn_bind_line_edit.text, Settings.listening_port_spin_box.value)
Edge.set_network(Settings.network_name_line_edit.text, Settings.network_password_line_edit.text)
Edge.set_supernode(Settings.server_line_edit.text, Settings.server_port_spin_box.value)
Edge.set_management(Settings.management_port_spin_box.value, Settings.management_port_spin_box.value + 1)
@@ -150,21 +155,30 @@ func _on_online_check_button_toggled(toggled_on):
_go_offline()
+func _on_peers_tree_button_clicked(item, _column, id, mouse_button_index):
+ # Handle left click.
+ if mouse_button_index == 1:
+ if id == BUTTON_CHAT:
+ Chat.open(item.get_meta("peer"))
+
+
func _ready():
+ About.about_window.visible = false
+ Manual.manual_window.visible = false
Settings.startup()
Process.startup(Settings.management_port_spin_box.value + 2)
+ Peers.startup()
+ Chat.startup()
# If we're on Windows, be sure we're running as Administrator.
# This is going to bork up network shares later.
if OS.get_name() == "Windows":
if !Process.is_elevated():
Process.elevate(OS.get_executable_path())
- Process.shutdown()
- Engine.get_main_loop().quit()
- Network.startup()
- Peers.startup()
+ _shutdown()
Edge.startup()
Edge.data_recieved.connect(_data_recieved)
Edge.network_died.connect(_network_died)
+ Chat.open(null) # Debug
func _show_exit_dialog():
@@ -173,8 +187,12 @@ func _show_exit_dialog():
message = message + "\n\nThis will disconnect you from the network!\n "
var result = await Dialog.confirm("Exit", message)
if result:
- if online_check_button.button_pressed:
- _go_offline()
- Process.shutdown()
- Edge.shutdown()
- Engine.get_main_loop().quit()
+ _shutdown()
+
+
+func _shutdown():
+ if online_check_button.button_pressed:
+ _go_offline()
+ Process.shutdown()
+ Edge.shutdown()
+ Engine.get_main_loop().quit()
diff --git a/hamncheese/Scripts/network.gd b/hamncheese/Scripts/network.gd
deleted file mode 100644
index 75ae436..0000000
--- a/hamncheese/Scripts/network.gd
+++ /dev/null
@@ -1,45 +0,0 @@
-#
-# Ham'n'Cheese
-# Copyright (C) 2023-2024 Scott Duensing
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 3
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, see
-#
-
-
-extends Node
-
-
-func _process(_delta):
- pass
-
-
-func _received_packet(peer, verb, payload):
- match verb:
- "status":
- for key in payload:
- if peer[key] != payload[key]:
- peer[key] = payload[key]
- peer["refresh"] = true
- if !peer["online"]:
- Peers.disconnect_peer(peer)
-
- _:
- print("UNKNOWN VERB: ", verb)
- print(peer)
- print(payload)
- print()
-
-
-func startup():
- Peers.peer_received_packet.connect(_received_packet)
diff --git a/hamncheese/Scripts/peers.gd b/hamncheese/Scripts/peers.gd
index 5cac290..59b1500 100644
--- a/hamncheese/Scripts/peers.gd
+++ b/hamncheese/Scripts/peers.gd
@@ -20,13 +20,15 @@
extends Node
-signal peer_received_packet(peer, verb, payload)
+#signal peer_received_packet(peer, verb, payload)
-var _tcp := TCPServer.new()
-var _basePort : int
-var _serverRunning: bool
-var _timer : Timer
+var _tcp := TCPServer.new()
+var _basePort : int
+var _serverRunning : bool
+var _timer : Timer
+var _subscriptions := {}
+
# This array is of dictionary elements that contain:
# "id": Unique ID of this peer (1-254) based on IP.
@@ -83,6 +85,15 @@ func _sort_by_ip(a, b):
return false
+func _status_update(peer, payload):
+ for key in payload:
+ if peer[key] != payload[key]:
+ peer[key] = payload[key]
+ peer["refresh"] = true
+ if !peer["online"]:
+ Peers.disconnect_peer(peer)
+
+
func _tcp_server():
if _tcp.is_listening():
# New inbound connection.
@@ -127,8 +138,20 @@ func _tcp_server():
# Data waiting?
if peer["tcp"].get_status() == StreamPeerTCP.STATUS_CONNECTED:
if peer["tcp"].get_available_bytes() > 0:
- var data = peer["tcp"].get_var()
- peer_received_packet.emit(peer, data["verb"], data["payload"])
+ var data = peer["tcp"].get_var()
+ var verb = data["verb"]
+ var payload = data["payload"]
+ var found = false
+ for key in _subscriptions:
+ if key == verb:
+ found = true
+ _subscriptions[key].call(peer, payload)
+ break
+ if !found:
+ print("UNKNOWN VERB: ", verb)
+ print(peer)
+ print(payload)
+ print()
func clear():
@@ -178,6 +201,7 @@ func start_server(address, portStart, user, uuid):
func startup():
_serverRunning = false
clear()
+ subscribe("status", _status_update)
_timer = Timer.new()
_timer.wait_time = 1
_timer.one_shot = false
@@ -195,6 +219,10 @@ func stop_server():
_tcp.stop()
+func subscribe(verb: String, callback: Callable):
+ _subscriptions[verb] = callback
+
+
func update(peersFromCPP: Array):
var found
var changed = false
diff --git a/hamncheese/project.godot b/hamncheese/project.godot
index ed0d96d..c0fdae4 100644
--- a/hamncheese/project.godot
+++ b/hamncheese/project.godot
@@ -28,10 +28,10 @@ Process="*res://Scripts/process.gd"
Dialog="*res://Scripts/dialog.gd"
Edge="*res://Scripts/edge.gd"
Peers="*res://Scripts/peers.gd"
-Network="*res://Scripts/network.gd"
Settings="*res://Scenes/settings.tscn"
About="*res://Scenes/about.tscn"
Manual="*res://Scenes/manual.tscn"
+Chat="*res://Scenes/chat.tscn"
[display]