mirror of
https://github.com/TimeCrafters/timecrafters_configuration_tool_desktop.git
synced 2025-12-16 13:52:34 +00:00
Imported FTC Clock
This commit is contained in:
162
lib/game_clock/net/client.rb
Normal file
162
lib/game_clock/net/client.rb
Normal file
@@ -0,0 +1,162 @@
|
||||
require "securerandom"
|
||||
|
||||
module TAC
|
||||
class PracticeGameClock
|
||||
class ClockNet
|
||||
class Client
|
||||
TAG = "ClockNet|Client"
|
||||
CHUNK_SIZE = 4096
|
||||
PACKET_TAIL = "\r\n\n"
|
||||
|
||||
attr_reader :uuid, :read_queue, :write_queue, :socket,
|
||||
:packets_sent, :packets_received,
|
||||
:data_sent, :data_received
|
||||
attr_accessor :sync_interval, :last_socket_error, :socket_error
|
||||
def initialize
|
||||
@uuid = SecureRandom.uuid
|
||||
@read_queue = []
|
||||
@write_queue = []
|
||||
|
||||
@sync_interval = 100
|
||||
|
||||
@last_socket_error = nil
|
||||
@socket_error = false
|
||||
@bound = false
|
||||
|
||||
@packets_sent, @packets_received = 0, 0
|
||||
@data_sent, @data_received = 0, 0
|
||||
end
|
||||
|
||||
def uuid=(id)
|
||||
@uuid = id
|
||||
end
|
||||
|
||||
def socket=(socket)
|
||||
@socket = socket
|
||||
@bound = true
|
||||
|
||||
listen
|
||||
end
|
||||
|
||||
def listen
|
||||
Thread.new do
|
||||
while connected?
|
||||
# Read from socket
|
||||
while message_in = read
|
||||
if message_in.empty?
|
||||
break
|
||||
else
|
||||
log.i(TAG, "Read: " + message_in)
|
||||
|
||||
@read_queue << message_in
|
||||
|
||||
@packets_received += 1
|
||||
@data_received += message_in.length
|
||||
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.to_s.length
|
||||
log.i(TAG, "Write: " + message_out.to_s)
|
||||
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)
|
||||
|
||||
log.i(TAG, "Writing to Queue: " + message)
|
||||
|
||||
message = gets
|
||||
end
|
||||
end
|
||||
|
||||
def socket_error?
|
||||
@socket_error
|
||||
end
|
||||
|
||||
def connected?
|
||||
if closed? == true || closed? == nil
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
def closed?
|
||||
@socket.closed? if @socket
|
||||
end
|
||||
|
||||
def write(message)
|
||||
begin
|
||||
@socket.puts("#{message}#{PACKET_TAIL}")
|
||||
rescue => error
|
||||
@last_socket_error = error
|
||||
@socket_error = true
|
||||
log.e(TAG, error.message)
|
||||
close
|
||||
end
|
||||
end
|
||||
|
||||
def read
|
||||
begin
|
||||
message = @socket.gets
|
||||
rescue => error
|
||||
@last_socket_error = error
|
||||
@socket_error = true
|
||||
|
||||
message = ""
|
||||
end
|
||||
|
||||
|
||||
return message.strip
|
||||
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
|
||||
end
|
||||
97
lib/game_clock/net/connection.rb
Normal file
97
lib/game_clock/net/connection.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
module TAC
|
||||
class PracticeGameClock
|
||||
class ClockNet
|
||||
class Connection
|
||||
TAG = "ClockNet|Connection"
|
||||
attr_reader :hostname, :port, :client, :proxy_object
|
||||
|
||||
def initialize(hostname: "localhost", port: 4567, proxy_object:)
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
@proxy_object = proxy_object
|
||||
|
||||
@client = nil
|
||||
|
||||
@last_sync_time = Gosu.milliseconds
|
||||
@sync_interval = SYNC_INTERVAL
|
||||
|
||||
@last_heartbeat_sent = Gosu.milliseconds
|
||||
@heartbeat_interval = HEARTBEAT_INTERVAL
|
||||
|
||||
@connection_handler = proc do
|
||||
handle_connection
|
||||
end
|
||||
|
||||
@packet_handler = PacketHandler.new(host_is_a_connection: true, proxy_object: @proxy_object)
|
||||
end
|
||||
|
||||
def connect
|
||||
return if @client
|
||||
|
||||
@client = Client.new
|
||||
|
||||
Thread.new do
|
||||
begin
|
||||
@client.socket = Socket.tcp(@hostname, @port, connect_timeout: 5)
|
||||
@client.socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
||||
log.i(TAG, "Connected to: #{@hostname}:#{@port}")
|
||||
|
||||
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
|
||||
log.e(TAG, error)
|
||||
|
||||
if @client
|
||||
@client.close
|
||||
@client.last_socket_error = error
|
||||
@client.socket_error = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_connection
|
||||
if @client && @client.connected?
|
||||
message = @client.gets
|
||||
|
||||
@packet_handler.handle(message) if message
|
||||
|
||||
if Gosu.milliseconds > @last_heartbeat_sent + @heartbeat_interval
|
||||
@last_heartbeat_sent = Gosu.milliseconds
|
||||
|
||||
@client.puts(PacketHandler.packet_heartbeat)
|
||||
end
|
||||
|
||||
sleep @sync_interval / 1000.0
|
||||
end
|
||||
end
|
||||
|
||||
def puts(packet)
|
||||
@client.puts(packet)
|
||||
end
|
||||
|
||||
def gets
|
||||
@client.gets
|
||||
end
|
||||
|
||||
def connected?
|
||||
@client.connected? if @client
|
||||
end
|
||||
|
||||
def closed?
|
||||
@client.closed? if @client
|
||||
end
|
||||
|
||||
def close
|
||||
@client.close if @client
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
109
lib/game_clock/net/packet.rb
Normal file
109
lib/game_clock/net/packet.rb
Normal file
@@ -0,0 +1,109 @@
|
||||
module TAC
|
||||
class PracticeGameClock
|
||||
class ClockNet
|
||||
SYNC_INTERVAL = 250
|
||||
HEARTBEAT_INTERVAL = 1_500
|
||||
|
||||
class Packet
|
||||
PROTOCOL_VERSION = 1
|
||||
PROTOCOL_SEPERATOR = "|"
|
||||
PROTOCOL_HEARTBEAT = "heartbeat"
|
||||
|
||||
PACKET_TYPES = {
|
||||
handshake: 0,
|
||||
heartbeat: 1,
|
||||
error: 2,
|
||||
shutdown: 3,
|
||||
|
||||
start_clock: 10,
|
||||
abort_clock: 11,
|
||||
|
||||
set_clock_title: 20,
|
||||
get_clock_title: 21,
|
||||
clock_title: 22,
|
||||
clock_time: 23,
|
||||
|
||||
randomizer_visible: 27,
|
||||
|
||||
jukebox_previous_track: 30,
|
||||
jukebox_next_track: 31,
|
||||
jukebox_stop: 32,
|
||||
jukebox_play: 33,
|
||||
jukebox_pause: 34,
|
||||
jukebox_set_volume: 35,
|
||||
jukebox_get_volume: 36,
|
||||
jukebox_volume: 37,
|
||||
jukebox_get_current_track: 38,
|
||||
jukebox_current_track: 39,
|
||||
jukebox_get_sound_effects: 40,
|
||||
jukebox_set_sound_effects: 41,
|
||||
jukebox_sound_effects: 42,
|
||||
}
|
||||
|
||||
def self.from_stream(message)
|
||||
slice = message.split("|", 4)
|
||||
|
||||
if slice.size < 4
|
||||
warn "Failed to split packet along first 4 " + PROTOCOL_SEPERATOR + ". Raw return: " + slice.to_s
|
||||
return nil
|
||||
end
|
||||
|
||||
if slice.first != PROTOCOL_VERSION.to_s
|
||||
warn "Incompatible protocol version received, expected: " + PROTOCOL_VERSION.to_s + " got: " + slice.first
|
||||
return nil
|
||||
end
|
||||
|
||||
unless valid_packet_type?(Integer(slice[1]))
|
||||
warn "Unknown packet type detected: #{slice[1]}"
|
||||
return nil
|
||||
end
|
||||
|
||||
protocol_version = Integer(slice[0])
|
||||
type = PACKET_TYPES.key(Integer(slice[1]))
|
||||
content_length = Integer(slice[2])
|
||||
body = slice[3]
|
||||
|
||||
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_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 :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
|
||||
end
|
||||
|
||||
def encode_header
|
||||
string = ""
|
||||
string += protocol_version.to_s
|
||||
string += PROTOCOL_SEPERATOR
|
||||
string += PACKET_TYPES[type].to_s
|
||||
string += PROTOCOL_SEPERATOR
|
||||
string += content_length.to_s
|
||||
string += PROTOCOL_SEPERATOR
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
def valid?
|
||||
true
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#{encode_header}#{body}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
304
lib/game_clock/net/packet_handler.rb
Normal file
304
lib/game_clock/net/packet_handler.rb
Normal file
@@ -0,0 +1,304 @@
|
||||
module TAC
|
||||
class PracticeGameClock
|
||||
class ClockNet
|
||||
class PacketHandler
|
||||
TAG = "ClockNet|PacketHandler"
|
||||
def initialize(host_is_a_connection: false, proxy_object:)
|
||||
@host_is_a_connection = host_is_a_connection
|
||||
@proxy_object = proxy_object
|
||||
end
|
||||
|
||||
def handle(message)
|
||||
packet = Packet.from_stream(message)
|
||||
|
||||
if packet
|
||||
log.i(TAG, "Received packet of type: #{packet.type}")
|
||||
hand_off(packet)
|
||||
else
|
||||
log.d(TAG, "Rejected raw packet: #{message}")
|
||||
end
|
||||
end
|
||||
|
||||
def hand_off(packet)
|
||||
case packet.type
|
||||
when :handshake
|
||||
handle_handshake(packet)
|
||||
when :heartbeat
|
||||
handle_heartbeat(packet)
|
||||
when :error
|
||||
handle_error(packet)
|
||||
|
||||
when :start_clock
|
||||
handle_start_clock(packet)
|
||||
when :abort_clock
|
||||
handle_abort_clock(packet)
|
||||
when :get_clock_title
|
||||
handle_get_clock_title(packet)
|
||||
when :set_clock_title
|
||||
handle_set_clock_title(packet)
|
||||
when :clock_title
|
||||
handle_clock_title(packet)
|
||||
when :jukebox_previous_track
|
||||
handle_jukebox_previous_track(packet)
|
||||
when :jukebox_next_track
|
||||
handle_jukebox_next_track(packet)
|
||||
when :jukebox_play
|
||||
handle_jukebox_play(packet)
|
||||
when :jukebox_pause
|
||||
handle_jukebox_pause(packet)
|
||||
when :jukebox_stop
|
||||
handle_jukebox_stop(packet)
|
||||
when :jukebox_set_volume
|
||||
handle_jukebox_set_volume(packet)
|
||||
when :jukebox_volume
|
||||
handle_jukebox_volume(packet)
|
||||
when :jukebox_set_sound_effects
|
||||
handle_jukebox_set_sound_effects(packet)
|
||||
when :jukebox_current_track
|
||||
handle_jukebox_current_track(packet)
|
||||
when :clock_time
|
||||
handle_clock_time(packet)
|
||||
when :randomizer_visible
|
||||
handle_randomizer_visible(packet)
|
||||
when :shutdown
|
||||
handle_shutdown(packet)
|
||||
else
|
||||
log.d(TAG, "No hand off available for packet type: #{packet.type}")
|
||||
end
|
||||
end
|
||||
|
||||
def handle_handshake(packet)
|
||||
if @host_is_a_connection
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Reset socket timeout
|
||||
def handle_heartbeat(packet)
|
||||
end
|
||||
|
||||
# TODO: Handle errors
|
||||
def handle_error(packet)
|
||||
title, message = packet.body.split(Packet::PROTOCOL_SEPERATOR, 2)
|
||||
log.e(TAG, "Remote error: #{title}: #{message}")
|
||||
end
|
||||
|
||||
def handle_start_clock(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.start_clock(packet.body.to_sym)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_abort_clock(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.abort_clock
|
||||
end
|
||||
end
|
||||
|
||||
def handle_set_clock_title(packet)
|
||||
unless @host_is_a_connection
|
||||
title = packet.body
|
||||
@proxy_object.set_clock_title(title)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_get_clock_title(packet)
|
||||
unless @host_is_a_connection
|
||||
$RemoteControl.server.active_client.puts(Packet.clock_title(@proxy_object.clock.title))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_previous_track(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.jukebox_previous_track
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_current_track(@proxy_object.jukebox_current_track))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_next_track(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.jukebox_next_track
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_current_track(@proxy_object.jukebox_current_track))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_play(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.jukebox_play
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_current_track(@proxy_object.jukebox_current_track))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_pause(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.jukebox_pause
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_current_track(@proxy_object.jukebox_current_track))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_stop(packet)
|
||||
unless @host_is_a_connection
|
||||
@proxy_object.jukebox_stop
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_current_track(@proxy_object.jukebox_current_track))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_set_volume(packet)
|
||||
unless @host_is_a_connection
|
||||
float = packet.body.to_f
|
||||
float = float.clamp(0.0, 1.0)
|
||||
|
||||
@proxy_object.jukebox_set_volume(float)
|
||||
|
||||
float = @proxy_object.jukebox_volume
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_volume(float))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_get_volume(packet)
|
||||
unless @host_is_a_connection
|
||||
float = @proxy_object.jukebox_volume
|
||||
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_jukebox_volume(float))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_volume(packet)
|
||||
if @host_is_a_connection
|
||||
float = packet.body.to_f
|
||||
|
||||
@proxy_object.volume_changed(float)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_set_sound_effects(packet)
|
||||
unless @host_is_a_connection
|
||||
boolean = packet.body == "true"
|
||||
|
||||
@proxy_object.jukebox_set_sound_effects(boolean)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_jukebox_current_track(packet)
|
||||
if @host_is_a_connection
|
||||
@proxy_object.track_changed(packet.body)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_clock_time(packet)
|
||||
if @host_is_a_connection
|
||||
@proxy_object.clock_changed(packet.body)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_randomizer_visible(packet)
|
||||
boolean = packet.body == "true"
|
||||
|
||||
@proxy_object.randomizer_changed(boolean)
|
||||
|
||||
unless @host_is_a_connection
|
||||
# Send confirmation to client
|
||||
$RemoteControl.server.active_client.puts(PacketHandler.packet_randomizer_visible(boolean))
|
||||
end
|
||||
end
|
||||
|
||||
def handle_shutdown(packet)
|
||||
unless @host_is_a_connection
|
||||
# $RemoteControl.server.close
|
||||
# $window.close
|
||||
Gosu::Song.current_song&.stop
|
||||
exit
|
||||
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_HEARTBEAT)
|
||||
end
|
||||
|
||||
def self.packet_error(error_code, message)
|
||||
Packet.create(Packet::PACKET_TYPES[:error], error_code.to_s, message.to_s)
|
||||
end
|
||||
|
||||
def self.packet_start_clock(mode)
|
||||
Packet.create(Packet::PACKET_TYPES[:start_clock], mode.to_s)
|
||||
end
|
||||
|
||||
def self.packet_abort_clock
|
||||
Packet.create(Packet::PACKET_TYPES[:abort_clock], "")
|
||||
end
|
||||
|
||||
def self.packet_set_clock_title(string)
|
||||
Packet.create(Packet::PACKET_TYPES[:set_clock_title], string.to_s)
|
||||
end
|
||||
|
||||
def self.packet_get_clock_title
|
||||
Packet.create(Packet::PACKET_TYPES[:get_clock_title], "")
|
||||
end
|
||||
|
||||
def self.packet_clock_title(string)
|
||||
Packet.create(Packet::PACKET_TYPES[:clock_title], string.to_s)
|
||||
end
|
||||
|
||||
def self.packet_jukebox_previous_track
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_previous_track], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_next_track
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_next_track], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_play
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_play], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_pause
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_pause], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_stop
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_stop], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_set_volume(float)
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_set_volume], float.to_s)
|
||||
end
|
||||
|
||||
def self.packet_jukebox_get_volume
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_get_volume], "")
|
||||
end
|
||||
|
||||
def self.packet_jukebox_volume(float)
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_volume], float.to_s)
|
||||
end
|
||||
|
||||
def self.packet_jukebox_set_sound_effects(boolean)
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_set_sound_effects], boolean.to_s)
|
||||
end
|
||||
|
||||
def self.packet_jukebox_current_track(name)
|
||||
Packet.create(Packet::PACKET_TYPES[:jukebox_current_track], name)
|
||||
end
|
||||
|
||||
def self.packet_clock_time(string)
|
||||
Packet.create(Packet::PACKET_TYPES[:clock_time], string)
|
||||
end
|
||||
|
||||
def self.packet_randomizer_visible(boolean)
|
||||
Packet.create(Packet::PACKET_TYPES[:randomizer_visible], boolean.to_s)
|
||||
end
|
||||
|
||||
def self.packet_shutdown
|
||||
Packet.create(Packet::PACKET_TYPES[:shutdown], "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
146
lib/game_clock/net/server.rb
Normal file
146
lib/game_clock/net/server.rb
Normal file
@@ -0,0 +1,146 @@
|
||||
module TAC
|
||||
class PracticeGameClock
|
||||
class ClockNet
|
||||
class Server
|
||||
TAG = "ClockNet|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(hostname: "localhost", port: 4567, proxy_object: )
|
||||
$server = self
|
||||
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
@proxy_object = proxy_object
|
||||
|
||||
@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 = Gosu.milliseconds
|
||||
@sync_interval = SYNC_INTERVAL
|
||||
|
||||
@last_heartbeat_sent = Gosu.milliseconds
|
||||
@heartbeat_interval = HEARTBEAT_INTERVAL
|
||||
|
||||
@client_handler_proc = proc do
|
||||
handle_client
|
||||
end
|
||||
|
||||
@packet_handler = PacketHandler.new(proxy_object: @proxy_object)
|
||||
end
|
||||
|
||||
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)
|
||||
@socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
||||
rescue IOError => error
|
||||
log.e(TAG, error)
|
||||
|
||||
@connection_attempts += 1
|
||||
retry if @connection_attempts < @max_connection_attempts
|
||||
end
|
||||
end
|
||||
|
||||
while @socket && !@socket.closed?
|
||||
begin
|
||||
run_server
|
||||
rescue IOError => error
|
||||
log.e(TAG, error)
|
||||
@socket.close if @socket
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
thread.join if run_on_main_thread
|
||||
end
|
||||
|
||||
def run_server
|
||||
while !@socket.closed?
|
||||
client = Client.new
|
||||
client.sync_interval = @sync_interval
|
||||
client.socket = @socket.accept
|
||||
|
||||
if @active_client && @active_client.connected?
|
||||
log.i(TAG, "Too many clients, already have one connected!")
|
||||
client.close("Too many clients!")
|
||||
else
|
||||
@active_client = client
|
||||
# TODO: Backup local config
|
||||
# SEND CONFIG
|
||||
|
||||
@active_client.puts(PacketHandler.packet_handshake(@active_client.uuid))
|
||||
|
||||
log.i(TAG, "Client connected!")
|
||||
|
||||
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
|
||||
|
||||
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_heartbeat)
|
||||
end
|
||||
|
||||
sleep @sync_interval / 1000.0
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
@socket.close
|
||||
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
|
||||
end
|
||||
Reference in New Issue
Block a user