Added basic dialogs, added 'blindman' implementation of TACNET networking code, added font

This commit is contained in:
2020-06-07 15:18:31 -05:00
parent 5ea8d13653
commit c694d29050
20 changed files with 870 additions and 5 deletions

122
lib/tacnet/client.rb Normal file
View File

@@ -0,0 +1,122 @@
module TAC
class TACNET
class Client
CHUNK_SIZE = 4096
attr_reader :uuid, :read_queue, :write_queue, :socket,
:packets_sent, :packets_received,
:data_sent, :data_received
attr_accessor :sync_interval
def initialize
@uuid = SecureRandom.uuid
@read_queue = []
@write_queue = []
@sync_interval = 100
@packets_sent, @packets_received = 0, 0
@data_sent, @data_received = 0, 0
end
def socket=(socket)
@socket = socket
listen
end
def listen
Thread.new do
while connected?
# Read from socket
while message_in = read
if message_in.empty?
break
else
@read_queue << message_in
@packets_received += 1
@data_received += message_in.length
end
end
# Write to socket
while message_out = @write_queue.shift
write(message_out)
@packets_sent += 1
@data_sent += message_out.length
end
sleep @sync_interval / 1000.0
end
end
end
def sync(&block)
block.call
end
def handle_read_queue
message = gets
while message
puts(message)
message = gets
end
end
def connected?
!closed?
end
def bound?
@socket.bound? if @socket
end
def closed?
@socket.closed? if @socket
end
def write(message)
@socket.puts("#{message}\r\n\n")
end
def read
message = ""
begin
data = @socket.readpartial(CHUNK_SIZE)
message += message
end until message.end_with?("\r\n\n")
return message
end
def puts(message)
@write_queue << message
end
def gets
@read_queue.shift
end
def encode(message)
return message
end
def decode(blob)
return blob
end
def flush
@socket.flush if socket
end
def close(reason = nil)
write(reason) if reason
@socket.close if @socket
end
end
end
end

58
lib/tacnet/connection.rb Normal file
View File

@@ -0,0 +1,58 @@
module TAC
class TACNET
class Connection
def initialize(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT)
@hostname = hostname
@port = port
@last_sync_time = 0
@sync_interval = SYNC_INTERVAL
@last_heartbeat_sent = 0
@heartbeat_interval = HEARTBEAT_INTERVAL
@connection_handler = proc do
handle_connection
end
end
def connect(error_callback)
return if @client
@client = Client.new
Thread.new do
begin
@client.socket = Socket.tcp(@hostname, @port, connect_timeout: 5)
while @client && @client.connected?
if Gosu.milliseconds > @last_sync_time + @sync_interval
@last_sync_time = Gosu.milliseconds
@client.sync(@connection_handler)
end
end
rescue => error
p error
error_callback.call(error)
end
end
end
def handle_connection
if @client && @client.connected?
message = @client.gets
PacketHandler.handle(message) if message
if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval
last_heartbeat_sent = Gosu.milliseconds
client.puts(PacketHandler.packet_heartbeat)
end
end
end
end
end
end

89
lib/tacnet/packet.rb Normal file
View File

