diff --git a/lib/logger.rb b/lib/logger.rb new file mode 100644 index 0000000..8e1bb7c --- /dev/null +++ b/lib/logger.rb @@ -0,0 +1,23 @@ +module TAC + class Logger + def printer(message) + puts "#{Time.now.strftime("%Y-%m-%d %H:%M:%S %Z")} #{message}" + end + + def i(tag, message) + printer("INFO #{tag}: #{message}") + end + + def d(tag, message) + printer("DEBUG #{tag}: #{message}") + end + + def e(tag, message) + printer("ERROR #{tag}: #{message}") + end + end +end + +def log + @logger ||= TAC::Logger.new +end \ No newline at end of file diff --git a/lib/states/editor.rb b/lib/states/editor.rb index 72f1afc..17bee5a 100644 --- a/lib/states/editor.rb +++ b/lib/states/editor.rb @@ -34,7 +34,7 @@ module TAC stack width: 0.499 do @tacnet_status = label "Connection Error", background: TAC::Palette::TACNET_CONNECTION_ERROR, text_size: 18, padding: 5, margin_top: 2 @tacnet_connection_button = button "Connect", text_size: 18 do - window.backend.tacnet.connect + window.backend.tacnet.connect("localhost") end end end diff --git a/lib/tacnet.rb b/lib/tacnet.rb index 4f0c365..bd4c285 100644 --- a/lib/tacnet.rb +++ b/lib/tacnet.rb @@ -12,10 +12,9 @@ module TAC end def connect(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT, error_callback = proc {}) - return if @connection && @connect.connected? + return if @connection && @connection.connected? @connection = Connection.new(hostname, port) - puts "Connecting..." @connection.connect(error_callback) end end diff --git a/lib/tacnet/client.rb b/lib/tacnet/client.rb index 5ab6131..23a2d50 100644 --- a/lib/tacnet/client.rb +++ b/lib/tacnet/client.rb @@ -1,6 +1,7 @@ module TAC class TACNET class Client + TAG = "TACNET|Client" CHUNK_SIZE = 4096 attr_reader :uuid, :read_queue, :write_queue, :socket, @@ -32,6 +33,8 @@ module TAC if message_in.empty? break else + log.i(TAG, "Read: " + message_in) + @read_queue << message_in @packets_received += 1 @@ -39,12 +42,19 @@ module TAC end end + sleep @sync_interval / 1000.0 + end + end + + Thread.new do + while connected? # Write to socket while message_out = @write_queue.shift write(message_out) @packets_sent += 1 - @data_sent += message_out.length + @data_sent += message_out.to_s.length + log.i(TAG, "Write: " + message_out.to_s) end sleep @sync_interval / 1000.0 @@ -52,7 +62,7 @@ module TAC end end - def sync(&block) + def sync(block) block.call end @@ -62,6 +72,8 @@ module TAC while message puts(message) + log.i(TAG, "Writing to Queue: " + message) + message = gets end end @@ -79,7 +91,12 @@ module TAC end def write(message) - @socket.puts("#{message}\r\n\n") + begin + @socket.puts("#{message}\r\n\n") + rescue Errno::EPIPE, IOError => error + log.e(TAG, error.message) + close + end end def read @@ -87,10 +104,14 @@ module TAC begin data = @socket.readpartial(CHUNK_SIZE) - message += message + message += data + rescue Errno::EPIPE, EOFError + message = "" + break end until message.end_with?("\r\n\n") - return message + + return message.strip end def puts(message) diff --git a/lib/tacnet/connection.rb b/lib/tacnet/connection.rb index dd20249..5042e58 100644 --- a/lib/tacnet/connection.rb +++ b/lib/tacnet/connection.rb @@ -1,6 +1,7 @@ module TAC class TACNET class Connection + TAG = "TACNET|Connection" def initialize(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT) @hostname = hostname @port = port @@ -14,6 +15,8 @@ module TAC @connection_handler = proc do handle_connection end + + @packet_handler = PacketHandler.new end def connect(error_callback) @@ -24,6 +27,7 @@ module TAC Thread.new do begin @client.socket = Socket.tcp(@hostname, @port, connect_timeout: 5) + log.i(TAG, "Connected to: #{@hostname}:#{@port}") while @client && @client.connected? if Gosu.milliseconds > @last_sync_time + @sync_interval @@ -44,15 +48,23 @@ module TAC if @client && @client.connected? message = @client.gets - PacketHandler.handle(message) if message + @packet_handler.handle(message) if message if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval - last_heartbeat_sent = Gosu.milliseconds + @last_heartbeat_sent = Gosu.milliseconds - client.puts(PacketHandler.packet_heartbeat) + @client.puts(PacketHandler.packet_heartbeat) end end end + + def connected? + !closed? + end + + def closed? + @client.closed? if @client + end end end end \ No newline at end of file diff --git a/lib/tacnet/packet.rb b/lib/tacnet/packet.rb index ae7ab1f..2f04208 100644 --- a/lib/tacnet/packet.rb +++ b/lib/tacnet/packet.rb @@ -1,7 +1,7 @@ module TAC class TACNET class Packet - PROTOCOL_VERSION = "0" + PROTOCOL_VERSION = 0 PROTOCOL_HEADER_SEPERATOR = "|" PROTOCOL_HEARTBEAT = "heartbeat" @@ -27,12 +27,12 @@ module TAC slice = message.split("|", 4) if slice.size < 4 - warn "Failed to split packet along first 4 " + PROTOCOL_HEADER_SEPERATOR + ". Raw return: " + Arrays.toString(slice) + warn "Failed to split packet along first 4 " + PROTOCOL_HEADER_SEPERATOR + ". Raw return: " + slice.to_s return nil end - if slice.first != PROTOCOL_VERSION - warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION + " got: " + slice.first + if slice.first != PROTOCOL_VERSION.to_s + warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION.to_s + " got: " + slice.first return nil end @@ -41,25 +41,27 @@ module TAC return nil end - version = slice[0] + protocol_version = Integer(slice[0]) type = PACKET_TYPES.key(Integer(slice[1])) content_length = Integer(slice[2]) - content = slice[3] + body = slice[3] - return Packet.new(version, type, content_length, body) + raise "Type is #{type.inspect} [#{type.class}]" unless type.is_a?(Symbol) + + return Packet.new(protocol_version, type, content_length, body) end def self.create(packet_type, body) - Packet.new(PROTOCOL_VERSION, packet_type, body.length, body) + Packet.new(PROTOCOL_VERSION, PACKET_TYPES.key(packet_type), body.length, body) end def self.valid_packet_type?(packet_type) PACKET_TYPES.values.find { |t| t == packet_type } end - attr_reader :version, :type, :content_length, :body - def initialize(version, type, content_length, body) - @version = version + attr_reader :protocol_version, :type, :content_length, :body + def initialize(protocol_version, type, content_length, body) + @protocol_version = protocol_version @type = type @content_length = content_length @body = body @@ -67,11 +69,11 @@ module TAC def encode_header string = "" - string += PROTOCOL_VERSION + string += protocol_version.to_s string += PROTOCOL_HEADER_SEPERATOR - string += packet_type + string += PACKET_TYPES[type].to_s string += PROTOCOL_HEADER_SEPERATOR - string += content_length + string += content_length.to_s string += PROTOCOL_HEADER_SEPERATOR return string diff --git a/lib/tacnet/packet_handler.rb b/lib/tacnet/packet_handler.rb index 03420dc..12fb691 100644 --- a/lib/tacnet/packet_handler.rb +++ b/lib/tacnet/packet_handler.rb @@ -1,6 +1,7 @@ module TAC class TACNET class PacketHandler + TAG = "TACNET|PacketHandler" def initialize(host_is_a_connection: false) @host_is_a_connection = host_is_a_connection end @@ -11,7 +12,7 @@ module TAC if packet hand_off(packet) else - warn "Rejected raw packet: #{message}" + log.d(TAG, "Rejected raw packet: #{message}") end end @@ -24,13 +25,17 @@ module TAC when :dump_config handle_dump_config(packet) else - warn "No hand off available for packet type: #{packet.type}" + log.d(TAG, "No hand off available for packet type: #{packet.type}") end end def handle_handshake(packet) + if @host_is_a_connection + # TODO: Set Connection client id to received uuid + end end + # TODO: Reset socket timeout def handle_heartbeat(packet) end @@ -52,7 +57,7 @@ module TAC end def self.packet_heartbeat - Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_VERSION) + Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_HEARTBEAT) end def self.packet_dump_config(string) diff --git a/lib/tacnet/server.rb b/lib/tacnet/server.rb index 118c49e..662ccdb 100644 --- a/lib/tacnet/server.rb +++ b/lib/tacnet/server.rb @@ -1,6 +1,7 @@ module TAC class TACNET class Server + TAG = "TACNET|Server" attr_reader :active_client, :packets_sent, :packets_received, :data_sent, :data_received, :client_last_packets_sent, :client_last_packets_received, :client_last_data_sent, :client_last_data_received @@ -28,28 +29,31 @@ module TAC @packet_handler = PacketHandler.new end - def start - Thread.new do - while !@socket && @connection_attempts < @max_connection_attempts + def start(run_on_main_thread: false) + thread = Thread.new do + while (!@socket && @connection_attempts < @max_connection_attempts) begin + log.i(TAG, "Starting server...") @socket = TCPServer.new(@port) rescue => error - p error + log.e(TAG, error) @connection_attempts += 1 - retry + retry if @connection_attempts < @max_connection_attempts end end - while !@socket.closed? + while @socket && !@socket.closed? begin run_server rescue => error p error - @socket.close + @socket.close if @socket end end end + + thread.join if run_on_main_thread end def run_server @@ -58,9 +62,10 @@ module TAC client.sync_interval = @sync_interval client.socket = @socket.accept - unless @active_client && @active_client.closed? - warn "Too many clients, already have one connected!" + if @active_client && @active_client.connected? + log.i(TAG, "Too many clients, already have one connected!") client.close("Too many clients!") + pp @active_client.connected? else @active_client = client # TODO: Backup local config @@ -70,6 +75,8 @@ module TAC @active_client.puts(PacketHandler.packet_handshake(@active_client.uuid)) @active_client.puts(PacketHandler.packet_dump_config(config)) + log.i(TAG, "Client connected!") + Thread.new do while @active_client && @active_client.connected? if Gosu.milliseconds > @last_sync_time + @sync_interval @@ -96,14 +103,14 @@ module TAC if @active_client && @active_client.connected? message = @active_client.gets - unless message.empty? + if message && !message.empty? @packet_handler.handle(message) end if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval @last_heartbeat_sent = Gosu.milliseconds - @active_client.puts(PacketHandler.packet_heartbeart) + @active_client.puts(PacketHandler.packet_heartbeat) end end end diff --git a/tacnet_test_server.rb b/tacnet_test_server.rb new file mode 100644 index 0000000..7a45358 --- /dev/null +++ b/tacnet_test_server.rb @@ -0,0 +1,17 @@ +require "gosu" +require "socket" +require "securerandom" + +require_relative "lib/tac" +require_relative "lib/logger" + +require_relative "lib/tacnet" +require_relative "lib/tacnet/packet" +require_relative "lib/tacnet/packet_handler" +require_relative "lib/tacnet/client" +require_relative "lib/tacnet/server" + +Thread.report_on_exception = true + +server = TAC::TACNET::Server.new +server.start(run_on_main_thread: true) \ No newline at end of file diff --git a/timecrafters_action_configurator.rb b/timecrafters_action_configurator.rb index 1227b3f..5fa4fc1 100644 --- a/timecrafters_action_configurator.rb +++ b/timecrafters_action_configurator.rb @@ -1,5 +1,6 @@ require_relative "../cyberarm_engine/lib/cyberarm_engine" require "socket" +require "securerandom" require "json" require "faker" @@ -12,6 +13,7 @@ require_relative "lib/storage" require_relative "lib/backend" require_relative "lib/states/editor" require_relative "lib/theme" +require_relative "lib/logger" require_relative "lib/dialog" require_relative "lib/dialogs/name_prompt_dialog" require_relative "lib/tacnet" @@ -21,6 +23,6 @@ require_relative "lib/tacnet/client" require_relative "lib/tacnet/connection" require_relative "lib/tacnet/server" -Thread.abort_on_exception = true +# Thread.abort_on_exception = true TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true).show \ No newline at end of file