mirror of
https://github.com/TimeCrafters/timecrafters_configuration_tool_desktop.git
synced 2025-12-16 05:42:35 +00:00
Added basic dialogs, added 'blindman' implementation of TACNET networking code, added font
This commit is contained in:
122
lib/tacnet/client.rb
Normal file
122
lib/tacnet/client.rb
Normal 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
58
lib/tacnet/connection.rb
Normal 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
89
lib/tacnet/packet.rb
Normal 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
|
||||
65
lib/tacnet/packet_handler.rb
Normal file
65
lib/tacnet/packet_handler.rb
Normal 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
129
lib/tacnet/server.rb
Normal 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
|
||||
Reference in New Issue
Block a user