@@ -0,0 +1,89 @@
module TAC
class TACNET
class Packet
PROTOCOL_VERSION = "0"
PROTOCOL_HEADER_SEPERATOR = "|"
PROTOCOL_HEARTBEAT = "heartbeat"
PACKET_TYPES = {
handshake: 0,
heartbeat: 1,
dump_config: 2,
add_group: 3,
update_group: 4,
delete_group: 5,
add_action: 6,
update_action: 7,
delete_action: 8,
add_variable: 9,
update_variable: 10,
delete_variable: 11,
}
def self.from_stream(message)
slice = message.split("|", 4)
if slice.size < 4
warn "Failed to split packet along first 4 " + PROTOCOL_HEADER_SEPERATOR + ". Raw return: " + Arrays.toString(slice)
return nil
end
if slice.first != PROTOCOL_VERSION
warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION + " got: " + slice.first
return nil
end
unless valid_packet_type?(Integer(slice[1]))
warn "Unknown packet type detected: #{slice[1]}"
return nil
end
version = slice[0]
type = PACKET_TYPES.key(Integer(slice[1]))
content_length = Integer(slice[2])
content = slice[3]
return Packet.new(version, type, content_length, body)
end
def self.create(packet_type, body)
Packet.new(PROTOCOL_VERSION, 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
@type = type
@content_length = content_length
@body = body
end
def encode_header
string = ""
string += PROTOCOL_VERSION
string += PROTOCOL_HEADER_SEPERATOR
string += packet_type
string += PROTOCOL_HEADER_SEPERATOR
string += content_length
string += PROTOCOL_HEADER_SEPERATOR
return string
end
def valid?
true
end
def to_s
"#{encode_header}#{body}"
end
end
end
end

View File

@@ -0,0 +1,65 @@
module TAC
class TACNET
class PacketHandler
def initialize(host_is_a_connection: false)
@host_is_a_connection = host_is_a_connection
end
def handle(message)
packet = Packet.from_stream(message)
if packet
hand_off(packet)
else
warn "Rejected raw packet: #{message}"
end
end
def hand_off(packet)
case packet.type
when :handshake
handle_handshake(packet)
when :heartbeat
handle_heartbeat(packet)
when :dump_config
handle_dump_config(packet)
else
warn "No hand off available for packet type: #{packet.type}"
end
end
def handle_handshake(packet)
end
def handle_heartbeat(packet)
end
def handle_dump_config(packet)
begin
hash = JSON.parse(packet.body)
if @host_is_a_connection
File.open("#{TAC::ROOT_PATH}/data/config.json", "w") { |f| f.write packet.body }
$window.backend.update_config
end
rescue JSON::ParserError
end
end
def self.packet_handshake(client_uuid)
Packet.create(Packet::PACKET_TYPES[:handshake], client_uuid)
end
def self.packet_heartbeat
Packet.create(Packet::PACKET_TYPES[:heartbeat], Packet::PROTOCOL_VERSION)
end
def self.packet_dump_config(string)
string = string.gsub("\n", " ")
Packet.create(Packet::PACKET_TYPES[:dump_config], string)
end
end
end
end

129
lib/tacnet/server.rb Normal file
View File

@@ -0,0 +1,129 @@
module TAC
class TACNET
class 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
def initialize(port = DEFAULT_PORT)
@port = port
@socket = nil
@active_client = nil
@connection_attempts = 0
@max_connection_attempts = 10
@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
@sync_interval = SYNC_INTERVAL
@last_heartbeat_sent = 0
@heartbeat_interval = HEARTBEAT_INTERVAL
@client_handler_proc = proc do
handle_client
end
@packet_handler = PacketHandler.new
end
def start
Thread.new do
while !@socket && @connection_attempts < @max_connection_attempts
begin
@socket = TCPServer.new(@port)
rescue => error
p error
@connection_attempts += 1
retry
end
end
while !@socket.closed?
begin
run_server
rescue => error
p error
@socket.close
end
end
end
end
def run_server
while !@socket.closed?
client = Client.new
client.sync_interval = @sync_interval
client.socket = @socket.accept
unless @active_client && @active_client.closed?
warn "Too many clients, already have one connected!"
client.close("Too many clients!")
else
@active_client = client
# TODO: Backup local config
# SEND CONFIG
config = File.read(TAC::CONFIG_PATH)
@active_client.puts(PacketHandler.packet_handshake(@active_client.uuid))
@active_client.puts(PacketHandler.packet_dump_config(config))
Thread.new do
while @active_client && @active_client.connected?
if Gosu.milliseconds > @last_sync_time + @sync_interval
@last_sync_time = Gosu.milliseconds
@active_client.sync(@client_handler_proc)
update_stats
end
end
update_stats
@active_client = nil
@client_last_packets_sent = 0
@client_last_packets_received = 0
@client_last_data_sent = 0
@client_last_data_received = 0
end
end
end
end
def handle_client
if @active_client && @active_client.connected?
message = @active_client.gets
unless 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)
end
end
end
private def update_stats
if @active_client
# NOTE: Sent and Received are reversed for Server stats
@packets_sent += @active_client.packets_received - @client_last_packets_received
@packets_received += @active_client.packets_sent - @client_last_packets_sent
@data_sent += @active_client.data_received - @client_last_data_received
@data_received += @active_client.data_sent - @client_last_data_sent
@client_last_packets_sent = @active_client.packets_sent
@client_last_packets_received = @active_client.packets_received
@client_last_data_sent = @active_client.data_sent
@client_last_data_received = @active_client.data_received
end
end
end
end
end