From f04dfb01e7dc42f9371a9f9f52b25eedf6662938 Mon Sep 17 00:00:00 2001 From: Cyberarm Date: Mon, 8 Jun 2020 16:08:41 -0500 Subject: [PATCH] Added logo, added Boot screen, renamed dump_config packet to upload_config, misc changes and UX improvements --- lib/backend.rb | 4 +- lib/dialogs/confirm_dialog.rb | 4 +- lib/states/boot.rb | 36 ++++++++++++++++++ lib/states/editor.rb | 12 +++--- lib/tacnet/client.rb | 11 ++---- lib/tacnet/connection.rb | 6 ++- lib/tacnet/packet.rb | 23 +++++------ lib/tacnet/packet_handler.rb | 57 +++++++++++++++++++++++----- lib/tacnet/server.rb | 10 +++-- lib/window.rb | 2 +- media/{LICENSE => FONT_LICENSE} | 0 media/logo.png | Bin 0 -> 29605 bytes timecrafters_action_configurator.rb | 1 + 13 files changed, 124 insertions(+), 42 deletions(-) create mode 100644 lib/states/boot.rb rename media/{LICENSE => FONT_LICENSE} (100%) create mode 100644 media/logo.png diff --git a/lib/backend.rb b/lib/backend.rb index 0e78b64..d3c9070 100644 --- a/lib/backend.rb +++ b/lib/backend.rb @@ -41,12 +41,14 @@ module TAC def upload_config if @tacnet.connected? - @tacnet.puts(TAC::TACNET::PacketHandler.packet_dump_config(json)) + json = JSON.dump(@config) + @tacnet.puts(TAC::TACNET::PacketHandler.packet_upload_config(json)) end end def download_config if @tacnet.connected? + @tacnet.puts(TAC::TACNET::PacketHandler.packet_download_config) end end diff --git a/lib/dialogs/confirm_dialog.rb b/lib/dialogs/confirm_dialog.rb index 771ed69..f65f9b6 100644 --- a/lib/dialogs/confirm_dialog.rb +++ b/lib/dialogs/confirm_dialog.rb @@ -10,9 +10,9 @@ module TAC close end button "Okay", width: 0.475, text_size: 18 do - @options[:callback_method].call - close + + @options[:callback_method].call end end end diff --git a/lib/states/boot.rb b/lib/states/boot.rb new file mode 100644 index 0000000..247327e --- /dev/null +++ b/lib/states/boot.rb @@ -0,0 +1,36 @@ +module TAC + class States + class Boot < CyberarmEngine::GuiState + def setup + stack width: 1.0, height: 1.0 do + background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY, TAC::Palette::TIMECRAFTERS_TERTIARY, TAC::Palette::TIMECRAFTERS_PRIMARY] + end + + @title_font = CyberarmEngine::Text.new(TAC::NAME, z: 100, size: 72, shadow: true, shadow_size: 3, font: THEME[:Label][:font]) + @logo = Gosu::Image.new("#{TAC::ROOT_PATH}/media/logo.png") + + @animator = CyberarmEngine::Animator.new(start_time: 0, duration: 3_000, from: 0, to: 255) + @transition_color = Gosu::Color.new(0x00_000000) + end + + def draw + super + + @title_font.draw + @logo.draw(window.width / 2 - @logo.width / 2, window.height / 2 - @logo.height / 2, 99) + Gosu.draw_rect(0, 0, window.width, window.height, @transition_color, 10_00) + end + + def update + super + + @title_font.x = window.width / 2 - @title_font.width / 2 + @title_font.y = window.height / 2 - (@logo.height / 2 + @title_font.height) + + @transition_color.alpha = @animator.transition(0, 255, :sine) + + push_state(Editor) if @transition_color.alpha >= 255 + end + end + end +end \ No newline at end of file diff --git a/lib/states/editor.rb b/lib/states/editor.rb index fa82659..c627d2a 100644 --- a/lib/states/editor.rb +++ b/lib/states/editor.rb @@ -37,7 +37,7 @@ module TAC window.backend.upload_config end button "▼", text_size: 18, margin_left: 10, tip: "Download remote config, if connected." do - push_state(Dialog::ConfirmDialog, title: "Are you sure?", message: "Replace local config with\n remote config?", callback_method: proc { window.backend.download_config }) + window.backend.download_config end end end @@ -57,7 +57,7 @@ module TAC when :connected, :connecting window.backend.tacnet.close when :not_connected, :connection_error - window.backend.tacnet.connect("localhost") + window.backend.tacnet.connect("localhost")#("192.168.1.5") end end button "Status", text_size: 18, width: 0.475 do @@ -77,8 +77,8 @@ module TAC button "+", text_size: 18 do push_state(TAC::Dialog::NamePromptDialog, title: "Create Group", callback_method: method(:create_group)) end - button "Clone", text_size: 18 - button "Create Preset", text_size: 18 + button "Clone", text_size: 18, tip: "Clone currently selected group" + button "Create Preset", text_size: 18, tip: "Save group as preset" end @groups_list = stack width: 1.0 do @@ -95,8 +95,8 @@ module TAC push_state(TAC::Dialog::AlertDialog, title: "Error", message: "Unable to create action,\nno group selected.") end end - button "Clone", text_size: 18 - button "Create Preset", text_size: 18 + button "Clone", text_size: 18, tip: "Clone currently selected action" + button "Create Preset", text_size: 18, tip: "Save action as preset" end @actions_list = stack width: 1.0 do diff --git a/lib/tacnet/client.rb b/lib/tacnet/client.rb index f8b8eea..74c3b91 100644 --- a/lib/tacnet/client.rb +++ b/lib/tacnet/client.rb @@ -3,6 +3,7 @@ module TAC class Client TAG = "TACNET|Client" CHUNK_SIZE = 4096 + PACKET_TAIL = "\r\n\n" attr_reader :uuid, :read_queue, :write_queue, :socket, :packets_sent, :packets_received, @@ -105,7 +106,7 @@ module TAC def write(message) begin - @socket.puts("#{message}\r\n\n") + @socket.puts("#{message}#{PACKET_TAIL}") rescue => error @last_socket_error = error @socket_error = true @@ -115,18 +116,14 @@ module TAC end def read - message = "" - begin - data = @socket.readpartial(CHUNK_SIZE) - message += data + message = @socket.gets rescue => error @last_socket_error = error @socket_error = true message = "" - break - end until message.end_with?("\r\n\n") + end return message.strip diff --git a/lib/tacnet/connection.rb b/lib/tacnet/connection.rb index 2454af5..cfacd18 100644 --- a/lib/tacnet/connection.rb +++ b/lib/tacnet/connection.rb @@ -9,10 +9,10 @@ module TAC @client = nil - @last_sync_time = 0 + @last_sync_time = Gosu.milliseconds @sync_interval = SYNC_INTERVAL - @last_heartbeat_sent = 0 + @last_heartbeat_sent = Gosu.milliseconds @heartbeat_interval = HEARTBEAT_INTERVAL @connection_handler = proc do @@ -63,6 +63,8 @@ module TAC @client.puts(PacketHandler.packet_heartbeat) end + + sleep @sync_interval / 1000.0 end end diff --git a/lib/tacnet/packet.rb b/lib/tacnet/packet.rb index 2f04208..9a67bcf 100644 --- a/lib/tacnet/packet.rb +++ b/lib/tacnet/packet.rb @@ -1,26 +1,27 @@ module TAC class TACNET class Packet - PROTOCOL_VERSION = 0 + PROTOCOL_VERSION = 1 PROTOCOL_HEADER_SEPERATOR = "|" PROTOCOL_HEARTBEAT = "heartbeat" PACKET_TYPES = { handshake: 0, heartbeat: 1, - dump_config: 2, + download_config: 2, + upload_config: 3, - add_group: 3, - update_group: 4, - delete_group: 5, + add_group: 20, + update_group: 21, + delete_group: 22, - add_action: 6, - update_action: 7, - delete_action: 8, + add_action: 30, + update_action: 31, + delete_action: 32, - add_variable: 9, - update_variable: 10, - delete_variable: 11, + add_variable: 40, + update_variable: 41, + delete_variable: 42, } def self.from_stream(message) diff --git a/lib/tacnet/packet_handler.rb b/lib/tacnet/packet_handler.rb index bdfae1f..3fcd1d2 100644 --- a/lib/tacnet/packet_handler.rb +++ b/lib/tacnet/packet_handler.rb @@ -22,8 +22,10 @@ module TAC handle_handshake(packet) when :heartbeat handle_heartbeat(packet) - when :dump_config - handle_dump_config(packet) + when :download_config + handle_download_config(packet) + when :upload_config + handle_upload_config(packet) else log.d(TAG, "No hand off available for packet type: #{packet.type}") end @@ -39,16 +41,49 @@ module TAC def handle_heartbeat(packet) end - def handle_dump_config(packet) + def handle_upload_config(packet) begin - hash = JSON.parse(packet.body) + data = JSON.parse(packet.body, symbolize_names: true) if @host_is_a_connection - File.open("#{TAC::ROOT_PATH}/data/config.json", "w") { |f| f.write packet.body } + if data.is_a?(Array) + # OLDEST CONFIG, upgrade? + $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config to old.") - $window.backend.update_config + elsif data.is_a?(Hash) && data.dig(:config, :spec_version) == TAC::CONFIG_SPEC_VERSION + $window.push_state(TAC::Dialog::ConfirmDialog, title: "Replace Config", message: "Replace local config\nwith remote config?", callback_method: proc { + File.open("#{TAC::ROOT_PATH}/data/config.json", "w") { |f| f.write packet.body } + + $window.backend.update_config + }) + + elsif data.is_a?(Hash) && data.dig(:config, :spec_version) < TAC::CONFIG_SPEC_VERSION + # OLD CONFIG, Upgrade? + $window.push_state(TAC::Dialog::ConfirmDialog, title: "Upgrade Config", message: "Remote config is an older\nspec version.\nTry to upgrade?", callback_method: proc {}) + + elsif data.is_a?(Hash) && data.dig(:config, :spec_version) > TAC::CONFIG_SPEC_VERSION + # NEWER CONFIG, Error Out + $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Client outdated, check for\nupdates.\nSupported config spec:\nv#{TAC::CONFIG_SPEC_VERSION} got v#{data.dig(:config, :spec_version)}") + + else + # CONFIG is unknown + $window.push_state(TAC::Dialog::AlertDialog, title: "Invalid Config", message: "Remote config is not supported.") + end + end + rescue JSON::ParserError => e + log.e(TAG, "JSON parsing error: #{e}") + end + end + + def handle_download_config(packet) + if @host_is_a_connection + json = JSON.dump($window.backend.config) + $window.backend.tacnet.puts(PacketHandler.packet_upload_config(json)) + else + if $server.active_client && $server.active_client.connected? + json = File.read(TAC::CONFIG_PATH) + $server.active_client.puts(PacketHandler.packet_upload_config(json)) end - rescue JSON::ParserError end end @@ -60,10 +95,14 @@ module TAC Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_HEARTBEAT) end - def self.packet_dump_config(string) + def self.packet_download_config + Packet.create(Packet::PACKET_TYPES[:download_config], "") + end + + def self.packet_upload_config(string) string = string.gsub("\n", " ") - Packet.create(Packet::PACKET_TYPES[:dump_config], string) + Packet.create(Packet::PACKET_TYPES[:upload_config], string) end end end diff --git a/lib/tacnet/server.rb b/lib/tacnet/server.rb index faadbb7..116a323 100644 --- a/lib/tacnet/server.rb +++ b/lib/tacnet/server.rb @@ -6,6 +6,8 @@ module TAC :packets_sent, :packets_received, :data_sent, :data_received, :client_last_packets_sent, :client_last_packets_received, :client_last_data_sent, :client_last_data_received def initialize(port = DEFAULT_PORT) + $server = self + @port = port @socket = nil @@ -16,10 +18,10 @@ module TAC @packets_sent, @packets_received, @client_last_packets_sent, @client_last_packets_received = 0, 0, 0, 0 @data_sent, @data_received, @client_last_data_sent, @client_last_data_received = 0, 0, 0, 0 - @last_sync_time = 0 + @last_sync_time = Gosu.milliseconds @sync_interval = SYNC_INTERVAL - @last_heartbeat_sent = 0 + @last_heartbeat_sent = Gosu.milliseconds @heartbeat_interval = HEARTBEAT_INTERVAL @client_handler_proc = proc do @@ -72,7 +74,7 @@ module TAC config = File.read(TAC::CONFIG_PATH) @active_client.puts(PacketHandler.packet_handshake(@active_client.uuid)) - @active_client.puts(PacketHandler.packet_dump_config(config)) + @active_client.puts(PacketHandler.packet_upload_config(config)) log.i(TAG, "Client connected!") @@ -111,6 +113,8 @@ module TAC @active_client.puts(PacketHandler.packet_heartbeat) end + + sleep @sync_interval / 1000.0 end end diff --git a/lib/window.rb b/lib/window.rb index c5015bb..2e40716 100644 --- a/lib/window.rb +++ b/lib/window.rb @@ -6,7 +6,7 @@ module TAC self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME})" @backend = Backend.new - push_state(TAC::States::Editor) + push_state(TAC::States::Boot) end def needs_cursor? diff --git a/media/LICENSE b/media/FONT_LICENSE similarity index 100% rename from media/LICENSE rename to media/FONT_LICENSE diff --git a/media/logo.png b/media/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b5bc0c0ea005c293acc4a5987f50b8c6d8a2e777 GIT binary patch literal 29605 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 DaHAsN literal 0 HcmV?d00001 diff --git a/timecrafters_action_configurator.rb b/timecrafters_action_configurator.rb index c44e90e..c12c7e5 100644 --- a/timecrafters_action_configurator.rb +++ b/timecrafters_action_configurator.rb @@ -9,6 +9,7 @@ require_relative "lib/window" require_relative "lib/version" require_relative "lib/storage" require_relative "lib/backend" +require_relative "lib/states/boot" require_relative "lib/states/editor" require_relative "lib/states/manage_presets" require_relative "lib/theme"