mirror of
https://github.com/TimeCrafters/timecrafters_configuration_tool_desktop.git
synced 2025-12-15 21:32:35 +00:00
Added basic dialogs, added 'blindman' implementation of TACNET networking code, added font
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
data/config.josn
|
||||||
0
data/.gitkeep
Normal file
0
data/.gitkeep
Normal file
1
data/config.json
Normal file
1
data/config.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"config":{"spec_version":2,"hostname":"192.168.49.1","port":8962,"presets":[]},"data":{"groups":[],"actions":[],"values":[]}}
|
||||||
49
lib/backend.rb
Normal file
49
lib/backend.rb
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
module TAC
|
||||||
|
class Backend
|
||||||
|
attr_reader :config, :tacnet
|
||||||
|
def initialize
|
||||||
|
@config = load_config
|
||||||
|
@tacnet = TACNET.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_config
|
||||||
|
if File.exist?(TAC::CONFIG_PATH)
|
||||||
|
JSON.parse(File.read( TAC::CONFIG_PATH ))
|
||||||
|
else
|
||||||
|
write_default_config
|
||||||
|
load_config
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_default_config
|
||||||
|
File.open(TAC::CONFIG_PATH, "w") do |f|
|
||||||
|
f.write JSON.dump(
|
||||||
|
{
|
||||||
|
config: {
|
||||||
|
spec_version: TAC::CONFIG_SPEC_VERSION,
|
||||||
|
hostname: TACNET::DEFAULT_HOSTNAME,
|
||||||
|
port: TACNET::DEFAULT_PORT,
|
||||||
|
presets: [],
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
groups: [],
|
||||||
|
actions: [],
|
||||||
|
values: [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_config
|
||||||
|
load_config
|
||||||
|
|
||||||
|
$window.states.clear
|
||||||
|
$window.push_state(Editor)
|
||||||
|
end
|
||||||
|
|
||||||
|
def refresh_tacnet_status
|
||||||
|
$window.current_state.refresh_tacnet_status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
66
lib/dialog.rb
Normal file
66
lib/dialog.rb
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
module TAC
|
||||||
|
class Dialog < CyberarmEngine::GuiState
|
||||||
|
def setup
|
||||||
|
theme(THEME)
|
||||||
|
background Gosu::Color.new(0x88_000000)
|
||||||
|
|
||||||
|
@title = @options[:title] ? @options[:title] : "#{self.class}"
|
||||||
|
@window_width, @window_height = window.width, window.height
|
||||||
|
|
||||||
|
@dialog_root = stack width: 250, height: 400, border_thickness: 2, border_color: [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY] do
|
||||||
|
# Title bar
|
||||||
|
flow width: 1.0, height: 0.1 do
|
||||||
|
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
|
||||||
|
|
||||||
|
# title
|
||||||
|
flow width: 0.9 do
|
||||||
|
label @title
|
||||||
|
end
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
flow width: 0.1 do
|
||||||
|
button "X", text_size: 24 do
|
||||||
|
close
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Dialog body
|
||||||
|
stack width: 1.0, height: 0.9 do
|
||||||
|
build
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
center_dialog
|
||||||
|
end
|
||||||
|
|
||||||
|
def build
|
||||||
|
end
|
||||||
|
|
||||||
|
def center_dialog
|
||||||
|
@dialog_root.style.x = window.width / 2 - @dialog_root.style.width / 2
|
||||||
|
@dialog_root.style.y = window.height / 2 - @dialog_root.style.height / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
def draw
|
||||||
|
$window.previous_state.draw
|
||||||
|
Gosu.flush
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
super
|
||||||
|
|
||||||
|
if window.width != @window_width or window.height != @window_height
|
||||||
|
center_dialog
|
||||||
|
|
||||||
|
@window_width, @window_height = window.width, window.height
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def close
|
||||||
|
$window.pop_state
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
25
lib/dialogs/name_prompt_dialog.rb
Normal file
25
lib/dialogs/name_prompt_dialog.rb
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
module TAC
|
||||||
|
class Dialog
|
||||||
|
class NamePromptDialog < Dialog
|
||||||
|
def build
|
||||||
|
background Gosu::Color::GRAY
|
||||||
|
label @options[:subtitle]
|
||||||
|
|
||||||
|
flow width: 1.0 do
|
||||||
|
label "Name", width: 0.25
|
||||||
|
edit_line "", width: 0.70
|
||||||
|
end
|
||||||
|
|
||||||
|
flow width: 1.0 do
|
||||||
|
button "Cancel", width: 0.475 do
|
||||||
|
close
|
||||||
|
end
|
||||||
|
|
||||||
|
button @options[:submit_label], width: 0.475 do
|
||||||
|
@options[:callback].call(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -6,6 +6,7 @@ module TAC
|
|||||||
|
|
||||||
TIMECRAFTERS_PRIMARY = Gosu::Color.new(0xff008000)
|
TIMECRAFTERS_PRIMARY = Gosu::Color.new(0xff008000)
|
||||||
TIMECRAFTERS_SECONDARY = Gosu::Color.new(0xff006000)
|
TIMECRAFTERS_SECONDARY = Gosu::Color.new(0xff006000)
|
||||||
|
TIMECRAFTERS_TERTIARY = Gosu::Color.new(0xff00d000)
|
||||||
|
|
||||||
TACNET_PRIMARY = Gosu::Color.new(0xff000080)
|
TACNET_PRIMARY = Gosu::Color.new(0xff000080)
|
||||||
TACNET_SECONDARY = Gosu::Color.new(0xff000060)
|
TACNET_SECONDARY = Gosu::Color.new(0xff000060)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ module TAC
|
|||||||
class States
|
class States
|
||||||
class Editor < CyberarmEngine::GuiState
|
class Editor < CyberarmEngine::GuiState
|
||||||
def setup
|
def setup
|
||||||
|
theme(THEME)
|
||||||
|
|
||||||
stack width: 1.0, height: 1.0 do
|
stack width: 1.0, height: 1.0 do
|
||||||
stack width: 1.0, height: 0.1 do
|
stack width: 1.0, height: 0.1 do
|
||||||
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
|
background [TAC::Palette::TIMECRAFTERS_PRIMARY, TAC::Palette::TIMECRAFTERS_SECONDARY]
|
||||||
@@ -11,21 +13,29 @@ module TAC
|
|||||||
label TAC::NAME, color: Gosu::Color::BLACK, bold: true
|
label TAC::NAME, color: Gosu::Color::BLACK, bold: true
|
||||||
|
|
||||||
flow do
|
flow do
|
||||||
[:add, :delete, :clone, :create, :simulate].each do |b|
|
button "Add Group", text_size: 18 do
|
||||||
button b.capitalize, text_size: 18
|
push_state(TAC::Dialog::NamePromptDialog, title: "Create Group", subtitle: "Add Group", submit_label: "Add", callback: proc {|instance| instance.close })
|
||||||
|
end
|
||||||
|
button "Add Action", text_size: 18 do
|
||||||
|
push_state(TAC::Dialog::NamePromptDialog, title: "Create Action", subtitle: "Add Action", submit_label: "Add", callback: proc {|instance| instance.close })
|
||||||
|
end
|
||||||
|
button "Add Value", text_size: 18 do
|
||||||
|
push_state(TAC::Dialog::NamePromptDialog, title: "Create Value", subtitle: "Add Value", submit_label: "Add", callback: proc {|instance| instance.close })
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
flow width: 0.299 do
|
flow width: 0.299 do
|
||||||
stack width: 0.5 do
|
stack width: 0.5 do
|
||||||
label "TACNET", color: TAC::Palette::TACNET_PRIMARY
|
label "TACNET v#{TACNET::Packet::PROTOCOL_VERSION}", color: TAC::Palette::TACNET_PRIMARY
|
||||||
@tacnet_ip_address = label "192.168.49.1", color: TAC::Palette::TACNET_SECONDARY
|
@tacnet_ip_address = label "#{TACNET::DEFAULT_HOSTNAME}:#{TACNET::DEFAULT_PORT}", color: TAC::Palette::TACNET_SECONDARY
|
||||||
end
|
end
|
||||||
|
|
||||||
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
|
@tacnet_connection_button = button "Connect", text_size: 18 do
|
||||||
|
window.backend.tacnet.connect
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
6
lib/tac.rb
Normal file
6
lib/tac.rb
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module TAC
|
||||||
|
ROOT_PATH = File.expand_path("../..", __FILE__)
|
||||||
|
CONFIG_PATH = "#{ROOT_PATH}/data/config.json"
|
||||||
|
|
||||||
|
CONFIG_SPEC_VERSION = 2
|
||||||
|
end
|
||||||
22
lib/tacnet.rb
Normal file
22
lib/tacnet.rb
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
module TAC
|
||||||
|
class TACNET
|
||||||
|
DEFAULT_HOSTNAME = "192.168.49.1"
|
||||||
|
DEFAULT_PORT = 8962
|
||||||
|
|
||||||
|
SYNC_INTERVAL = 250 # ms
|
||||||
|
HEARTBEAT_INTERVAL = 1_500 # ms
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@connection = nil
|
||||||
|
@server = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def connect(hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT, error_callback = proc {})
|
||||||
|
return if @connection && @connect.connected?
|
||||||
|
|
||||||
|
@connection = Connection.new(hostname, port)
|
||||||
|
puts "Connecting..."
|
||||||
|
@connection.connect(error_callback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
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
|
||||||
19
lib/theme.rb
Normal file
19
lib/theme.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module TAC
|
||||||
|
THEME = {
|
||||||
|
Label: {
|
||||||
|
font: "#{TAC::ROOT_PATH}/media/DejaVuSansCondensed.ttf",
|
||||||
|
text_size: 28
|
||||||
|
},
|
||||||
|
Button: {
|
||||||
|
background: TAC::Palette::TIMECRAFTERS_PRIMARY,
|
||||||
|
border_thickness: 1,
|
||||||
|
border_color: Gosu::Color.new(0xff_111111),
|
||||||
|
hover: {
|
||||||
|
background: TAC::Palette::TIMECRAFTERS_SECONDARY,
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
background: TAC::Palette::TIMECRAFTERS_TERTIARY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
module TAC
|
module TAC
|
||||||
class Window < CyberarmEngine::Window
|
class Window < CyberarmEngine::Window
|
||||||
|
attr_reader :backend
|
||||||
def initialize(**args)
|
def initialize(**args)
|
||||||
super(**args)
|
super(**args)
|
||||||
|
|
||||||
self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME})"
|
self.caption = "#{TAC::NAME} v#{TAC::VERSION} (#{TAC::RELEASE_NAME})"
|
||||||
|
@backend = Backend.new
|
||||||
push_state(TAC::States::Editor)
|
push_state(TAC::States::Editor)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
BIN
media/DejaVuSansCondensed.ttf
Normal file
BIN
media/DejaVuSansCondensed.ttf
Normal file
Binary file not shown.
187
media/LICENSE
Normal file
187
media/LICENSE
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||||
|
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
||||||
|
|
||||||
|
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
||||||
|
a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of the fonts accompanying this license ("Fonts") and associated
|
||||||
|
documentation files (the "Font Software"), to reproduce and distribute the
|
||||||
|
Font Software, including without limitation the rights to use, copy, merge,
|
||||||
|
publish, distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice shall
|
||||||
|
be included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular
|
||||||
|
the designs of glyphs or characters in the Fonts may be modified and
|
||||||
|
additional glyphs or characters may be added to the Fonts, only if the fonts
|
||||||
|
are renamed to names not containing either the words "Bitstream" or the word
|
||||||
|
"Vera".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or Font
|
||||||
|
Software that has been modified and is distributed under the "Bitstream
|
||||||
|
Vera" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but no
|
||||||
|
copy of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||||
|
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||||
|
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
||||||
|
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
||||||
|
FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the names of Gnome, the Gnome
|
||||||
|
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||||
|
otherwise to promote the sale, use or other dealings in this Font Software
|
||||||
|
without prior written authorization from the Gnome Foundation or Bitstream
|
||||||
|
Inc., respectively. For further information, contact: fonts at gnome dot
|
||||||
|
org.
|
||||||
|
|
||||||
|
Arev Fonts Copyright
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the fonts accompanying this license ("Fonts") and
|
||||||
|
associated documentation files (the "Font Software"), to reproduce
|
||||||
|
and distribute the modifications to the Bitstream Vera Font Software,
|
||||||
|
including without limitation the rights to use, copy, merge, publish,
|
||||||
|
distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice
|
||||||
|
shall be included in all copies of one or more of the Font Software
|
||||||
|
typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in
|
||||||
|
particular the designs of glyphs or characters in the Fonts may be
|
||||||
|
modified and additional glyphs or characters may be added to the
|
||||||
|
Fonts, only if the fonts are renamed to names not containing either
|
||||||
|
the words "Tavmjong Bah" or the word "Arev".
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts
|
||||||
|
or Font Software that has been modified and is distributed under the
|
||||||
|
"Tavmjong Bah Arev" names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but
|
||||||
|
no copy of one or more of the Font Software typefaces may be sold by
|
||||||
|
itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||||
|
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of Tavmjong Bah shall not
|
||||||
|
be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
dealings in this Font Software without prior written authorization
|
||||||
|
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
||||||
|
. fr.
|
||||||
|
|
||||||
|
TeX Gyre DJV Math
|
||||||
|
-----------------
|
||||||
|
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||||
|
|
||||||
|
Math extensions done by B. Jackowski, P. Strzelczyk and P. Pianowski
|
||||||
|
(on behalf of TeX users groups) are in public domain.
|
||||||
|
|
||||||
|
Letters imported from Euler Fraktur from AMSfonts are (c) American
|
||||||
|
Mathematical Society (see below).
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera
|
||||||
|
is a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of the fonts accompanying this license (“Fonts”) and associated
|
||||||
|
documentation
|
||||||
|
files (the “Font Software”), to reproduce and distribute the Font Software,
|
||||||
|
including without limitation the rights to use, copy, merge, publish,
|
||||||
|
distribute,
|
||||||
|
and/or sell copies of the Font Software, and to permit persons to whom
|
||||||
|
the Font Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice
|
||||||
|
shall be
|
||||||
|
included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular
|
||||||
|
the designs of glyphs or characters in the Fonts may be modified and
|
||||||
|
additional
|
||||||
|
glyphs or characters may be added to the Fonts, only if the fonts are
|
||||||
|
renamed
|
||||||
|
to names not containing either the words “Bitstream” or the word “Vera”.
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or
|
||||||
|
Font Software
|
||||||
|
that has been modified and is distributed under the “Bitstream Vera”
|
||||||
|
names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but
|
||||||
|
no copy
|
||||||
|
of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||||
|
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||||
|
FOUNDATION
|
||||||
|
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL,
|
||||||
|
SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN
|
||||||
|
ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR
|
||||||
|
INABILITY TO USE
|
||||||
|
THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
Except as contained in this notice, the names of GNOME, the GNOME
|
||||||
|
Foundation,
|
||||||
|
and Bitstream Inc., shall not be used in advertising or otherwise to promote
|
||||||
|
the sale, use or other dealings in this Font Software without prior written
|
||||||
|
authorization from the GNOME Foundation or Bitstream Inc., respectively.
|
||||||
|
For further information, contact: fonts at gnome dot org.
|
||||||
|
|
||||||
|
AMSFonts (v. 2.2) copyright
|
||||||
|
|
||||||
|
The PostScript Type 1 implementation of the AMSFonts produced by and
|
||||||
|
previously distributed by Blue Sky Research and Y&Y, Inc. are now freely
|
||||||
|
available for general use. This has been accomplished through the
|
||||||
|
cooperation
|
||||||
|
of a consortium of scientific publishers with Blue Sky Research and Y&Y.
|
||||||
|
Members of this consortium include:
|
||||||
|
|
||||||
|
Elsevier Science IBM Corporation Society for Industrial and Applied
|
||||||
|
Mathematics (SIAM) Springer-Verlag American Mathematical Society (AMS)
|
||||||
|
|
||||||
|
In order to assure the authenticity of these fonts, copyright will be
|
||||||
|
held by
|
||||||
|
the American Mathematical Society. This is not meant to restrict in any way
|
||||||
|
the legitimate use of the fonts, such as (but not limited to) electronic
|
||||||
|
distribution of documents containing these fonts, inclusion of these fonts
|
||||||
|
into other public domain or commercial font collections or computer
|
||||||
|
applications, use of the outline data to create derivative fonts and/or
|
||||||
|
faces, etc. However, the AMS does require that the AMS copyright notice be
|
||||||
|
removed from any derivative versions of the fonts which have been altered in
|
||||||
|
any way. In addition, to ensure the fidelity of TeX documents using Computer
|
||||||
|
Modern fonts, Professor Donald Knuth, creator of the Computer Modern faces,
|
||||||
|
has requested that any alterations which yield different font metrics be
|
||||||
|
given a different name.
|
||||||
|
|
||||||
|
$Id$
|
||||||
@@ -4,10 +4,23 @@ require "json"
|
|||||||
|
|
||||||
require "faker"
|
require "faker"
|
||||||
|
|
||||||
|
require_relative "lib/tac"
|
||||||
require_relative "lib/palette"
|
require_relative "lib/palette"
|
||||||
require_relative "lib/window"
|
require_relative "lib/window"
|
||||||
require_relative "lib/version"
|
require_relative "lib/version"
|
||||||
require_relative "lib/storage"
|
require_relative "lib/storage"
|
||||||
|
require_relative "lib/backend"
|
||||||
require_relative "lib/states/editor"
|
require_relative "lib/states/editor"
|
||||||
|
require_relative "lib/theme"
|
||||||
|
require_relative "lib/dialog"
|
||||||
|
require_relative "lib/dialogs/name_prompt_dialog"
|
||||||
|
require_relative "lib/tacnet"
|
||||||
|
require_relative "lib/tacnet/packet"
|
||||||
|
require_relative "lib/tacnet/packet_handler"
|
||||||
|
require_relative "lib/tacnet/client"
|
||||||
|
require_relative "lib/tacnet/connection"
|
||||||
|
require_relative "lib/tacnet/server"
|
||||||
|
|
||||||
|
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
|
||||||
Reference in New Issue
Block a user