Added Logger, added TACNET test server, networking now seems to work(heartbeats are sent and received)

This commit is contained in:
2020-06-07 21:23:16 -05:00
parent c694d29050
commit 95209ded73
10 changed files with 128 additions and 40 deletions

23
lib/logger.rb Normal file
View File

@@ -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

View File

@@ -34,7 +34,7 @@ module TAC
stack width: 0.499 do 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_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 @tacnet_connection_button = button "Connect", text_size: 18 do
window.backend.tacnet.connect window.backend.tacnet.connect("localhost")
end end
end end
end end

View File

@@ -12,10 +12,9 @@ module TAC
end end
def connect(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT, error_callback = proc {}) 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) @connection = Connection.new(hostname, port)
puts "Connecting..."
@connection.connect(error_callback) @connection.connect(error_callback)
end end
end end

View File

@@ -1,6 +1,7 @@
module TAC module TAC
class TACNET class TACNET
class Client class Client
TAG = "TACNET|Client"
CHUNK_SIZE = 4096 CHUNK_SIZE = 4096
attr_reader :uuid, :read_queue, :write_queue, :socket, attr_reader :uuid, :read_queue, :write_queue, :socket,
@@ -32,6 +33,8 @@ module TAC
if message_in.empty? if message_in.empty?
break break
else else
log.i(TAG, "Read: " + message_in)
@read_queue << message_in @read_queue << message_in
@packets_received += 1 @packets_received += 1
@@ -39,12 +42,19 @@ module TAC
end end
end end
sleep @sync_interval / 1000.0
end
end
Thread.new do
while connected?
# Write to socket # Write to socket
while message_out = @write_queue.shift while message_out = @write_queue.shift
write(message_out) write(message_out)
@packets_sent += 1 @packets_sent += 1
@data_sent += message_out.length @data_sent += message_out.to_s.length
log.i(TAG, "Write: " + message_out.to_s)
end end
sleep @sync_interval / 1000.0 sleep @sync_interval / 1000.0
@@ -52,7 +62,7 @@ module TAC
end end
end end
def sync(&block) def sync(block)
block.call block.call
end end
@@ -62,6 +72,8 @@ module TAC
while message while message
puts(message) puts(message)
log.i(TAG, "Writing to Queue: " + message)
message = gets message = gets
end end
end end
@@ -79,7 +91,12 @@ module TAC
end end
def write(message) 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 end
def read def read
@@ -87,10 +104,14 @@ module TAC
begin begin
data = @socket.readpartial(CHUNK_SIZE) data = @socket.readpartial(CHUNK_SIZE)
message += message message += data
rescue Errno::EPIPE, EOFError
message = ""
break
end until message.end_with?("\r\n\n") end until message.end_with?("\r\n\n")
return message
return message.strip
end end
def puts(message) def puts(message)

View File

@@ -1,6 +1,7 @@
module TAC module TAC
class TACNET class TACNET
class Connection class Connection
TAG = "TACNET|Connection"
def initialize(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT) def initialize(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT)
@hostname = hostname @hostname = hostname
@port = port @port = port
@@ -14,6 +15,8 @@ module TAC
@connection_handler = proc do @connection_handler = proc do
handle_connection handle_connection
end end
@packet_handler = PacketHandler.new
end end
def connect(error_callback) def connect(error_callback)
@@ -24,6 +27,7 @@ module TAC
Thread.new do Thread.new do
begin begin
@client.socket = Socket.tcp(@hostname, @port, connect_timeout: 5) @client.socket = Socket.tcp(@hostname, @port, connect_timeout: 5)
log.i(TAG, "Connected to: #{@hostname}:#{@port}")
while @client && @client.connected? while @client && @client.connected?
if Gosu.milliseconds > @last_sync_time + @sync_interval if Gosu.milliseconds > @last_sync_time + @sync_interval
@@ -44,15 +48,23 @@ module TAC
if @client && @client.connected? if @client && @client.connected?
message = @client.gets message = @client.gets
PacketHandler.handle(message) if message @packet_handler.handle(message) if message
if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval 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 end
end end
def connected?
!closed?
end
def closed?
@client.closed? if @client
end
end end
end end
end end

View File

@@ -1,7 +1,7 @@
module TAC module TAC
class TACNET class TACNET
class Packet class Packet
PROTOCOL_VERSION = "0" PROTOCOL_VERSION = 0
PROTOCOL_HEADER_SEPERATOR = "|" PROTOCOL_HEADER_SEPERATOR = "|"
PROTOCOL_HEARTBEAT = "heartbeat" PROTOCOL_HEARTBEAT = "heartbeat"
@@ -27,12 +27,12 @@ module TAC
slice = message.split("|", 4) slice = message.split("|", 4)
if slice.size < 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 return nil
end end
if slice.first != PROTOCOL_VERSION if slice.first != PROTOCOL_VERSION.to_s
warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION + " got: " + slice.first warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION.to_s + " got: " + slice.first
return nil return nil
end end
@@ -41,25 +41,27 @@ module TAC
return nil return nil
end end
version = slice[0] protocol_version = Integer(slice[0])
type = PACKET_TYPES.key(Integer(slice[1])) type = PACKET_TYPES.key(Integer(slice[1]))
content_length = Integer(slice[2]) 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 end
def self.create(packet_type, body) 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 end
def self.valid_packet_type?(packet_type) def self.valid_packet_type?(packet_type)
PACKET_TYPES.values.find { |t| t == packet_type } PACKET_TYPES.values.find { |t| t == packet_type }
end end
attr_reader :version, :type, :content_length, :body attr_reader :protocol_version, :type, :content_length, :body
def initialize(version, type, content_length, body) def initialize(protocol_version, type, content_length, body)
@version = version @protocol_version = protocol_version
@type = type @type = type
@content_length = content_length @content_length = content_length
@body = body @body = body
@@ -67,11 +69,11 @@ module TAC
def encode_header def encode_header
string = "" string = ""
string += PROTOCOL_VERSION string += protocol_version.to_s
string += PROTOCOL_HEADER_SEPERATOR string += PROTOCOL_HEADER_SEPERATOR
string += packet_type string += PACKET_TYPES[type].to_s
string += PROTOCOL_HEADER_SEPERATOR string += PROTOCOL_HEADER_SEPERATOR
string += content_length string += content_length.to_s
string += PROTOCOL_HEADER_SEPERATOR string += PROTOCOL_HEADER_SEPERATOR
return string return string

View File

@@ -1,6 +1,7 @@
module TAC module TAC
class TACNET class TACNET
class PacketHandler class PacketHandler
TAG = "TACNET|PacketHandler"
def initialize(host_is_a_connection: false) def initialize(host_is_a_connection: false)
@host_is_a_connection = host_is_a_connection @host_is_a_connection = host_is_a_connection
end end
@@ -11,7 +12,7 @@ module TAC
if packet if packet
hand_off(packet) hand_off(packet)
else else
warn "Rejected raw packet: #{message}" log.d(TAG, "Rejected raw packet: #{message}")
end end
end end
@@ -24,13 +25,17 @@ module TAC
when :dump_config when :dump_config
handle_dump_config(packet) handle_dump_config(packet)
else 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
end end
def handle_handshake(packet) def handle_handshake(packet)
if @host_is_a_connection
# TODO: Set Connection client id to received uuid
end
end end
# TODO: Reset socket timeout
def handle_heartbeat(packet) def handle_heartbeat(packet)
end end
@@ -52,7 +57,7 @@ module TAC
end end
def self.packet_heartbeat def self.packet_heartbeat
Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_VERSION) Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_HEARTBEAT)
end end
def self.packet_dump_config(string) def self.packet_dump_config(string)

View File

@@ -1,6 +1,7 @@
module TAC module TAC
class TACNET class TACNET
class Server class Server
TAG = "TACNET|Server"
attr_reader :active_client, attr_reader :active_client,
:packets_sent, :packets_received, :data_sent, :data_received, :packets_sent, :packets_received, :data_sent, :data_received,
:client_last_packets_sent, :client_last_packets_received, :client_last_data_sent, :client_last_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 @packet_handler = PacketHandler.new
end end
def start def start(run_on_main_thread: false)
Thread.new do thread = Thread.new do
while !@socket && @connection_attempts < @max_connection_attempts while (!@socket && @connection_attempts < @max_connection_attempts)
begin begin
log.i(TAG, "Starting server...")
@socket = TCPServer.new(@port) @socket = TCPServer.new(@port)
rescue => error rescue => error
p error log.e(TAG, error)
@connection_attempts += 1 @connection_attempts += 1
retry retry if @connection_attempts < @max_connection_attempts
end end
end end
while !@socket.closed? while @socket && !@socket.closed?
begin begin
run_server run_server
rescue => error rescue => error
p error p error
@socket.close @socket.close if @socket
end end
end end
end end
thread.join if run_on_main_thread
end end
def run_server def run_server
@@ -58,9 +62,10 @@ module TAC
client.sync_interval = @sync_interval client.sync_interval = @sync_interval
client.socket = @socket.accept client.socket = @socket.accept
unless @active_client && @active_client.closed? if @active_client && @active_client.connected?
warn "Too many clients, already have one connected!" log.i(TAG, "Too many clients, already have one connected!")
client.close("Too many clients!") client.close("Too many clients!")
pp @active_client.connected?
else else
@active_client = client @active_client = client
# TODO: Backup local config # TODO: Backup local config
@@ -70,6 +75,8 @@ module TAC
@active_client.puts(PacketHandler.packet_handshake(@active_client.uuid)) @active_client.puts(PacketHandler.packet_handshake(@active_client.uuid))
@active_client.puts(PacketHandler.packet_dump_config(config)) @active_client.puts(PacketHandler.packet_dump_config(config))
log.i(TAG, "Client connected!")
Thread.new do Thread.new do
while @active_client && @active_client.connected? while @active_client && @active_client.connected?
if Gosu.milliseconds > @last_sync_time + @sync_interval if Gosu.milliseconds > @last_sync_time + @sync_interval
@@ -96,14 +103,14 @@ module TAC
if @active_client && @active_client.connected? if @active_client && @active_client.connected?
message = @active_client.gets message = @active_client.gets
unless message.empty? if message && !message.empty?
@packet_handler.handle(message) @packet_handler.handle(message)
end end
if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval
@last_heartbeat_sent = Gosu.milliseconds @last_heartbeat_sent = Gosu.milliseconds
@active_client.puts(PacketHandler.packet_heartbeart) @active_client.puts(PacketHandler.packet_heartbeat)
end end
end end
end end

17
tacnet_test_server.rb Normal file
View File

@@ -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)

View File

@@ -1,5 +1,6 @@
require_relative "../cyberarm_engine/lib/cyberarm_engine" require_relative "../cyberarm_engine/lib/cyberarm_engine"
require "socket" require "socket"
require "securerandom"
require "json" require "json"
require "faker" require "faker"
@@ -12,6 +13,7 @@ require_relative "lib/storage"
require_relative "lib/backend" require_relative "lib/backend"
require_relative "lib/states/editor" require_relative "lib/states/editor"
require_relative "lib/theme" require_relative "lib/theme"
require_relative "lib/logger"
require_relative "lib/dialog" require_relative "lib/dialog"
require_relative "lib/dialogs/name_prompt_dialog" require_relative "lib/dialogs/name_prompt_dialog"
require_relative "lib/tacnet" require_relative "lib/tacnet"
@@ -21,6 +23,6 @@ require_relative "lib/tacnet/client"
require_relative "lib/tacnet/connection" require_relative "lib/tacnet/connection"
require_relative "lib/tacnet/server" 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 TAC::Window.new(width: (Gosu.screen_width * 0.8).round, height: (Gosu.screen_height * 0.8).round, resizable: true).show